2016-07-12 00:40:13 +00:00
# include "xmpp.h"
# ifdef JINGLE
2017-10-12 12:02:25 +00:00
static struct c2c_s * JCL_JingleAddContentToSession ( jclient_t * jcl , struct c2c_s * c2c , const char * with , bresource_t * bres , qboolean creator , const char * sid , const char * cname , int method , int mediatype )
2016-07-12 00:40:13 +00:00
{
struct icestate_s * ice = NULL ;
char generatedname [ 64 ] ;
char stunhost [ 256 ] ;
int c ;
if ( ! bres )
return NULL ;
//make sure we can add more contents to this session
//block dupe content names.
if ( c2c )
{
if ( c2c - > contents = = sizeof ( c2c - > content ) / sizeof ( c2c - > content [ 0 ] ) )
return NULL ;
for ( c = 0 ; c < c2c - > contents ; c + + )
if ( ! strcmp ( c2c - > content [ c ] . name , cname ) )
return NULL ;
}
if ( piceapi )
ice = piceapi - > ICE_Create ( NULL , sid , with , method , mediatype ) ;
if ( ice )
{
piceapi - > ICE_Get ( ice , " sid " , generatedname , sizeof ( generatedname ) ) ;
sid = generatedname ;
//the controlling role MUST be assumed by the initiator and the controlled role MUST be assumed by the responder
piceapi - > ICE_Set ( ice , " controller " , creator ? " 1 " : " 0 " ) ;
if ( creator & & mediatype = = ICEP_VOICE )
{
//note: the engine will ignore codecs it does not support.
2017-06-21 01:24:25 +00:00
piceapi - > ICE_Set ( ice , " codec96 " , " opus@48000 " ) ;
piceapi - > ICE_Set ( ice , " codec97 " , " speex@16000 " ) ; //wide
piceapi - > ICE_Set ( ice , " codec98 " , " speex@8000 " ) ; //narrow
piceapi - > ICE_Set ( ice , " codec99 " , " speex@32000 " ) ; //ultrawide
piceapi - > ICE_Set ( ice , " codec0 " , " pcmu@8000 " ) ;
piceapi - > ICE_Set ( ice , " codec8 " , " pcma@8000 " ) ;
2016-07-12 00:40:13 +00:00
}
}
else
return NULL ; //no way to get the local ip otherwise, which means things won't work proper
if ( ! c2c )
{
c2c = malloc ( sizeof ( * c2c ) + strlen ( sid ) ) ;
memset ( c2c , 0 , sizeof ( * c2c ) ) ;
c2c - > next = jcl - > c2c ;
jcl - > c2c = c2c ;
strcpy ( c2c - > sid , sid ) ; //safe due to trailing space.
c2c - > with = strdup ( with ) ;
c2c - > peercaps = bres - > caps ;
c2c - > creator = creator ;
}
Q_strlcpy ( c2c - > content [ c2c - > contents ] . name , cname , sizeof ( c2c - > content [ c2c - > contents ] . name ) ) ;
c2c - > content [ c2c - > contents ] . method = method ;
c2c - > content [ c2c - > contents ] . mediatype = mediatype ;
//copy out the interesting parameters
c2c - > content [ c2c - > contents ] . ice = ice ;
c2c - > contents + + ;
//query dns to see if there's a stunserver hosted by the same domain
//nslookup -querytype=SRV _stun._udp.example.com
//for msn, live.com has one, messanger.live.com has one, but messenger.live.com does NOT. seriously, the typo has more services. wtf microsoft?
//google doesn't provide a stun srv entry
//facebook doesn't provide a stun srv entry
Q_snprintf ( stunhost , sizeof ( stunhost ) , " _stun._udp.%s " , jcl - > domain ) ;
if ( NET_DNSLookup_SRV ( stunhost , stunhost , sizeof ( stunhost ) ) )
piceapi - > ICE_Set ( ice , " stunip " , stunhost ) ;
else
{
//there is no real way to query stun servers from the xmpp server.
//while there is some extdisco extension (aka: the 'standard' way), it has some huge big fat do-not-implement message (and googletalk does not implement it).
//google provide their own jingleinfo extension. Which also has some huge fat 'do-not-implement' message. hardcoding the address should provide equivelent longevity. :(
//google also don't provide stun srv records.
//so we're basically screwed if we want to work with the googletalk xmpp service long term.
//more methods are best, I suppose, but I'm lazy.
//yes, hardcoding means that other services might 'borrow' googles' stun servers.
piceapi - > ICE_Set ( ice , " stunport " , " 19302 " ) ;
piceapi - > ICE_Set ( ice , " stunip " , " stun.l.google.com " ) ;
}
return c2c ;
}
static qboolean JCL_JingleAcceptAck ( jclient_t * jcl , xmltree_t * tree , struct iq_s * iq )
{
int i ;
struct c2c_s * c2c ;
if ( tree )
{
for ( c2c = jcl - > c2c ; c2c ; c2c = c2c - > next )
{
if ( c2c = = iq - > usrptr )
{
for ( i = 0 ; i < c2c - > contents ; i + + )
{
if ( c2c - > content [ i ] . ice )
piceapi - > ICE_Set ( c2c - > content [ i ] . ice , " state " , STRINGIFY ( ICE_CONNECTING ) ) ;
}
}
}
}
return true ;
}
static void JCL_PopulateAudioDescription ( xmltree_t * description , struct icestate_s * ice )
{
xmltree_t * payload ;
int i ;
2017-06-21 01:24:25 +00:00
int pcma = - 1 , pcmu = - 1 ;
for ( i = 0 ; i < = 127 ; i + + )
2016-07-12 00:40:13 +00:00
{
char codecname [ 64 ] ;
char argn [ 64 ] ;
Q_snprintf ( argn , sizeof ( argn ) , " codec%i " , i ) ;
2017-06-21 01:24:25 +00:00
if ( piceapi - > ICE_Get ( ice , argn , codecname , sizeof ( codecname ) ) )
{
if ( ! strcasecmp ( codecname , " speex@8000 " ) | | ! strcasecmp ( codecname , " speex@16000 " ) | | ! strcasecmp ( codecname , " speex@32000 " ) )
{ //speex narrowband
payload = XML_CreateNode ( description , " payload-type " , " " , " " ) ;
XML_AddParameter ( payload , " channels " , " 1 " ) ;
XML_AddParameter ( payload , " clockrate " , codecname + 6 ) ;
XML_AddParameter ( payload , " id " , argn + 5 ) ;
XML_AddParameter ( payload , " name " , " speex " ) ;
}
else if ( ! strcasecmp ( codecname , " opus " ) | | ! strcasecmp ( codecname , " opus@48000 " ) )
{ //opus codec. implicitly at 48khz
payload = XML_CreateNode ( description , " payload-type " , " " , " " ) ;
XML_AddParameter ( payload , " channels " , " 1 " ) ;
XML_AddParameter ( payload , " id " , argn + 5 ) ;
XML_AddParameter ( payload , " name " , " opus " ) ;
}
else if ( ! strcasecmp ( codecname , " pcma@8000 " ) | | ! strcasecmp ( codecname , " pcmu@8000 " ) )
{ //pcma/pcmu.
//these get flagged to ensure they appear last, because they're not very good, esp compared to opus,
// however they are simple and more widely distributed on traditional voice services,
// so they're an important fallback
if ( ! strcasecmp ( codecname , " pcma@8000 " ) & & pcma < 0 )
pcma = i ;
else if ( ! strcasecmp ( codecname , " pcmu@8000 " ) & & pcmu < 0 )
pcmu = i ;
else
{
payload = XML_CreateNode ( description , " payload-type " , " " , " " ) ;
XML_AddParameter ( payload , " channels " , " 1 " ) ;
XML_AddParameter ( payload , " clockrate " , codecname + 5 ) ;
XML_AddParameter ( payload , " id " , argn + 5 ) ;
codecname [ 4 ] = 0 ;
XML_AddParameter ( payload , " name " , codecname ) ;
}
}
2016-07-12 00:40:13 +00:00
}
}
2017-06-21 01:24:25 +00:00
if ( pcma > = 0 )
{
payload = XML_CreateNode ( description , " payload-type " , " " , " " ) ;
XML_AddParameter ( payload , " channels " , " 1 " ) ;
XML_AddParameter ( payload , " clockrate " , " 8000 " ) ;
XML_AddParameteri ( payload , " id " , pcma ) ;
XML_AddParameter ( payload , " name " , " pcma " ) ;
}
if ( pcmu > = 0 )
{
payload = XML_CreateNode ( description , " payload-type " , " " , " " ) ;
XML_AddParameter ( payload , " channels " , " 1 " ) ;
XML_AddParameter ( payload , " clockrate " , " 8000 " ) ;
XML_AddParameteri ( payload , " id " , pcmu ) ;
XML_AddParameter ( payload , " name " , " pcmu " ) ;
}
2016-07-12 00:40:13 +00:00
}
enum
{
JE_ACKNOWLEDGE ,
JE_UNSUPPORTED ,
JE_OUTOFORDER ,
JE_TIEBREAK ,
JE_UNKNOWNSESSION ,
JE_UNSUPPORTEDINFO
} ;
2017-10-12 12:02:25 +00:00
static void JCL_JingleError ( jclient_t * jcl , xmltree_t * tree , const char * from , const char * id , int type )
2016-07-12 00:40:13 +00:00
{
switch ( type )
{
case JE_ACKNOWLEDGE :
JCL_AddClientMessagef ( jcl ,
" <iq type='result' to='%s' id='%s' /> " , from , id ) ;
break ;
case JE_UNSUPPORTED :
JCL_AddClientMessagef ( jcl ,
" <iq id='%s' to='%s' type='error'> "
" <error type='cancel'> "
" <bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> "
" </error> "
" </iq> " , id , from ) ;
break ;
case JE_UNKNOWNSESSION :
JCL_AddClientMessagef ( jcl ,
" <iq id='%s' to='%s' type='error'> "
" <error type='modify'> "
" <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> "
" <unknown-session xmlns='urn:xmpp:jingle:errors:1'/> "
" </error> "
" </iq> " , id , from ) ;
break ;
case JE_UNSUPPORTEDINFO :
JCL_AddClientMessagef ( jcl ,
" <iq id='%s' to='%s' type='error'> "
" <error type='modify'> "
" <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> "
" <unsupported-info xmlns='urn:xmpp:jingle:errors:1'/> "
" </error> "
" </iq> " , id , from ) ;
break ;
}
}
/*
sends a jingle message to the peer .
action should be one of multiple things :
session - terminate - totally not acceptable . this also closes the c2c
session - accept - details are okay . this also begins ice polling ( on iq ack , once we ' re sure the peer got our message )
( internally generated ) transport - replace - details are okay , except we want a different transport method .
*/
static qboolean JCL_JingleSend ( jclient_t * jcl , struct c2c_s * c2c , char * action )
{
qboolean result ;
xmltree_t * jingle ;
int c ;
// struct icestate_s *ice = c2c->ice;
qboolean wasaccept = false ;
int transportmode = ICEM_ICE ;
# ifdef VOIP_LEGACY
int cap = c2c - > peercaps ;
# endif
if ( ! c2c - > contents )
action = " session-terminate " ;
# ifdef VOIP_LEGACY
if ( ( cap & CAP_GOOGLE_VOICE ) & & ! ( cap & CAP_VOICE ) )
{
//legacy crap for google compatibility.
if ( ! strcmp ( action , " session-initiate " ) )
{
xmltree_t * session = XML_CreateNode ( NULL , " session " , " http://www.google.com/session " , " " ) ;
xmltree_t * description = XML_CreateNode ( session , " description " , " http://www.google.com/session/phone " , " " ) ;
XML_AddParameter ( session , " id " , c2c - > sid ) ;
XML_AddParameter ( session , " initiator " , jcl - > jid ) ;
XML_AddParameter ( session , " type " , " initiate " ) ;
for ( c = 0 ; c < c2c - > contents ; c + + )
JCL_PopulateAudioDescription ( description , c2c - > content [ c ] . ice ) ;
JCL_SendIQNode ( jcl , NULL , " set " , c2c - > with , session , true ) ;
}
else if ( ! strcmp ( action , " session-accept " ) )
{
xmltree_t * session = XML_CreateNode ( NULL , " session " , " http://www.google.com/session " , " " ) ;
xmltree_t * description = XML_CreateNode ( session , " description " , " http://www.google.com/session/phone " , " " ) ;
XML_AddParameter ( session , " id " , c2c - > sid ) ;
XML_AddParameter ( session , " initiator " , c2c - > with ) ;
XML_AddParameter ( session , " type " , " accept " ) ;
for ( c = 0 ; c < c2c - > contents ; c + + )
JCL_PopulateAudioDescription ( description , c2c - > content [ c ] . ice ) ;
JCL_SendIQNode ( jcl , JCL_JingleAcceptAck , " set " , c2c - > with , session , true ) - > usrptr = c2c ;
c2c - > accepted = true ;
}
else if ( ! strcmp ( action , " transport-info " ) )
{
/*FIXME
struct icecandinfo_s * ca ;
while ( ( ca = piceapi - > ICE_GetLCandidateInfo ( ice ) ) )
{
xmltree_t * session = XML_CreateNode ( NULL , " session " , " http://www.google.com/session " , " " ) ;
XML_AddParameter ( session , " id " , c2c - > sid ) ;
XML_AddParameter ( session , " initiator " , c2c - > creator ? jcl - > jid : c2c - > with ) ;
XML_AddParameter ( session , " type " , " candidates " ) ;
//one per message, apparently
if ( ca )
{
char * ctypename [ ] = { " local " , " stun " , " stun " , " relay " } ;
char * pref [ ] = { " 1 " , " 0.9 " , " 0.5 " , " 0.2 " } ;
xmltree_t * candidate = XML_CreateNode ( session , " candidate " , " " , " " ) ;
char pwd [ 64 ] ;
char uname [ 64 ] ;
piceapi - > ICE_Get ( ice , " lufrag " , uname , sizeof ( uname ) ) ;
piceapi - > ICE_Get ( ice , " lpwd " , pwd , sizeof ( pwd ) ) ;
XML_AddParameter ( candidate , " address " , ca - > addr ) ;
XML_AddParameteri ( candidate , " port " , ca - > port ) ;
XML_AddParameter ( candidate , " name " , ( ca - > component = = 1 ) ? " rtp " : " rtcp " ) ;
XML_AddParameter ( candidate , " username " , uname ) ;
XML_AddParameter ( candidate , " password " , pwd ) ;
XML_AddParameter ( candidate , " preference " , pref [ ca - > type ] ) ; //FIXME: c->priority
XML_AddParameter ( candidate , " protocol " , " udp " ) ;
XML_AddParameter ( candidate , " type " , ctypename [ ca - > type ] ) ;
XML_AddParameteri ( candidate , " generation " , ca - > generation ) ;
XML_AddParameteri ( candidate , " network " , ca - > network ) ;
}
JCL_SendIQNode ( jcl , NULL , " set " , c2c - > with , session , true ) ;
}
*/ Con_Printf ( " No idea how to write candidates for gingle \n " ) ;
}
else if ( ! strcmp ( action , " session-terminate " ) )
{
struct c2c_s * * link ;
xmltree_t * session = XML_CreateNode ( NULL , " session " , " http://www.google.com/session " , " " ) ;
XML_AddParameter ( session , " id " , c2c - > sid ) ;
XML_AddParameter ( session , " initiator " , c2c - > creator ? jcl - > jid : c2c - > with ) ;
XML_AddParameter ( session , " type " , " terminate " ) ;
JCL_SendIQNode ( jcl , NULL , " set " , c2c - > with , session , true ) ;
for ( link = & jcl - > c2c ; * link ; link = & ( * link ) - > next )
{
if ( * link = = c2c )
{
* link = c2c - > next ;
break ;
}
}
for ( c = 0 ; c < c2c - > contents ; c + + )
{
if ( c2c - > content [ c ] . ice )
piceapi - > ICE_Close ( c2c - > content [ c ] . ice ) ;
c2c - > content [ c ] . ice = NULL ;
}
return false ;
}
else
{
return false ;
}
return true ;
}
# endif
jingle = XML_CreateNode ( NULL , " jingle " , " urn:xmpp:jingle:1 " , " " ) ;
XML_AddParameter ( jingle , " sid " , c2c - > sid ) ;
if ( ! strcmp ( action , " session-initiate " ) )
{ //these attributes are meant to only be present in initiate. for call forwarding etc. which we don't properly support.
2017-09-20 11:27:13 +00:00
XML_AddParameter ( jingle , " initiator " , jcl - > fulljid ) ;
2016-07-12 00:40:13 +00:00
}
if ( ! strcmp ( action , " session-terminate " ) )
{
struct c2c_s * * link ;
for ( link = & jcl - > c2c ; * link ; link = & ( * link ) - > next )
{
if ( * link = = c2c )
{
* link = c2c - > next ;
break ;
}
}
for ( c = 0 ; c < c2c - > contents ; c + + )
{
if ( c2c - > content [ c ] . ice )
piceapi - > ICE_Close ( c2c - > content [ c ] . ice ) ;
c2c - > content [ c ] . ice = NULL ;
}
result = false ;
}
else
{
result = true ;
for ( c = 0 ; c < c2c - > contents ; c + + )
{
xmltree_t * content = XML_CreateNode ( jingle , " content " , " " , " " ) ;
struct icestate_s * ice = c2c - > content [ c ] . ice ;
XML_AddParameter ( content , " name " , c2c - > content [ c ] . name ) ;
if ( ! strcmp ( action , " session-accept " ) )
{
if ( c2c - > content [ c ] . method = = transportmode )
2017-09-20 11:27:13 +00:00
XML_AddParameter ( jingle , " responder " , jcl - > fulljid ) ;
2016-07-12 00:40:13 +00:00
else
action = " transport-replace " ;
}
{
xmltree_t * description ;
xmltree_t * transport ;
if ( transportmode = = ICEM_RAW )
{
transport = XML_CreateNode ( content , " transport " , " urn:xmpp:jingle:transports:raw-udp:1 " , " " ) ;
{
xmltree_t * candidate ;
struct icecandinfo_s * b = NULL ;
struct icecandinfo_s * c ;
while ( ( c = piceapi - > ICE_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 ( transportmode = = ICEM_ICE )
{
char val [ 64 ] ;
transport = XML_CreateNode ( content , " transport " , " urn:xmpp:jingle:transports:ice-udp:1 " , " " ) ;
piceapi - > ICE_Get ( ice , " lufrag " , val , sizeof ( val ) ) ;
XML_AddParameter ( transport , " ufrag " , val ) ;
piceapi - > ICE_Get ( ice , " lpwd " , val , sizeof ( val ) ) ;
XML_AddParameter ( transport , " pwd " , val ) ;
{
struct icecandinfo_s * c ;
while ( ( c = piceapi - > ICE_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 ) ;
}
}
}
if ( strcmp ( action , " transport-info " ) )
{
# ifdef VOIP
if ( c2c - > content [ c ] . mediatype = = ICEP_VOICE )
{
XML_AddParameter ( content , " senders " , " both " ) ;
XML_AddParameter ( content , " creator " , " initiator " ) ;
description = XML_CreateNode ( content , " description " , " urn:xmpp:jingle:apps:rtp:1 " , " " ) ;
XML_AddParameter ( description , " media " , MEDIATYPE_AUDIO ) ;
JCL_PopulateAudioDescription ( description , ice ) ;
}
else if ( c2c - > content [ c ] . mediatype = = ICEP_VIDEO )
{
XML_AddParameter ( content , " senders " , " both " ) ;
XML_AddParameter ( content , " creator " , " initiator " ) ;
description = XML_CreateNode ( content , " description " , " urn:xmpp:jingle:apps:rtp:1 " , " " ) ;
XML_AddParameter ( description , " media " , MEDIATYPE_VIDEO ) ;
//JCL_PopulateAudioDescription(description, ice);
}
# endif
else
{
description = XML_CreateNode ( content , " description " , QUAKEMEDIAXMLNS , " " ) ;
XML_AddParameter ( description , " media " , MEDIATYPE_QUAKE ) ;
if ( c2c - > content [ c ] . mediatype = = ICEP_QWSERVER )
XML_AddParameter ( description , " host " , " me " ) ;
else if ( c2c - > content [ c ] . mediatype = = ICEP_QWCLIENT )
XML_AddParameter ( description , " host " , " you " ) ;
}
}
}
}
if ( ! strcmp ( action , " session-accept " ) )
c2c - > accepted = wasaccept = true ;
}
XML_AddParameter ( jingle , " action " , action ) ;
// Con_Printf("Sending Jingle:\n");
// XML_ConPrintTree(jingle, 1);
JCL_SendIQNode ( jcl , wasaccept ? JCL_JingleAcceptAck : NULL , " set " , c2c - > with , jingle , true ) - > usrptr = c2c ;
return result ;
}
void JCL_JingleTimeouts ( jclient_t * jcl , qboolean killall )
{
int c ;
struct c2c_s * c2c ;
for ( c2c = jcl - > c2c ; c2c ; c2c = c2c - > next )
{
for ( c = 0 ; c < c2c - > contents ; c + + )
{
if ( c2c - > content [ c ] . method = = ICEM_ICE )
{
char bah [ 2 ] ;
piceapi - > ICE_Get ( c2c - > content [ c ] . ice , " newlc " , bah , sizeof ( bah ) ) ;
if ( atoi ( bah ) )
{
Con_DPrintf ( " Sending updated local addresses \n " ) ;
JCL_JingleSend ( jcl , c2c , " transport-info " ) ;
break ;
}
}
}
}
}
2017-10-12 12:02:25 +00:00
void JCL_Join ( jclient_t * jcl , const char * target , const char * sid , qboolean allow , int protocol )
2016-07-12 00:40:13 +00:00
{
struct c2c_s * c2c = NULL , * * link ;
char autotarget [ 256 ] ;
buddy_t * b ;
bresource_t * br ;
int c ;
if ( ! jcl )
return ;
if ( ! JCL_FindBuddy ( jcl , target , & b , & br , true ) )
{
Con_Printf ( " user/resource not known \n " ) ;
return ;
}
if ( ! br )
br = b - > defaultresource ;
if ( ! br )
br = b - > resources ;
if ( ! strchr ( target , ' / ' ) )
{
if ( ! br )
{
Con_Printf ( " User name not valid \n " ) ;
return ;
}
Q_snprintf ( autotarget , sizeof ( autotarget ) , " %s/%s " , b - > accountdomain , br - > resource ) ;
target = autotarget ;
}
for ( link = & jcl - > c2c ; * link & & ! c2c ; link = & ( * link ) - > next )
{
if ( ! strcmp ( ( * link ) - > with , target ) & & ( ! sid | | ! strcmp ( ( * link ) - > sid , sid ) ) )
{
if ( protocol = = ICEP_INVALID )
c2c = * link ;
else
{
for ( c = 0 ; c < ( * link ) - > contents ; c + + )
if ( ( * link ) - > content [ c ] . mediatype = = protocol )
c2c = * link ;
}
}
}
if ( allow )
{
if ( ! c2c )
{
if ( ! sid )
{
char convolink [ 512 ] , hanguplink [ 512 ] ;
if ( protocol = = ICEP_INVALID )
protocol = ICEP_QWCLIENT ;
c2c = JCL_JingleAddContentToSession ( jcl , NULL , target , br , true , sid , " foobar2000 " , DEFAULTICEMODE , protocol ) ;
JCL_JingleSend ( jcl , c2c , " session-initiate " ) ;
JCL_GenLink ( jcl , convolink , sizeof ( convolink ) , NULL , target , NULL , NULL , " %s " , target ) ;
JCL_GenLink ( jcl , hanguplink , sizeof ( hanguplink ) , " jdeny " , target , NULL , c2c - > sid , " %s " , " Hang Up " ) ;
XMPP_ConversationPrintf ( b - > accountdomain , b - > name , " %s %s %s. \n " , protocol = = ICEP_VOICE ? " Calling " : " Requesting session with " , convolink , hanguplink ) ;
}
else
XMPP_ConversationPrintf ( b - > accountdomain , b - > name , " That session has expired. \n " ) ;
}
else if ( c2c - > creator )
{
char convolink [ 512 ] ;
//resend initiate if they've not acked it... I dunno...
JCL_JingleSend ( jcl , c2c , " session-initiate " ) ;
JCL_GenLink ( jcl , convolink , sizeof ( convolink ) , NULL , target , NULL , NULL , " %s " , target ) ;
XMPP_ConversationPrintf ( b - > accountdomain , b - > name , " Restarting session with %s. \n " , convolink ) ;
}
else if ( c2c - > accepted )
XMPP_ConversationPrintf ( b - > accountdomain , b - > name , " That session was already accepted. \n " ) ;
else
{
char convolink [ 512 ] ;
JCL_JingleSend ( jcl , c2c , " session-accept " ) ;
JCL_GenLink ( jcl , convolink , sizeof ( convolink ) , NULL , target , NULL , NULL , " %s " , target ) ;
XMPP_ConversationPrintf ( b - > accountdomain , b - > name , " Accepting session from %s. \n " , convolink ) ;
}
}
else
{
if ( c2c )
{
char convolink [ 512 ] ;
JCL_JingleSend ( jcl , c2c , " session-terminate " ) ;
JCL_GenLink ( jcl , convolink , sizeof ( convolink ) , NULL , target , NULL , NULL , " %s " , target ) ;
XMPP_ConversationPrintf ( b - > accountdomain , b - > name , " Terminating session with %s. \n " , convolink ) ;
}
else
XMPP_ConversationPrintf ( b - > accountdomain , b - > name , " That session has already expired. \n " ) ;
}
}
2017-10-12 12:02:25 +00:00
static void JCL_JingleParsePeerPorts ( jclient_t * jcl , struct c2c_s * c2c , xmltree_t * inj , const char * from , const char * sid )
2016-07-12 00:40:13 +00:00
{
xmltree_t * incontent ;
xmltree_t * intransport ;
xmltree_t * incandidate ;
struct icecandinfo_s rem ;
struct icestate_s * ice ;
int i , contid ;
2017-10-12 12:02:25 +00:00
const char * cname ;
2016-07-12 00:40:13 +00:00
if ( ! c2c - > sid )
return ;
if ( strcmp ( c2c - > with , from ) | | strcmp ( c2c - > sid , sid ) )
{
Con_Printf ( " %s is trying to mess with our connections... \n " , from ) ;
return ;
}
//a message can contain multiple contents
for ( contid = 0 ; ; contid + + )
{
incontent = XML_ChildOfTree ( inj , " content " , contid ) ;
if ( ! incontent )
break ;
cname = XML_GetParameter ( incontent , " name " , " " ) ;
//find which content this node refers to.
ice = NULL ;
for ( i = 0 ; i < c2c - > contents ; i + + )
if ( ! strcmp ( c2c - > content [ i ] . name , cname ) )
ice = c2c - > content [ i ] . ice ;
if ( ! ice )
{
//err... this content doesn't exist?
continue ;
}
intransport = XML_ChildOfTree ( incontent , " transport " , 0 ) ;
if ( ! intransport )
continue ; //err, I guess it wasn't a transport update then (or related).
piceapi - > ICE_Set ( ice , " rufrag " , XML_GetParameter ( intransport , " ufrag " , " " ) ) ;
piceapi - > ICE_Set ( ice , " rpwd " , XML_GetParameter ( intransport , " pwd " , " " ) ) ;
for ( i = 0 ; ( incandidate = XML_ChildOfTree ( intransport , " candidate " , i ) ) ; i + + )
{
2017-10-12 12:02:25 +00:00
const char * s ;
2016-07-12 00:40:13 +00:00
memset ( & rem , 0 , sizeof ( rem ) ) ;
Q_strlcpy ( rem . addr , XML_GetParameter ( incandidate , " ip " , " " ) , sizeof ( rem . addr ) ) ;
Q_strlcpy ( rem . candidateid , XML_GetParameter ( incandidate , " id " , " " ) , sizeof ( rem . candidateid ) ) ;
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 ;
piceapi - > ICE_AddRCandidateInfo ( ice , & rem ) ;
}
}
}
# ifdef VOIP_LEGACY
static void JCL_JingleParsePeerPorts_GoogleSession ( jclient_t * jcl , struct c2c_s * c2c , xmltree_t * inj , char * from , char * sid )
{
xmltree_t * incandidate ;
struct icecandinfo_s rem ;
int i ;
int c ;
if ( strcmp ( c2c - > with , from ) | | strcmp ( c2c - > sid , sid ) )
{
Con_Printf ( " %s is trying to mess with our connections... \n " , from ) ;
return ;
}
if ( ! c2c - > sid )
return ;
//with google's session api, every session uses a single set of ports.
for ( i = 0 ; ( incandidate = XML_ChildOfTree ( inj , " candidate " , i ) ) ; i + + )
{
char * s ;
memset ( & rem , 0 , sizeof ( rem ) ) ;
Q_strlcpy ( rem . addr , XML_GetParameter ( incandidate , " address " , " " ) , sizeof ( rem . addr ) ) ;
// Q_strlcpy(rem.candidateid, XML_GetParameter(incandidate, "id", ""), sizeof(rem.candidateid));
s = XML_GetParameter ( incandidate , " type " , " " ) ;
if ( s & & ! strcmp ( s , " stun " ) )
rem . type = ICE_SRFLX ;
else if ( s & & ! strcmp ( s , " stun " ) )
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 " ) ) ;
s = XML_GetParameter ( incandidate , " name " , " rtp " ) ;
if ( ! strcmp ( s , " rtp " ) )
rem . component = 1 ;
else if ( ! strcmp ( s , " rtcp " ) )
rem . component = 2 ;
else
rem . component = 0 ;
s = XML_GetParameter ( incandidate , " protocol " , " udp " ) ;
if ( s & & ! strcmp ( s , " udp " ) )
rem . transport = 0 ;
else
rem . transport = 0 ;
for ( c = 0 ; c < c2c - > contents ; c + + )
if ( c2c - > content [ c ] . ice )
piceapi - > ICE_AddRCandidateInfo ( c2c - > content [ c ] . ice , & rem ) ;
}
}
static qboolean JCL_JingleHandleInitiate_GoogleSession ( jclient_t * jcl , xmltree_t * inj , char * from )
{
xmltree_t * indescription = XML_ChildOfTree ( inj , " description " , 0 ) ;
char * descriptionxmlns = indescription ? indescription - > xmlns : " " ;
char * sid = XML_GetParameter ( inj , " id " , " " ) ;
qboolean accepted = false ;
char * response = " terminate " ;
char * offer = " pwn you " ;
char * autocvar = " xmpp_autoaccepthax " ;
char * initiator ;
struct c2c_s * c2c = NULL ;
int mt = ICEP_INVALID ;
buddy_t * b ;
bresource_t * br ;
//FIXME: add support for session forwarding so that we might forward the connection to the real server. for now we just reject it.
initiator = XML_GetParameter ( inj , " initiator " , " " ) ;
if ( strcmp ( initiator , from ) )
return false ;
if ( indescription & & ! strcmp ( descriptionxmlns , " http://www.google.com/session/phone " ) )
{
mt = ICEP_VOICE ;
offer = " is trying to call you " ;
autocvar = " xmpp_autoacceptvoice " ;
}
if ( mt = = ICEP_INVALID )
return false ;
if ( ! JCL_FindBuddy ( jcl , from , & b , & br , true ) )
return false ;
//FIXME: if both people try to establish a connection to the other simultaneously, the higher session id is meant to be canceled, and the lower accepted automagically.
c2c = JCL_JingleAddContentToSession ( jcl , NULL , from , br , false ,
sid , " the mystical magical content name " ,
ICEM_ICE ,
mt
) ;
if ( c2c )
{
int c = c2c - > contents - 1 ;
c2c - > peercaps = CAP_GOOGLE_VOICE ;
if ( mt = = ICEP_VOICE )
{
qboolean okay = false ;
int i = 0 ;
xmltree_t * payload ;
//chuck it at the engine and see what sticks. at least one must...
while ( ( payload = XML_ChildOfTree ( indescription , " payload-type " , i + + ) ) )
{
char * name = XML_GetParameter ( payload , " name " , " " ) ;
char * clock = XML_GetParameter ( payload , " clockrate " , " " ) ;
char * id = XML_GetParameter ( payload , " id " , " " ) ;
char parm [ 64 ] ;
char val [ 64 ] ;
//note: the engine will ignore codecs it does not support, returning false.
2017-06-21 01:24:25 +00:00
if ( ! strcasecmp ( name , " speex " ) )
2016-07-12 00:40:13 +00:00
{
Q_snprintf ( parm , sizeof ( parm ) , " codec%i " , atoi ( id ) ) ;
Q_snprintf ( val , sizeof ( val ) , " speex@%i " , atoi ( clock ) ) ;
okay | = piceapi - > ICE_Set ( c2c - > content [ c ] . ice , parm , val ) ;
}
2017-06-21 01:24:25 +00:00
else if ( ! strcasecmp ( name , " pcma " ) | | ! strcasecmp ( name , " pcmu " ) )
2016-07-12 00:40:13 +00:00
{
Q_snprintf ( parm , sizeof ( parm ) , " codec%i " , atoi ( id ) ) ;
2017-06-21 01:24:25 +00:00
Q_snprintf ( val , sizeof ( val ) , " %s@%i " , name , atoi ( clock ) ) ;
okay | = piceapi - > ICE_Set ( c2c - > content [ c ] . ice , parm , val ) ;
}
else if ( ! strcasecmp ( name , " opus " ) )
{
Q_snprintf ( parm , sizeof ( parm ) , " codec%i " , atoi ( id ) ) ;
okay | = piceapi - > ICE_Set ( c2c - > content [ c ] . ice , parm , " opus@48000 " ) ;
2016-07-12 00:40:13 +00:00
}
}
//don't do it if we couldn't successfully set any codecs, because the engine doesn't support the ones that were listed, or something.
//we really ought to give a reason, but we're rude.
if ( ! okay )
{
char convolink [ 512 ] ;
JCL_JingleSend ( jcl , c2c , " terminate " ) ;
JCL_GenLink ( jcl , convolink , sizeof ( convolink ) , NULL , from , NULL , NULL , " %s " , b - > name ) ;
XMPP_ConversationPrintf ( b - > accountdomain , b - > name , " %s does not support any compatible audio codecs, and is unable to call you. \n " , convolink ) ;
return false ;
}
}
if ( mt ! = ICEP_INVALID )
{
char convolink [ 512 ] ;
JCL_GenLink ( jcl , convolink , sizeof ( convolink ) , NULL , from , NULL , NULL , " %s " , b - > name ) ;
if ( ! pCvar_GetFloat ( autocvar ) )
{
char authlink [ 512 ] ;
char denylink [ 512 ] ;
JCL_GenLink ( jcl , authlink , sizeof ( authlink ) , " jauth " , from , NULL , sid , " %s " , " Accept " ) ;
JCL_GenLink ( jcl , denylink , sizeof ( denylink ) , " jdeny " , from , NULL , sid , " %s " , " Reject " ) ;
//show a prompt for it, send the reply when the user decides.
XMPP_ConversationPrintf ( b - > accountdomain , b - > name ,
" %s %s. %s %s \n " , convolink , offer , authlink , denylink ) ;
pCon_SetActive ( b - > name ) ;
return true ;
}
else
{
XMPP_ConversationPrintf ( b - > accountdomain , b - > name , " Auto-accepting session from %s \n " , convolink ) ;
response = " accept " ;
}
}
}
JCL_JingleSend ( jcl , c2c , response ) ;
return true ;
}
# endif
2017-10-12 12:02:25 +00:00
static struct c2c_s * JCL_JingleHandleInitiate ( jclient_t * jcl , xmltree_t * inj , const char * from )
2016-07-12 00:40:13 +00:00
{
2017-10-12 12:02:25 +00:00
const char * sid = XML_GetParameter ( inj , " sid " , " " ) ;
2016-07-12 00:40:13 +00:00
qboolean okay ;
2017-10-12 12:02:25 +00:00
const char * initiator ;
2016-07-12 00:40:13 +00:00
struct c2c_s * c2c = NULL ;
int mt = ICEP_INVALID ;
int i , c ;
buddy_t * b ;
bresource_t * br ;
//FIXME: add support for session forwarding so that we might forward the connection to the real server. for now we just reject it.
initiator = XML_GetParameter ( inj , " initiator " , " " ) ;
if ( strcmp ( initiator , from ) )
return NULL ;
//reject it if we don't know them.
if ( ! JCL_FindBuddy ( jcl , from , & b , & br , false ) )
return NULL ;
for ( i = 0 ; ; i + + )
{
xmltree_t * incontent = XML_ChildOfTree ( inj , " content " , i ) ;
2017-10-12 12:02:25 +00:00
const char * cname = XML_GetParameter ( incontent , " name " , " " ) ;
2016-07-12 00:40:13 +00:00
xmltree_t * intransport = XML_ChildOfTree ( incontent , " transport " , 0 ) ;
xmltree_t * indescription = XML_ChildOfTree ( incontent , " description " , 0 ) ;
2017-10-12 12:02:25 +00:00
const char * transportxmlns = intransport ? intransport - > xmlns : " " ;
const char * descriptionxmlns = indescription ? indescription - > xmlns : " " ;
const char * descriptionmedia = XML_GetParameter ( indescription , " media " , " " ) ;
2016-07-12 00:40:13 +00:00
if ( ! incontent )
break ;
mt = ICEP_INVALID ;
if ( incontent & & ! strcmp ( descriptionmedia , MEDIATYPE_QUAKE ) & & ! strcmp ( descriptionxmlns , QUAKEMEDIAXMLNS ) )
{
2017-10-12 12:02:25 +00:00
const char * host = XML_GetParameter ( indescription , " host " , " you " ) ;
2016-07-12 00:40:13 +00:00
if ( ! strcmp ( host , " you " ) )
mt = ICEP_QWSERVER ;
else if ( ! strcmp ( host , " me " ) )
mt = ICEP_QWCLIENT ;
}
if ( incontent & & ! strcmp ( descriptionmedia , MEDIATYPE_AUDIO ) & & ! strcmp ( descriptionxmlns , " urn:xmpp:jingle:apps:rtp:1 " ) )
mt = ICEP_VOICE ;
if ( incontent & & ! strcmp ( descriptionmedia , MEDIATYPE_VIDEO ) & & ! strcmp ( descriptionxmlns , " urn:xmpp:jingle:apps:rtp:1 " ) )
mt = ICEP_VIDEO ;
if ( mt = = ICEP_INVALID )
continue ;
c2c = JCL_JingleAddContentToSession ( jcl , NULL , from , br , false , sid , cname ,
strcmp ( transportxmlns , " urn:xmpp:jingle:transports:raw-udp:1 " ) ? ICEM_ICE : ICEM_RAW ,
mt
) ;
if ( ! c2c )
continue ;
c = c2c - > contents - 1 ;
okay = false ;
if ( mt = = ICEP_VOICE )
{
int i = 0 ;
xmltree_t * payload ;
//chuck it at the engine and see what sticks. at least one must...
while ( ( payload = XML_ChildOfTree ( indescription , " payload-type " , i + + ) ) )
{
2017-10-12 12:02:25 +00:00
const char * name = XML_GetParameter ( payload , " name " , " " ) ;
const char * clock = XML_GetParameter ( payload , " clockrate " , " " ) ;
const char * id = XML_GetParameter ( payload , " id " , " " ) ;
2016-07-12 00:40:13 +00:00
char parm [ 64 ] ;
char val [ 64 ] ;
//note: the engine will ignore codecs it does not support, returning false.
2017-06-21 01:24:25 +00:00
if ( ! strcasecmp ( name , " speex " ) | | ! strcasecmp ( name , " pcma " ) | | ! strcasecmp ( name , " pcmu " ) )
2016-07-12 00:40:13 +00:00
{
Q_snprintf ( parm , sizeof ( parm ) , " codec%i " , atoi ( id ) ) ;
2017-06-21 01:24:25 +00:00
Q_snprintf ( val , sizeof ( val ) , " %s@%i " , name , atoi ( clock ) ) ;
2016-07-12 00:40:13 +00:00
okay | = piceapi - > ICE_Set ( c2c - > content [ c ] . ice , parm , val ) ;
}
2017-06-21 01:24:25 +00:00
else if ( ! strcasecmp ( name , " opus " ) )
2016-07-12 00:40:13 +00:00
{
Q_snprintf ( parm , sizeof ( parm ) , " codec%i " , atoi ( id ) ) ;
2017-06-21 01:24:25 +00:00
okay | = piceapi - > ICE_Set ( c2c - > content [ c ] . ice , parm , " opus@48000 " ) ;
2016-07-12 00:40:13 +00:00
}
}
}
else
okay = true ;
//don't do it if we couldn't successfully set any codecs, because the engine doesn't support the ones that were listed, or something.
//we really ought to give a reason, but we're rude.
if ( ! okay )
{
char convolink [ 512 ] ;
JCL_GenLink ( jcl , convolink , sizeof ( convolink ) , NULL , from , NULL , NULL , " %s " , b - > name ) ;
XMPP_ConversationPrintf ( b - > accountdomain , b - > name , " %s does not support any compatible codecs, and is unable to call you. \n " , convolink ) ;
if ( c2c - > content [ c ] . ice )
piceapi - > ICE_Close ( c2c - > content [ c ] . ice ) ;
c2c - > content [ c ] . ice = NULL ;
c2c - > contents - - ;
}
}
if ( ! c2c )
return NULL ;
if ( ! c2c - > contents )
{
if ( jcl - > c2c = = c2c )
{
jcl - > c2c = c2c - > next ;
free ( c2c ) ;
}
else
Con_Printf ( " ^1error in " __FILE__ " %i \n " , __LINE__ ) ;
return NULL ;
}
//if they speak this, we never want to speak gingle at them!
c2c - > peercaps & = ~ ( CAP_GOOGLE_VOICE ) ;
JCL_JingleParsePeerPorts ( jcl , c2c , inj , from , sid ) ;
return c2c ;
}
static qboolean JCL_JingleHandleSessionTerminate ( jclient_t * jcl , xmltree_t * tree , struct c2c_s * c2c , struct c2c_s * * link , buddy_t * b )
{
xmltree_t * reason = XML_ChildOfTree ( tree , " reason " , 0 ) ;
int c ;
if ( ! c2c )
{
XMPP_ConversationPrintf ( b - > accountdomain , b - > name , " Received session-terminate without an active session \n " ) ;
return false ;
}
if ( reason & & reason - > child )
XMPP_ConversationPrintf ( b - > accountdomain , b - > name , " Session ended: %s \n " , reason - > child - > name ) ;
else
XMPP_ConversationPrintf ( b - > accountdomain , b - > name , " Session ended \n " ) ;
//unlink it
for ( link = & jcl - > c2c ; * link ; link = & ( * link ) - > next )
{
if ( * link = = c2c )
{
* link = c2c - > next ;
break ;
}
}
// XML_ConPrintTree(tree, 0);
for ( c = 0 ; c < c2c - > contents ; c + + )
if ( c2c - > content [ c ] . ice )
piceapi - > ICE_Close ( c2c - > content [ c ] . ice ) ;
free ( c2c ) ;
return true ;
}
2017-10-12 12:02:25 +00:00
static qboolean JCL_JingleHandleSessionAccept ( jclient_t * jcl , xmltree_t * tree , const char * from , struct c2c_s * c2c , buddy_t * b )
2016-07-12 00:40:13 +00:00
{
//peer accepted our session
//make sure it actually was ours, and not theirs. sneaky sneaky.
//will generally contain some port info.
if ( ! c2c )
{
Con_DPrintf ( " Unknown session acceptance \n " ) ;
return false ;
}
else if ( ! c2c - > creator )
{
Con_DPrintf ( " Peer tried to accept a session that *they* created! \n " ) ;
return false ;
}
else if ( c2c - > accepted )
{
//pidgin is buggy and can dupe-accept sessions multiple times.
Con_DPrintf ( " Duplicate session-accept from peer. \n " ) ;
//XML_ConPrintTree(tree, 0);
return false ;
}
else
{
2017-10-12 12:02:25 +00:00
const char * responder = XML_GetParameter ( tree , " responder " , from ) ;
2016-07-12 00:40:13 +00:00
int c ;
if ( strcmp ( responder , from ) )
{
return false ;
}
XMPP_ConversationPrintf ( b - > accountdomain , b - > name , " Session Accepted! \n " ) ;
// XML_ConPrintTree(tree, 0);
JCL_JingleParsePeerPorts ( jcl , c2c , tree , from , XML_GetParameter ( tree , " sid " , " " ) ) ;
c2c - > accepted = true ;
//if we didn't error out, the ICE stuff is meant to start sending handshakes/media as soon as the connection is accepted
for ( c = 0 ; c < c2c - > contents ; c + + )
if ( c2c - > content [ c ] . ice )
piceapi - > ICE_Set ( c2c - > content [ c ] . ice , " state " , STRINGIFY ( ICE_CONNECTING ) ) ;
}
return true ;
}
# ifdef VOIP_LEGACY
qboolean JCL_HandleGoogleSession ( jclient_t * jcl , xmltree_t * tree , char * from , char * iqid )
{
char * sid = XML_GetParameter ( tree , " id " , " " ) ;
char * type = XML_GetParameter ( tree , " type " , " " ) ;
struct c2c_s * c2c = NULL , * * link ;
buddy_t * b ;
//validate+find sender
for ( link = & jcl - > c2c ; * link ; link = & ( * link ) - > next )
{
if ( ! strcmp ( ( * link ) - > sid , sid ) )
{
c2c = * link ;
if ( ! c2c - > accepted )
break ;
}
}
if ( ! JCL_FindBuddy ( jcl , from , & b , NULL , true ) )
return false ;
if ( c2c & & strcmp ( c2c - > with , from ) )
{
Con_Printf ( " %s is trying to mess with our connections... \n " , from ) ;
return false ;
}
Con_Printf ( " google session with %s: \n " , from ) ;
XML_ConPrintTree ( tree , " " , 0 ) ;
if ( ! strcmp ( type , " accept " ) )
{
if ( ! JCL_JingleHandleSessionAccept ( jcl , tree , from , c2c , b ) )
return false ;
}
else if ( ! strcmp ( type , " initiate " ) )
{
if ( ! JCL_JingleHandleInitiate_GoogleSession ( jcl , tree , from ) )
return false ;
}
else if ( ! strcmp ( type , " terminate " ) )
{
JCL_JingleHandleSessionTerminate ( jcl , tree , c2c , link , b ) ;
}
else if ( ! strcmp ( type , " candidates " ) )
{
if ( ! c2c )
return false ;
JCL_JingleParsePeerPorts_GoogleSession ( jcl , c2c , tree , from , sid ) ;
}
else
{
Con_Printf ( " Unknown google session action: %s \n " , type ) ;
// XML_ConPrintTree(tree, 0);
return false ;
}
JCL_AddClientMessagef ( jcl ,
" <iq type='result' to='%s' id='%s' /> " , from , iqid ) ;
return true ;
}
# endif
2017-10-12 12:02:25 +00:00
qboolean JCL_ParseJingle ( jclient_t * jcl , xmltree_t * tree , const char * from , const char * id )
2016-07-12 00:40:13 +00:00
{
2017-10-12 12:02:25 +00:00
const char * action = XML_GetParameter ( tree , " action " , " " ) ;
const char * sid = XML_GetParameter ( tree , " sid " , " " ) ;
2016-07-12 00:40:13 +00:00
struct c2c_s * c2c = NULL , * * link ;
buddy_t * b ;
for ( link = & jcl - > c2c ; * link ; link = & ( * link ) - > next )
{
if ( ! strcmp ( ( * link ) - > sid , sid ) )
{
c2c = * link ;
if ( ! c2c - > accepted )
break ;
}
}
if ( ! JCL_FindBuddy ( jcl , from , & b , NULL , true ) )
return false ;
//validate sender
if ( c2c & & strcmp ( c2c - > with , from ) )
{
Con_Printf ( " %s is trying to mess with our connections... \n " , from ) ;
return false ;
}
//FIXME: transport-info, transport-replace
if ( ! strcmp ( action , " session-terminate " ) )
{
if ( c2c )
{
JCL_JingleError ( jcl , tree , from , id , JE_ACKNOWLEDGE ) ;
JCL_JingleHandleSessionTerminate ( jcl , tree , c2c , link , b ) ;
}
else
JCL_JingleError ( jcl , tree , from , id , JE_UNKNOWNSESSION ) ;
}
else if ( ! strcmp ( action , " content-accept " ) )
{
//response from content-add
if ( c2c )
JCL_JingleError ( jcl , tree , from , id , JE_UNSUPPORTED ) ;
else
JCL_JingleError ( jcl , tree , from , id , JE_UNKNOWNSESSION ) ;
}
else if ( ! strcmp ( action , " content-add " ) )
{
//FIXME: must send content-reject
if ( c2c )
JCL_JingleError ( jcl , tree , from , id , JE_UNSUPPORTED ) ;
else
JCL_JingleError ( jcl , tree , from , id , JE_UNKNOWNSESSION ) ;
}
else if ( ! strcmp ( action , " content-modify " ) )
{
//send an error to reject it
JCL_JingleError ( jcl , tree , from , id , JE_UNSUPPORTED ) ;
}
else if ( ! strcmp ( action , " content-reject " ) )
{
//response from content-add. an error until we actually generate content-adds...
if ( c2c )
JCL_JingleError ( jcl , tree , from , id , JE_UNSUPPORTED ) ;
else
JCL_JingleError ( jcl , tree , from , id , JE_UNKNOWNSESSION ) ;
}
else if ( ! strcmp ( action , " content-remove " ) )
{
if ( c2c )
JCL_JingleError ( jcl , tree , from , id , JE_UNSUPPORTED ) ;
else
JCL_JingleError ( jcl , tree , from , id , JE_UNKNOWNSESSION ) ;
}
else if ( ! strcmp ( action , " description-info " ) )
{
//The description-info action is used to send informational hints about parameters related to the application type, such as the suggested height and width of a video display area or suggested configuration for an audio stream.
//just ack and ignore it
if ( c2c )
JCL_JingleError ( jcl , tree , from , id , JE_UNSUPPORTED ) ;
else
JCL_JingleError ( jcl , tree , from , id , JE_UNKNOWNSESSION ) ;
}
else if ( ! strcmp ( action , " security-info " ) )
{
//The security-info action is used to send information related to establishment or maintenance of security preconditions.
//no security mechanisms supported...
if ( c2c )
JCL_JingleError ( jcl , tree , from , id , JE_UNSUPPORTED ) ;
else
JCL_JingleError ( jcl , tree , from , id , JE_UNKNOWNSESSION ) ;
}
else if ( ! strcmp ( action , " session-info " ) )
{
if ( tree - > child )
JCL_JingleError ( jcl , tree , from , id , JE_UNSUPPORTEDINFO ) ;
else
JCL_JingleError ( jcl , tree , from , id , JE_ACKNOWLEDGE ) ; //serves as a ping.
}
else if ( ! strcmp ( action , " transport-info " ) )
{ //peer wants to add ports.
if ( c2c )
{
JCL_JingleError ( jcl , tree , from , id , JE_ACKNOWLEDGE ) ;
JCL_JingleParsePeerPorts ( jcl , c2c , tree , from , sid ) ;
}
else
JCL_JingleError ( jcl , tree , from , id , JE_UNKNOWNSESSION ) ;
}
//FIXME: we need to add support for this to downgrade to raw if someone tries calling through a SIP gateway
else if ( ! strcmp ( action , " transport-replace " ) )
{
if ( c2c )
{
if ( 1 )
{
JCL_JingleError ( jcl , tree , from , id , JE_ACKNOWLEDGE ) ;
JCL_JingleSend ( jcl , c2c , " transport-reject " ) ;
}
else
{
JCL_JingleError ( jcl , tree , from , id , JE_ACKNOWLEDGE ) ;
JCL_JingleParsePeerPorts ( jcl , c2c , tree , from , sid ) ;
JCL_JingleSend ( jcl , c2c , " transport-accept " ) ;
}
}
else
JCL_JingleError ( jcl , tree , from , id , JE_UNKNOWNSESSION ) ;
}
else if ( ! strcmp ( action , " transport-reject " ) )
{
JCL_JingleError ( jcl , tree , from , id , JE_ACKNOWLEDGE ) ;
JCL_JingleSend ( jcl , c2c , " session-terminate " ) ;
}
else if ( ! strcmp ( action , " session-accept " ) )
{
if ( c2c )
{
//response from a message we sent.
if ( JCL_JingleHandleSessionAccept ( jcl , tree , from , c2c , b ) )
JCL_JingleError ( jcl , tree , from , id , JE_ACKNOWLEDGE ) ;
else
JCL_JingleError ( jcl , tree , from , id , JE_OUTOFORDER ) ;
}
else
JCL_JingleError ( jcl , tree , from , id , JE_UNKNOWNSESSION ) ;
}
else if ( ! strcmp ( action , " session-initiate " ) )
{
// Con_Printf("Peer initiating connection!\n");
// XML_ConPrintTree(tree, 0);
c2c = JCL_JingleHandleInitiate ( jcl , tree , from ) ;
if ( c2c )
{
qboolean voice = false , video = false , server = false , client = false ;
char * offer ;
char convolink [ 512 ] ;
int c ;
qboolean doprompt = false ;
JCL_JingleError ( jcl , tree , from , id , JE_ACKNOWLEDGE ) ;
for ( c = 0 ; c < c2c - > contents ; c + + )
{
switch ( c2c - > content [ c ] . mediatype )
{
case ICEP_INVALID : break ;
case ICEP_VOICE : voice = true ; doprompt | = ! pCvar_GetFloat ( " xmpp_autoacceptvoice " ) ; break ;
case ICEP_VIDEO : video = true ; doprompt | = ! pCvar_GetFloat ( " xmpp_autoacceptvoice " ) ; break ;
case ICEP_QWSERVER : server = true ; doprompt | = ! pCvar_GetFloat ( " xmpp_autoacceptjoins " ) ; break ;
case ICEP_QWCLIENT : client = true ; doprompt | = ! pCvar_GetFloat ( " xmpp_autoacceptinvites " ) ; break ;
default : doprompt | = true ; break ;
}
}
if ( video )
{
if ( server )
offer = " wants to join your game (with ^1video^7) " ;
else if ( client )
offer = " wants to invite you to thier game (with ^1video^7) " ;
else
offer = " is trying to ^1video^7 call you " ;
}
else if ( voice )
{
if ( server )
offer = " wants to join your game (with voice) " ;
else if ( client )
offer = " wants to invite you to thier game (with voice) " ;
else
offer = " is trying to call you " ;
}
else
{
if ( server )
offer = " wants to join your game " ;
else if ( client )
offer = " wants to invite you to thier game " ;
else
offer = " is trying to waste your time " ;
}
JCL_GenLink ( jcl , convolink , sizeof ( convolink ) , NULL , from , NULL , NULL , " %s " , b - > name ) ;
if ( doprompt )
{
char authlink [ 512 ] ;
char denylink [ 512 ] ;
JCL_GenLink ( jcl , authlink , sizeof ( authlink ) , " jauth " , from , NULL , sid , " %s " , " Accept " ) ;
JCL_GenLink ( jcl , denylink , sizeof ( denylink ) , " jdeny " , from , NULL , sid , " %s " , " Reject " ) ;
//show a prompt for it, send the reply when the user decides.
XMPP_ConversationPrintf ( b - > accountdomain , b - > name ,
" %s %s. %s %s \n " , convolink , offer , authlink , denylink ) ;
pCon_SetActive ( b - > accountdomain ) ;
}
else
{
XMPP_ConversationPrintf ( b - > accountdomain , b - > name , " Auto-accepting session from %s \n " , convolink ) ;
JCL_Join ( jcl , from , sid , true , ICEP_INVALID ) ;
}
}
else
JCL_JingleError ( jcl , tree , from , id , JE_UNSUPPORTED ) ;
}
else
{
JCL_JingleError ( jcl , tree , from , id , JE_UNSUPPORTED ) ;
}
return true ;
}
2013-08-21 07:42:42 +00:00
# endif