2005-12-11 20:11:22 +00:00
//Released under the terms of the gpl as this file uses a bit of quake derived code. All sections of the like are marked as such
# include "../plugin.h"
2013-03-31 04:21:08 +00:00
# include <time.h>
2013-06-23 02:33:52 +00:00
# include "../../engine/common/netinc.h"
# include "xml.h"
# define NOICE
# define QUAKEMEDIATYPE "quake"
# define QUAKEMEDIAXMLNS "fteqw.com:netmedia"
struct icestate_s * ( QDECL * pICE_Create ) ( void * module , char * conname , char * peername , enum icemode_e mode ) ; //doesn't start pinging anything.
struct icestate_s * ( QDECL * pICE_Find ) ( void * module , char * conname ) ;
void ( QDECL * pICE_Begin ) ( struct icestate_s * con , char * stunip , int stunport ) ; //begins sending stun packets and stuff as required.
struct icecandidate_s * ( QDECL * pICE_GetLCandidateInfo ) ( struct icestate_s * con ) ; //stuff that needs reporting to the peer.
void ( QDECL * pICE_AddRCandidateInfo ) ( struct icestate_s * con , struct icecandidate_s * cand ) ; //stuff that came from the peer.
void ( QDECL * pICE_Close ) ( struct icestate_s * con ) ; //bye then.
2005-12-11 20:11:22 +00:00
# define Q_strncpyz(o, i, l) do {strncpy(o, i, l-1);o[l-1]='\0';}while(0)
2013-06-23 02:33:52 +00:00
# define JCL_BUILD "3"
2005-12-11 20:11:22 +00:00
2013-03-31 04:21:08 +00:00
# define ARGNAMES ,sock,certhostname
BUILTINR ( int , Net_SetTLSClient , ( qhandle_t sock , const char * certhostname ) ) ;
2005-12-11 20:11:22 +00:00
# undef ARGNAMES
2013-06-23 02:33:52 +00:00
# define ARGNAMES ,funcname
BUILTINR ( void * , Plug_GetNativePointer , ( const char * funcname ) ) ;
# undef ARGNAMES
2013-03-31 04:21:08 +00:00
void ( * Con_TrySubPrint ) ( const char * conname , const char * message ) ;
void Fallback_ConPrint ( const char * conname , const char * message )
{
2013-06-23 18:43:59 +00:00
pCon_Print ( message ) ;
2013-03-31 04:21:08 +00:00
}
2005-12-11 20:11:22 +00:00
void Con_SubPrintf ( char * subname , char * format , . . . )
{
va_list argptr ;
static char string [ 1024 ] ;
va_start ( argptr , format ) ;
2013-05-11 14:02:55 +00:00
Q_vsnprintf ( string , sizeof ( string ) , format , argptr ) ;
2005-12-11 20:11:22 +00:00
va_end ( argptr ) ;
2013-03-31 04:21:08 +00:00
Con_TrySubPrint ( subname , string ) ;
2005-12-11 20:11:22 +00:00
}
//porting zone:
# define COLOURGREEN "^2"
# define COLORWHITE "^7"
# define COLOURWHITE "^7" // word
# define COLOURRED "^1"
# define COLOURYELLOW "^3"
# define COLOURPURPLE "^5"
2013-06-23 02:33:52 +00:00
# define COMMANDPREFIX "xmpp"
# define COMMANDPREFIX2 "jab"
# define COMMANDPREFIX3 "jabbercl"
2005-12-11 20:11:22 +00:00
# define playsound(s)
# define TL_NETGETPACKETERROR "NET_GetPacket Error %s\n"
2013-06-23 18:43:59 +00:00
static char * JCL_ParseOut ( char * data , char * buf , int bufsize ) //this is taken out of quake
2005-12-11 20:11:22 +00:00
{
int c ;
int len ;
len = 0 ;
2013-06-23 02:33:52 +00:00
buf [ 0 ] = 0 ;
2005-12-11 20:11:22 +00:00
if ( ! data )
return NULL ;
// skip whitespace
while ( ( c = * data ) < = ' ' )
{
if ( c = = 0 )
return NULL ; // end of file;
data + + ;
}
// handle quoted strings specially
if ( c = = ' \" ' )
{
data + + ;
while ( 1 )
{
2013-06-23 02:33:52 +00:00
if ( len > = bufsize - 1 )
2005-12-11 20:11:22 +00:00
return data ;
c = * data + + ;
if ( c = = ' \" ' | | ! c )
{
2013-06-23 02:33:52 +00:00
buf [ len ] = 0 ;
2005-12-11 20:11:22 +00:00
return data ;
}
2013-06-23 02:33:52 +00:00
buf [ len ] = c ;
2005-12-11 20:11:22 +00:00
len + + ;
}
}
// parse a regular word
do
{
2013-06-23 02:33:52 +00:00
if ( len > = bufsize - 1 )
2005-12-11 20:11:22 +00:00
return data ;
2013-06-23 02:33:52 +00:00
buf [ len ] = c ;
2005-12-11 20:11:22 +00:00
data + + ;
len + + ;
c = * data ;
} while ( c > 32 ) ;
2013-06-23 02:33:52 +00:00
buf [ len ] = 0 ;
2005-12-11 20:11:22 +00:00
return data ;
}
2013-06-23 02:33:52 +00:00
void RenameConsole ( char * totrim ) ;
void JCL_Command ( char * consolename ) ;
void JCL_LoadConfig ( void ) ;
void JCL_WriteConfig ( void ) ;
2005-12-11 20:11:22 +00:00
2013-03-31 04:21:08 +00:00
qintptr_t JCL_ExecuteCommand ( qintptr_t * args )
2005-12-11 20:11:22 +00:00
{
2007-09-17 20:55:15 +00:00
char cmd [ 256 ] ;
2013-06-23 18:43:59 +00:00
pCmd_Argv ( 0 , cmd , sizeof ( cmd ) ) ;
2013-06-23 02:33:52 +00:00
if ( ! strcmp ( cmd , COMMANDPREFIX ) | | ! strcmp ( cmd , COMMANDPREFIX2 ) | | ! strcmp ( cmd , COMMANDPREFIX3 ) )
2005-12-11 20:11:22 +00:00
{
2013-06-23 02:33:52 +00:00
if ( ! args [ 0 ] )
JCL_Command ( " " ) ;
2005-12-11 20:11:22 +00:00
return true ;
}
return false ;
}
2013-06-23 02:33:52 +00:00
qintptr_t JCL_ConsoleLink ( qintptr_t * args ) ;
2013-03-31 04:21:08 +00:00
qintptr_t JCL_ConExecuteCommand ( qintptr_t * args ) ;
2005-12-11 20:11:22 +00:00
2013-03-31 04:21:08 +00:00
qintptr_t JCL_Frame ( qintptr_t * args ) ;
2005-12-11 20:11:22 +00:00
2013-03-31 04:21:08 +00:00
qintptr_t Plug_Init ( qintptr_t * args )
2005-12-11 20:11:22 +00:00
{
if ( Plug_Export ( " Tick " , JCL_Frame ) & &
2005-12-15 02:01:57 +00:00
Plug_Export ( " ExecuteCommand " , JCL_ExecuteCommand ) )
2005-12-11 20:11:22 +00:00
{
CHECKBUILTIN ( Net_SetTLSClient ) ;
if ( ! BUILTINISVALID ( Net_SetTLSClient ) )
2013-06-23 18:43:59 +00:00
Con_Printf ( " XMPP Plugin Loaded ^1without^7 TLS \n " ) ;
2005-12-15 02:01:57 +00:00
else
2013-06-23 18:43:59 +00:00
Con_Printf ( " XMPP Plugin Loaded. For help, use: ^[/ " COMMANDPREFIX " /help^] \n " ) ;
2013-06-23 02:33:52 +00:00
Plug_Export ( " ConsoleLink " , JCL_ConsoleLink ) ;
2005-12-15 02:01:57 +00:00
if ( ! Plug_Export ( " ConExecuteCommand " , JCL_ConExecuteCommand ) )
{
2013-06-23 02:33:52 +00:00
Con_Printf ( " XMPP plugin in single-console mode \n " ) ;
2013-03-31 04:21:08 +00:00
Con_TrySubPrint = Fallback_ConPrint ;
2005-12-15 02:01:57 +00:00
}
2005-12-11 20:11:22 +00:00
else
2013-06-23 18:43:59 +00:00
Con_TrySubPrint = pCon_SubPrint ;
2005-12-15 02:01:57 +00:00
2013-06-23 18:43:59 +00:00
pCmd_AddCommand ( COMMANDPREFIX ) ;
pCmd_AddCommand ( COMMANDPREFIX2 ) ;
pCmd_AddCommand ( COMMANDPREFIX3 ) ;
2013-06-23 02:33:52 +00:00
CHECKBUILTIN ( Plug_GetNativePointer ) ;
if ( BUILTINISVALID ( Plug_GetNativePointer ) )
{
2013-06-23 18:43:59 +00:00
pICE_Create = pPlug_GetNativePointer ( " ICE_Create " ) ;
pICE_Find = pPlug_GetNativePointer ( " ICE_Find " ) ;
pICE_Begin = pPlug_GetNativePointer ( " ICE_Begin " ) ;
pICE_GetLCandidateInfo = pPlug_GetNativePointer ( " ICE_GetLCandidateInfo " ) ;
pICE_AddRCandidateInfo = pPlug_GetNativePointer ( " ICE_AddRCandidateInfo " ) ;
pICE_Close = pPlug_GetNativePointer ( " ICE_Close " ) ;
2013-06-23 02:33:52 +00:00
}
JCL_LoadConfig ( ) ;
2005-12-11 20:11:22 +00:00
return 1 ;
}
else
2013-06-23 18:43:59 +00:00
Con_Printf ( " JCL Client Plugin failed \n " ) ;
2005-12-11 20:11:22 +00:00
return 0 ;
}
//\r\n is used to end a line.
//meaning \0s are valid.
//but never used cos it breaks strings
2013-03-31 04:21:08 +00:00
# define JCL_MAXMSGLEN 10000
2005-12-11 20:11:22 +00:00
2013-06-23 02:33:52 +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
struct bresource_s * next ;
char resource [ 1 ] ;
} bresource_t ;
typedef struct buddy_s
{
bresource_t * resources ;
bresource_t * defaultresource ; //this is the one that last replied
int defaulttimestamp ;
qboolean friended ;
char name [ 256 ] ;
2005-12-11 20:11:22 +00:00
2013-06-23 02:33:52 +00:00
struct buddy_s * next ;
char accountdomain [ 1 ] ; //no resource on there
} buddy_t ;
typedef struct jclient_s
{
2005-12-11 20:11:22 +00:00
char server [ 64 ] ;
int port ;
qhandle_t socket ;
2013-06-23 02:33:52 +00:00
char * outbuf ;
int outbufpos ;
int outbuflen ;
int outbufmax ;
2013-03-31 04:21:08 +00:00
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.
2005-12-11 20:11:22 +00:00
int bufferedinammount ;
char defaultdest [ 256 ] ;
char domain [ 256 ] ;
char username [ 256 ] ;
char password [ 256 ] ;
char resource [ 256 ] ;
2013-06-23 02:33:52 +00:00
char jid [ 256 ] ; //this is our full username@domain/resource string
2005-12-11 20:11:22 +00:00
int tagdepth ;
int openbracket ;
int instreampos ;
2013-06-23 02:33:52 +00:00
qboolean tlsconnect ; //the old tls method on port 5223.
2013-03-31 04:21:08 +00:00
qboolean connected ; //fully on server and authed and everything.
qboolean issecure ; //tls enabled
2013-06-23 02:33:52 +00:00
qboolean streamdebug ; //echo the stream to subconsoles
qboolean preapproval ;
2013-03-31 04:21:08 +00:00
char curquakeserver [ 2048 ] ;
char defaultnamespace [ 2048 ] ; //should be 'jabber:client' or blank (and spammy with all the extra xmlns attribs)
2013-06-23 02:33:52 +00:00
struct iq_s {
struct iq_s * next ;
char id [ 64 ] ;
int timeout ;
qboolean ( * callback ) ( struct jclient_s * jcl , struct subtree_s * tree ) ;
} * pendingiqs ;
struct c2c_s
{
struct c2c_s * next ;
char * sessionname ;
buddy_t * tob ;
bresource_t * tor ;
2013-06-23 18:43:59 +00:00
} * p2p ;
2013-06-23 02:33:52 +00:00
buddy_t * buddies ;
2005-12-11 20:11:22 +00:00
} jclient_t ;
jclient_t * jclient ;
2013-06-23 02:33:52 +00:00
struct subtree_s ;
void JCL_AddClientMessagef ( jclient_t * jcl , char * fmt , . . . ) ;
2013-06-23 18:43:59 +00:00
qboolean JCL_FindBuddy ( jclient_t * jcl , char * jid , buddy_t * * buddy , bresource_t * * bres ) ;
2013-06-23 02:33:52 +00:00
void JCL_GeneratePresence ( qboolean force ) ;
void JCL_SendIQf ( jclient_t * jcl , qboolean ( * callback ) ( jclient_t * jcl , struct subtree_s * tree ) , char * iqtype , char * target , char * fmt , . . . ) ;
void JCL_SendIQNode ( jclient_t * jcl , qboolean ( * callback ) ( jclient_t * jcl , xmltree_t * tree ) , char * iqtype , char * target , xmltree_t * node , qboolean destroynode ) ;
void JCL_Join ( jclient_t * jcl , char * target )
{
xmltree_t * jingle ;
struct icestate_s * ice ;
if ( ! jcl )
return ;
# ifdef NOICE
ice = pICE_Create ( NULL , NULL , target , ICE_RAW ) ;
# else
ice = pICE_Create ( NULL , NULL , target , ICE_ICE ) ;
# endif
if ( ! ice )
{
Con_Printf ( " Unable to connect to %s (dedicated servers cannot initiate connections) \n " , target ) ;
return ;
}
//FIXME: record the session name
jingle = XML_CreateNode ( NULL , " jingle " , " urn:xmpp:jingle:1 " , " " ) ;
XML_AddParameter ( jingle , " sid " , ice - > conname ) ;
XML_AddParameter ( jingle , " responder " , target ) ;
XML_AddParameter ( jingle , " initiator " , jcl - > jid ) ;
XML_AddParameter ( jingle , " action " , " session-initiate " ) ;
{
xmltree_t * content = XML_CreateNode ( jingle , " content " , " " , " " ) ;
XML_AddParameter ( content , " senders " , " both " ) ;
XML_AddParameter ( content , " name " , " some-old-quake-game " ) ;
XML_AddParameter ( content , " creator " , " initiator " ) ;
{
xmltree_t * description ;
xmltree_t * transport ;
if ( ice - > mode = = ICE_RAW )
{
transport = XML_CreateNode ( content , " transport " , " urn:xmpp:jingle:transports:raw-udp:1 " , " " ) ;
{
xmltree_t * candidate ;
struct icecandidate_s * b = NULL ;
struct icecandidate_s * c ;
while ( ( c = pICE_GetLCandidateInfo ( ice ) ) )
{
if ( ! b | | b - > priority < c - > priority )
b = c ;
}
if ( b )
{
candidate = XML_CreateNode ( transport , " candidate " , " " , " " ) ;
XML_AddParameter ( candidate , " ip " , b - > addr ) ;
XML_AddParameteri ( candidate , " port " , b - > port ) ;
XML_AddParameter ( candidate , " id " , b - > candidateid ) ;
XML_AddParameteri ( candidate , " generation " , b - > generation ) ;
XML_AddParameteri ( candidate , " component " , b - > component ) ;
}
}
}
else if ( ice - > mode = = ICE_ICE )
{
transport = XML_CreateNode ( content , " transport " , " urn:xmpp:jingle:transports:ice-udp:1 " , " " ) ;
XML_AddParameter ( transport , " ufrag " , ice - > lfrag ) ;
XML_AddParameter ( transport , " pwd " , ice - > lpwd ) ;
{
struct icecandidate_s * c ;
while ( ( c = pICE_GetLCandidateInfo ( ice ) ) )
{
char * ctypename [ ] = { " host " , " srflx " , " prflx " , " relay " } ;
xmltree_t * candidate = XML_CreateNode ( transport , " candidate " , " " , " " ) ;
XML_AddParameter ( candidate , " type " , ctypename [ c - > type ] ) ;
XML_AddParameter ( candidate , " protocol " , " udp " ) ; //is this not just a little bit redundant? ice-udp? seriously?
XML_AddParameteri ( candidate , " priority " , c - > priority ) ;
XML_AddParameteri ( candidate , " port " , c - > port ) ;
XML_AddParameteri ( candidate , " network " , c - > network ) ;
XML_AddParameter ( candidate , " ip " , c - > addr ) ;
XML_AddParameter ( candidate , " id " , c - > candidateid ) ;
XML_AddParameteri ( candidate , " generation " , c - > generation ) ;
XML_AddParameteri ( candidate , " foundation " , c - > foundation ) ;
XML_AddParameteri ( candidate , " component " , c - > component ) ;
}
}
}
description = XML_CreateNode ( content , " description " , QUAKEMEDIAXMLNS , " " ) ;
XML_AddParameter ( description , " media " , QUAKEMEDIATYPE ) ;
{
/*
xmltree_t * candidate = XML_CreateNode ( description , " payload-type " , " " , " " ) ;
XML_AddParameter ( candidate , " channels " , " 1 " ) ;
XML_AddParameter ( candidate , " clockrate " , " 8000 " ) ;
XML_AddParameter ( candidate , " id " , " 104 " ) ;
XML_AddParameter ( candidate , " name " , " SPEEX " ) ;
*/
}
}
}
Con_Printf ( " Sending connection start: \n " ) ;
XML_ConPrintTree ( jingle , 0 ) ;
JCL_SendIQNode ( jcl , NULL , " set " , target , jingle , true ) ;
}
void JCL_JingleParsePeerPorts ( jclient_t * jcl , xmltree_t * inj , char * from )
{
xmltree_t * incontent = XML_ChildOfTree ( inj , " content " , 0 ) ;
xmltree_t * intransport = XML_ChildOfTree ( incontent , " transport " , 0 ) ;
xmltree_t * incandidate ;
struct icestate_s * ice ;
struct icecandidate_s rem ;
int i ;
ice = pICE_Find ( NULL , XML_GetParameter ( inj , " sid " , " " ) ) ;
if ( ice & & strcmp ( ice - > friendlyname , from ) )
{
Con_Printf ( " %s is trying to mess with our connections... \n " , from ) ;
return ;
}
2013-06-23 18:43:59 +00:00
for ( i = 0 ; ( incandidate = XML_ChildOfTree ( intransport , " candidate " , i ) ) ; i + + )
2013-06-23 02:33:52 +00:00
{
char * s ;
memset ( & rem , 0 , sizeof ( rem ) ) ;
rem . addr = XML_GetParameter ( incandidate , " ip " , " " ) ;
rem . candidateid = XML_GetParameter ( incandidate , " id " , " " ) ;
s = XML_GetParameter ( incandidate , " type " , " " ) ;
if ( s & & ! strcmp ( s , " srflx " ) )
rem . type = ICE_SRFLX ;
else if ( s & & ! strcmp ( s , " prflx " ) )
rem . type = ICE_PRFLX ;
else if ( s & & ! strcmp ( s , " relay " ) )
rem . type = ICE_RELAY ;
else
rem . type = ICE_HOST ;
rem . port = atoi ( XML_GetParameter ( incandidate , " port " , " 0 " ) ) ;
rem . priority = atoi ( XML_GetParameter ( incandidate , " priority " , " 0 " ) ) ;
rem . network = atoi ( XML_GetParameter ( incandidate , " network " , " 0 " ) ) ;
rem . generation = atoi ( XML_GetParameter ( incandidate , " generation " , " 0 " ) ) ;
rem . foundation = atoi ( XML_GetParameter ( incandidate , " foundation " , " 0 " ) ) ;
rem . component = atoi ( XML_GetParameter ( incandidate , " component " , " 0 " ) ) ;
s = XML_GetParameter ( incandidate , " protocol " , " udp " ) ;
if ( s & & ! strcmp ( s , " udp " ) )
rem . transport = 0 ;
else
rem . transport = 0 ;
pICE_AddRCandidateInfo ( ice , & rem ) ;
}
}
struct icestate_s * JCL_JingleHandleInitiate ( jclient_t * jcl , xmltree_t * inj , char * from )
{
/*inj contains something like:
< jingle sid = ' purplea84196dc ' responder = ' me @ example . com / Quake ' initiator = ' them @ example . com / Quake ' action = ' session - initiate ' xmlns = ' urn : xmpp : jingle : 1 ' >
< content senders = ' both ' name = ' audio - session ' creator = ' initiator ' >
< transport ufrag = ' SES2 ' pwd = ' 3 XkwverVxJLy2lRXD1lOAb ' xmlns = ' urn : xmpp : jingle : transports : ice - udp : 1 ' >
< candidate type = ' host ' protocol = ' udp ' priority = ' 2013266431 ' port = ' 53177 ' network = ' 0 ' ip = ' 192.168 .0 .182 ' id = ' purplea84196de ' generation = ' 0 ' foundation = ' 1 ' component = ' 1 ' / >
< candidate type = ' host ' protocol = ' udp ' priority = ' 2013266430 ' port = ' 36480 ' network = ' 0 ' ip = ' 192.168 .0 .182 ' id = ' purplea84196dd ' generation = ' 0 ' foundation = ' 1 ' component = ' 2 ' / >
< / transport >
< description media = ' audio ' xmlns = ' urn : xmpp : jingle : apps : rtp : 1 ' >
< payload - type channels = ' 1 ' clockrate = ' 8000 ' id = ' 104 ' name = ' SPEEX ' / >
< payload - type channels = ' 1 ' clockrate = ' 16000 ' id = ' 103 ' name = ' SPEEX ' / >
< / description >
< / content >
< / jingle >
*/
xmltree_t * incontent = XML_ChildOfTree ( inj , " content " , 0 ) ;
xmltree_t * intransport = XML_ChildOfTree ( incontent , " transport " , 0 ) ;
xmltree_t * indescription = XML_ChildOfTree ( incontent , " description " , 0 ) ;
char * transportxmlns = intransport ? intransport - > xmlns : " " ;
char * descriptionxmlns = indescription ? indescription - > xmlns : " " ;
char * descriptionmedia = XML_GetParameter ( indescription , " media " , " " ) ;
xmltree_t * jingle ;
struct icestate_s * ice ;
qboolean accepted = false ;
enum icemode_e imode ;
imode = strcmp ( transportxmlns , " urn:xmpp:jingle:transports:raw-udp:1 " ) ? ICE_ICE : ICE_RAW ;
if ( ! incontent | | strcmp ( descriptionmedia , QUAKEMEDIATYPE ) | | strcmp ( descriptionxmlns , QUAKEMEDIAXMLNS ) )
{
//decline it
ice = NULL ;
}
else
ice = pICE_Create ( NULL , XML_GetParameter ( inj , " sid " , " " ) , from , imode ) ;
jingle = XML_CreateNode ( NULL , " jingle " , " urn:xmpp:jingle:1 " , " " ) ;
XML_AddParameter ( jingle , " sid " , ice - > conname ) ;
XML_AddParameter ( jingle , " responder " , XML_GetParameter ( inj , " responder " , " " ) ) ;
XML_AddParameter ( jingle , " initiator " , XML_GetParameter ( inj , " initiator " , " " ) ) ;
if ( ! ice )
XML_AddParameter ( jingle , " action " , " session-terminate " ) ;
else
{
xmltree_t * content = XML_CreateNode ( jingle , " content " , " " , " " ) ;
# ifdef NOICE
if ( imode ! = ICE_RAW )
{
char buf [ 256 ] ;
XML_AddParameter ( jingle , " action " , " transport-replace " ) ;
Q_snprintf ( buf , sizeof ( buf ) , " raw-%s " , XML_GetParameter ( incontent , " name " , " " ) ) ;
XML_AddParameter ( content , " name " , buf ) ;
}
else
# endif
{
XML_AddParameter ( jingle , " action " , " session-accept " ) ;
XML_AddParameter ( content , " name " , XML_GetParameter ( incontent , " name " , " " ) ) ;
accepted = true ;
}
XML_AddParameter ( content , " senders " , " both " ) ;
XML_AddParameter ( content , " creator " , " initiator " ) ;
{
xmltree_t * description ;
xmltree_t * transport ;
transport = XML_CreateNode ( content , " transport " , transportxmlns , " " ) ;
if ( imode = = ICE_RAW )
{
//raw-udp can send only one candidate
xmltree_t * candidate ;
struct icecandidate_s * b = NULL ;
struct icecandidate_s * c ;
while ( ( c = pICE_GetLCandidateInfo ( ice ) ) )
{
if ( ! b | | b - > priority < c - > priority )
b = c ;
}
if ( b )
{
candidate = XML_CreateNode ( transport , " candidate " , " " , " " ) ;
XML_AddParameter ( candidate , " ip " , b - > addr ) ;
XML_AddParameteri ( candidate , " port " , b - > port ) ;
XML_AddParameter ( candidate , " id " , b - > candidateid ) ;
XML_AddParameteri ( candidate , " generation " , b - > generation ) ;
XML_AddParameteri ( candidate , " component " , b - > component ) ;
}
}
else if ( imode = = ICE_ICE )
{
//ice can send multiple candidates
struct icecandidate_s * c ;
XML_AddParameter ( transport , " ufrag " , ice - > lfrag ) ;
XML_AddParameter ( transport , " pwd " , ice - > lpwd ) ;
while ( ( c = pICE_GetLCandidateInfo ( ice ) ) )
{
char * ctypename [ ] = { " host " , " srflx " , " prflx " , " relay " } ;
xmltree_t * candidate = XML_CreateNode ( transport , " candidate " , " " , " " ) ;
XML_AddParameter ( candidate , " type " , ctypename [ c - > type ] ) ;
XML_AddParameter ( candidate , " protocol " , " udp " ) ; //is this not just a little bit redundant? ice-udp? seriously?
XML_AddParameteri ( candidate , " priority " , c - > priority ) ;
XML_AddParameteri ( candidate , " port " , c - > port ) ;
XML_AddParameteri ( candidate , " network " , c - > network ) ;
XML_AddParameter ( candidate , " ip " , c - > addr ) ;
XML_AddParameter ( candidate , " id " , c - > candidateid ) ;
XML_AddParameteri ( candidate , " generation " , c - > generation ) ;
XML_AddParameteri ( candidate , " foundation " , c - > foundation ) ;
XML_AddParameteri ( candidate , " component " , c - > component ) ;
}
}
else
{
//egads! can't cope with that.
}
description = XML_CreateNode ( content , " description " , QUAKEMEDIAXMLNS , " " ) ;
XML_AddParameter ( description , " media " , QUAKEMEDIATYPE ) ;
{
/*
xmltree_t * candidate = XML_CreateNode ( description , " payload-type " , " " , " " ) ;
XML_AddParameter ( candidate , " channels " , " 1 " ) ;
XML_AddParameter ( candidate , " clockrate " , " 8000 " ) ;
XML_AddParameter ( candidate , " id " , " 104 " ) ;
XML_AddParameter ( candidate , " name " , " SPEEX " ) ;
*/
}
}
}
if ( ! ice )
Con_Printf ( " Sending reject: \n " ) ;
else
Con_Printf ( " Sending accept: \n " ) ;
XML_ConPrintTree ( jingle , 0 ) ;
JCL_SendIQNode ( jcl , NULL , " set " , from , jingle , true ) ;
if ( ice )
JCL_JingleParsePeerPorts ( jcl , inj , from ) ;
//if we didn't error out, the ICE stuff is meant to start sending handshakes/media as soon as the connection is accepted
if ( ice & & accepted )
pICE_Begin ( ice , NULL , 0 ) ;
return ice ;
}
void JCL_ParseJingle ( jclient_t * jcl , xmltree_t * tree , char * from , char * id )
{
char * action = XML_GetParameter ( tree , " action " , " " ) ;
2013-06-23 18:43:59 +00:00
// char *initiator = XML_GetParameter(tree, "initiator", "");
// char *responder = XML_GetParameter(tree, "responder", "");
2013-06-23 02:33:52 +00:00
char * sid = XML_GetParameter ( tree , " sid " , " " ) ;
//validate sender
struct icestate_s * ice = pICE_Find ( NULL , sid ) ;
if ( ice & & strcmp ( ice - > friendlyname , from ) )
{
Con_Printf ( " %s is trying to mess with our connections... \n " , from ) ;
return ;
}
if ( ! strcmp ( action , " session-terminate " ) )
{
if ( ice )
pICE_Close ( ice ) ;
Con_Printf ( " Session ended \n " ) ;
XML_ConPrintTree ( tree , 0 ) ;
}
else if ( ! strcmp ( action , " session-accept " ) )
{
if ( ! ice )
{
Con_Printf ( " Cannot accept a session that was never created \n " ) ;
}
else
{
Con_Printf ( " Session Accepted! \n " ) ;
XML_ConPrintTree ( tree , 0 ) ;
if ( ice )
JCL_JingleParsePeerPorts ( jcl , tree , from ) ;
//if we didn't error out, the ICE stuff is meant to start sending handshakes/media as soon as the connection is accepted
if ( ice )
pICE_Begin ( ice , NULL , 0 ) ;
}
}
else if ( ! strcmp ( action , " session-initiate " ) )
{
Con_Printf ( " Peer initiating connection! \n " ) ;
XML_ConPrintTree ( tree , 0 ) ;
ice = JCL_JingleHandleInitiate ( jcl , tree , from ) ;
}
else
{
Con_Printf ( " Unknown jingle action: %s \n " , action ) ;
XML_ConPrintTree ( tree , 0 ) ;
}
JCL_AddClientMessagef ( jcl ,
" <iq type='result' to='%s' id='%s' /> " , from , id ) ;
}
qintptr_t JCL_ConsoleLink ( qintptr_t * args )
{
char text [ 256 ] ;
char link [ 256 ] ;
2013-06-23 18:43:59 +00:00
pCmd_Argv ( 0 , text , sizeof ( text ) ) ;
pCmd_Argv ( 1 , link , sizeof ( link ) ) ;
2013-06-23 02:33:52 +00:00
if ( ! strncmp ( link , " \\ xmppauth \\ " , 6 ) )
{
//we should friend them too.
if ( jclient )
JCL_AddClientMessagef ( jclient , " <presence to='%s' type='subscribed'/> " , link + 10 ) ;
return true ;
}
if ( ! strncmp ( link , " \\ xmppdeny \\ " , 6 ) )
{
if ( jclient )
JCL_AddClientMessagef ( jclient , " <presence to='%s' type='unsubscribed'/> " , link + 10 ) ;
return true ;
}
if ( ! strncmp ( link , " \\ xmppjoin \\ " , 6 ) )
{
JCL_Join ( jclient , link + 10 ) ;
return false ;
}
if ( ! strncmp ( link , " \\ xmpp \\ " , 6 ) )
{
if ( jclient )
{
char * f ;
buddy_t * b ;
bresource_t * br ;
JCL_FindBuddy ( jclient , link + 6 , & b , & br ) ;
f = b - > name ;
b - > defaultresource = br ;
if ( BUILTINISVALID ( Con_SubPrint ) )
2013-06-23 18:43:59 +00:00
pCon_SubPrint ( f , " " ) ;
2013-06-23 02:33:52 +00:00
if ( BUILTINISVALID ( Con_SetActive ) )
2013-06-23 18:43:59 +00:00
pCon_SetActive ( f ) ;
2013-06-23 02:33:52 +00:00
}
return true ;
}
return false ;
}
2013-03-31 04:21:08 +00:00
qintptr_t JCL_ConExecuteCommand ( qintptr_t * args )
2005-12-11 20:11:22 +00:00
{
2013-06-23 02:33:52 +00:00
buddy_t * b ;
char consolename [ 256 ] ;
2005-12-11 20:11:22 +00:00
if ( ! jclient )
{
char buffer [ 256 ] ;
2013-06-23 18:43:59 +00:00
pCmd_Argv ( 0 , buffer , sizeof ( buffer ) ) ;
Con_SubPrintf ( buffer , " You were disconnected \n " ) ;
2005-12-11 20:11:22 +00:00
return true ;
}
2013-06-23 18:43:59 +00:00
pCmd_Argv ( 0 , consolename , sizeof ( consolename ) ) ;
2013-06-23 02:33:52 +00:00
for ( b = jclient - > buddies ; b ; b = b - > next )
{
if ( ! strcmp ( b - > name , consolename ) )
{
if ( b - > defaultresource )
Q_snprintf ( jclient - > defaultdest , sizeof ( jclient - > defaultdest ) , " %s/%s " , b - > accountdomain , b - > defaultresource - > resource ) ;
else
Q_snprintf ( jclient - > defaultdest , sizeof ( jclient - > defaultdest ) , " %s " , b - > accountdomain ) ;
break ;
}
}
JCL_Command ( consolename ) ;
2005-12-11 20:11:22 +00:00
return true ;
}
2013-06-23 02:33:52 +00:00
void JCL_FlushOutgoing ( jclient_t * jcl )
{
int sent ;
if ( ! jcl | | ! jcl - > outbuflen )
return ;
2013-06-23 18:43:59 +00:00
sent = pNet_Send ( jcl - > socket , jcl - > outbuf + jcl - > outbufpos , jcl - > outbuflen ) ; //FIXME: This needs rewriting to cope with errors.
2013-06-23 02:33:52 +00:00
if ( sent > 0 )
{
//and print it on some subconsole if we're debugging
if ( jcl - > streamdebug )
{
char t = jcl - > outbuf [ jcl - > outbufpos + sent ] ;
jcl - > outbuf [ jcl - > outbufpos + sent ] = 0 ;
Con_SubPrintf ( " xmppout " , COLOURYELLOW " %s \n " , jcl - > outbuf + jcl - > outbufpos ) ;
jcl - > outbuf [ jcl - > outbufpos + sent ] = t ;
}
jcl - > outbufpos + = sent ;
jcl - > outbuflen - = sent ;
}
2013-06-24 09:04:00 +00:00
// else
// Con_Printf("Unable to send anything\n");
2013-06-23 02:33:52 +00:00
}
2005-12-11 20:11:22 +00:00
void JCL_AddClientMessage ( jclient_t * jcl , char * msg , int datalen )
{
2013-06-23 02:33:52 +00:00
//handle overflows
if ( jcl - > outbufpos + jcl - > outbuflen + datalen > jcl - > outbufmax )
{
if ( jcl - > outbuflen + datalen < = jcl - > outbufmax )
{
//can get away with just moving the data
memmove ( jcl - > outbuf , jcl - > outbuf + jcl - > outbufpos , jcl - > outbuflen ) ;
jcl - > outbufpos = 0 ;
}
else
{
//need to expand the buffer.
int newmax = ( jcl - > outbuflen + datalen ) * 2 ;
char * newbuf ;
if ( newmax < jcl - > outbuflen )
newbuf = NULL ; //eep... some special kind of evil overflow.
else
newbuf = malloc ( newmax + 1 ) ;
if ( newbuf )
{
memcpy ( newbuf , jcl - > outbuf + jcl - > outbufpos , jcl - > outbuflen ) ;
jcl - > outbufmax = newmax ;
jcl - > outbufpos = 0 ;
jcl - > outbuf = newbuf ;
}
else
datalen = 0 ; //eep!
}
}
//and write our data to it
memcpy ( jcl - > outbuf + jcl - > outbufpos + jcl - > outbuflen , msg , datalen ) ;
jcl - > outbuflen + = datalen ;
//try and flush it now
JCL_FlushOutgoing ( jcl ) ;
2005-12-11 20:11:22 +00:00
}
void JCL_AddClientMessageString ( jclient_t * jcl , char * msg )
{
JCL_AddClientMessage ( jcl , msg , strlen ( msg ) ) ;
}
2013-06-23 02:33:52 +00:00
void JCL_AddClientMessagef ( jclient_t * jcl , char * fmt , . . . )
{
va_list argptr ;
char body [ 2048 ] ;
2013-06-23 18:43:59 +00:00
2013-06-23 02:33:52 +00:00
va_start ( argptr , fmt ) ;
2013-06-23 18:43:59 +00:00
Q_vsnprintf ( body , sizeof ( body ) , fmt , argptr ) ;
2013-06-23 02:33:52 +00:00
va_end ( argptr ) ;
2005-12-11 20:11:22 +00:00
2013-06-23 02:33:52 +00:00
JCL_AddClientMessageString ( jcl , body ) ;
}
2005-12-11 20:11:22 +00:00
jclient_t * JCL_Connect ( char * server , int defport , qboolean usesecure , char * account , char * password )
{
jclient_t * jcl ;
char * at ;
if ( usesecure )
{
if ( ! BUILTINISVALID ( Net_SetTLSClient ) )
{
Con_Printf ( " JCL_OpenSocket: TLS is not supported \n " ) ;
return NULL ;
}
}
at = strchr ( account , ' @ ' ) ;
if ( ! at )
return NULL ;
jcl = malloc ( sizeof ( jclient_t ) ) ;
if ( ! jcl )
return NULL ;
memset ( jcl , 0 , sizeof ( jclient_t ) ) ;
2013-06-23 18:43:59 +00:00
jcl - > socket = pNet_TCPConnect ( server , defport ) ; //port is only used if the url doesn't contain one. It's a default.
2005-12-11 20:11:22 +00:00
//not yet blocking. So no frequent attempts please...
//non blocking prevents connect from returning worthwhile sensible value.
if ( ( int ) jcl - > socket < 0 )
{
Con_Printf ( " JCL_OpenSocket: couldn't connect \n " ) ;
free ( jcl ) ;
return NULL ;
}
if ( usesecure )
{
2013-06-23 18:43:59 +00:00
if ( pNet_SetTLSClient ( jcl - > socket , server ) < 0 )
2005-12-11 20:11:22 +00:00
{
2013-06-23 18:43:59 +00:00
pNet_Close ( jcl - > socket ) ;
2009-04-19 01:48:10 +00:00
free ( jcl ) ;
jcl = NULL ;
2005-12-11 20:11:22 +00:00
return NULL ;
}
jcl - > issecure = true ;
}
else
jcl - > issecure = false ;
// gethostname(jcl->hostname, sizeof(jcl->hostname));
// jcl->hostname[sizeof(jcl->hostname)-1] = 0;
2013-06-23 02:33:52 +00:00
jcl - > tlsconnect = usesecure ;
2013-06-23 18:43:59 +00:00
jcl - > streamdebug = ! ! pCvar_GetFloat ( " xmpp_debug " ) ;
2005-12-11 20:11:22 +00:00
* at = ' \0 ' ;
2013-06-23 02:33:52 +00:00
Q_strlcpy ( jcl - > server , server , sizeof ( jcl - > server ) ) ;
2013-05-11 14:02:55 +00:00
Q_strlcpy ( jcl - > username , account , sizeof ( jcl - > username ) ) ;
Q_strlcpy ( jcl - > domain , at + 1 , sizeof ( jcl - > domain ) ) ;
Q_strlcpy ( jcl - > password , password , sizeof ( jcl - > password ) ) ;
2005-12-11 20:11:22 +00:00
2013-05-11 14:02:55 +00:00
Q_strlcpy ( jcl - > resource , " Quake " , sizeof ( jcl - > password ) ) ;
2005-12-11 20:11:22 +00:00
2013-06-23 02:33:52 +00:00
Con_Printf ( " Trying to connect to %s \n " , at + 1 ) ;
2005-12-11 20:11:22 +00:00
JCL_AddClientMessageString ( jcl ,
" <?xml version='1.0' ?> "
" <stream:stream to=' " ) ;
JCL_AddClientMessageString ( jcl , jcl - > domain ) ;
JCL_AddClientMessageString ( jcl , " ' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'> " ) ;
return jcl ;
}
char base64 [ 512 + 1 ] ;
unsigned int base64_len ; //current output length
unsigned int base64_cur ; //current pending value
unsigned int base64_bits ; //current pending bits
char Base64_From64 ( int byt )
{
if ( byt > = 0 & & byt < 26 )
return ' A ' + byt - 0 ;
if ( byt > = 26 & & byt < 52 )
return ' a ' + byt - 26 ;
if ( byt > = 52 & & byt < 62 )
return ' 0 ' + byt - 52 ;
if ( byt = = 62 )
return ' + ' ;
if ( byt = = 63 )
return ' / ' ;
return ' ! ' ;
}
void Base64_Byte ( unsigned int byt )
{
if ( base64_len + 8 > = sizeof ( base64 ) - 1 )
return ;
base64_cur | = byt < < ( 16 - base64_bits ) ; //first byte fills highest bits
base64_bits + = 8 ;
if ( base64_bits = = 24 )
{
base64 [ base64_len + + ] = Base64_From64 ( ( base64_cur > > 18 ) & 63 ) ;
base64 [ base64_len + + ] = Base64_From64 ( ( base64_cur > > 12 ) & 63 ) ;
base64 [ base64_len + + ] = Base64_From64 ( ( base64_cur > > 6 ) & 63 ) ;
base64 [ base64_len + + ] = Base64_From64 ( ( base64_cur > > 0 ) & 63 ) ;
base64 [ base64_len ] = ' \0 ' ;
2013-03-31 04:21:08 +00:00
// Con_Printf("base64: %s\n", base64+base64_len-4);
2005-12-11 20:11:22 +00:00
base64_bits = 0 ;
base64_cur = 0 ;
}
}
void Base64_Add ( char * s , int len )
{
unsigned char * us = ( unsigned char * ) s ;
while ( len - - > 0 )
Base64_Byte ( * us + + ) ;
}
void Base64_Finish ( void )
{
//output is always a multiple of four
//0(0)->0(0)
//1(8)->2(12)
//2(16)->3(18)
//3(24)->4(24)
if ( base64_bits ! = 0 )
{
2013-06-23 02:33:52 +00:00
base64 [ base64_len + + ] = Base64_From64 ( ( base64_cur > > 18 ) & 63 ) ;
base64 [ base64_len + + ] = Base64_From64 ( ( base64_cur > > 12 ) & 63 ) ;
if ( base64_bits = = 8 )
{
base64 [ base64_len + + ] = ' = ' ;
base64 [ base64_len + + ] = ' = ' ;
}
else
{
base64 [ base64_len + + ] = Base64_From64 ( ( base64_cur > > 6 ) & 63 ) ;
if ( base64_bits = = 16 )
base64 [ base64_len + + ] = ' = ' ;
else
base64 [ base64_len + + ] = Base64_From64 ( ( base64_cur > > 0 ) & 63 ) ;
}
}
base64 [ base64_len + + ] = ' \0 ' ;
base64_len = 0 ; //for next time (use strlen)
base64_bits = 0 ;
base64_cur = 0 ;
}
2013-06-24 09:04:00 +00:00
//decode a base64 byte to a 0-63 value. Cannot cope with =.
static int Base64_DecodeByte ( char byt )
{
if ( byt > = ' A ' & & byt < = ' Z ' )
return ( byt - ' A ' ) + 0 ;
if ( byt > = ' a ' & & byt < = ' z ' )
return ( byt - ' a ' ) + 26 ;
if ( byt > = ' 0 ' & & byt < = ' 9 ' )
return ( byt - ' 0 ' ) + 52 ;
if ( byt = = ' + ' )
return 62 ;
if ( byt = = ' / ' )
return 63 ;
return - 1 ;
}
int Base64_Decode ( char * out , int outlen , char * src , int srclen )
{
int len = 0 ;
int result ;
//4 input chars give 3 output chars
while ( srclen > = 4 )
{
if ( len + 3 > outlen )
break ;
result = Base64_DecodeByte ( src [ 0 ] ) < < 18 ;
result | = Base64_DecodeByte ( src [ 1 ] ) < < 12 ;
out [ len + + ] = ( result > > 16 ) & 0xff ;
if ( src [ 2 ] ! = ' = ' )
{
result | = Base64_DecodeByte ( src [ 2 ] ) < < 6 ;
out [ len + + ] = ( result > > 8 ) & 0xff ;
if ( src [ 3 ] ! = ' = ' )
{
result | = Base64_DecodeByte ( src [ 3 ] ) < < 0 ;
out [ len + + ] = ( result > > 0 ) & 0xff ;
}
}
if ( result & 0xff000000 )
return 0 ; //some kind of invalid char
src + = 4 ;
srclen - = 4 ;
}
//some kind of error
if ( srclen )
return 0 ;
return len ;
}
2013-06-23 02:33:52 +00:00
char * TrimResourceFromJid ( char * jid )
{
char * slash ;
slash = strchr ( jid , ' / ' ) ;
if ( slash )
{
* slash = ' \0 ' ;
return slash + 1 ;
}
return NULL ;
}
qboolean JCL_FindBuddy ( jclient_t * jcl , char * jid , buddy_t * * buddy , bresource_t * * bres )
{
char name [ 256 ] ;
char * res ;
buddy_t * b ;
bresource_t * r = NULL ;
Q_strlcpy ( name , jid , sizeof ( name ) ) ;
res = TrimResourceFromJid ( name ) ;
for ( b = jcl - > buddies ; b ; b = b - > next )
{
if ( ! strcmp ( b - > accountdomain , name ) )
break ;
}
if ( ! b )
{
b = malloc ( sizeof ( * b ) + strlen ( name ) ) ;
memset ( b , 0 , sizeof ( * b ) ) ;
b - > next = jcl - > buddies ;
jcl - > buddies = b ;
strcpy ( b - > accountdomain , name ) ;
Q_strlcpy ( b - > name , name , sizeof ( b - > name ) ) ; //default
}
* buddy = b ;
if ( res & & bres )
{
for ( r = b - > resources ; r ; r = r - > next )
{
if ( ! strcmp ( r - > resource , res ) )
break ;
}
if ( ! r )
{
r = malloc ( sizeof ( * r ) + strlen ( res ) ) ;
memset ( r , 0 , sizeof ( * r ) ) ;
r - > next = b - > resources ;
b - > resources = r ;
strcpy ( r - > resource , res ) ;
}
* bres = r ;
}
else if ( bres )
* bres = NULL ;
return false ;
}
void JCL_SendIQ ( jclient_t * jcl , qboolean ( * callback ) ( jclient_t * jcl , xmltree_t * tree ) , char * iqtype , char * target , char * body )
{
struct iq_s * iq ;
iq = malloc ( sizeof ( * iq ) ) ;
iq - > next = jcl - > pendingiqs ;
jcl - > pendingiqs = iq ;
2013-06-23 18:43:59 +00:00
Q_snprintf ( iq - > id , sizeof ( iq - > id ) , " %i " , rand ( ) ) ;
2013-06-23 02:33:52 +00:00
iq - > callback = callback ;
if ( target )
{
if ( * jcl - > jid )
JCL_AddClientMessagef ( jcl , " <iq type='%s' id='%s' from='%s' to='%s'> " , iqtype , iq - > id , jcl - > jid , target ) ;
else
JCL_AddClientMessagef ( jcl , " <iq type='%s' id='%s' to='%s'> " , iqtype , iq - > id , target ) ;
}
else
{
if ( * jcl - > jid )
JCL_AddClientMessagef ( jcl , " <iq type='%s' id='%s' from='%s'> " , iqtype , iq - > id , jcl - > jid ) ;
else
JCL_AddClientMessagef ( jcl , " <iq type='%s' id='%s'> " , iqtype , iq - > id ) ;
}
JCL_AddClientMessageString ( jcl , body ) ;
JCL_AddClientMessageString ( jcl , " </iq> " ) ;
}
void JCL_SendIQf ( jclient_t * jcl , qboolean ( * callback ) ( jclient_t * jcl , xmltree_t * tree ) , char * iqtype , char * target , char * fmt , . . . )
{
va_list argptr ;
char body [ 2048 ] ;
va_start ( argptr , fmt ) ;
2013-06-23 18:43:59 +00:00
Q_vsnprintf ( body , sizeof ( body ) , fmt , argptr ) ;
2013-06-23 02:33:52 +00:00
va_end ( argptr ) ;
JCL_SendIQ ( jcl , callback , iqtype , target , body ) ;
}
void JCL_SendIQNode ( jclient_t * jcl , qboolean ( * callback ) ( jclient_t * jcl , xmltree_t * tree ) , char * iqtype , char * target , xmltree_t * node , qboolean destroynode )
{
char * s = XML_GenerateString ( node ) ;
JCL_SendIQ ( jcl , callback , iqtype , target , s ) ;
free ( s ) ;
if ( destroynode )
XML_Destroy ( node ) ;
}
static void JCL_RosterUpdate ( jclient_t * jcl , xmltree_t * listp )
{
xmltree_t * i ;
buddy_t * buddy ;
int cnum = 0 ;
while ( ( i = XML_ChildOfTree ( listp , " item " , cnum + + ) ) )
{
char * name = XML_GetParameter ( i , " name " , " " ) ;
char * jid = XML_GetParameter ( i , " jid " , " " ) ;
2013-06-23 18:43:59 +00:00
// char *sub = XML_GetParameter(i, "subscription", "");
2013-06-23 02:33:52 +00:00
JCL_FindBuddy ( jcl , jid , & buddy , NULL ) ;
if ( * name )
Q_strlcpy ( buddy - > name , name , sizeof ( buddy - > name ) ) ;
buddy - > friended = true ;
}
}
static qboolean JCL_RosterReply ( jclient_t * jcl , xmltree_t * tree )
{
2013-06-23 18:43:59 +00:00
xmltree_t * c ;
2013-06-23 02:33:52 +00:00
c = XML_ChildOfTree ( tree , " query " , 0 ) ;
if ( c )
{
JCL_RosterUpdate ( jcl , c ) ;
2013-06-24 09:04:00 +00:00
JCL_GeneratePresence ( true ) ;
2013-06-23 02:33:52 +00:00
return true ;
}
JCL_GeneratePresence ( true ) ;
return false ;
}
static qboolean JCL_BindReply ( jclient_t * jcl , xmltree_t * tree )
{
xmltree_t * c ;
c = XML_ChildOfTree ( tree , " bind " , 0 ) ;
if ( c )
{
c = XML_ChildOfTree ( c , " jid " , 0 ) ;
if ( c )
2005-12-11 20:11:22 +00:00
{
2013-06-23 02:33:52 +00:00
Q_strlcpy ( jcl - > jid , c - > body , sizeof ( jcl - > jid ) ) ;
Con_Printf ( " Bound to jid ^[[%s] \\ xmpp \\ %s^] \n " , jcl - > jid , jcl - > jid ) ;
return true ;
2005-12-11 20:11:22 +00:00
}
}
2013-06-23 02:33:52 +00:00
return false ;
}
static qboolean JCL_SessionReply ( jclient_t * jcl , xmltree_t * tree )
{
JCL_SendIQf ( jcl , JCL_RosterReply , " get " , NULL , " <query xmlns='jabber:iq:roster'/> " ) ;
return true ;
2005-12-11 20:11:22 +00:00
}
2013-06-23 02:33:52 +00:00
static char * caps [ ] =
{
# if 1
" http://jabber.org/protocol/caps " ,
" http://jabber.org/protocol/disco#info " ,
// "http://jabber.org/protocol/disco#items",
// "http://www.google.com/xmpp/protocol/camera/v1",
// "http://www.google.com/xmpp/protocol/session",
// "http://www.google.com/xmpp/protocol/voice/v1",
// "http://www.google.com/xmpp/protocol/video/v1",
" jabber:iq:version " ,
" urn:xmpp:jingle:1 " ,
QUAKEMEDIAXMLNS ,
// "urn:xmpp:jingle:apps:rtp:1", //we don't support rtp video/voice chat
// "urn:xmpp:jingle:apps:rtp:audio",//we don't support rtp voice chat
// "urn:xmpp:jingle:apps:rtp:video",//we don't support rtp video chat
" urn:xmpp:jingle:transports:raw-udp:1 " ,
# ifndef NOICE
" urn:xmpp:jingle:transports:ice-udp:1 " ,
# endif
# ifndef Q3_VM
" urn:xmpp:time " ,
# endif
" urn:xmpp:ping " , //FIXME: I'm not keen on this. I only added support to stop errors from pidgin when trying to debug.
# else
//for testing, this is the list of features pidgin supports (which is the other client I'm testing against).
" jabber:iq:last " ,
" jabber:iq:oob " ,
" urn:xmpp:time " ,
" jabber:iq:version " ,
" jabber:x:conference " ,
" http://jabber.org/protocol/bytestreams " ,
" http://jabber.org/protocol/caps " ,
" http://jabber.org/protocol/chatstates " ,
" http://jabber.org/protocol/disco#info " ,
" http://jabber.org/protocol/disco#items " ,
" http://jabber.org/protocol/muc " ,
" http://jabber.org/protocol/muc#user " ,
" http://jabber.org/protocol/si " ,
// "http://jabber.org/protocol/si/profile/file-transfer",
" http://jabber.org/protocol/xhtml-im " ,
" urn:xmpp:ping " ,
" urn:xmpp:attention:0 " ,
" urn:xmpp:bob " ,
" urn:xmpp:jingle:1 " ,
" http://www.google.com/xmpp/protocol/session " ,
" http://www.google.com/xmpp/protocol/voice/v1 " ,
" http://www.google.com/xmpp/protocol/video/v1 " ,
" http://www.google.com/xmpp/protocol/camera/v1 " ,
" urn:xmpp:jingle:apps:rtp:1 " ,
" urn:xmpp:jingle:apps:rtp:audio " ,
" urn:xmpp:jingle:apps:rtp:video " ,
" urn:xmpp:jingle:transports:raw-udp:1 " ,
" urn:xmpp:jingle:transports:ice-udp:1 " ,
" urn:xmpp:avatar:metadata " ,
" urn:xmpp:avatar:data " ,
" urn:xmpp:avatar:metadata+notify " ,
" http://jabber.org/protocol/mood " ,
" http://jabber.org/protocol/mood+notify " ,
" http://jabber.org/protocol/tune " ,
" http://jabber.org/protocol/tune+notify " ,
" http://jabber.org/protocol/nick " ,
" http://jabber.org/protocol/nick+notify " ,
" http://jabber.org/protocol/ibb " ,
# endif
NULL
} ;
static void buildcaps ( char * out , int outlen )
2005-12-11 20:11:22 +00:00
{
2013-06-23 02:33:52 +00:00
int i ;
Q_strncpyz ( out , " <identity category='client' type='pc' name='FTEQW'/> " , outlen ) ;
2005-12-11 20:11:22 +00:00
2013-06-23 02:33:52 +00:00
for ( i = 0 ; caps [ i ] ; i + + )
2005-12-11 20:11:22 +00:00
{
2013-06-23 02:33:52 +00:00
Q_strlcat ( out , " <feature var=' " , outlen ) ;
Q_strlcat ( out , caps [ i ] , outlen ) ;
Q_strlcat ( out , " '/> " , outlen ) ;
2005-12-11 20:11:22 +00:00
}
}
2013-06-23 02:33:52 +00:00
static int qsortcaps ( const void * va , const void * vb )
{
char * a = * ( char * * ) va ;
char * b = * ( char * * ) vb ;
return strcmp ( a , b ) ;
}
int SHA1 ( char * digest , int maxdigestsize , char * string , int stringlen ) ;
char * buildcapshash ( void )
{
int i , l ;
char out [ 8192 ] ;
int outlen = sizeof ( out ) ;
unsigned char digest [ 64 ] ;
Q_strlcpy ( out , " client/pc//FTEQW< " , outlen ) ;
qsort ( caps , sizeof ( caps ) / sizeof ( caps [ 0 ] ) - 1 , sizeof ( char * ) , qsortcaps ) ;
for ( i = 0 ; caps [ i ] ; i + + )
{
Q_strlcat ( out , caps [ i ] , outlen ) ;
Q_strlcat ( out , " < " , outlen ) ;
}
l = SHA1 ( digest , sizeof ( digest ) , out , strlen ( out ) ) ;
for ( i = 0 ; i < l ; i + + )
Base64_Byte ( digest [ i ] ) ;
Base64_Finish ( ) ;
return base64 ;
}
2005-12-11 20:11:22 +00:00
# define JCL_DONE 0
# define JCL_CONTINUE 1
# define JCL_KILL 2
int JCL_ClientFrame ( jclient_t * jcl )
{
int pos ;
xmltree_t * tree , * ot ;
char * f ;
int ret ;
qboolean unparsable ;
int olddepth ;
2013-06-23 18:43:59 +00:00
ret = pNet_Recv ( jcl - > socket , jcl - > bufferedinmessage + jcl - > bufferedinammount , sizeof ( jcl - > bufferedinmessage ) - 1 - jcl - > bufferedinammount ) ;
2005-12-11 20:11:22 +00:00
if ( ret = = 0 )
{
2013-03-31 04:21:08 +00:00
if ( ! jcl - > bufferedinammount ) //if we are half way through a message, read any possible conjunctions.
return JCL_DONE ; //remove
2005-12-11 20:11:22 +00:00
}
if ( ret < 0 )
{
2013-03-31 04:21:08 +00:00
Con_Printf ( " JCL: socket error \n " ) ;
return JCL_KILL ;
2005-12-11 20:11:22 +00:00
}
if ( ret > 0 )
2013-03-31 04:21:08 +00:00
{
2005-12-11 20:11:22 +00:00
jcl - > bufferedinammount + = ret ;
2013-03-31 04:21:08 +00:00
jcl - > bufferedinmessage [ jcl - > bufferedinammount ] = 0 ;
}
2005-12-11 20:11:22 +00:00
olddepth = jcl - > tagdepth ;
//we never end parsing in the middle of a < >
//this means we can filter out the <? ?>, <!-- --> and < /> stuff properly
for ( pos = jcl - > instreampos ; pos < jcl - > bufferedinammount ; pos + + )
{
if ( jcl - > bufferedinmessage [ pos ] = = ' < ' )
{
jcl - > instreampos = pos ;
}
else if ( jcl - > bufferedinmessage [ pos ] = = ' > ' )
{
if ( pos < 1 )
break ; //erm...
if ( jcl - > bufferedinmessage [ pos - 1 ] ! = ' / ' ) //<blah/> is a tag without a body
{
if ( jcl - > bufferedinmessage [ jcl - > instreampos + 1 ] ! = ' ? ' ) //<? blah ?> is a tag without a body
{
if ( jcl - > bufferedinmessage [ pos - 1 ] ! = ' ? ' )
{
if ( jcl - > bufferedinmessage [ jcl - > instreampos + 1 ] = = ' / ' ) //</blah> is the end of a tag with a body
jcl - > tagdepth - - ;
else
jcl - > tagdepth + + ; //<blah> is the start of a tag with a body
}
}
}
jcl - > instreampos = pos + 1 ;
}
}
if ( jcl - > tagdepth = = 1 & & olddepth = = 0 )
{ //first bit of info
pos = 0 ;
2013-03-31 04:21:08 +00:00
tree = XML_Parse ( jcl - > bufferedinmessage , & pos , jcl - > instreampos , true , " " ) ;
2005-12-11 20:11:22 +00:00
while ( tree & & ! strcmp ( tree - > name , " ?xml " ) )
{
XML_Destroy ( tree ) ;
2013-03-31 04:21:08 +00:00
tree = XML_Parse ( jcl - > bufferedinmessage , & pos , jcl - > instreampos , true , " " ) ;
2005-12-11 20:11:22 +00:00
}
2013-06-23 02:33:52 +00:00
if ( jcl - > streamdebug )
{
char t = jcl - > bufferedinmessage [ pos ] ;
jcl - > bufferedinmessage [ pos ] = 0 ;
Con_TrySubPrint ( " xmppin " , jcl - > bufferedinmessage ) ;
Con_TrySubPrint ( " xmppin " , " \n " ) ;
jcl - > bufferedinmessage [ pos ] = t ;
}
2005-12-11 20:11:22 +00:00
if ( ! tree )
{
Con_Printf ( " Not an xml stream \n " ) ;
return JCL_KILL ;
}
2013-03-31 04:21:08 +00:00
if ( strcmp ( tree - > name , " stream " ) | | strcmp ( tree - > xmlns , " http://etherx.jabber.org/streams " ) )
2005-12-11 20:11:22 +00:00
{
Con_Printf ( " Not an xmpp stream \n " ) ;
return JCL_KILL ;
}
2013-05-11 14:02:55 +00:00
Q_strlcpy ( jcl - > defaultnamespace , tree - > xmlns_dflt , sizeof ( jcl - > defaultnamespace ) ) ;
2005-12-11 20:11:22 +00:00
ot = tree ;
tree = tree - > child ;
ot - > child = NULL ;
2013-03-31 04:21:08 +00:00
// Con_Printf("Discard\n");
// XML_ConPrintTree(ot, 0);
2005-12-11 20:11:22 +00:00
XML_Destroy ( ot ) ;
if ( ! tree )
{
memmove ( jcl - > bufferedinmessage , jcl - > bufferedinmessage + pos , jcl - > bufferedinammount - ( pos ) ) ;
jcl - > bufferedinammount - = pos ;
jcl - > instreampos - = pos ;
return JCL_DONE ;
}
}
else
{
if ( jcl - > tagdepth ! = 1 )
{
2009-04-19 01:48:10 +00:00
if ( jcl - > tagdepth < 1 & & jcl - > bufferedinammount = = jcl - > instreampos )
2005-12-11 20:11:22 +00:00
{
Con_Printf ( " End of XML stream \n " ) ;
return JCL_KILL ;
}
return JCL_DONE ;
}
pos = 0 ;
2013-03-31 04:21:08 +00:00
tree = XML_Parse ( jcl - > bufferedinmessage , & pos , jcl - > instreampos , false , jcl - > defaultnamespace ) ;
2005-12-11 20:11:22 +00:00
2013-06-23 02:33:52 +00:00
if ( jcl - > streamdebug )
{
char t = jcl - > bufferedinmessage [ pos ] ;
jcl - > bufferedinmessage [ pos ] = 0 ;
Con_TrySubPrint ( " xmppin " , jcl - > bufferedinmessage ) ;
Con_TrySubPrint ( " xmppin " , " \n " ) ;
jcl - > bufferedinmessage [ pos ] = t ;
}
2005-12-11 20:11:22 +00:00
if ( ! tree )
{
// Con_Printf("No input tree: %s", jcl->bufferedinmessage);
return JCL_DONE ;
}
}
2013-03-31 04:21:08 +00:00
// Con_Printf("read\n");
// XML_ConPrintTree(tree, 0);
2005-12-11 20:11:22 +00:00
unparsable = true ;
2013-03-31 04:21:08 +00:00
if ( ! strcmp ( tree - > name , " features " ) )
2005-12-11 20:11:22 +00:00
{
if ( ( ot = XML_ChildOfTree ( tree , " bind " , 0 ) ) )
{
unparsable = false ;
2013-06-23 02:33:52 +00:00
JCL_SendIQf ( jcl , JCL_BindReply , " set " , NULL , " <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><resource>%s</resource></bind> " , jcl - > resource ) ;
2005-12-11 20:11:22 +00:00
}
if ( ( ot = XML_ChildOfTree ( tree , " session " , 0 ) ) )
{
unparsable = false ;
2013-06-23 02:33:52 +00:00
JCL_SendIQf ( jcl , JCL_SessionReply , " set " , NULL , " <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/> " ) ;
2013-03-31 04:21:08 +00:00
jcl - > connected = true ;
2013-06-23 02:33:52 +00:00
JCL_WriteConfig ( ) ;
// JCL_AddClientMessageString(jcl, "<iq type='get' to='gmail.com' id='H_2'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>");
2005-12-11 20:11:22 +00:00
}
if ( unparsable )
{
2013-06-23 18:43:59 +00:00
if ( ( ! jclient - > issecure ) & & BUILTINISVALID ( Net_SetTLSClient ) & & XML_ChildOfTree ( tree , " starttls " , 0 ) ! = NULL & & ! pCvar_GetFloat ( " xmpp_disabletls " ) )
2005-12-11 20:11:22 +00:00
{
2013-03-31 04:21:08 +00:00
Con_Printf ( " Attempting to switch to TLS \n " ) ;
2005-12-11 20:11:22 +00:00
JCL_AddClientMessageString ( jcl , " <starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls' /> " ) ;
unparsable = false ;
}
else if ( ( ot = XML_ChildOfTree ( tree , " mechanisms " , 0 ) ) )
{
2013-06-24 09:04:00 +00:00
qboolean canplain = false ;
// qboolean canmd5 = false;
// qboolean canscramsha1 = false;
// qboolean canxoath2 = false;
2005-12-11 20:11:22 +00:00
for ( ot = ot - > child ; ot ; ot = ot - > sibling )
{
if ( ! strcmp ( ot - > body , " PLAIN " ) )
2013-06-24 09:04:00 +00:00
canplain = true ;
// else if (!strcmp(ot->body, "SCRAM-SHA-1"))
// cansha1 = true;
// else if (!strcmp(ot->body, "DIGEST-MD5"))
// canmd5 = true;
// else if (!strcmp(ot->body, "X-OAUTH2"))
// canxoath2 = true;
// else
// Con_Printf("Unknown auth method \'%s\'\n", ot->body);
}
/*
if ( canscramsha1 )
{
Con_Printf ( " Using scram-sha-1%s \n " , jclient - > issecure ? " over tls/ssl " : " " ) ;
strcpy ( jcl - > authnonce , " abcdefghijklmnopqrstuvwxyz " ) ; //FIXME: should be random
Base64_Add ( " n,,n= " , 5 ) ;
Base64_Add ( jclient - > username , strlen ( jcl - > username ) ) ;
Base64_Add ( " ,r= " , 3 ) ;
Base64_Add ( jcl - > authnonce , strlen ( jcl - > authnonce ) ) ; //must be random ascii.
Base64_Finish ( ) ;
JCL_AddClientMessagef ( jcl , " <auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='SCRAM-SHA-1'>%s</auth> " , base64 ) ;
unparsable = false ;
}
else
*/
if ( canplain & & ( jclient - > issecure | | pCvar_GetFloat ( " xmpp_allowplainauth " ) ) )
{
//plain can still be read with man-in-the-middle attacks, of course, even with tls if the certificate is spoofed, so this should always be the lowest priority.
//we just hope that the tls certificate cannot be spoofed.
Con_Printf ( " Using plain auth%s \n " , jclient - > issecure ? " over tls/ssl " : " " ) ;
Base64_Add ( " " , 1 ) ;
Base64_Add ( jclient - > username , strlen ( jcl - > username ) ) ;
Base64_Add ( " " , 1 ) ;
Base64_Add ( jcl - > password , strlen ( jcl - > password ) ) ;
Base64_Finish ( ) ;
JCL_AddClientMessagef ( jcl , " <auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>%s</auth> " , base64 ) ;
unparsable = false ;
2005-12-11 20:11:22 +00:00
}
2013-06-24 09:04:00 +00:00
else
2005-12-11 20:11:22 +00:00
{
2013-06-24 09:04:00 +00:00
Con_Printf ( " XMPP: No suitable auth methods. Unable to connect. \n " ) ;
XML_ConPrintTree ( tree , 0 ) ;
XML_Destroy ( tree ) ;
return JCL_KILL ;
2005-12-11 20:11:22 +00:00
}
}
else //we cannot auth, no suitable method.
{
2013-06-24 09:04:00 +00:00
Con_Printf ( " XMPP: Neither SASL or TLS are usable \n " ) ;
XML_Destroy ( tree ) ;
return JCL_KILL ;
2005-12-11 20:11:22 +00:00
}
}
}
2013-06-24 09:04:00 +00:00
/*
else if ( ! strcmp ( tree - > name , " challenge " ) & & ! strcmp ( tree - > xmlns , " urn:ietf:params:xml:ns:xmpp-sasl " ) )
{
//sasl SCRAM-SHA-1 challenge
//send back the same 'r' attribute
buf saslchal ;
int l , i ;
buf salt ;
buf csn ;
buf itr ;
buf final ;
buf sigkey ;
char salted_password [ 20 ] ;
char proof [ 20 ] ;
char proof64 [ 30 ] ;
char clientkey [ 20 ] ;
char storedkey [ 20 ] ;
void hmacsha1 ( char * out , char * key , int keysize , char * data , int datalen ) ;
void Hi ( char * out , char * password , buf salt , int i ) ;
saslchal . len = Base64_Decode ( saslchal . buf , sizeof ( saslchal . buf ) , tree - > body , strlen ( tree - > body ) ) ;
//be warned, these CAN contain nulls.
csn = saslattr ( & saslchal , ' r ' ) ;
salt = saslattr ( & saslchal , ' s ' ) ;
itr = saslattr ( & saslchal , ' i ' ) ;
//this is the first part of the message we're about to send, with no proof.
//c(channel) is mandatory but nulled and forms part of the hash
final . len = 0 ;
buf_cat ( & final , " c= " , 2 ) ;
Base64_Add ( " n,, " , 3 ) ) ;
Base64_Finish ( ) ;
final . cat ( & final , base64 , strlen ( base64 ) ) ;
final . cat ( & final , " r= " , 2 ) ;
final . cat ( & final , csn . buf , csn . len ) ;
//our original message + ',' + challenge + ',' + the message we're about to send.
sigkey . len = 0 ;
buf_cat ( & sigkey , " n,,n= " , 5 ) ;
buf_cat ( & sigkey , jcl - > username , strlen ( jcl - > username ) ) ;
buf_cat ( & sigkey , " r= " , 2 ) ;
buf_cat ( & sigkey , jcl - > authnonce , strlen ( jcl - > authnonce ) ) ;
buf_cat ( & sigkey , " , " , 1 ) ;
buf_cat ( & sigkey , saslchal . buf , saslchal . len ) ;
buf_cat ( & sigkey , " , " , 1 ) ;
buf_cat ( & sigkey , final . buf , final . len ) ;
Hi ( salted_password , password , salt , atoi ( itr ) ) ;
hmacsha1 ( clientkey , salted_password , sizeof ( salted_password ) , " Client Key " , strlen ( " Client Key " ) ) ;
storedkey = sha1 ( clientkey , sizeof ( clientkey ) ) ;
hmacsha1 ( clientsignature , storedkey , sizeof ( storedkey ) , sigkey . buf , sigkey . len ) ;
for ( i = 0 ; i < sizeof ( proof ) ; i + + )
proof [ i ] = clientkey [ i ] ^ clientsignature [ i ] ;
Base64_Add ( proof , sizeof ( proof ) ) ;
Base64_Finish ( ) ;
strcpy ( proof64 , base64 ) ;
Base64_Add ( final , buflen ( final ) ) ;
Base64_Add ( " ,p= " , 3 ) ;
Base64_Add ( proof64 , strlen ( proof64 ) ) ;
Base64_Finish ( ) ;
JCL_AddClientMessagef ( jcl , " <response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>%s</response> " , base64 ) ;
}
*/
2005-12-11 20:11:22 +00:00
else if ( ! strcmp ( tree - > name , " proceed " ) )
{
//switch to TLS, if we can
//Restart everything, basically.
jcl - > bufferedinammount = 0 ;
jcl - > instreampos = 0 ;
jcl - > tagdepth = 0 ;
if ( ! BUILTINISVALID ( Net_SetTLSClient ) )
{
Con_Printf ( " JCL: proceed without TLS \n " ) ;
2013-03-31 04:21:08 +00:00
XML_Destroy ( tree ) ;
2005-12-11 20:11:22 +00:00
return JCL_KILL ;
}
2013-06-23 18:43:59 +00:00
if ( pNet_SetTLSClient ( jcl - > socket , jcl - > domain ) < 0 )
2005-12-11 20:11:22 +00:00
{
Con_Printf ( " JCL: failed to switch to TLS \n " ) ;
2013-03-31 04:21:08 +00:00
XML_Destroy ( tree ) ;
2005-12-11 20:11:22 +00:00
return JCL_KILL ;
}
jclient - > issecure = true ;
JCL_AddClientMessageString ( jcl ,
" <?xml version='1.0' ?> "
" <stream:stream to=' " ) ;
JCL_AddClientMessageString ( jcl , jcl - > domain ) ;
JCL_AddClientMessageString ( jcl , " ' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'> " ) ;
2013-03-31 04:21:08 +00:00
XML_Destroy ( tree ) ;
2005-12-11 20:11:22 +00:00
return JCL_DONE ;
}
else if ( ! strcmp ( tree - > name , " stream:error " ) )
{
}
else if ( ! strcmp ( tree - > name , " failure " ) )
{
if ( tree - > child )
Con_Printf ( " JCL: Failure: %s \n " , tree - > child - > name ) ;
else
Con_Printf ( " JCL: Unknown failure \n " ) ;
2013-03-31 04:21:08 +00:00
XML_Destroy ( tree ) ;
2005-12-11 20:11:22 +00:00
return JCL_KILL ;
}
else if ( ! strcmp ( tree - > name , " success " ) )
{
//Restart everything, basically, AGAIN! (third time lucky?)
jcl - > bufferedinammount = 0 ;
jcl - > instreampos = 0 ;
jcl - > tagdepth = 0 ;
JCL_AddClientMessageString ( jcl ,
" <?xml version='1.0' ?> "
" <stream:stream to=' " ) ;
JCL_AddClientMessageString ( jcl , jcl - > domain ) ;
JCL_AddClientMessageString ( jcl , " ' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'> " ) ;
return JCL_DONE ;
}
else if ( ! strcmp ( tree - > name , " iq " ) )
{
char * from ;
2013-06-23 18:43:59 +00:00
// char *to;
2005-12-11 20:11:22 +00:00
char * id ;
2013-06-23 02:33:52 +00:00
id = XML_GetParameter ( tree , " id " , " " ) ;
from = XML_GetParameter ( tree , " from " , " " ) ;
2013-06-23 18:43:59 +00:00
// to = XML_GetParameter(tree, "to", "");
2005-12-11 20:11:22 +00:00
2013-06-23 02:33:52 +00:00
f = XML_GetParameter ( tree , " type " , " " ) ;
if ( ! strcmp ( f , " get " ) )
2005-12-11 20:11:22 +00:00
{
ot = XML_ChildOfTree ( tree , " query " , 0 ) ;
if ( ot )
{
2013-03-31 04:21:08 +00:00
if ( from & & ! strcmp ( ot - > xmlns , " http://jabber.org/protocol/disco#info " ) )
{ //http://xmpp.org/extensions/xep-0030.html
char msg [ 2048 ] ;
2013-06-23 02:33:52 +00:00
char * hash ;
2013-03-31 04:21:08 +00:00
unparsable = false ;
2013-06-23 02:33:52 +00:00
buildcaps ( msg , sizeof ( msg ) ) ;
hash = buildcapshash ( ) ;
JCL_AddClientMessagef ( jcl ,
2013-03-31 04:21:08 +00:00
" <iq type='result' to='%s' id='%s'> "
2013-06-23 02:33:52 +00:00
" <query xmlns='http://jabber.org/protocol/disco#info' node='http://fteqw.com/ftexmppplug#%s'> "
" %s "
2013-03-31 04:21:08 +00:00
" </query> "
2013-06-23 02:33:52 +00:00
" </iq> " , from , id , hash , msg ) ;
2013-03-31 04:21:08 +00:00
}
else if ( from & & ! strcmp ( ot - > xmlns , " jabber:iq:version " ) )
2005-12-11 20:11:22 +00:00
{ //client->client version request
2013-03-31 04:21:08 +00:00
char msg [ 2048 ] ;
unparsable = false ;
2013-05-11 14:02:55 +00:00
Q_snprintf ( msg , sizeof ( msg ) ,
2013-03-31 04:21:08 +00:00
" <iq type='result' to='%s' id='%s'> "
" <query xmlns='jabber:iq:version'> "
2013-06-23 02:33:52 +00:00
" <name>FTEQW XMPP</name> "
2013-03-31 04:21:08 +00:00
" <version>V " JCL_BUILD " </version> "
2005-12-11 20:11:22 +00:00
# ifdef Q3_VM
2013-03-31 04:21:08 +00:00
" <os>QVM plugin</os> "
2013-06-23 02:33:52 +00:00
# else
//don't specify the os otherwise, as it gives away required base addresses etc for exploits
2005-12-11 20:11:22 +00:00
# endif
2013-03-31 04:21:08 +00:00
" </query> "
" </iq> " , from , id ) ;
2005-12-11 20:11:22 +00:00
2013-03-31 04:21:08 +00:00
JCL_AddClientMessageString ( jcl , msg ) ;
}
/* else if (from && !strcmp(ot->xmlns, "jabber:iq:last"))
{ //http://xmpp.org/extensions/xep-0012.html
char msg [ 2048 ] ;
int idletime = 0 ;
unparsable = false ;
//last activity
2013-05-11 14:02:55 +00:00
Q_snprintf ( msg , sizeof ( msg ) ,
2013-03-31 04:21:08 +00:00
" <iq type='result' to='%s' id='%s'> "
" <query xmlns='jabber:iq:last' seconds='%i'/> "
" </iq> " , from , id , idletime ) ;
JCL_AddClientMessageString ( jcl , msg ) ;
2005-12-11 20:11:22 +00:00
}
2013-03-31 04:21:08 +00:00
*/
}
# ifndef Q3_VM
ot = XML_ChildOfTree ( tree , " time " , 0 ) ;
if ( ot & & ! strcmp ( ot - > xmlns , " urn:xmpp:time " ) )
{ //http://xmpp.org/extensions/xep-0202.html
char msg [ 2048 ] ;
char tz [ 256 ] ;
char timestamp [ 256 ] ;
struct tm * timeinfo ;
2013-06-23 02:33:52 +00:00
int tzh , tzm ;
2013-03-31 04:21:08 +00:00
time_t rawtime ;
time ( & rawtime ) ;
2013-06-23 02:33:52 +00:00
timeinfo = localtime ( & rawtime ) ;
tzh = timeinfo - > tm_hour ;
tzm = timeinfo - > tm_min ;
2013-03-31 04:21:08 +00:00
timeinfo = gmtime ( & rawtime ) ;
2013-06-23 02:33:52 +00:00
tzh - = timeinfo - > tm_hour ;
tzm - = timeinfo - > tm_min ;
Q_snprintf ( tz , sizeof ( tz ) , " %+i:%i " , tzh , tzm ) ;
2013-03-31 04:21:08 +00:00
strftime ( timestamp , sizeof ( timestamp ) , " %Y-%m-%dT%H:%M:%SZ " , timeinfo ) ;
unparsable = false ;
//strftime
2013-05-11 14:02:55 +00:00
Q_snprintf ( msg , sizeof ( msg ) ,
2013-03-31 04:21:08 +00:00
" <iq type='result' to='%s' id='%s'> "
" <time xmlns='urn:xmpp:time'> "
2013-06-23 02:33:52 +00:00
" <tzo>%s</tzo> "
" <utc>%s</utc> "
2013-03-31 04:21:08 +00:00
" </time> "
" </iq> " , from , id , tz , timestamp ) ;
JCL_AddClientMessageString ( jcl , msg ) ;
}
# endif
2013-06-23 02:33:52 +00:00
ot = XML_ChildOfTree ( tree , " ping " , 0 ) ;
if ( ot & & ! strcmp ( ot - > xmlns , " urn:xmpp:ping " ) )
{
JCL_AddClientMessagef ( jcl , " <iq type='result' to='%s' id='%s' /> " , from , id ) ;
}
2013-03-31 04:21:08 +00:00
if ( unparsable )
{ //unsupported stuff
char msg [ 2048 ] ;
unparsable = false ;
2013-06-23 18:43:59 +00:00
Con_Printf ( " Unsupported iq get \n " ) ;
2013-03-31 04:21:08 +00:00
XML_ConPrintTree ( tree , 0 ) ;
//tell them OH NOES, instead of requiring some timeout.
2013-05-11 14:02:55 +00:00
Q_snprintf ( msg , sizeof ( msg ) ,
2013-03-31 04:21:08 +00:00
" <iq type='error' to='%s' id='%s'> "
" <error type='cancel'> "
" <service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> "
" </error> "
" </iq> " , from , id ) ;
JCL_AddClientMessageString ( jcl , msg ) ;
2005-12-11 20:11:22 +00:00
}
}
2013-06-23 02:33:52 +00:00
else if ( ! strcmp ( f , " set " ) )
{
xmltree_t * c ;
c = XML_ChildOfTree ( tree , " query " , 0 ) ;
if ( c & & ! strcmp ( c - > xmlns , " jabber:iq:roster " ) )
{
unparsable = false ;
JCL_RosterUpdate ( jcl , c ) ;
}
c = XML_ChildOfTree ( tree , " jingle " , 0 ) ;
if ( c & & ! strcmp ( c - > xmlns , " urn:xmpp:jingle:1 " ) )
{
JCL_ParseJingle ( jcl , c , from , id ) ;
unparsable = false ;
}
}
else if ( ! strcmp ( f , " result " ) | | ! strcmp ( f , " error " ) )
2013-03-31 04:21:08 +00:00
{
2013-06-23 02:33:52 +00:00
char * id = XML_GetParameter ( tree , " id " , " " ) ;
struct iq_s * * link , * iq ;
2013-03-31 04:21:08 +00:00
unparsable = false ;
2013-06-23 02:33:52 +00:00
for ( link = & jcl - > pendingiqs ; * link ; link = & ( * link ) - > next )
{
iq = * link ;
if ( ! strcmp ( iq - > id , id ) )
break ;
}
if ( * link )
2013-03-31 04:21:08 +00:00
{
2013-06-23 02:33:52 +00:00
iq = * link ;
* link = iq - > next ;
if ( iq - > callback )
{
if ( ! iq - > callback ( jcl , ! strcmp ( f , " error " ) ? NULL : tree ) )
{
2013-06-23 18:43:59 +00:00
Con_Printf ( " Invalid iq result \n " ) ;
2013-06-23 02:33:52 +00:00
XML_ConPrintTree ( tree , 0 ) ;
}
}
free ( iq ) ;
2013-03-31 04:21:08 +00:00
}
else
{
2013-06-23 18:43:59 +00:00
Con_Printf ( " Unrecognised iq result \n " ) ;
2013-03-31 04:21:08 +00:00
XML_ConPrintTree ( tree , 0 ) ;
}
}
if ( unparsable )
{
unparsable = false ;
2013-06-23 18:43:59 +00:00
Con_Printf ( " Unrecognised iq type \n " ) ;
2013-03-31 04:21:08 +00:00
XML_ConPrintTree ( tree , 0 ) ;
}
2005-12-11 20:11:22 +00:00
}
else if ( ! strcmp ( tree - > name , " message " ) )
{
2013-06-23 02:33:52 +00:00
f = XML_GetParameter ( tree , " from " , NULL ) ;
2013-03-31 04:21:08 +00:00
2013-06-23 02:33:52 +00:00
if ( f & & ! strcmp ( f , jcl - > jid ) )
unparsable = false ;
else
2013-03-31 04:21:08 +00:00
{
2013-06-23 02:33:52 +00:00
if ( f )
2013-03-31 04:21:08 +00:00
{
2013-06-23 02:33:52 +00:00
buddy_t * b ;
bresource_t * br ;
Q_strlcpy ( jcl - > defaultdest , f , sizeof ( jcl - > defaultdest ) ) ;
JCL_FindBuddy ( jcl , f , & b , & br ) ;
f = b - > name ;
b - > defaultresource = br ;
2013-03-31 04:21:08 +00:00
}
2013-06-23 02:33:52 +00:00
if ( f )
2013-03-31 04:21:08 +00:00
{
2013-06-23 02:33:52 +00:00
ot = XML_ChildOfTree ( tree , " composing " , 0 ) ;
if ( ot & & ! strcmp ( ot - > xmlns , " http://jabber.org/protocol/chatstates " ) )
{
unparsable = false ;
Con_SubPrintf ( f , " %s is typing \r " , f ) ;
}
ot = XML_ChildOfTree ( tree , " paused " , 0 ) ;
if ( ot & & ! strcmp ( ot - > xmlns , " http://jabber.org/protocol/chatstates " ) )
{
unparsable = false ;
Con_SubPrintf ( f , " %s has stopped typing \r " , f ) ;
}
ot = XML_ChildOfTree ( tree , " inactive " , 0 ) ;
if ( ot & & ! strcmp ( ot - > xmlns , " http://jabber.org/protocol/chatstates " ) )
{
unparsable = false ;
Con_SubPrintf ( f , " \r " , f ) ;
}
ot = XML_ChildOfTree ( tree , " active " , 0 ) ;
if ( ot & & ! strcmp ( ot - > xmlns , " http://jabber.org/protocol/chatstates " ) )
{
unparsable = false ;
Con_SubPrintf ( f , " \r " , f ) ;
}
ot = XML_ChildOfTree ( tree , " gone " , 0 ) ;
if ( ot & & ! strcmp ( ot - > xmlns , " http://jabber.org/protocol/chatstates " ) )
{
unparsable = false ;
Con_SubPrintf ( f , " %s has gone away \r " , f ) ;
}
2013-03-31 04:21:08 +00:00
}
2013-06-23 02:33:52 +00:00
ot = XML_ChildOfTree ( tree , " body " , 0 ) ;
if ( ot )
2013-03-31 04:21:08 +00:00
{
unparsable = false ;
2013-06-23 02:33:52 +00:00
if ( f )
Con_SubPrintf ( f , " %s: %s \n " , f , ot - > body ) ;
else
2013-06-23 18:43:59 +00:00
Con_Printf ( ot - > body ) ;
2013-06-23 02:33:52 +00:00
if ( BUILTINISVALID ( LocalSound ) )
2013-06-23 18:43:59 +00:00
pLocalSound ( " misc/talk.wav " ) ;
2013-03-31 04:21:08 +00:00
}
2013-06-23 02:33:52 +00:00
if ( unparsable )
2013-03-31 04:21:08 +00:00
{
unparsable = false ;
2013-06-23 02:33:52 +00:00
if ( jcl - > streamdebug )
{
2013-06-23 18:43:59 +00:00
Con_Printf ( " Received a message without a body \n " ) ;
2013-06-23 02:33:52 +00:00
XML_ConPrintTree ( tree , 0 ) ;
}
2013-03-31 04:21:08 +00:00
}
}
2005-12-11 20:11:22 +00:00
}
else if ( ! strcmp ( tree - > name , " presence " ) )
{
2013-06-23 02:33:52 +00:00
buddy_t * buddy ;
bresource_t * bres ;
char * from = XML_GetParameter ( tree , " from " , " " ) ;
2013-03-31 04:21:08 +00:00
xmltree_t * show = XML_ChildOfTree ( tree , " show " , 0 ) ;
xmltree_t * status = XML_ChildOfTree ( tree , " status " , 0 ) ;
xmltree_t * quake = XML_ChildOfTree ( tree , " quake " , 0 ) ;
2013-06-23 02:33:52 +00:00
char * type = XML_GetParameter ( tree , " type " , " " ) ;
char * serverip = NULL ;
char * servermap = NULL ;
2013-03-31 04:21:08 +00:00
if ( quake & & ! strcmp ( quake - > xmlns , " fteqw.com:game " ) )
2013-06-23 02:33:52 +00:00
{
serverip = XML_GetParameter ( quake , " serverip " , NULL ) ;
servermap = XML_GetParameter ( quake , " servermap " , NULL ) ;
}
2013-03-31 04:21:08 +00:00
2013-06-23 02:33:52 +00:00
if ( type & & ! strcmp ( type , " subscribe " ) )
{
Con_Printf ( " ^[[%s] \\ xmpp \\ %s^] wants to be your friend! ^[[Authorize] \\ xmppauth \\ %s^] ^[[Deny] \\ xmppdeny \\ %s^] \n " , from , from , from , from ) ;
}
else if ( type & & ! strcmp ( type , " unsubscribe " ) )
{
Con_Printf ( " ^[[%s] \\ xmpp \\ %s^] has unfriended you \n " , from , from ) ;
}
else if ( type & & ! strcmp ( type , " unsubscribed " ) )
{
Con_Printf ( " ^[[%s] \\ xmpp \\ %s^] is no longer unfriended you \n " , from , from ) ;
}
2013-03-31 04:21:08 +00:00
else
2013-06-23 02:33:52 +00:00
{
JCL_FindBuddy ( jcl , from , & buddy , & bres ) ;
if ( bres )
{
if ( servermap )
{
bres - > servertype = 2 ;
Q_strlcpy ( bres - > server , servermap , sizeof ( bres - > server ) ) ;
}
else if ( serverip )
{
bres - > servertype = 1 ;
Q_strlcpy ( bres - > server , serverip , sizeof ( bres - > server ) ) ;
}
else
{
bres - > servertype = 0 ;
Q_strlcpy ( bres - > server , " " , sizeof ( bres - > server ) ) ;
}
Q_strlcpy ( bres - > fstatus , ( status & & * status - > body ) ? status - > body : " " , sizeof ( bres - > fstatus ) ) ;
if ( ! tree - > child )
Q_strlcpy ( bres - > bstatus , " offline " , sizeof ( bres - > bstatus ) ) ;
else
Q_strlcpy ( bres - > bstatus , ( show & & * show - > body ) ? show - > body : " present " , sizeof ( bres - > bstatus ) ) ;
if ( bres - > servertype = = 2 )
Con_Printf ( " ^[[%s] \\ xmpp \\ %s^] is now ^[[Playing Quake - %s] \\ xmppjoin \\ %s^] \n " , buddy - > name , from , bres - > server , from ) ;
else if ( bres - > servertype = = 1 )
Con_Printf ( " ^[[%s] \\ xmpp \\ %s^] is now ^[[Playing Quake - %s] \\ observe \\ %s^] \n " , buddy - > name , from , bres - > server , bres - > server ) ;
else if ( * bres - > fstatus )
Con_Printf ( " ^[[%s] \\ xmpp \\ %s^] is now %s: %s \n " , buddy - > name , from , bres - > bstatus , bres - > fstatus ) ;
else
Con_Printf ( " ^[[%s] \\ xmpp \\ %s^] is now %s \n " , buddy - > name , from , bres - > bstatus ) ;
if ( ! tree - > child )
{
//remove this buddy resource
}
}
else
{
Con_Printf ( " Weird presence: \n " ) ;
XML_ConPrintTree ( tree , 0 ) ;
}
}
2013-03-31 04:21:08 +00:00
2005-12-11 20:11:22 +00:00
//we should keep a list of the people that we know of.
unparsable = false ;
}
else
2013-06-23 02:33:52 +00:00
{
2005-12-11 20:11:22 +00:00
Con_Printf ( " JCL unrecognised stanza: %s \n " , tree - > name ) ;
2013-06-23 02:33:52 +00:00
XML_ConPrintTree ( tree , 0 ) ;
}
2005-12-11 20:11:22 +00:00
XML_Destroy ( tree ) ;
memmove ( jcl - > bufferedinmessage , jcl - > bufferedinmessage + pos , jcl - > bufferedinammount - pos ) ;
jcl - > bufferedinammount - = pos ;
jcl - > instreampos - = pos ;
if ( unparsable )
{
Con_Printf ( " JCL: Input corrupt, urecognised, or unusable. Disconnecting. " ) ;
return JCL_KILL ;
}
return JCL_CONTINUE ;
}
void JCL_CloseConnection ( jclient_t * jcl )
{
Con_Printf ( " JCL: Disconnected from %s@%s \n " , jcl - > username , jcl - > domain ) ;
JCL_AddClientMessageString ( jcl , " </stream:stream> " ) ;
2013-06-23 18:43:59 +00:00
pNet_Close ( jcl - > socket ) ;
2005-12-11 20:11:22 +00:00
free ( jcl ) ;
}
2013-06-23 02:33:52 +00:00
//can be polled for server address updates
void JCL_GeneratePresence ( qboolean force )
{
int dummystat ;
char serveraddr [ 1024 * 16 ] ;
char servermap [ 1024 * 16 ] ;
//get the last server address
serveraddr [ 0 ] = 0 ;
servermap [ 0 ] = 0 ;
2013-06-23 18:43:59 +00:00
if ( ! pCvar_GetFloat ( " xmpp_nostatus " ) )
2013-06-23 02:33:52 +00:00
{
2013-06-23 18:43:59 +00:00
if ( pCvar_GetFloat ( " sv.state " ) )
2013-06-23 02:33:52 +00:00
{
2013-06-23 18:43:59 +00:00
pCvar_GetString ( " sv.mapname " , servermap , sizeof ( servermap ) ) ;
2013-06-23 02:33:52 +00:00
}
else
{
2013-06-23 18:43:59 +00:00
if ( ! pCvar_GetString ( " cl_serveraddress " , serveraddr , sizeof ( serveraddr ) ) )
2013-06-23 02:33:52 +00:00
serveraddr [ 0 ] = 0 ;
if ( BUILTINISVALID ( CL_GetStats ) )
{
//if we can't get any stats, its because we're not actually on the server.
2013-06-23 18:43:59 +00:00
if ( ! pCL_GetStats ( 0 , & dummystat , 1 ) )
2013-06-23 02:33:52 +00:00
serveraddr [ 0 ] = 0 ;
}
}
}
if ( force | | strcmp ( jclient - > curquakeserver , * servermap ? servermap : serveraddr ) )
{
char caps [ 256 ] ;
Q_strlcpy ( jclient - > curquakeserver , * servermap ? servermap : serveraddr , sizeof ( jclient - > curquakeserver ) ) ;
2013-06-24 09:04:00 +00:00
Con_Printf ( " Sending presence %s \n " , jclient - > curquakeserver ) ;
2013-06-23 02:33:52 +00:00
//note: ext='voice-v1 camera-v1 video-v1' is some legacy nonsense, and is required for voice calls with googletalk clients or something stupid like that
Q_snprintf ( caps , sizeof ( caps ) , " <c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='http://fteqw.com/ftexmppplugin' ver='%s'/> " , buildcapshash ( ) ) ;
if ( ! * jclient - > curquakeserver )
JCL_AddClientMessagef ( jclient ,
" <presence> "
" %s "
" </presence> " , caps ) ;
else if ( * servermap ) //if we're running a server, say so
JCL_AddClientMessagef ( jclient ,
" <presence> "
" <quake xmlns='fteqw.com:game' servermap='%s'/> "
" %s "
" </presence> "
, servermap , caps ) ;
else //if we're connected to a server, say so
JCL_AddClientMessagef ( jclient ,
" <presence> "
" <quake xmlns='fteqw.com:game' serverip='%s' /> "
" %s "
" </presence> "
, jclient - > curquakeserver , caps ) ;
}
}
2005-12-11 20:11:22 +00:00
//functions above this line allow connections to multiple servers.
//it is just the control functions that only allow one server.
2013-03-31 04:21:08 +00:00
qintptr_t JCL_Frame ( qintptr_t * args )
2005-12-11 20:11:22 +00:00
{
int stat = JCL_CONTINUE ;
if ( jclient )
{
2013-03-31 04:21:08 +00:00
if ( jclient - > connected )
{
2013-06-23 02:33:52 +00:00
JCL_GeneratePresence ( false ) ;
2013-03-31 04:21:08 +00:00
}
2005-12-11 20:11:22 +00:00
while ( stat = = JCL_CONTINUE )
stat = JCL_ClientFrame ( jclient ) ;
if ( stat = = JCL_KILL )
{
JCL_CloseConnection ( jclient ) ;
jclient = NULL ;
}
2013-06-23 02:33:52 +00:00
JCL_FlushOutgoing ( jclient ) ;
2005-12-11 20:11:22 +00:00
}
return 0 ;
}
2013-06-23 02:33:52 +00:00
void JCL_WriteConfig ( void )
{
if ( jclient - > connected )
{
qhandle_t config ;
2013-06-23 18:43:59 +00:00
pFS_Open ( " **plugconfig " , & config , 2 ) ;
2013-06-23 02:33:52 +00:00
if ( config > = 0 )
{
char buffer [ 8192 ] ;
Q_snprintf ( buffer , sizeof ( buffer ) , " %i \" %s \" \" %s@%s \" \" %s \" \n " ,
jclient - > tlsconnect , jclient - > server , jclient - > username , jclient - > domain , jclient - > password ) ;
2013-06-23 18:43:59 +00:00
pFS_Write ( config , buffer , strlen ( buffer ) ) ;
pFS_Close ( config ) ;
2013-06-23 02:33:52 +00:00
}
}
}
void JCL_LoadConfig ( void )
{
if ( ! jclient )
{
int len ;
qhandle_t config ;
char buf [ 8192 ] ;
char tls [ 256 ] ;
char server [ 256 ] ;
char account [ 256 ] ;
char password [ 256 ] ;
char * line = buf ;
qboolean oldtls ;
2013-06-23 18:43:59 +00:00
len = pFS_Open ( " **plugconfig " , & config , 1 ) ;
2013-06-23 02:33:52 +00:00
if ( config > = 0 )
{
if ( len > = sizeof ( buf ) )
len = sizeof ( buf ) - 1 ;
buf [ len ] = 0 ;
2013-06-23 18:43:59 +00:00
pFS_Read ( config , buf , len ) ;
pFS_Close ( config ) ;
2013-06-23 02:33:52 +00:00
2013-06-23 18:43:59 +00:00
line = JCL_ParseOut ( line , tls , sizeof ( tls ) ) ;
line = JCL_ParseOut ( line , server , sizeof ( server ) ) ;
line = JCL_ParseOut ( line , account , sizeof ( account ) ) ;
line = JCL_ParseOut ( line , password , sizeof ( password ) ) ;
2013-06-23 02:33:52 +00:00
oldtls = atoi ( tls ) ;
jclient = JCL_Connect ( server , oldtls ? 5223 : 5222 , oldtls , account , password ) ;
}
}
}
void JCL_PrintBuddyList ( char * console , jclient_t * jcl , qboolean all )
{
buddy_t * b ;
bresource_t * r ;
if ( ! jcl - > buddies )
Con_SubPrintf ( console , " You have no friends \n " ) ;
for ( b = jcl - > buddies ; b ; b = b - > next )
{
//if we don't actually know them, don't list them.
if ( ! b - > friended )
continue ;
if ( ! b - > resources ) //offline
{
if ( all )
Con_SubPrintf ( console , " ^[^7[%s] \\ xmpp \\ %s^]: offline \n " , b - > name , b - > accountdomain ) ;
}
else if ( b - > resources - > next )
{ //multiple potential resources
Con_SubPrintf ( console , " ^[[%s] \\ xmpp \\ %s^] \n " , b - > name , b - > accountdomain ) ;
for ( r = b - > resources ; r ; r = r - > next )
{
if ( r - > servertype = = 2 )
Con_SubPrintf ( console , " ^[[%s] \\ xmpp \\ %s/%s^]: ^[[Playing Quake - %s] \\ xmppjoin \\ %s/%s^] \n " , r - > resource , b - > accountdomain , r - > resource , r - > server , b - > accountdomain , r - > resource ) ;
else if ( r - > servertype )
Con_SubPrintf ( console , " ^[[%s] \\ xmpp \\ %s/%s^]: ^[[Playing Quake - %s] \\ observe \\ %s^] \n " , r - > resource , b - > accountdomain , r - > resource , r - > server , r - > server ) ;
else if ( * r - > fstatus )
Con_SubPrintf ( console , " ^[[%s] \\ xmpp \\ %s/%s^]: %s - %s \n " , r - > resource , b - > accountdomain , r - > resource , r - > bstatus , r - > fstatus ) ;
else
Con_SubPrintf ( console , " ^[[%s] \\ xmpp \\ %s/%s^]: %s \n " , r - > resource , b - > accountdomain , r - > resource , r - > bstatus ) ;
}
}
else //only one resource
{
r = b - > resources ;
if ( ! strcmp ( r - > server , " - " ) )
Con_SubPrintf ( console , " ^[[%s] \\ xmpp \\ %s/%s^]: ^[[Playing Quake] \\ xmppjoin \\ %s/%s^] \n " , b - > name , b - > accountdomain , r - > resource , b - > accountdomain , r - > resource ) ;
else if ( * r - > server )
Con_SubPrintf ( console , " ^[[%s] \\ xmpp \\ %s/%s^]: ^[[Playing Quake - %s] \\ observe \\ %s^] \n " , b - > name , b - > accountdomain , r - > resource , r - > server , r - > server ) ;
else if ( * r - > fstatus )
Con_SubPrintf ( console , " ^[[%s] \\ xmpp \\ %s/%s^]: %s - %s \n " , b - > name , b - > accountdomain , r - > resource , r - > bstatus , r - > fstatus ) ;
else
Con_SubPrintf ( console , " ^[[%s] \\ xmpp \\ %s/%s^]: %s \n " , b - > name , b - > accountdomain , r - > resource , r - > bstatus ) ;
}
}
}
void JCL_SendMessage ( jclient_t * jcl , char * to , char * msg )
{
char markup [ 256 ] ;
buddy_t * b ;
bresource_t * br ;
JCL_FindBuddy ( jcl , to , & b , & br ) ;
if ( br )
JCL_AddClientMessagef ( jcl , " <message to='%s/%s'><body> " , b - > accountdomain , br - > resource ) ;
else
JCL_AddClientMessagef ( jcl , " <message to='%s'><body> " , b - > accountdomain ) ;
JCL_AddClientMessage ( jcl , markup , XML_Markup ( msg , markup , sizeof ( markup ) ) - markup ) ;
JCL_AddClientMessageString ( jcl , " </body></message> " ) ;
Con_SubPrintf ( b - > name , " %s: " COLOURYELLOW " %s \n " , " >> " , msg ) ;
}
void JCL_Command ( char * console )
2005-12-11 20:11:22 +00:00
{
char imsg [ 8192 ] ;
char arg [ 6 ] [ 256 ] ;
char * msg ;
int i ;
2013-06-23 18:43:59 +00:00
pCmd_Args ( imsg , sizeof ( imsg ) ) ;
2005-12-11 20:11:22 +00:00
msg = imsg ;
for ( i = 0 ; i < 6 ; i + + )
{
if ( ! msg )
continue ;
2013-06-23 18:43:59 +00:00
msg = JCL_ParseOut ( msg , arg [ i ] , sizeof ( arg [ i ] ) ) ;
2005-12-11 20:11:22 +00:00
}
if ( * arg [ 0 ] = = ' / ' )
{
if ( ! strcmp ( arg [ 0 ] + 1 , " tlsopen " ) | | ! strcmp ( arg [ 0 ] + 1 , " tlsconnect " ) )
2013-06-23 02:33:52 +00:00
{ //tlsconnect is 'old'.
2009-04-19 01:48:10 +00:00
if ( ! * arg [ 1 ] )
{
2013-06-23 02:33:52 +00:00
Con_TrySubPrint ( console , " tlsopen [server] [account] [password] \n " ) ;
2009-04-19 01:48:10 +00:00
return ;
}
2005-12-11 20:11:22 +00:00
if ( jclient )
{
2013-06-23 02:33:52 +00:00
Con_TrySubPrint ( console , " You are already connected \n Please /quit first \n " ) ;
2005-12-11 20:11:22 +00:00
return ;
}
2007-09-17 20:55:15 +00:00
if ( ! * arg [ 1 ] )
{
2013-06-23 02:33:52 +00:00
Con_SubPrintf ( console , " %s <server[:port]> <account[@domain]> <password> \n " , arg [ 0 ] + 1 ) ;
2007-09-17 20:55:15 +00:00
return ;
}
2005-12-11 20:11:22 +00:00
jclient = JCL_Connect ( arg [ 1 ] , 5223 , true , arg [ 2 ] , arg [ 3 ] ) ;
2009-04-19 01:48:10 +00:00
if ( ! jclient )
{
2013-06-23 02:33:52 +00:00
Con_TrySubPrint ( console , " Connect failed \n " ) ;
2009-04-19 01:48:10 +00:00
return ;
}
2005-12-11 20:11:22 +00:00
}
else if ( ! strcmp ( arg [ 0 ] + 1 , " open " ) | | ! strcmp ( arg [ 0 ] + 1 , " connect " ) )
{
2009-04-19 01:48:10 +00:00
if ( ! * arg [ 1 ] )
{
2013-06-23 02:33:52 +00:00
Con_TrySubPrint ( console , " open [server] [account] [password] \n " ) ;
2009-04-19 01:48:10 +00:00
return ;
}
2005-12-11 20:11:22 +00:00
if ( jclient )
{
2013-06-23 02:33:52 +00:00
Con_TrySubPrint ( console , " You are already connected \n Please /quit first \n " ) ;
2005-12-11 20:11:22 +00:00
return ;
}
2007-09-17 20:55:15 +00:00
if ( ! * arg [ 1 ] )
{
2013-06-23 02:33:52 +00:00
Con_SubPrintf ( console , " %s <server[:port]> <account[@domain]> <password> \n " , arg [ 0 ] + 1 ) ;
2007-09-17 20:55:15 +00:00
return ;
}
2005-12-11 20:11:22 +00:00
jclient = JCL_Connect ( arg [ 1 ] , 5222 , false , arg [ 2 ] , arg [ 3 ] ) ;
2009-04-19 01:48:10 +00:00
if ( ! jclient )
{
2013-06-23 02:33:52 +00:00
Con_TrySubPrint ( console , " Connect failed \n " ) ;
2009-04-19 01:48:10 +00:00
return ;
}
2005-12-11 20:11:22 +00:00
}
2013-06-23 02:33:52 +00:00
else if ( ! strcmp ( arg [ 0 ] + 1 , " help " ) )
{
Con_TrySubPrint ( console , " ^[/ " COMMANDPREFIX " /tlsconnect XMPPSERVER USERNAME@DOMAIN PASSWORD^] \n " ) ;
if ( BUILTINISVALID ( Net_SetTLSClient ) )
Con_Printf ( " for example: ^[/ " COMMANDPREFIX " /tlsconnect talk.google.com myusername@gmail.com mypassword^] \n "
" Note that this info will be used the next time you start quake. \n " ) ;
//small note:
//for the account 'me@example.com' the server to connect to can be displayed with:
//nslookup -querytype=SRV _xmpp-client._tcp.example.com
//srv resolving seems to be non-standard on each system, I don't like having to special case things.
Con_TrySubPrint ( console , " ^[/ " COMMANDPREFIX " /help^] \n "
" This text... \n " ) ;
Con_TrySubPrint ( console , " ^[/ " COMMANDPREFIX " /raw <XML STANZAS/>^] \n "
" For debug hackery. \n " ) ;
Con_TrySubPrint ( console , " ^[/ " COMMANDPREFIX " /friend accountname friendlyname^] \n "
" Befriends accountname, and shows them in your various lists using the friendly name. Can also be used to rename friends. \n " ) ;
Con_TrySubPrint ( console , " ^[/ " COMMANDPREFIX " /unfriend accountname^] \n "
" Ostracise your new best enemy. You will no longer see them and they won't be able to contact you. \n " ) ;
Con_TrySubPrint ( console , " ^[/ " COMMANDPREFIX " /blist^] \n "
" Show all your friends! Names are clickable and will begin conversations. \n " ) ;
Con_TrySubPrint ( console , " ^[/ " COMMANDPREFIX " /quit^] \n "
" Disconnect from the XMPP server, noone will be able to hear you scream. \n " ) ;
Con_TrySubPrint ( console , " ^[/ " COMMANDPREFIX " /msg ACCOUNTNAME your message goes here^] \n "
" Sends a message to the named person. If given a resource postfix, your message will be sent only to that resource. \n " ) ;
Con_TrySubPrint ( console , " If no arguments, will print out your friends list. If no /command is used, the arguments will be sent as a message to the person you last sent a message to. \n " ) ;
}
2005-12-11 20:11:22 +00:00
else if ( ! jclient )
{
2013-06-23 02:33:52 +00:00
Con_SubPrintf ( console , " You are not connected. Cannot %s \n " , arg [ 0 ] ) ;
2005-12-11 20:11:22 +00:00
}
else if ( ! strcmp ( arg [ 0 ] + 1 , " quit " ) )
{
2013-06-23 02:33:52 +00:00
//disconnect from the xmpp server.
2005-12-11 20:11:22 +00:00
JCL_CloseConnection ( jclient ) ;
jclient = NULL ;
}
2013-06-23 02:33:52 +00:00
else if ( ! strcmp ( arg [ 0 ] + 1 , " blist " ) )
{
//print out a full list of everyone, even those offline.
JCL_PrintBuddyList ( console , jclient , true ) ;
}
else if ( ! strcmp ( arg [ 0 ] + 1 , " clear " ) )
{
//just clears the current console.
if ( * console )
{
2013-06-23 18:43:59 +00:00
pCon_Destroy ( console ) ;
Con_SubPrintf ( console , " " ) ;
pCon_SetActive ( console ) ;
2013-06-23 02:33:52 +00:00
}
else
2013-06-23 18:43:59 +00:00
pCmd_AddText ( " \n clear \n " , true ) ;
2013-06-23 02:33:52 +00:00
}
2005-12-11 20:11:22 +00:00
else if ( ! strcmp ( arg [ 0 ] + 1 , " msg " ) )
{
2013-06-23 02:33:52 +00:00
//FIXME: validate the dest. deal with xml markup in dest.
2013-05-11 14:02:55 +00:00
Q_strlcpy ( jclient - > defaultdest , arg [ 1 ] , sizeof ( jclient - > defaultdest ) ) ;
2005-12-11 20:11:22 +00:00
msg = arg [ 2 ] ;
2013-06-23 02:33:52 +00:00
JCL_SendMessage ( jclient , jclient - > defaultdest , msg ) ;
}
else if ( ! strcmp ( arg [ 0 ] + 1 , " friend " ) )
{
//FIXME: validate the name. deal with xml markup.
//can also rename. We should probably read back the groups for the update.
JCL_SendIQf ( jclient , NULL , " set " , NULL , " <query xmlns='jabber:iq:roster'><item jid='%s' name='%s'></item></query> " , arg [ 1 ] , arg [ 2 ] ) ;
2005-12-11 20:11:22 +00:00
2013-06-23 02:33:52 +00:00
//start looking for em
JCL_AddClientMessagef ( jclient , " <presence to='%s' type='subscribe'/> " , arg [ 1 ] ) ;
//let em see us
if ( jclient - > preapproval )
JCL_AddClientMessagef ( jclient , " <presence to='%s' type='subscribed'/> " , arg [ 1 ] ) ;
}
else if ( ! strcmp ( arg [ 0 ] + 1 , " unfriend " ) )
{
//FIXME: validate the name. deal with xml markup.
//hide from em
JCL_AddClientMessagef ( jclient , " <presence to='%s' type='unsubscribed'/> " , arg [ 1 ] ) ;
//stop looking for em
JCL_AddClientMessagef ( jclient , " <presence to='%s' type='unsubscribe'/> " , arg [ 1 ] ) ;
//stop listing em
JCL_SendIQf ( jclient , NULL , " set " , NULL , " <query xmlns='jabber:iq:roster'><item jid='%s' subscription='remove' /></query> " , arg [ 1 ] ) ;
}
else if ( ! strcmp ( arg [ 0 ] + 1 , " join " ) )
{
JCL_Join ( jclient , arg [ 1 ] ) ;
2005-12-11 20:11:22 +00:00
}
2013-06-23 02:33:52 +00:00
else if ( ! strcmp ( arg [ 0 ] + 1 , " raw " ) )
2013-03-31 04:21:08 +00:00
{
2013-06-23 02:33:52 +00:00
jclient - > streamdebug = true ;
2013-03-31 04:21:08 +00:00
JCL_AddClientMessageString ( jclient , arg [ 1 ] ) ;
}
2005-12-11 20:11:22 +00:00
else
2013-06-23 02:33:52 +00:00
Con_SubPrintf ( console , " Unrecognised command: %s \n " , arg [ 0 ] ) ;
2005-12-11 20:11:22 +00:00
}
else
{
if ( jclient )
{
msg = imsg ;
2013-06-23 02:33:52 +00:00
if ( ! * msg )
{
if ( ! * console )
{
JCL_PrintBuddyList ( console , jclient , false ) ;
Con_TrySubPrint ( console , " For help, type \" ^[/ " COMMANDPREFIX " /help^] \" \n " ) ;
}
}
else
{
JCL_SendMessage ( jclient , jclient - > defaultdest , msg ) ;
}
2005-12-11 20:11:22 +00:00
}
else
2007-09-17 20:55:15 +00:00
{
2013-06-23 02:33:52 +00:00
Con_TrySubPrint ( console , " Not connected. For help, type \" ^[/ " COMMANDPREFIX " /help^] \" \n " ) ;
2007-09-17 20:55:15 +00:00
}
2005-12-11 20:11:22 +00:00
}
}