Fixed stream iq stanza decoding. Increased jingle stanza timeout. Changed debug and comments.

git-svn-id: http://yate.null.ro/svn/yate/trunk@1918 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
marian 2008-04-17 09:24:04 +00:00
parent eaad179c28
commit 1b00fcf7f4
4 changed files with 135 additions and 131 deletions

View File

@ -1333,7 +1333,7 @@ XMPPUser::~XMPPUser()
}
// Add a resource for the user
bool XMPPUser::addLocalRes(JIDResource* resource)
bool XMPPUser::addLocalRes(JIDResource* resource, bool send)
{
if (!resource)
return false;
@ -1341,11 +1341,11 @@ bool XMPPUser::addLocalRes(JIDResource* resource)
if (!m_localRes.add(resource))
return false;
DDebug(m_local->engine(),DebugAll,
"User(%s). Added resource name=%s audio=%s [%p]",
"User(%s). Added local resource name=%s audio=%s avail=%s [%p]",
m_local->jid().c_str(),resource->name().c_str(),
String::boolText(resource->hasCap(JIDResource::CapAudio)),this);
resource->setPresence(true);
if (m_subscription.from())
String::boolText(resource->hasCap(JIDResource::CapAudio)),
String::boolText(resource->available()),this);
if (send && m_subscription.from())
sendPresence(resource,0,true);
return true;
}
@ -1353,15 +1353,18 @@ bool XMPPUser::addLocalRes(JIDResource* resource)
// Remove a resource of the user
void XMPPUser::removeLocalRes(JIDResource* resource)
{
if (!(resource && m_localRes.get(resource->name())))
if (!(resource && m_localRes.get(resource->name()))) {
TelEngine::destruct(resource);
return;
}
Lock lock(this);
resource->setPresence(false);
if (m_subscription.from())
sendPresence(resource,0);
m_localRes.remove(resource);
DDebug(m_local->engine(),DebugAll,"User(%s). Removed resource name=%s [%p]",
DDebug(m_local->engine(),DebugAll,
"User(%s). Removing local resource name=%s [%p]",
m_local->jid().c_str(),resource->name().c_str(),this);
m_localRes.remove(resource);
}
// Remove all user's resources

View File

@ -59,8 +59,8 @@ TokenDict JBStream::s_flagName[] = {
static String s_version = "1.0"; // Protocol version
static String s_declaration = "<?xml version='1.0' encoding='UTF-8'?>";
// Utility: append a param to a string
inline void s_appendParam(String& dest, const char* name, const char* value,
// Utility: append a quoted param to a string
inline void appendQParam(String& dest, const char* name, const char* value,
bool quotes, bool first = false)
{
if (!first)
@ -72,6 +72,27 @@ inline void s_appendParam(String& dest, const char* name, const char* value,
dest << value;
}
// Check if an element exists and have an expected namespace
inline bool checkValidXmlns(XMLElement* e, XMPPNamespace::Type ns,
XMPPError::Type& error)
{
if (e && XMPPUtils::hasXmlns(*e,ns))
return true;
error = e ? XMPPError::SFeatureNotImpl : XMPPError::SBadRequest;
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; }
@ -774,19 +795,6 @@ void JBStream::processRunning(XMLElement* xml)
sendStanza(XMPPUtils::createError(xml,XMPPError::TypeModify,error));
}
// Helper function to make the code simpler
inline bool checkChild(XMLElement* e, XMPPNamespace::Type ns, XMPPError::Type& error)
{
if (!e) {
error = XMPPError::SBadRequest;
return false;
}
if (XMPPUtils::hasXmlns(*e,ns))
return true;
error = XMPPError::SFeatureNotImpl;
return false;
}
// Process a received element in Securing state
void JBStream::processSecuring(XMLElement* xml)
{
@ -1060,121 +1068,102 @@ void JBStream::processStarted(XMLElement* xml)
}
// Create an iq event from a received iq stanza
// Filter iq stanzas to generate an appropriate event
// Get iq type : set/get, error, result
// result: MAY have a first child with a response
// set/get: MUST have a first child
// error: MAY have a first child with the sent stanza
// MUST have an 'error' child
// Check type and the first child's namespace
JBEvent* JBStream::getIqEvent(XMLElement* xml, int iqType, XMPPError::Type& error)
{
// Filter iq stanzas to generate an appropriate event
// Get iq type : set/get, error, result
// result: MAY have a first child with a response
// set/get: MUST have a first child
// error: MAY have a first child with the sent stanza
// MUST have an 'error' child
// Check type and the first child's namespace
XMLElement* child = xml->findFirstChild();
#define IQEVENT_SET_REQ(get,set) evType = (iqType == XMPPUtils::IqGet) ? get : set
#define IQEVENT_SET_RSP(res,err) evType = (iqType == XMPPUtils::IqResult) ? res : err
JBEvent::Type evType;
switch (iqType) {
case XMPPUtils::IqGet:
case XMPPUtils::IqSet:
evType = JBEvent::Iq;
break;
case XMPPUtils::IqResult:
evType = JBEvent::IqResult;
break;
case XMPPUtils::IqError:
evType = JBEvent::IqError;
break;
default:
error = XMPPError::SFeatureNotImpl;
return 0;
}
error = XMPPError::NoError;
XMLElement* child = 0;
// Fix some element name conflicts
if (child && child->type() == XMLElement::Session &&
child->hasAttribute("xmlns",s_ns[XMPPNamespace::Jingle]))
child->changeType(XMLElement::Jingle);
// Create event
if (iqType == XMPPUtils::IqResult || iqType == XMPPUtils::IqSet ||
iqType == XMPPUtils::IqGet) {
// Request (type is set or get): check the child (MUST exists)
// Response (type is result or error). Check it only if it has an 'iq' child
if (evType == JBEvent::Iq) {
child = xml->findFirstChild();
// No child: request what ???
if (!child) {
if (iqType == XMPPUtils::IqResult)
return new JBEvent(JBEvent::IqResult,this,xml);
return new JBEvent(JBEvent::Iq,this,xml);
error = XMPPError::SBadRequest;
return 0;
}
fixXmlType(child);
switch (child->type()) {
case XMLElement::Jingle:
if (!checkChild(child,XMPPNamespace::Jingle,error))
return 0;
switch (iqType) {
case XMPPUtils::IqGet:
return new JBEvent(JBEvent::IqJingleGet,this,xml,child);
case XMPPUtils::IqSet:
return new JBEvent(JBEvent::IqJingleSet,this,xml,child);
case XMPPUtils::IqResult:
return new JBEvent(JBEvent::IqJingleRes,this,xml,child);
}
if (checkValidXmlns(child,XMPPNamespace::Jingle,error))
IQEVENT_SET_REQ(JBEvent::IqJingleGet,JBEvent::IqJingleSet);
break;
case XMLElement::Query:
if (checkChild(child,XMPPNamespace::DiscoInfo,error))
switch (iqType) {
case XMPPUtils::IqGet:
return new JBEvent(JBEvent::IqDiscoInfoGet,this,xml,child);
case XMPPUtils::IqSet:
return new JBEvent(JBEvent::IqDiscoInfoSet,this,xml,child);
case XMPPUtils::IqResult:
return new JBEvent(JBEvent::IqDiscoInfoRes,this,xml,child);
}
else if (checkChild(child,XMPPNamespace::DiscoItems,error))
switch (iqType) {
case XMPPUtils::IqGet:
return new JBEvent(JBEvent::IqDiscoItemsGet,this,xml,child);
case XMPPUtils::IqSet:
return new JBEvent(JBEvent::IqDiscoItemsSet,this,xml,child);
case XMPPUtils::IqResult:
return new JBEvent(JBEvent::IqDiscoItemsRes,this,xml,child);
}
else if (checkChild(child,XMPPNamespace::Roster,error))
switch (iqType) {
case XMPPUtils::IqGet:
error = XMPPError::SBadRequest;
break;
case XMPPUtils::IqSet:
return new JBEvent(JBEvent::IqRosterSet,this,xml,child);
case XMPPUtils::IqResult:
return new JBEvent(JBEvent::IqRosterRes,this,xml,child);
}
return 0;
if (checkValidXmlns(child,XMPPNamespace::DiscoInfo,error))
IQEVENT_SET_REQ(JBEvent::IqDiscoInfoGet,JBEvent::IqDiscoInfoSet);
else if (checkValidXmlns(child,XMPPNamespace::DiscoItems,error))
IQEVENT_SET_REQ(JBEvent::IqDiscoItemsGet,JBEvent::IqDiscoItemsSet);
else if (checkValidXmlns(child,XMPPNamespace::Roster,error))
if (iqType == XMPPUtils::IqGet)
error = XMPPError::SBadRequest;
else
evType = JBEvent::IqRosterSet;
break;
case XMLElement::Command:
if (!checkChild(child,XMPPNamespace::Command,error))
return 0;
switch (iqType) {
case XMPPUtils::IqGet:
return new JBEvent(JBEvent::IqCommandGet,this,xml,child);
case XMPPUtils::IqSet:
return new JBEvent(JBEvent::IqCommandSet,this,xml,child);
case XMPPUtils::IqResult:
return new JBEvent(JBEvent::IqCommandRes,this,xml,child);
}
if (checkValidXmlns(child,XMPPNamespace::Command,error))
IQEVENT_SET_REQ(JBEvent::IqCommandGet,JBEvent::IqCommandSet);
break;
default: ;
}
// Unhandled child
if (iqType != XMPPUtils::IqResult)
return new JBEvent(JBEvent::Iq,this,xml,child);
return new JBEvent(JBEvent::IqResult,this,xml,child);
}
else if (iqType == XMPPUtils::IqError) {
JBEvent::Type evType = JBEvent::IqError;
// First child may be a sent stanza
if (child && child->type() != XMLElement::Error) {
switch (child->type()) {
else {
child = xml->findFirstChild(XMLElement::Iq);
XMLElement* c = child ? child->findFirstChild() : 0;
// The child of this 'iq' sets the event's type
if (c) {
fixXmlType(c);
switch (c->type()) {
case XMLElement::Jingle:
evType = JBEvent::IqJingleErr;
if (XMPPUtils::hasXmlns(*c,XMPPNamespace::Jingle))
IQEVENT_SET_RSP(JBEvent::IqJingleRes,JBEvent::IqJingleErr);
break;
case XMLElement::Query:
if (XMPPUtils::hasXmlns(*xml,XMPPNamespace::DiscoInfo))
evType = JBEvent::IqDiscoInfoErr;
else if (XMPPUtils::hasXmlns(*xml,XMPPNamespace::DiscoItems))
evType = JBEvent::IqDiscoItemsErr;
else if (XMPPUtils::hasXmlns(*xml,XMPPNamespace::Roster))
evType = JBEvent::IqRosterErr;
if (XMPPUtils::hasXmlns(*c,XMPPNamespace::DiscoInfo))
IQEVENT_SET_RSP(JBEvent::IqDiscoInfoRes,JBEvent::IqDiscoInfoErr);
else if (XMPPUtils::hasXmlns(*c,XMPPNamespace::DiscoItems))
IQEVENT_SET_RSP(JBEvent::IqDiscoItemsRes,JBEvent::IqDiscoItemsErr);
else if (XMPPUtils::hasXmlns(*c,XMPPNamespace::Roster))
IQEVENT_SET_RSP(JBEvent::IqRosterRes,JBEvent::IqRosterErr);
break;
case XMLElement::Command:
evType = JBEvent::IqCommandErr;
if (XMPPUtils::hasXmlns(*c,XMPPNamespace::Command))
IQEVENT_SET_RSP(JBEvent::IqCommandRes,JBEvent::IqCommandErr);
break;
default: ;
}
child = xml->findNextChild(child);
}
if (!(child && child->type() == XMLElement::Error))
child = 0;
return new JBEvent(evType,this,xml,child);
}
error = XMPPError::SBadRequest;
if (error == XMPPError::NoError)
return new JBEvent(evType,this,xml,child);
return 0;
#undef IQEVENT_SET_REQ
#undef IQEVENT_SET_RSP
}
// Send declaration and stream start
@ -1453,28 +1442,28 @@ void JBStream::buildSaslResponse(String& response, String* realm, String* nonce)
// Digest MD5. See RFC 2831 2.1.2.1
MD5 md5(String((unsigned int)::random()));
m_cnonce = md5.hexDigest();
s_appendParam(response,"username",m_local.node(),true,true);
appendQParam(response,"username",m_local.node(),true,true);
if (realm) {
m_realm = *realm;
s_appendParam(response,"realm",m_realm,true);
appendQParam(response,"realm",m_realm,true);
if (nonce) {
m_nonce = *nonce;
s_appendParam(response,"nonce",m_nonce,true);
appendQParam(response,"nonce",m_nonce,true);
m_nonceCount++;
char tmp[9];
::sprintf(tmp,"%08x",m_nonceCount);
m_nc = tmp;
s_appendParam(response,"nc",m_nc,false);
appendQParam(response,"nc",m_nc,false);
}
}
s_appendParam(response,"cnonce",m_cnonce,true);
s_appendParam(response,"digest-uri",String("xmpp/")+m_local.domain(),true);
s_appendParam(response,"qop",s_qop,true);
appendQParam(response,"cnonce",m_cnonce,true);
appendQParam(response,"digest-uri",String("xmpp/")+m_local.domain(),true);
appendQParam(response,"qop",s_qop,true);
String rsp;
buildDigestMD5Sasl(rsp,true);
s_appendParam(response,"response",rsp,false);
s_appendParam(response,"charset","utf-8",false);
s_appendParam(response,"algorithm","md5-sess",false);
appendQParam(response,"response",rsp,false);
appendQParam(response,"charset","utf-8",false);
appendQParam(response,"algorithm","md5-sess",false);
Base64 base64((void*)response.c_str(),response.length());
base64.encode(response);
}

View File

@ -32,7 +32,7 @@ static XMPPError s_err;
*/
JGEngine::JGEngine(JBEngine* engine, const NamedList* params, int prio)
: JBService(engine,"jgengine",params,prio), m_sessionIdMutex(true),
m_sessionId(1), m_stanzaTimeout(10000), m_useSidAttr(false)
m_sessionId(1), m_stanzaTimeout(20000), m_useSidAttr(false)
{
JBThreadList::setOwner(this);
}

View File

@ -70,10 +70,9 @@ public:
Presence = 20, // m_element is a 'presence' stanza
Message = 30, // m_element is a 'message' stanza
Iq = 50, // m_element is an 'iq' set/get, m_child is it's first child
IqError = 51, // m_element is an 'iq' error, m_child is the 'error' child if any
IqResult = 52, // m_element is an 'iq' result, m_child is it's first child
IqError = 51, // m_element is an 'iq' error, m_child is the 'iq' child if any
IqResult = 52, // m_element is an 'iq' result, m_child is it's first child if any
// Disco: m_child is a 'query' element qualified by DiscoInfo/DiscoItems namespaces
// IqDisco error: m_child is the 'error' child, m_element has a 'query' child
IqDiscoInfoGet = 60,
IqDiscoInfoSet = 61,
IqDiscoInfoRes = 62,
@ -83,13 +82,11 @@ public:
IqDiscoItemsRes = 66,
IqDiscoItemsErr = 67,
// Command: m_child is a 'command' element qualified by Command namespace
// IqCommandError: m_child is the 'error' child, m_element has a 'command' child
IqCommandGet = 70,
IqCommandSet = 71,
IqCommandRes = 72,
IqCommandErr = 73,
// Jingle: m_child is a 'jingle' element qualified by Jingle namespace
// IqJingleError: m_child is the 'error' child, m_element has a 'jingle' child
IqJingleGet = 80,
IqJingleSet = 81,
IqJingleRes = 82,
@ -2237,14 +2234,29 @@ public:
inline XMPPDirVal& subscription()
{ return m_subscription; }
/**
* Get the local resource list
* @return The local resource list
*/
inline JIDResourceList& localRes()
{ return m_localRes; }
/**
* Get the remote resource list
* @return The remote resource list
*/
inline JIDResourceList& remoteRes()
{ return m_remoteRes; }
/**
* Add a local resource to the list.
* Send presence if the remote peer is subscribed to the local one.
* This method is thread safe.
* @param resource The resource to add.
* @param send True to send presence from the resource if it is a new one
* @return False if the the resource already exists in the list.
*/
bool addLocalRes(JIDResource* resource);
bool addLocalRes(JIDResource* resource, bool send = true);
/**
* Remove a local resource from the list.