Implemented old jingle protocol support.
git-svn-id: http://voip.null.ro/svn/yate@2666 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
parent
7c21fa4f63
commit
283dd92294
|
@ -3,6 +3,12 @@
|
|||
[general]
|
||||
; Global settings of Jabber and Jingle engines
|
||||
|
||||
; jingleversion: integer: Jingle session version to use for outgoing calls
|
||||
; Defaults to the newest version if missing or invalid
|
||||
; Allowed values are 0 or 1
|
||||
; This parameter can be overridden when routing by an 'ojingleversion' parameter
|
||||
;jingleversion=
|
||||
|
||||
; secure_rtp: boolean: Offer SRTP when negotiating Jingle sessions
|
||||
; Defaults to disable
|
||||
;secure_rtp=disable
|
||||
|
@ -152,7 +158,7 @@
|
|||
default=false
|
||||
|
||||
; mulaw: bool: Companded-only G711 mu-law (PCMU/8000)
|
||||
mulaw=default
|
||||
mulaw=true
|
||||
|
||||
; alaw: bool: Companded-only G711 a-law (PCMU/8000)
|
||||
alaw=true
|
||||
|
|
|
@ -84,17 +84,6 @@ inline bool checkValidXmlns(XMLElement* e, XMPPNamespace::Type ns,
|
|||
return false;
|
||||
}
|
||||
|
||||
// Fix some element name conflicts: change type according to their namespace
|
||||
// Some elements may have the same name in different namespaces
|
||||
inline void fixXmlType(XMLElement* xml)
|
||||
{
|
||||
if (!xml)
|
||||
return;
|
||||
if (xml->type() == XMLElement::Session &&
|
||||
xml->hasAttribute("xmlns",s_ns[XMPPNamespace::Jingle]))
|
||||
xml->changeType(XMLElement::Jingle);
|
||||
}
|
||||
|
||||
#define DROP_AND_EXIT { dropXML(xml); return; }
|
||||
#define INVALIDXML_AND_EXIT(code,reason) { invalidStreamXML(xml,code,reason); return; }
|
||||
#define ERRORXML_AND_EXIT { errorStreamXML(xml); return; }
|
||||
|
@ -1352,8 +1341,6 @@ JBEvent* JBStream::getIqEvent(XMLElement* xml, int iqType, XMPPError::Type& erro
|
|||
}
|
||||
error = XMPPError::NoError;
|
||||
XMLElement* child = xml->findFirstChild();
|
||||
if (child)
|
||||
fixXmlType(child);
|
||||
|
||||
// Request (type is set or get): check the child (MUST exists)
|
||||
// Result: check it only if it has a child
|
||||
|
@ -1369,6 +1356,10 @@ JBEvent* JBStream::getIqEvent(XMLElement* xml, int iqType, XMPPError::Type& erro
|
|||
if (checkValidXmlns(child,XMPPNamespace::Jingle,error))
|
||||
IQEVENT_SET_REQ(JBEvent::IqJingleGet,JBEvent::IqJingleSet);
|
||||
break;
|
||||
case XMLElement::Session:
|
||||
if (checkValidXmlns(child,XMPPNamespace::JingleSession,error))
|
||||
IQEVENT_SET_REQ(JBEvent::IqJingleGet,JBEvent::IqJingleSet);
|
||||
break;
|
||||
case XMLElement::Query:
|
||||
if (XMPPUtils::hasXmlns(*child,XMPPNamespace::DiscoInfo))
|
||||
IQEVENT_SET_REQ(JBEvent::IqDiscoInfoGet,JBEvent::IqDiscoInfoSet);
|
||||
|
@ -1394,6 +1385,10 @@ JBEvent* JBStream::getIqEvent(XMLElement* xml, int iqType, XMPPError::Type& erro
|
|||
if (XMPPUtils::hasXmlns(*child,XMPPNamespace::Jingle))
|
||||
IQEVENT_SET_RSP(JBEvent::IqJingleRes,JBEvent::IqJingleErr);
|
||||
break;
|
||||
case XMLElement::Session:
|
||||
if (XMPPUtils::hasXmlns(*child,XMPPNamespace::JingleSession))
|
||||
IQEVENT_SET_RSP(JBEvent::IqJingleRes,JBEvent::IqJingleErr);
|
||||
break;
|
||||
case XMLElement::Query:
|
||||
if (XMPPUtils::hasXmlns(*child,XMPPNamespace::DiscoInfo))
|
||||
IQEVENT_SET_RSP(JBEvent::IqDiscoInfoRes,JBEvent::IqDiscoInfoErr);
|
||||
|
|
|
@ -93,7 +93,7 @@ void JGEngine::initialize(const NamedList& params)
|
|||
}
|
||||
|
||||
// Make an outgoing call
|
||||
JGSession* JGEngine::call(const String& localJID, const String& remoteJID,
|
||||
JGSession* JGEngine::call(JGSession::Version ver, const String& localJID, const String& remoteJID,
|
||||
const ObjList& contents, XMLElement* extra, const char* message,
|
||||
const char* subject)
|
||||
{
|
||||
|
@ -113,10 +113,19 @@ JGSession* JGEngine::call(const String& localJID, const String& remoteJID,
|
|||
// Create outgoing session
|
||||
bool hasStream = (0 != stream);
|
||||
if (hasStream) {
|
||||
JGSession* session = new JGSession1(this,stream,localJID,remoteJID,
|
||||
contents,extra,message,subject);
|
||||
JGSession* session = 0;
|
||||
switch (ver) {
|
||||
case JGSession::Version1:
|
||||
session = new JGSession1(this,stream,localJID,remoteJID,message);
|
||||
break;
|
||||
case JGSession::Version0:
|
||||
session = new JGSession0(this,stream,localJID,remoteJID,message);
|
||||
break;
|
||||
case JGSession::VersionUnknown:
|
||||
;
|
||||
}
|
||||
TelEngine::destruct(stream);
|
||||
if (session->state() != JGSession::Destroy) {
|
||||
if (session && session->initiate(contents,extra,subject)) {
|
||||
Lock lock(this);
|
||||
m_sessions.append(session);
|
||||
return (session && session->ref()) ? session : 0;
|
||||
|
@ -200,6 +209,10 @@ bool JGEngine::accept(JBEvent* event, bool& processed, bool& insert)
|
|||
ver = JGSession::Version1;
|
||||
sid = child->getAttribute("sid");
|
||||
}
|
||||
else if (XMPPUtils::hasXmlns(*child,XMPPNamespace::JingleSession)) {
|
||||
ver = JGSession::Version0;
|
||||
sid = child->getAttribute("id");
|
||||
}
|
||||
DDebug(this,DebugAll,"Accepting event=%s child=%s sid=%s version=%d",
|
||||
event->name(),child->name(),sid.c_str(),ver);
|
||||
if (sid.null()) {
|
||||
|
@ -229,6 +242,9 @@ bool JGEngine::accept(JBEvent* event, bool& processed, bool& insert)
|
|||
case JGSession::Version1:
|
||||
m_sessions.append(new JGSession1(this,event,sid));
|
||||
break;
|
||||
case JGSession::Version0:
|
||||
m_sessions.append(new JGSession0(this,event,sid));
|
||||
break;
|
||||
default:
|
||||
Debug(this,DebugStub,
|
||||
"JGEngine::accept(): unhandled session version %d",ver);
|
||||
|
@ -336,7 +352,17 @@ void JGEvent::init(JGSession* session)
|
|||
m_session = session;
|
||||
if (m_element) {
|
||||
m_id = m_element->getAttribute("id");
|
||||
m_jingle = m_element->findFirstChild(XMLElement::Jingle);
|
||||
if (m_session)
|
||||
switch (m_session->version()) {
|
||||
case JGSession::Version1:
|
||||
m_jingle = m_element->findFirstChild(XMLElement::Jingle);
|
||||
break;
|
||||
case JGSession::Version0:
|
||||
m_jingle = m_element->findFirstChild(XMLElement::Session);
|
||||
break;
|
||||
case JGSession::VersionUnknown:
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,15 +41,82 @@ static void addJingleContents(XMLElement* xml, const ObjList& contents, bool min
|
|||
TelEngine::destruct(jingle);
|
||||
}
|
||||
|
||||
// Utility: add session content(s) to an already created stanza's jingle child
|
||||
// This method is used by the version 0 of the session
|
||||
static void addJingleContents0(String& name, XMLElement* xml, const ObjList& contents, bool minimal,
|
||||
bool addDesc, bool addTrans)
|
||||
{
|
||||
if (!xml)
|
||||
return;
|
||||
XMLElement* jingle = xml->findFirstChild(XMLElement::Session);
|
||||
if (!jingle)
|
||||
return;
|
||||
for (ObjList* o = contents.skipNull(); o; o = o->skipNext()) {
|
||||
JGSessionContent* c = static_cast<JGSessionContent*>(o->get());
|
||||
if (c->type() != JGSessionContent::RtpIceUdp)
|
||||
continue;
|
||||
name = c->toString();
|
||||
if (addDesc) {
|
||||
XMLElement* desc = XMPPUtils::createElement(XMLElement::Description,
|
||||
XMPPNamespace::JingleAudio);
|
||||
for (ObjList* o = c->m_rtpMedia.skipNull(); o; o = o->skipNext()) {
|
||||
JGRtpMedia* a = static_cast<JGRtpMedia*>(o->get());
|
||||
desc->addChild(a->toXML());
|
||||
}
|
||||
JGRtpMedia* te = new JGRtpMedia("106","telephone-event","8000","","");
|
||||
desc->addChild(te->toXML());
|
||||
TelEngine::destruct(te);
|
||||
jingle->addChild(desc);
|
||||
}
|
||||
if (addTrans) {
|
||||
XMLElement* trans = XMPPUtils::createElement(XMLElement::Transport,
|
||||
XMPPNamespace::JingleTransport);
|
||||
if (!minimal) {
|
||||
for (ObjList* o = c->m_rtpLocalCandidates.skipNull(); o; o = o->skipNext()) {
|
||||
JGRtpCandidate* rc = static_cast<JGRtpCandidate*>(o->get());
|
||||
XMLElement* xml = new XMLElement(XMLElement::Candidate);
|
||||
xml->setAttribute("name","rtp");
|
||||
xml->setAttributeValid("generation",rc->m_generation);
|
||||
xml->setAttributeValid("address",rc->m_address);
|
||||
xml->setAttributeValid("port",rc->m_port);
|
||||
xml->setAttributeValid("network","0");
|
||||
xml->setAttributeValid("protocol",rc->m_protocol);
|
||||
xml->setAttribute("username",c->m_rtpLocalCandidates.m_ufrag);
|
||||
xml->setAttribute("password",c->m_rtpLocalCandidates.m_password);
|
||||
xml->setAttributeValid("type","local");
|
||||
xml->setAttributeValid("preference","1");
|
||||
trans->addChild(xml);
|
||||
}
|
||||
}
|
||||
jingle->addChild(trans);
|
||||
}
|
||||
}
|
||||
TelEngine::destruct(jingle);
|
||||
}
|
||||
|
||||
// Utility: add xml element child to an already created stanza's jingle child
|
||||
static void addJingleChild(XMLElement* xml, XMLElement* child)
|
||||
{
|
||||
if (!(xml && child))
|
||||
return;
|
||||
XMLElement* jingle = xml->findFirstChild(XMLElement::Jingle);
|
||||
if (!jingle)
|
||||
if (jingle)
|
||||
jingle->addChild(child);
|
||||
else
|
||||
TelEngine::destruct(child);
|
||||
TelEngine::destruct(jingle);
|
||||
}
|
||||
|
||||
// Utility: add xml element child to an already created stanza's jingle child
|
||||
static void addJingleChild0(XMLElement* xml, XMLElement* child)
|
||||
{
|
||||
if (!(xml && child))
|
||||
return;
|
||||
jingle->addChild(child);
|
||||
XMLElement* jingle = xml->findFirstChild(XMLElement::Session);
|
||||
if (jingle)
|
||||
jingle->addChild(child);
|
||||
else
|
||||
TelEngine::destruct(child);
|
||||
TelEngine::destruct(jingle);
|
||||
}
|
||||
|
||||
|
@ -338,7 +405,7 @@ JGRtpCandidate* JGRtpCandidates::findByComponent(unsigned int component)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Generate a random password to be used with ICE-UDP transport
|
||||
// Generate a random password or username to be used with ICE-UDP transport
|
||||
// Maximum number of characters. The maxmimum value is 256.
|
||||
// The minimum value is 22 for password and 4 for username
|
||||
void JGRtpCandidates::generateIceToken(String& dest, bool pwd, unsigned int max)
|
||||
|
@ -357,6 +424,15 @@ void JGRtpCandidates::generateIceToken(String& dest, bool pwd, unsigned int max)
|
|||
dest = dest.substr(0,max);
|
||||
}
|
||||
|
||||
// Generate a random password or username to be used with old ICE-UDP transport
|
||||
void JGRtpCandidates::generateOldIceToken(String& dest)
|
||||
{
|
||||
dest = "";
|
||||
while (dest.length() < 16)
|
||||
dest << (int)random();
|
||||
dest = dest.substr(0,16);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* JGSessionContent
|
||||
|
@ -610,6 +686,12 @@ XMLElement* JGStreamHost::buildRsp(const char* jid)
|
|||
* JGSession
|
||||
*/
|
||||
|
||||
TokenDict JGSession::s_versions[] = {
|
||||
{"0", Version0},
|
||||
{"1", Version1},
|
||||
{0,0}
|
||||
};
|
||||
|
||||
TokenDict JGSession::s_states[] = {
|
||||
{"Idle", Idle},
|
||||
{"Pending", Pending},
|
||||
|
@ -634,6 +716,20 @@ TokenDict JGSession::s_reasons[] = {
|
|||
{0,0}
|
||||
};
|
||||
|
||||
TokenDict JGSession::s_actions0[] = {
|
||||
{"accept", ActAccept},
|
||||
{"initiate", ActInitiate},
|
||||
{"terminate", ActTerminate},
|
||||
{"info", ActInfo},
|
||||
{"transport-info", ActTransportInfo},
|
||||
{"transport-accept", ActTransportAccept},
|
||||
{"content-info", ActContentInfo},
|
||||
{"DTMF", ActDtmf},
|
||||
{"ringing", ActRinging},
|
||||
{"mute", ActMute},
|
||||
{0,0}
|
||||
};
|
||||
|
||||
TokenDict JGSession::s_actions1[] = {
|
||||
{"session-accept", ActAccept},
|
||||
{"session-initiate", ActInitiate},
|
||||
|
@ -821,7 +917,7 @@ bool JGSession::sendDtmf(const char* dtmf, unsigned int msDuration, String* stan
|
|||
if (!(dtmf && *dtmf))
|
||||
return false;
|
||||
|
||||
XMLElement* iq = createJingle(ActInfo);
|
||||
XMLElement* iq = createJingle(version() != Version0 ? ActInfo : ActContentInfo);
|
||||
XMLElement* sess = iq->findFirstChild();
|
||||
if (!sess) {
|
||||
TelEngine::destruct(iq);
|
||||
|
@ -1347,7 +1443,9 @@ const char* JGSession::lookupAction(int act, Version ver)
|
|||
switch (ver) {
|
||||
case Version1:
|
||||
return lookup(act,s_actions1);
|
||||
default:
|
||||
case Version0:
|
||||
return lookup(act,s_actions0);
|
||||
case VersionUnknown:
|
||||
;
|
||||
}
|
||||
return 0;
|
||||
|
@ -1359,33 +1457,302 @@ JGSession::Action JGSession::lookupAction(const char* str, Version ver)
|
|||
switch (ver) {
|
||||
case Version1:
|
||||
return (Action)lookup(str,s_actions1,ActCount);
|
||||
default:
|
||||
case Version0:
|
||||
return (Action)lookup(str,s_actions0,ActCount);
|
||||
case VersionUnknown:
|
||||
;
|
||||
}
|
||||
return ActCount;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* JGSession0
|
||||
*/
|
||||
|
||||
// Create an outgoing session
|
||||
JGSession0::JGSession0(JGEngine* engine, JBStream* stream,
|
||||
const String& callerJID, const String& calledJID, const char* msg)
|
||||
: JGSession(Version0,engine,stream,callerJID,calledJID,msg)
|
||||
{
|
||||
}
|
||||
|
||||
// Create an incoming session
|
||||
JGSession0::JGSession0(JGEngine* engine, JBEvent* event, const String& id)
|
||||
: JGSession(Version0,engine,event,id)
|
||||
{
|
||||
m_sessContentName = m_localSid + "_content";
|
||||
}
|
||||
|
||||
// Destructor
|
||||
JGSession0::~JGSession0()
|
||||
{
|
||||
}
|
||||
|
||||
// Check if a given XML element is valid jingle one
|
||||
XMLElement* JGSession0::checkJingle(XMLElement* xml)
|
||||
{
|
||||
if (xml && xml->type() == XMLElement::Session &&
|
||||
XMPPUtils::hasXmlns(*xml,XMPPNamespace::JingleSession))
|
||||
return xml;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Accept a Pending incoming session
|
||||
bool JGSession0::accept(const ObjList& contents, String* stanzaId)
|
||||
{
|
||||
Lock lock(this);
|
||||
if (outgoing() || state() != Pending)
|
||||
return false;
|
||||
XMLElement* xml = createJingle(ActAccept);
|
||||
addJingleContents0(m_sessContentName,xml,contents,true,true,true);
|
||||
if (!sendStanza(xml,stanzaId))
|
||||
return false;
|
||||
changeState(Active);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Send a stanza with session content(s)
|
||||
bool JGSession0::sendContent(Action action, const ObjList& contents, String* stanzaId)
|
||||
{
|
||||
Lock lock(this);
|
||||
if (state() != Pending && state() != Active)
|
||||
return false;
|
||||
bool minimal = false;
|
||||
bool addDesc = true;
|
||||
bool addTrans = true;
|
||||
switch (action) {
|
||||
case ActTransportInfo:
|
||||
addDesc = false;
|
||||
break;
|
||||
case ActTransportAccept:
|
||||
minimal = true;
|
||||
addDesc = false;
|
||||
addTrans = true;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
};
|
||||
// Make sure we dont't terminate the session on failure
|
||||
String tmp;
|
||||
if (!stanzaId) {
|
||||
tmp = "Content" + String(Time::secNow());
|
||||
stanzaId = &tmp;
|
||||
}
|
||||
XMLElement* xml = createJingle(action);
|
||||
addJingleContents0(m_sessContentName,xml,contents,minimal,addDesc,addTrans);
|
||||
return sendStanza(xml,stanzaId);
|
||||
}
|
||||
|
||||
// Build and send the initial message on an outgoing session
|
||||
bool JGSession0::initiate(const ObjList& contents, XMLElement* extra, const char* subject)
|
||||
{
|
||||
XMLElement* xml = createJingle(ActInitiate);
|
||||
addJingleContents0(m_sessContentName,xml,contents,true,true,true);
|
||||
addJingleChild0(xml,extra);
|
||||
if (!null(subject))
|
||||
addJingleChild0(xml,new XMLElement(XMLElement::Subject,0,subject));
|
||||
if (sendStanza(xml)) {
|
||||
changeState(Pending);
|
||||
return true;
|
||||
}
|
||||
changeState(Destroy);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Decode a valid jingle set event. Set the event's data on success
|
||||
JGEvent* JGSession0::decodeJingle(JBEvent* jbev)
|
||||
{
|
||||
XMLElement* jingle = jbev->child();
|
||||
if (!jingle) {
|
||||
confirm(jbev->releaseXML(),XMPPError::SBadRequest);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Action act = getAction(jingle);
|
||||
if (act == ActCount) {
|
||||
confirm(jbev->releaseXML(),XMPPError::SServiceUnavailable,
|
||||
"Unknown session action");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// *** ActTerminate
|
||||
if (act == ActTerminate) {
|
||||
// Confirm here: this is a final event,
|
||||
// stanza won't be confirmed in getEvent()
|
||||
m_recvTerminate = true;
|
||||
const char* reason = 0;
|
||||
const char* text = 0;
|
||||
XMLElement* res = jingle->findFirstChild(XMLElement::Reason);
|
||||
if (res) {
|
||||
XMLElement* tmp = res->findFirstChild();
|
||||
if (tmp && tmp->type() != XMLElement::Text)
|
||||
reason = tmp->name();
|
||||
TelEngine::destruct(tmp);
|
||||
tmp = res->findFirstChild(XMLElement::Text);
|
||||
if (tmp)
|
||||
text = tmp->getText();
|
||||
TelEngine::destruct(tmp);
|
||||
TelEngine::destruct(res);
|
||||
}
|
||||
JGEvent* ev = new JGEvent(JGEvent::Terminated,this,jbev->releaseXML(),reason,text);
|
||||
ev->setAction(act);
|
||||
ev->confirmElement();
|
||||
return ev;
|
||||
}
|
||||
|
||||
// *** ActContentInfo --> ActDtmf
|
||||
if (act == ActContentInfo) {
|
||||
// Check dtmf
|
||||
// Expect more then 1 'dtmf' child
|
||||
XMLElement* tmp = jingle->findFirstChild(XMLElement::Dtmf);
|
||||
String text;
|
||||
for (; tmp; tmp = jingle->findNextChild(tmp,XMLElement::Dtmf)) {
|
||||
String reason = tmp->getAttribute("action");
|
||||
if (reason == "button-up")
|
||||
text << tmp->getAttribute("code");
|
||||
}
|
||||
if (text)
|
||||
return new JGEvent(ActDtmf,this,jbev->releaseXML(),0,text);
|
||||
confirm(jbev->releaseXML(),XMPPError::SServiceUnavailable);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// *** ActInfo
|
||||
if (act == ActInfo) {
|
||||
// Check info element
|
||||
// Return ActInfo event to signal ping (XEP-0166 6.8)
|
||||
XMLElement* child = jingle->findFirstChild();
|
||||
if (!child)
|
||||
return new JGEvent(ActInfo,this,jbev->releaseXML());
|
||||
|
||||
JGEvent* event = 0;
|
||||
Action a = ActCount;
|
||||
XMPPNamespace::Type ns = XMPPNamespace::Count;
|
||||
// Check namespace and build event
|
||||
switch (child->type()) {
|
||||
case XMLElement::Ringing:
|
||||
a = ActRinging;
|
||||
ns = XMPPNamespace::JingleRtpInfoOld;
|
||||
break;
|
||||
case XMLElement::Mute:
|
||||
a = ActMute;
|
||||
ns = XMPPNamespace::JingleRtpInfoOld;
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
if (a != ActCount && XMPPUtils::hasXmlns(*child,ns))
|
||||
event = new JGEvent(a,this,jbev->releaseXML());
|
||||
else
|
||||
confirm(jbev->releaseXML(),XMPPError::SFeatureNotImpl);
|
||||
TelEngine::destruct(child);
|
||||
return event;
|
||||
}
|
||||
|
||||
if (act == ActTransportAccept) {
|
||||
confirm(jbev->element());
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get transport
|
||||
// Get media description
|
||||
// Create event, update transport and media
|
||||
XMLElement* media = 0;
|
||||
XMLElement* trans = jingle;
|
||||
JGSessionContent* c = 0;
|
||||
JGEvent* event = 0;
|
||||
while (true) {
|
||||
c = new JGSessionContent(JGSessionContent::RtpIceUdp,m_sessContentName,
|
||||
JGSessionContent::SendBoth,JGSessionContent::CreatorInitiator);
|
||||
c->m_rtpRemoteCandidates.m_type = JGRtpCandidates::RtpIceUdp;
|
||||
// Build media
|
||||
if (act == ActInitiate || act == ActAccept) {
|
||||
media = jingle->findFirstChild(XMLElement::Description);
|
||||
if (media && XMPPUtils::hasXmlns(*media,XMPPNamespace::JingleAudio)) {
|
||||
c->m_rtpMedia.fromXML(media);
|
||||
c->m_rtpMedia.m_media = JGRtpMediaList::Audio;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
// Build transport
|
||||
trans = trans->findFirstChild(XMLElement::Transport);
|
||||
if (trans && !XMPPUtils::hasXmlns(*trans,XMPPNamespace::JingleTransport)) {
|
||||
if (trans != jingle)
|
||||
TelEngine::destruct(trans);
|
||||
else
|
||||
trans = 0;
|
||||
}
|
||||
XMLElement* t = trans ? trans->findFirstChild(XMLElement::Candidate) : 0;
|
||||
if (t) {
|
||||
JGRtpCandidate* cd = new JGRtpCandidate(m_localSid + "_transport");
|
||||
cd->m_component = "1";
|
||||
cd->m_generation = t->getAttribute("generation");
|
||||
cd->m_address = t->getAttribute("address");
|
||||
cd->m_port = t->getAttribute("port");
|
||||
cd->m_protocol = t->getAttribute("protocol");;
|
||||
cd->m_generation = t->getAttribute("generation");
|
||||
cd->m_type = t->getAttribute("type");
|
||||
c->m_rtpRemoteCandidates.m_ufrag = t->getAttribute("username");
|
||||
c->m_rtpRemoteCandidates.m_password = t->getAttribute("password");
|
||||
c->m_rtpRemoteCandidates.append(cd);
|
||||
TelEngine::destruct(t);
|
||||
}
|
||||
else if (act == ActTransportInfo)
|
||||
break;
|
||||
// Don't set the event's element yet: this would invalidate the 'jingle' variable
|
||||
event = new JGEvent(act,this,jbev->releaseXML());
|
||||
event->m_contents.append(c);
|
||||
break;
|
||||
}
|
||||
if (trans != jingle)
|
||||
TelEngine::destruct(trans);
|
||||
TelEngine::destruct(media);
|
||||
if (!event) {
|
||||
TelEngine::destruct(c);
|
||||
confirm(jbev->releaseXML(),XMPPError::SServiceUnavailable);
|
||||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
// Create an 'iq' stanza with a 'jingle' child
|
||||
XMLElement* JGSession0::createJingle(Action action, XMLElement* element1,
|
||||
XMLElement* element2, XMLElement* element3)
|
||||
{
|
||||
XMLElement* iq = XMPPUtils::createIq(XMPPUtils::IqSet,m_localJID,m_remoteJID,0);
|
||||
XMLElement* jingle = XMPPUtils::createElement(XMLElement::Session,
|
||||
XMPPNamespace::JingleSession);
|
||||
if (action < ActCount)
|
||||
jingle->setAttribute("type",lookupAction(action,version()));
|
||||
jingle->setAttribute("initiator",outgoing() ? m_localJID : m_remoteJID);
|
||||
jingle->setAttribute("responder",outgoing() ? m_remoteJID : m_localJID);
|
||||
jingle->setAttribute("id",m_sid);
|
||||
jingle->addChild(element1);
|
||||
jingle->addChild(element2);
|
||||
jingle->addChild(element3);
|
||||
iq->addChild(jingle);
|
||||
return iq;
|
||||
}
|
||||
|
||||
// Create a dtmf XML element
|
||||
XMLElement* JGSession0::createDtmf(const char* dtmf, unsigned int msDuration)
|
||||
{
|
||||
XMLElement* xml = XMPPUtils::createElement(XMLElement::Dtmf,XMPPNamespace::DtmfOld);
|
||||
xml->setAttribute("action","button-up");
|
||||
xml->setAttribute("code",dtmf);
|
||||
return xml;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* JGSession1
|
||||
*/
|
||||
|
||||
// Create an outgoing session
|
||||
JGSession1::JGSession1(JGEngine* engine, JBStream* stream,
|
||||
const String& callerJID, const String& calledJID,
|
||||
const ObjList& contents, XMLElement* extra, const char* msg,
|
||||
const char* subject)
|
||||
const String& callerJID, const String& calledJID, const char* msg)
|
||||
: JGSession(Version1,engine,stream,callerJID,calledJID,msg)
|
||||
{
|
||||
XMLElement* xml = createJingle(ActInitiate);
|
||||
addJingleContents(xml,contents,false,true,true,true);
|
||||
addJingleChild(xml,extra);
|
||||
if (!null(subject))
|
||||
addJingleChild(xml,new XMLElement(XMLElement::Subject,0,subject));
|
||||
if (sendStanza(xml))
|
||||
changeState(Pending);
|
||||
else
|
||||
changeState(Destroy);
|
||||
}
|
||||
|
||||
// Create an incoming session
|
||||
|
@ -1399,6 +1766,22 @@ JGSession1::~JGSession1()
|
|||
{
|
||||
}
|
||||
|
||||
// Build and send the initial message on an outgoing session
|
||||
bool JGSession1::initiate(const ObjList& contents, XMLElement* extra, const char* subject)
|
||||
{
|
||||
XMLElement* xml = createJingle(ActInitiate);
|
||||
addJingleContents(xml,contents,false,true,true,true);
|
||||
addJingleChild(xml,extra);
|
||||
if (!null(subject))
|
||||
addJingleChild(xml,new XMLElement(XMLElement::Subject,0,subject));
|
||||
if (sendStanza(xml)) {
|
||||
changeState(Pending);
|
||||
return true;
|
||||
}
|
||||
changeState(Destroy);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if a given XML element is valid jingle one
|
||||
XMLElement* JGSession1::checkJingle(XMLElement* xml)
|
||||
{
|
||||
|
|
|
@ -88,6 +88,11 @@ TokenDict XMPPNamespace::s_value[] = {
|
|||
{"urn:xmpp:jingle:transports:bytestreams:0", JingleTransportByteStreams},
|
||||
{"urn:xmpp:jingle:transfer:0", JingleTransfer},
|
||||
{"urn:xmpp:jingle:dtmf:0", Dtmf},
|
||||
{"http://www.google.com/session", JingleSession},
|
||||
{"http://www.google.com/session/phone", JingleAudio},
|
||||
{"http://www.google.com/transport/p2p", JingleTransport},
|
||||
{"urn:xmpp:jingle:apps:rtp:info", JingleRtpInfoOld},
|
||||
{"http://jabber.org/protocol/jingle/info/dtmf", DtmfOld},
|
||||
{"http://jabber.org/protocol/commands", Command},
|
||||
{"http://www.google.com/xmpp/protocol/voice/v1", CapVoiceV1},
|
||||
{0,0}
|
||||
|
|
|
@ -212,6 +212,11 @@ public:
|
|||
JingleTransportByteStreams, // urn:xmpp:jingle:transports:bytestreams:0
|
||||
JingleTransfer, // urn:xmpp:jingle:transfer:0
|
||||
Dtmf, // urn:xmpp:jingle:dtmf:0
|
||||
JingleSession, // http://www.google.com/session
|
||||
JingleAudio, // http://www.google.com/session/phone
|
||||
JingleTransport, // http://www.google.com/transport/p2p
|
||||
JingleRtpInfoOld, // urn:xmpp:jingle:apps:rtp:info
|
||||
DtmfOld, // http://jabber.org/protocol/jingle/info/dtmf
|
||||
Command, // http://jabber.org/protocol/command
|
||||
CapVoiceV1, // http://www.google.com/xmpp/protocol/voice/v1
|
||||
Count,
|
||||
|
|
|
@ -40,6 +40,7 @@ class JGRtpCandidates; // A list of RTP transport candidates
|
|||
class JGSessionContent; // A Jingle session content
|
||||
class JGStreamHost; // A Jingle file transfer stream host
|
||||
class JGSession; // A basic Jingle session
|
||||
class JGSession0; // A session implementing the old jingle protocol
|
||||
class JGSession1; // The version 1 of a jingle session
|
||||
class JGEvent; // An event generated by a Jingle session
|
||||
class JGEngine; // The Jingle engine
|
||||
|
@ -215,7 +216,7 @@ public:
|
|||
* @param cryptoMandatory True to require media encryption
|
||||
*/
|
||||
inline JGRtpMediaList(Media m = MediaMissing, bool cryptoMandatory = false)
|
||||
: m_media(m), m_cryptoMandatory(cryptoMandatory)
|
||||
: m_media(m), m_cryptoMandatory(cryptoMandatory), m_ready(false)
|
||||
{}
|
||||
|
||||
/**
|
||||
|
@ -289,6 +290,11 @@ public:
|
|||
bool m_cryptoMandatory;
|
||||
ObjList m_cryptoLocal;
|
||||
ObjList m_cryptoRemote;
|
||||
|
||||
/**
|
||||
* Flag indicating wether media was negotiated
|
||||
*/
|
||||
bool m_ready;
|
||||
};
|
||||
|
||||
|
||||
|
@ -338,7 +344,7 @@ public:
|
|||
String m_network; // NIC card (diagnostic only)
|
||||
String m_priority; // Candidate priority
|
||||
String m_protocol; // The only allowable value is "udp"
|
||||
String m_type; // A Candidate Type as defined in ICE-CORE.
|
||||
String m_type; // A Candidate Type as defined in ICE-CORE
|
||||
};
|
||||
|
||||
|
||||
|
@ -381,6 +387,14 @@ public:
|
|||
generateIceToken(m_ufrag,false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill password and ufrag data using old transport restrictions (16 bytes length)
|
||||
*/
|
||||
inline void generateOldIceAuth() {
|
||||
generateOldIceToken(m_password);
|
||||
generateOldIceToken(m_ufrag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a candidate by its component value
|
||||
* @param component The value to search
|
||||
|
@ -403,7 +417,7 @@ public:
|
|||
void fromXML(XMLElement* element);
|
||||
|
||||
/**
|
||||
* Generate a random password to be used with ICE-UDP transport
|
||||
* Generate a random password or username to be used with ICE-UDP transport
|
||||
* @param dest Destination string
|
||||
* @param pwd True to generate a password, false to generate an username (ufrag)
|
||||
* @param max Maximum number of characters. The maxmimum value is 256.
|
||||
|
@ -411,6 +425,12 @@ public:
|
|||
*/
|
||||
static void generateIceToken(String& dest, bool pwd, unsigned int max = 0);
|
||||
|
||||
/**
|
||||
* Generate a random password or username to be used with old ICE-UDP transport
|
||||
* @param dest Destination string
|
||||
*/
|
||||
static void generateOldIceToken(String& dest);
|
||||
|
||||
/**
|
||||
* Get the name associated with a list's type
|
||||
* @param t The desired type
|
||||
|
@ -676,8 +696,9 @@ public:
|
|||
* Jingle session version
|
||||
*/
|
||||
enum Version {
|
||||
Version0 = 0,
|
||||
Version1 = 1,
|
||||
VersionUnknown,
|
||||
VersionUnknown
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -726,6 +747,7 @@ public:
|
|||
ActContentModify, // content-modify
|
||||
ActContentReject, // content-reject
|
||||
ActContentRemove, // content-remove
|
||||
ActContentInfo, // content-info
|
||||
ActTransfer, // session-info: Transfer
|
||||
ActRinging, // session-info: Ringing
|
||||
ActTrying, // session-info: Trying
|
||||
|
@ -981,6 +1003,24 @@ public:
|
|||
static XMLElement* buildTransfer(const String& transferTo, const String& transferFrom,
|
||||
const String& sid = String::empty());
|
||||
|
||||
/**
|
||||
* Get the session version associated with a text
|
||||
* @param value The version text
|
||||
* @param def Default value to return if not found
|
||||
* @return Session Version value
|
||||
*/
|
||||
static inline Version lookupVersion(const char* value, Version def = VersionUnknown)
|
||||
{ return (Version)lookup(value,s_versions,def); }
|
||||
|
||||
/**
|
||||
* Get the session version name
|
||||
* @param value The version value
|
||||
* @param def Default value to return if not found
|
||||
* @return Session version name or the default value if not found
|
||||
*/
|
||||
static inline const char* lookupVersion(int value, const char* def = "unknown")
|
||||
{ return lookup(value,s_versions,def); }
|
||||
|
||||
/**
|
||||
* Get the termination code associated with a text
|
||||
* @param value The termination text
|
||||
|
@ -1023,6 +1063,11 @@ public:
|
|||
*/
|
||||
static Action lookupAction(const char* str, Version ver);
|
||||
|
||||
/**
|
||||
* Session version names
|
||||
*/
|
||||
static TokenDict s_versions[];
|
||||
|
||||
/**
|
||||
* Termination reasons
|
||||
*/
|
||||
|
@ -1033,6 +1078,11 @@ public:
|
|||
*/
|
||||
static TokenDict s_states[];
|
||||
|
||||
/**
|
||||
* Action names for version Version0
|
||||
*/
|
||||
static TokenDict s_actions0[];
|
||||
|
||||
/**
|
||||
* Action names for version Version1
|
||||
*/
|
||||
|
@ -1061,6 +1111,16 @@ protected:
|
|||
*/
|
||||
JGSession(Version ver, JGEngine* engine, JBEvent* event, const String& id);
|
||||
|
||||
/**
|
||||
* Build and send the initial message on an outgoing session
|
||||
* @param contents The session contents to be sent with session initiate element
|
||||
* @param extra Optional extra child to be added to the session initiate element
|
||||
* @param subject Optional session subject
|
||||
* @return True on success
|
||||
*/
|
||||
virtual bool initiate(const ObjList& contents, XMLElement* extra,
|
||||
const char* subject = 0) = 0;
|
||||
|
||||
/**
|
||||
* Get a Jingle event from the queue.
|
||||
* This method is thread safe
|
||||
|
@ -1184,6 +1244,107 @@ private:
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* A session implementing the old jingle protocol
|
||||
* @short The version 0 of a jingle session
|
||||
*/
|
||||
class YJINGLE_API JGSession0 : public JGSession
|
||||
{
|
||||
friend class JGEvent;
|
||||
friend class JGEngine;
|
||||
public:
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~JGSession0();
|
||||
|
||||
/**
|
||||
* Check if a given XML element is valid jingle one
|
||||
* @param xml Element to check
|
||||
* @return The given element if it's a valid jingle element, 0 otherwise
|
||||
*/
|
||||
virtual XMLElement* checkJingle(XMLElement* xml);
|
||||
|
||||
/**
|
||||
* Accept a Pending incoming session.
|
||||
* This method is thread safe
|
||||
* @param contents The list of accepted contents
|
||||
* @param stanzaId Optional string to be filled with sent stanza id (used to track the response)
|
||||
* @return False if send failed
|
||||
*/
|
||||
virtual bool accept(const ObjList& contents, String* stanzaId = 0);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Constructor. Create an outgoing session
|
||||
* @param engine The engine that owns this session
|
||||
* @param stream The stream this session is bound to
|
||||
* @param callerJID The caller's full JID
|
||||
* @param calledJID The called party's full JID
|
||||
* @param msg Optional message to be sent before session initiate
|
||||
*/
|
||||
JGSession0(JGEngine* engine, JBStream* stream,
|
||||
const String& callerJID, const String& calledJID, const char* msg = 0);
|
||||
|
||||
/**
|
||||
* Constructor. Create an incoming session.
|
||||
* @param engine The engine that owns this session
|
||||
* @param event A valid Jabber Jingle event with action session initiate
|
||||
* @param id Session id
|
||||
*/
|
||||
JGSession0(JGEngine* engine, JBEvent* event, const String& id);
|
||||
|
||||
/**
|
||||
* Build and send the initial message on an outgoing session
|
||||
* @param contents The session contents to be sent with session initiate element
|
||||
* @param extra Optional extra child to be added to the session initiate element
|
||||
* @param subject Optional session subject
|
||||
* @return True on success
|
||||
*/
|
||||
virtual bool initiate(const ObjList& contents, XMLElement* extra,
|
||||
const char* subject = 0);
|
||||
|
||||
/**
|
||||
* Send a stanza with session content(s)
|
||||
* This method is thread safe
|
||||
* @param action Must be a transport- action
|
||||
* @param contents Non empty list with content(s) to send
|
||||
* @param stanzaId Optional string to be filled with sent
|
||||
* stanza id (used to track the response)
|
||||
* @return False if send failed
|
||||
*/
|
||||
virtual bool sendContent(Action action, const ObjList& contents, String* stanzaId = 0);
|
||||
|
||||
/**
|
||||
* Decode a valid jingle set event. Set the event's data on success
|
||||
* @param jbev The event to decode
|
||||
* @return JGEvent pointer or 0
|
||||
*/
|
||||
virtual JGEvent* decodeJingle(JBEvent* jbev);
|
||||
|
||||
/**
|
||||
* Create an 'iq' of type 'set' with a 'jingle' child
|
||||
* @param action The action of the Jingle stanza
|
||||
* @param element1 Optional child element
|
||||
* @param element2 Optional child element
|
||||
* @param element3 Optional child element
|
||||
* @return Valid XMLElement pointer
|
||||
*/
|
||||
virtual XMLElement* createJingle(Action action, XMLElement* element1 = 0,
|
||||
XMLElement* element2 = 0, XMLElement* element3 = 0);
|
||||
|
||||
/**
|
||||
* Create a dtmf XML element
|
||||
* @param dtmf The dtmf string
|
||||
* @param msDuration The tone duration in miliseconds. Ignored if 0
|
||||
* @return Valid XMLElement pointer or 0
|
||||
*/
|
||||
virtual XMLElement* createDtmf(const char* dtmf, unsigned int msDuration = 0);
|
||||
|
||||
protected:
|
||||
String m_sessContentName; // Content name advertised to upper layer
|
||||
};
|
||||
|
||||
/**
|
||||
* A session implementing the Jingle protocol including session transfer and file transfer
|
||||
* @short The version 1 of a jingle session
|
||||
|
@ -1264,15 +1425,10 @@ protected:
|
|||
* @param stream The stream this session is bound to
|
||||
* @param callerJID The caller's full JID
|
||||
* @param calledJID The called party's full JID
|
||||
* @param contents The session contents to be sent with session initiate element
|
||||
* @param extra Optional extra child to be added to the session initiate element
|
||||
* @param msg Optional message to be sent before session initiate
|
||||
* @param subject Optional session subject
|
||||
*/
|
||||
JGSession1(JGEngine* engine, JBStream* stream,
|
||||
const String& callerJID, const String& calledJID,
|
||||
const ObjList& contents, XMLElement* extra = 0, const char* msg = 0,
|
||||
const char* subject = 0);
|
||||
const String& callerJID, const String& calledJID, const char* msg = 0);
|
||||
|
||||
/**
|
||||
* Constructor. Create an incoming session.
|
||||
|
@ -1282,6 +1438,16 @@ protected:
|
|||
*/
|
||||
JGSession1(JGEngine* engine, JBEvent* event, const String& id);
|
||||
|
||||
/**
|
||||
* Build and send the initial message on an outgoing session
|
||||
* @param contents The session contents to be sent with session initiate element
|
||||
* @param extra Optional extra child to be added to the session initiate element
|
||||
* @param subject Optional session subject
|
||||
* @return True on success
|
||||
*/
|
||||
virtual bool initiate(const ObjList& contents, XMLElement* extra,
|
||||
const char* subject = 0);
|
||||
|
||||
/**
|
||||
* Decode a valid jingle set event. Set the event's data on success
|
||||
* @param jbev The event to decode
|
||||
|
@ -1313,7 +1479,6 @@ protected:
|
|||
*/
|
||||
virtual void processJabberIqEvent(JBEvent& ev);
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1323,6 +1488,7 @@ private:
|
|||
class YJINGLE_API JGEvent
|
||||
{
|
||||
friend class JGSession;
|
||||
friend class JGSession0;
|
||||
friend class JGSession1;
|
||||
public:
|
||||
/**
|
||||
|
@ -1587,6 +1753,7 @@ public:
|
|||
/**
|
||||
* Make an outgoing call.
|
||||
* 'media' and 'transport' will be invalid on exit. Don't delete them
|
||||
* @param ver The session version to use
|
||||
* @param callerName The local peer's username
|
||||
* @param remoteJID The remote peer's JID
|
||||
* @param contents The list of session content(s)
|
||||
|
@ -1595,7 +1762,7 @@ public:
|
|||
* @param subject Optional session subject
|
||||
* @return Valid JGSession pointer (referenced) on success
|
||||
*/
|
||||
JGSession* call(const String& callerName, const String& remoteJID,
|
||||
JGSession* call(JGSession::Version ver, const String& callerName, const String& remoteJID,
|
||||
const ObjList& contents, XMLElement* extra = 0, const char* msg = 0,
|
||||
const char* subject = 0);
|
||||
|
||||
|
|
|
@ -388,6 +388,8 @@ private:
|
|||
Mutex m_mutex; // Lock transport and session
|
||||
State m_state; // Connection state
|
||||
JGSession* m_session; // Jingle session attached to this connection
|
||||
bool m_rtpStarted; // RTP started flag used by version 0 of the jingle session
|
||||
JGSession::Version m_sessVersion; // Jingle session version
|
||||
JabberID m_local; // Local user's JID
|
||||
JabberID m_remote; // Remote user's JID
|
||||
ObjList m_audioContents; // The list of negotiated audio contents
|
||||
|
@ -621,6 +623,7 @@ static bool s_attachPresToCmd = false; // Attach presence to command
|
|||
static bool s_userRoster = false; // Send client roster with user.roster or resource.notify
|
||||
static bool s_useCrypto = false;
|
||||
static bool s_cryptoMandatory = false;
|
||||
static JGSession::Version s_sessVersion = JGSession::VersionUnknown; // Default jingle session version for outgoing calls
|
||||
static YJBEngine* s_jabber = 0;
|
||||
static YJGEngine* s_jingle = 0;
|
||||
static YJBMessage* s_message = 0;
|
||||
|
@ -1310,13 +1313,16 @@ YJGConnection::YJGConnection(Message& msg, const char* caller, const char* calle
|
|||
bool available, const char* file)
|
||||
: Channel(&plugin,0,true),
|
||||
m_mutex(true,"YJGConnection"),
|
||||
m_state(Pending), m_session(0), m_local(caller),
|
||||
m_remote(called), m_audioContent(0),
|
||||
m_state(Pending), m_session(0), m_rtpStarted(false), m_sessVersion(s_sessVersion),
|
||||
m_local(caller), m_remote(called), m_audioContent(0),
|
||||
m_callerPrompt(msg.getValue("callerprompt")), m_sendRawRtpFirst(true),
|
||||
m_useCrypto(s_useCrypto), m_cryptoMandatory(s_cryptoMandatory),
|
||||
m_hangup(false), m_timeout(0), m_transferring(false), m_recvTransferStanza(0),
|
||||
m_dataFlags(0), m_ftStatus(FTNone), m_ftHostDirection(FTHostNone)
|
||||
{
|
||||
NamedString* ver = msg.getParam("ojingleversion");
|
||||
if (ver)
|
||||
m_sessVersion = JGSession::lookupVersion(*ver);
|
||||
m_subject = msg.getValue("subject");
|
||||
String uri = msg.getValue("diverteruri",msg.getValue("diverter"));
|
||||
// Skip protocol if present
|
||||
|
@ -1388,7 +1394,8 @@ YJGConnection::YJGConnection(Message& msg, const char* caller, const char* calle
|
|||
YJGConnection::YJGConnection(JGEvent* event)
|
||||
: Channel(&plugin,0,false),
|
||||
m_mutex(true,"YJGConnection"),
|
||||
m_state(Active), m_session(event->session()),
|
||||
m_state(Active), m_session(event->session()), m_rtpStarted(false),
|
||||
m_sessVersion(event->session()->version()),
|
||||
m_local(event->session()->local()), m_remote(event->session()->remote()),
|
||||
m_audioContent(0), m_sendRawRtpFirst(true),
|
||||
m_useCrypto(s_useCrypto), m_cryptoMandatory(s_cryptoMandatory),
|
||||
|
@ -1479,6 +1486,13 @@ YJGConnection::YJGConnection(JGEvent* event)
|
|||
m_session->sendContent(JGSession::ActContentRemove,m_ftContents);
|
||||
m_ftContents.clear();
|
||||
}
|
||||
// Send transport accept now for version 0
|
||||
if (m_sessVersion == JGSession::Version0) {
|
||||
ObjList* o = m_audioContents.skipNull();
|
||||
if (o)
|
||||
m_session->sendContent(JGSession::ActTransportAccept,
|
||||
static_cast<JGSessionContent*>(o->get()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_state = Pending;
|
||||
|
@ -1516,6 +1530,7 @@ bool YJGConnection::route()
|
|||
m->addParam("calleruri",BUILD_XMPP_URI(m_remote));
|
||||
if (m_subject)
|
||||
m->addParam("subject",m_subject);
|
||||
m->addParam("jingleversion",JGSession::lookupVersion(m_sessVersion));
|
||||
m_mutex.lock();
|
||||
// TODO: add remote ip/port
|
||||
// Fill file transfer data
|
||||
|
@ -1603,9 +1618,11 @@ bool YJGConnection::msgAnswered(Message& msg)
|
|||
{
|
||||
Debug(this,DebugCall,"msgAnswered [%p]",this);
|
||||
if (m_ftStatus == FTNone) {
|
||||
clearEndpoint();
|
||||
if (m_sessVersion != JGSession::Version0)
|
||||
clearEndpoint();
|
||||
m_mutex.lock();
|
||||
resetCurrentAudioContent(true,false,false);
|
||||
if (m_sessVersion != JGSession::Version0 || !m_rtpStarted)
|
||||
resetCurrentAudioContent(true,false,true);
|
||||
ObjList tmp;
|
||||
if (m_audioContent)
|
||||
tmp.append(m_audioContent)->setDelete(false);
|
||||
|
@ -2107,25 +2124,38 @@ bool YJGConnection::handleEvent(JGEvent* event)
|
|||
return setupSocksFileTransfer(true);
|
||||
// Update media
|
||||
Debug(this,DebugCall,"Remote peer answered the call [%p]",this);
|
||||
m_state = Active;
|
||||
removeCurrentAudioContent();
|
||||
m_state = Active;
|
||||
// Remove current content only if not Version0
|
||||
// For this version we keep only 1 content
|
||||
if (m_sessVersion != JGSession::Version0)
|
||||
removeCurrentAudioContent();
|
||||
for (ObjList* o = event->m_contents.skipNull(); o; o = o->skipNext()) {
|
||||
JGSessionContent* recv = static_cast<JGSessionContent*>(o->get());
|
||||
JGSessionContent* c = findContent(*recv,m_audioContents);
|
||||
if (!c)
|
||||
continue;
|
||||
// Update credentials for ICE-UDP
|
||||
c->m_rtpRemoteCandidates.m_password = recv->m_rtpRemoteCandidates.m_password;
|
||||
c->m_rtpRemoteCandidates.m_ufrag = recv->m_rtpRemoteCandidates.m_ufrag;
|
||||
// only if not version 0 (this version only sends media in accept)
|
||||
if (m_sessVersion != JGSession::Version0) {
|
||||
c->m_rtpRemoteCandidates.m_password = recv->m_rtpRemoteCandidates.m_password;
|
||||
c->m_rtpRemoteCandidates.m_ufrag = recv->m_rtpRemoteCandidates.m_ufrag;
|
||||
}
|
||||
// Update media
|
||||
if (!matchMedia(*c,*recv)) {
|
||||
Debug(this,DebugInfo,"No common media for content=%s [%p]",
|
||||
c->toString().c_str(),this);
|
||||
continue;
|
||||
}
|
||||
c->m_rtpMedia.m_ready = true;
|
||||
// Update transport(s)
|
||||
bool changed = updateCandidate(1,*c,*recv);
|
||||
changed = updateCandidate(2,*c,*recv) || changed;
|
||||
// Force changed for Version0 (we have valid common media for this version)
|
||||
bool changed = false;
|
||||
if (m_sessVersion != JGSession::Version0) {
|
||||
changed = updateCandidate(1,*c,*recv);
|
||||
changed = updateCandidate(2,*c,*recv) || changed;
|
||||
}
|
||||
else
|
||||
changed = true;
|
||||
if (changed && !m_audioContent && recv->isSession())
|
||||
resetCurrentAudioContent(true,false,true,c);
|
||||
}
|
||||
|
@ -2237,19 +2267,26 @@ bool YJGConnection::presenceChanged(bool available)
|
|||
XMLElement* transfer = 0;
|
||||
if (m_transferFrom)
|
||||
transfer = JGSession::buildTransfer(String::empty(),m_transferFrom);
|
||||
if (m_sendRawRtpFirst) {
|
||||
addContent(true,buildAudioContent(JGRtpCandidates::RtpRawUdp));
|
||||
addContent(true,buildAudioContent(JGRtpCandidates::RtpIceUdp));
|
||||
if (m_sessVersion == JGSession::Version1) {
|
||||
if (m_sendRawRtpFirst) {
|
||||
addContent(true,buildAudioContent(JGRtpCandidates::RtpRawUdp));
|
||||
addContent(true,buildAudioContent(JGRtpCandidates::RtpIceUdp));
|
||||
}
|
||||
else {
|
||||
addContent(true,buildAudioContent(JGRtpCandidates::RtpIceUdp));
|
||||
addContent(true,buildAudioContent(JGRtpCandidates::RtpRawUdp));
|
||||
}
|
||||
}
|
||||
else {
|
||||
else if (m_sessVersion == JGSession::Version0)
|
||||
addContent(true,buildAudioContent(JGRtpCandidates::RtpIceUdp));
|
||||
addContent(true,buildAudioContent(JGRtpCandidates::RtpRawUdp));
|
||||
}
|
||||
m_session = s_jingle->call(m_local,m_remote,m_audioContents,transfer,
|
||||
m_session = s_jingle->call(m_sessVersion,m_local,m_remote,m_audioContents,transfer,
|
||||
m_callerPrompt,m_subject);
|
||||
// Init now the transport for version 0
|
||||
if (m_session && m_session->version() == JGSession::Version0)
|
||||
resetCurrentAudioContent(true,false);
|
||||
}
|
||||
else
|
||||
m_session = s_jingle->call(m_local,m_remote,m_ftContents,0,
|
||||
m_session = s_jingle->call(m_sessVersion,m_local,m_remote,m_ftContents,0,
|
||||
m_callerPrompt,m_subject);
|
||||
if (!m_session) {
|
||||
hangup("noconn");
|
||||
|
@ -2419,7 +2456,7 @@ void YJGConnection::processActionTransportInfo(JGEvent* event)
|
|||
if (!event)
|
||||
return;
|
||||
|
||||
event->confirmElement();
|
||||
bool ok = m_sessVersion != JGSession::Version0;
|
||||
bool startAudioContent = false;
|
||||
JGSessionContent* newContent = 0;
|
||||
for (ObjList* o = event->m_contents.skipNull(); o; o = o->skipNext()) {
|
||||
|
@ -2430,11 +2467,18 @@ void YJGConnection::processActionTransportInfo(JGEvent* event)
|
|||
event->actionName(),c->toString().c_str(),this);
|
||||
continue;
|
||||
}
|
||||
// Update transport(s)
|
||||
bool changed = updateCandidate(1,*cc,*c);
|
||||
// Version0: the session will give us only 1 content
|
||||
if (!changed && m_sessVersion == JGSession::Version0) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
ok = true;
|
||||
// Update credentials for ICE-UDP
|
||||
cc->m_rtpRemoteCandidates.m_password = c->m_rtpRemoteCandidates.m_password;
|
||||
cc->m_rtpRemoteCandidates.m_ufrag = c->m_rtpRemoteCandidates.m_ufrag;
|
||||
// Update transport(s)
|
||||
bool changed = updateCandidate(1,*cc,*c);
|
||||
// Check RTCP
|
||||
changed = updateCandidate(2,*cc,*c) || changed;
|
||||
if (!changed)
|
||||
continue;
|
||||
|
@ -2447,13 +2491,17 @@ void YJGConnection::processActionTransportInfo(JGEvent* event)
|
|||
else
|
||||
newContent = cc;
|
||||
}
|
||||
|
||||
if (newContent) {
|
||||
if (!dataFlags(OnHold))
|
||||
resetCurrentAudioContent(isAnswered(),!isAnswered(),true,newContent);
|
||||
if (ok) {
|
||||
event->confirmElement();
|
||||
if (newContent) {
|
||||
if (!dataFlags(OnHold))
|
||||
resetCurrentAudioContent(isAnswered(),!isAnswered(),true,newContent);
|
||||
}
|
||||
else if ((startAudioContent && !startRtp()) || !(m_audioContent || dataFlags(OnHold)))
|
||||
resetCurrentAudioContent(isAnswered(),!isAnswered());
|
||||
}
|
||||
else if ((startAudioContent && !startRtp()) || !(m_audioContent || dataFlags(OnHold)))
|
||||
resetCurrentAudioContent(isAnswered(),!isAnswered());
|
||||
else
|
||||
event->confirmElement(XMPPError::SNotAcceptable);
|
||||
enqueueCallProgress();
|
||||
}
|
||||
|
||||
|
@ -2465,6 +2513,13 @@ bool YJGConnection::updateCandidate(unsigned int component, JGSessionContent& lo
|
|||
if (!rtpRecv)
|
||||
return false;
|
||||
JGRtpCandidate* rtp = local.m_rtpRemoteCandidates.findByComponent(component);
|
||||
// Version0: check acceptable transport
|
||||
if (m_sessVersion == JGSession::Version0) {
|
||||
// Don't accept another transport if we already have one
|
||||
// Check for valid attributes
|
||||
if (rtp || rtpRecv->m_protocol != "udp" || rtpRecv->m_type != "local")
|
||||
return false;
|
||||
}
|
||||
if (!rtp) {
|
||||
DDebug(this,DebugAll,"Adding remote transport '%s' in content '%s' [%p]",
|
||||
rtpRecv->toString().c_str(),local.toString().c_str(),this);
|
||||
|
@ -2496,8 +2551,12 @@ void YJGConnection::addContent(bool local, JGSessionContent* c)
|
|||
c->m_rtpRemoteCandidates.m_type = c->m_rtpLocalCandidates.m_type;
|
||||
else
|
||||
c->m_rtpLocalCandidates.m_type = c->m_rtpRemoteCandidates.m_type;
|
||||
if (c->m_rtpLocalCandidates.m_type == JGRtpCandidates::RtpIceUdp)
|
||||
c->m_rtpLocalCandidates.generateIceAuth();
|
||||
if (c->m_rtpLocalCandidates.m_type == JGRtpCandidates::RtpIceUdp) {
|
||||
if (m_sessVersion != JGSession::Version0)
|
||||
c->m_rtpLocalCandidates.generateIceAuth();
|
||||
else
|
||||
c->m_rtpLocalCandidates.generateOldIceAuth();
|
||||
}
|
||||
// Fill synonym for received media
|
||||
if (!local) {
|
||||
for (ObjList* o = c->m_rtpMedia.skipNull(); o; o = o->skipNext()) {
|
||||
|
@ -2610,7 +2669,6 @@ bool YJGConnection::resetCurrentAudioContent(bool session, bool earlyMedia,
|
|||
}
|
||||
else if (!newContent->isValidAudio())
|
||||
return false;
|
||||
|
||||
if (newContent && newContent->ref()) {
|
||||
m_audioContent = newContent;
|
||||
Debug(this,DebugAll,"Using audio content '%s' [%p]",
|
||||
|
@ -2633,6 +2691,9 @@ bool YJGConnection::startRtp()
|
|||
return false;
|
||||
}
|
||||
|
||||
if (m_sessVersion == JGSession::Version0 && m_rtpStarted)
|
||||
return true;
|
||||
|
||||
JGRtpCandidate* rtpLocal = m_audioContent->m_rtpLocalCandidates.findByComponent(1);
|
||||
JGRtpCandidate* rtpRemote = m_audioContent->m_rtpRemoteCandidates.findByComponent(1);
|
||||
if (!(rtpLocal && rtpRemote)) {
|
||||
|
@ -2683,6 +2744,7 @@ bool YJGConnection::startRtp()
|
|||
|
||||
if (m_audioContent->m_rtpLocalCandidates.m_type == JGRtpCandidates::RtpIceUdp &&
|
||||
rtpRemote->m_address) {
|
||||
m_rtpStarted = true;
|
||||
// Start STUN
|
||||
Message* msg = new Message("socket.stun");
|
||||
msg->userData(m.userData());
|
||||
|
@ -2856,6 +2918,7 @@ bool YJGConnection::processContentAdd(const JGEvent& event, ObjList& ok, ObjList
|
|||
remove.append(c)->setDelete(false);
|
||||
continue;
|
||||
}
|
||||
c->m_rtpMedia.m_ready = true;
|
||||
|
||||
// Check crypto
|
||||
bool error = false;
|
||||
|
@ -4228,6 +4291,7 @@ void YJGDriver::initialize()
|
|||
s_userRoster = sect->getBoolValue("user.roster",false);
|
||||
s_useCrypto = sect->getBoolValue("secure_rtp",false);
|
||||
s_cryptoMandatory = s_useCrypto;
|
||||
s_sessVersion = JGSession::lookupVersion(sect->getValue("jingleversion"),JGSession::Version1);
|
||||
|
||||
// Init codecs in use. Check each codec in known codecs list against the configuration
|
||||
s_usedCodecs.clear();
|
||||
|
@ -4261,6 +4325,7 @@ void YJGDriver::initialize()
|
|||
if (debugAt(dbg)) {
|
||||
String s;
|
||||
s << " localip=" << s_localAddress ? s_localAddress.c_str() : "MISSING";
|
||||
s << " jingleversion=" << JGSession::lookupVersion(s_sessVersion);
|
||||
s << " singletone=" << String::boolText(m_singleTone);
|
||||
s << " pending_timeout=" << s_pendingTimeout;
|
||||
s << " anonymous_caller=" << s_anonymousCaller;
|
||||
|
|
Loading…
Reference in New Issue