You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1596 lines
35 KiB
1596 lines
35 KiB
/*
|
|
* protocol.cpp - XMPP-Core protocol state machine
|
|
* Copyright (C) 2004 Justin Karneges
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
// TODO: let the app know if tls is required
|
|
// require mutual auth for server out/in
|
|
// report ErrProtocol if server uses wrong NS
|
|
// use send() instead of writeElement() in CoreProtocol
|
|
|
|
#include "protocol.h"
|
|
|
|
#include <tqca.h>
|
|
#include "base64.h"
|
|
#include "hash.h"
|
|
|
|
#ifdef XMPP_TEST
|
|
#include "td.h"
|
|
#endif
|
|
|
|
using namespace XMPP;
|
|
|
|
// printArray
|
|
//
|
|
// This function prints out an array of bytes as latin characters, converting
|
|
// non-printable bytes into hex values as necessary. Useful for displaying
|
|
// TQByteArrays for debugging purposes.
|
|
static TQString printArray(const TQByteArray &a)
|
|
{
|
|
TQString s;
|
|
for(uint n = 0; n < a.size(); ++n) {
|
|
unsigned char c = (unsigned char)a[(int)n];
|
|
if(c < 32 || c >= 127) {
|
|
TQString str;
|
|
str.sprintf("[%02x]", c);
|
|
s += str;
|
|
}
|
|
else
|
|
s += c;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
// firstChildElement
|
|
//
|
|
// Get an element's first child element
|
|
static TQDomElement firstChildElement(const TQDomElement &e)
|
|
{
|
|
for(TQDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
|
|
if(n.isElement())
|
|
return n.toElement();
|
|
}
|
|
return TQDomElement();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Version
|
|
//----------------------------------------------------------------------------
|
|
Version::Version(int maj, int min)
|
|
{
|
|
major = maj;
|
|
minor = min;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// StreamFeatures
|
|
//----------------------------------------------------------------------------
|
|
StreamFeatures::StreamFeatures()
|
|
{
|
|
tls_supported = false;
|
|
sasl_supported = false;
|
|
bind_supported = false;
|
|
tls_required = false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// BasicProtocol
|
|
//----------------------------------------------------------------------------
|
|
BasicProtocol::SASLCondEntry BasicProtocol::saslCondTable[] =
|
|
{
|
|
{ "aborted", Aborted },
|
|
{ "incorrect-encoding", IncorrectEncoding },
|
|
{ "invalid-authzid", InvalidAuthzid },
|
|
{ "invalid-mechanism", InvalidMech },
|
|
{ "mechanism-too-weak", MechTooWeak },
|
|
{ "not-authorized", NotAuthorized },
|
|
{ "temporary-auth-failure", TemporaryAuthFailure },
|
|
{ 0, 0 },
|
|
};
|
|
|
|
BasicProtocol::StreamCondEntry BasicProtocol::streamCondTable[] =
|
|
{
|
|
{ "bad-format", BadFormat },
|
|
{ "bad-namespace-prefix", BadNamespacePrefix },
|
|
{ "conflict", Conflict },
|
|
{ "connection-timeout", ConnectionTimeout },
|
|
{ "host-gone", HostGone },
|
|
{ "host-unknown", HostUnknown },
|
|
{ "improper-addressing", ImproperAddressing },
|
|
{ "internal-server-error", InternalServerError },
|
|
{ "invalid-from", InvalidFrom },
|
|
{ "invalid-id", InvalidId },
|
|
{ "invalid-namespace", InvalidNamespace },
|
|
{ "invalid-xml", InvalidXml },
|
|
{ "not-authorized", StreamNotAuthorized },
|
|
{ "policy-violation", PolicyViolation },
|
|
{ "remote-connection-failed", RemoteConnectionFailed },
|
|
{ "resource-constraint", ResourceConstraint },
|
|
{ "restricted-xml", RestrictedXml },
|
|
{ "see-other-host", SeeOtherHost },
|
|
{ "system-shutdown", SystemShutdown },
|
|
{ "undefined-condition", UndefinedCondition },
|
|
{ "unsupported-encoding", UnsupportedEncoding },
|
|
{ "unsupported-stanza-type", UnsupportedStanzaType },
|
|
{ "unsupported-version", UnsupportedVersion },
|
|
{ "xml-not-well-formed", XmlNotWellFormed },
|
|
{ 0, 0 },
|
|
};
|
|
|
|
BasicProtocol::BasicProtocol()
|
|
:XmlProtocol()
|
|
{
|
|
init();
|
|
}
|
|
|
|
BasicProtocol::~BasicProtocol()
|
|
{
|
|
}
|
|
|
|
void BasicProtocol::init()
|
|
{
|
|
errCond = -1;
|
|
sasl_authed = false;
|
|
doShutdown = false;
|
|
delayedError = false;
|
|
closeError = false;
|
|
ready = false;
|
|
stanzasPending = 0;
|
|
stanzasWritten = 0;
|
|
}
|
|
|
|
void BasicProtocol::reset()
|
|
{
|
|
XmlProtocol::reset();
|
|
init();
|
|
|
|
to = TQString();
|
|
from = TQString();
|
|
id = TQString();
|
|
lang = TQString();
|
|
version = Version(1,0);
|
|
errText = TQString();
|
|
errAppSpec = TQDomElement();
|
|
otherHost = TQString();
|
|
spare.resize(0);
|
|
sasl_mech = TQString();
|
|
sasl_mechlist.clear();
|
|
sasl_step.resize(0);
|
|
stanzaToRecv = TQDomElement();
|
|
sendList.clear();
|
|
}
|
|
|
|
void BasicProtocol::sendStanza(const TQDomElement &e)
|
|
{
|
|
SendItem i;
|
|
i.stanzaToSend = e;
|
|
sendList += i;
|
|
}
|
|
|
|
void BasicProtocol::sendDirect(const TQString &s)
|
|
{
|
|
SendItem i;
|
|
i.stringToSend = s;
|
|
sendList += i;
|
|
}
|
|
|
|
void BasicProtocol::sendWhitespace()
|
|
{
|
|
SendItem i;
|
|
i.doWhitespace = true;
|
|
sendList += i;
|
|
}
|
|
|
|
TQDomElement BasicProtocol::recvStanza()
|
|
{
|
|
TQDomElement e = stanzaToRecv;
|
|
stanzaToRecv = TQDomElement();
|
|
return e;
|
|
}
|
|
|
|
void BasicProtocol::shutdown()
|
|
{
|
|
doShutdown = true;
|
|
}
|
|
|
|
void BasicProtocol::shutdownWithError(int cond, const TQString &str)
|
|
{
|
|
otherHost = str;
|
|
delayErrorAndClose(cond);
|
|
}
|
|
|
|
bool BasicProtocol::isReady() const
|
|
{
|
|
return ready;
|
|
}
|
|
|
|
void BasicProtocol::setReady(bool b)
|
|
{
|
|
ready = b;
|
|
}
|
|
|
|
TQString BasicProtocol::saslMech() const
|
|
{
|
|
return sasl_mech;
|
|
}
|
|
|
|
TQByteArray BasicProtocol::saslStep() const
|
|
{
|
|
return sasl_step;
|
|
}
|
|
|
|
void BasicProtocol::setSASLMechList(const TQStringList &list)
|
|
{
|
|
sasl_mechlist = list;
|
|
}
|
|
|
|
void BasicProtocol::setSASLFirst(const TQString &mech, const TQByteArray &step)
|
|
{
|
|
sasl_mech = mech;
|
|
sasl_step = step;
|
|
}
|
|
|
|
void BasicProtocol::setSASLNext(const TQByteArray &step)
|
|
{
|
|
sasl_step = step;
|
|
}
|
|
|
|
void BasicProtocol::setSASLAuthed()
|
|
{
|
|
sasl_authed = true;
|
|
}
|
|
|
|
int BasicProtocol::stringToSASLCond(const TQString &s)
|
|
{
|
|
for(int n = 0; saslCondTable[n].str; ++n) {
|
|
if(s == saslCondTable[n].str)
|
|
return saslCondTable[n].cond;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int BasicProtocol::stringToStreamCond(const TQString &s)
|
|
{
|
|
for(int n = 0; streamCondTable[n].str; ++n) {
|
|
if(s == streamCondTable[n].str)
|
|
return streamCondTable[n].cond;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
TQString BasicProtocol::saslCondToString(int x)
|
|
{
|
|
for(int n = 0; saslCondTable[n].str; ++n) {
|
|
if(x == saslCondTable[n].cond)
|
|
return saslCondTable[n].str;
|
|
}
|
|
return TQString();
|
|
}
|
|
|
|
TQString BasicProtocol::streamCondToString(int x)
|
|
{
|
|
for(int n = 0; streamCondTable[n].str; ++n) {
|
|
if(x == streamCondTable[n].cond)
|
|
return streamCondTable[n].str;
|
|
}
|
|
return TQString();
|
|
}
|
|
|
|
void BasicProtocol::extractStreamError(const TQDomElement &e)
|
|
{
|
|
TQString text;
|
|
TQDomElement appSpec;
|
|
|
|
TQDomElement t = firstChildElement(e);
|
|
if(t.isNull() || t.namespaceURI() != NS_STREAMS) {
|
|
// probably old-style error
|
|
errCond = -1;
|
|
errText = e.text();
|
|
}
|
|
else
|
|
errCond = stringToStreamCond(t.tagName());
|
|
|
|
if(errCond != -1) {
|
|
if(errCond == SeeOtherHost)
|
|
otherHost = t.text();
|
|
|
|
t = e.elementsByTagNameNS(NS_STREAMS, "text").item(0).toElement();
|
|
if(!t.isNull())
|
|
text = t.text();
|
|
|
|
// find first non-standard namespaced element
|
|
TQDomNodeList nl = e.childNodes();
|
|
for(uint n = 0; n < nl.count(); ++n) {
|
|
TQDomNode i = nl.item(n);
|
|
if(i.isElement() && i.namespaceURI() != NS_STREAMS) {
|
|
appSpec = i.toElement();
|
|
break;
|
|
}
|
|
}
|
|
|
|
errText = text;
|
|
errAppSpec = appSpec;
|
|
}
|
|
}
|
|
|
|
void BasicProtocol::send(const TQDomElement &e, bool clip)
|
|
{
|
|
writeElement(e, TypeElement, false, clip);
|
|
}
|
|
|
|
void BasicProtocol::sendStreamError(int cond, const TQString &text, const TQDomElement &appSpec)
|
|
{
|
|
TQDomElement se = doc.createElementNS(NS_ETHERX, "stream:error");
|
|
TQDomElement err = doc.createElementNS(NS_STREAMS, streamCondToString(cond));
|
|
if(!otherHost.isEmpty())
|
|
err.appendChild(doc.createTextNode(otherHost));
|
|
se.appendChild(err);
|
|
if(!text.isEmpty()) {
|
|
TQDomElement te = doc.createElementNS(NS_STREAMS, "text");
|
|
te.setAttributeNS(NS_XML, "xml:lang", "en");
|
|
te.appendChild(doc.createTextNode(text));
|
|
se.appendChild(te);
|
|
}
|
|
se.appendChild(appSpec);
|
|
|
|
writeElement(se, 100, false);
|
|
}
|
|
|
|
void BasicProtocol::sendStreamError(const TQString &text)
|
|
{
|
|
TQDomElement se = doc.createElementNS(NS_ETHERX, "stream:error");
|
|
se.appendChild(doc.createTextNode(text));
|
|
|
|
writeElement(se, 100, false);
|
|
}
|
|
|
|
bool BasicProtocol::errorAndClose(int cond, const TQString &text, const TQDomElement &appSpec)
|
|
{
|
|
closeError = true;
|
|
errCond = cond;
|
|
errText = text;
|
|
errAppSpec = appSpec;
|
|
sendStreamError(cond, text, appSpec);
|
|
return close();
|
|
}
|
|
|
|
bool BasicProtocol::error(int code)
|
|
{
|
|
event = EError;
|
|
errorCode = code;
|
|
return true;
|
|
}
|
|
|
|
void BasicProtocol::delayErrorAndClose(int cond, const TQString &text, const TQDomElement &appSpec)
|
|
{
|
|
errorCode = ErrStream;
|
|
errCond = cond;
|
|
errText = text;
|
|
errAppSpec = appSpec;
|
|
delayedError = true;
|
|
}
|
|
|
|
void BasicProtocol::delayError(int code)
|
|
{
|
|
errorCode = code;
|
|
delayedError = true;
|
|
}
|
|
|
|
TQDomElement BasicProtocol::docElement()
|
|
{
|
|
// create the root element
|
|
TQDomElement e = doc.createElementNS(NS_ETHERX, "stream:stream");
|
|
|
|
TQString defns = defaultNamespace();
|
|
TQStringList list = extraNamespaces();
|
|
|
|
// HACK: using attributes seems to be the only way to get additional namespaces in here
|
|
if(!defns.isEmpty())
|
|
e.setAttribute("xmlns", defns);
|
|
for(TQStringList::ConstIterator it = list.begin(); it != list.end();) {
|
|
TQString prefix = *(it++);
|
|
TQString uri = *(it++);
|
|
e.setAttribute(TQString("xmlns:") + prefix, uri);
|
|
}
|
|
|
|
// additional attributes
|
|
if(!isIncoming() && !to.isEmpty())
|
|
e.setAttribute("to", to);
|
|
if(isIncoming() && !from.isEmpty())
|
|
e.setAttribute("from", from);
|
|
if(!id.isEmpty())
|
|
e.setAttribute("id", id);
|
|
if(!lang.isEmpty())
|
|
e.setAttributeNS(NS_XML, "xml:lang", lang);
|
|
if(version.major > 0 || version.minor > 0)
|
|
e.setAttribute("version", TQString::number(version.major) + '.' + TQString::number(version.minor));
|
|
|
|
return e;
|
|
}
|
|
|
|
void BasicProtocol::handleDocOpen(const Parser::Event &pe)
|
|
{
|
|
if(isIncoming()) {
|
|
if(xmlEncoding() != "UTF-8") {
|
|
delayErrorAndClose(UnsupportedEncoding);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(pe.namespaceURI() == NS_ETHERX && pe.localName() == "stream") {
|
|
TQXmlAttributes atts = pe.atts();
|
|
|
|
// grab the version
|
|
int major = 0;
|
|
int minor = 0;
|
|
TQString verstr = atts.value("version");
|
|
if(!verstr.isEmpty()) {
|
|
int n = verstr.find('.');
|
|
if(n != -1) {
|
|
major = verstr.mid(0, n).toInt();
|
|
minor = verstr.mid(n+1).toInt();
|
|
}
|
|
else {
|
|
major = verstr.toInt();
|
|
minor = 0;
|
|
}
|
|
}
|
|
version = Version(major, minor);
|
|
|
|
if(isIncoming()) {
|
|
to = atts.value("to");
|
|
TQString peerLang = atts.value(NS_XML, "lang");
|
|
if(!peerLang.isEmpty())
|
|
lang = peerLang;
|
|
}
|
|
// outgoing
|
|
else {
|
|
from = atts.value("from");
|
|
lang = atts.value(NS_XML, "lang");
|
|
id = atts.value("id");
|
|
}
|
|
|
|
handleStreamOpen(pe);
|
|
}
|
|
else {
|
|
if(isIncoming())
|
|
delayErrorAndClose(BadFormat);
|
|
else
|
|
delayError(ErrProtocol);
|
|
}
|
|
}
|
|
|
|
bool BasicProtocol::handleError()
|
|
{
|
|
if(isIncoming())
|
|
return errorAndClose(XmlNotWellFormed);
|
|
else
|
|
return error(ErrParse);
|
|
}
|
|
|
|
bool BasicProtocol::handleCloseFinished()
|
|
{
|
|
if(closeError) {
|
|
event = EError;
|
|
errorCode = ErrStream;
|
|
// note: errCond and friends are already set at this point
|
|
}
|
|
else
|
|
event = EClosed;
|
|
return true;
|
|
}
|
|
|
|
bool BasicProtocol::doStep(const TQDomElement &e)
|
|
{
|
|
// handle pending error
|
|
if(delayedError) {
|
|
if(isIncoming())
|
|
return errorAndClose(errCond, errText, errAppSpec);
|
|
else
|
|
return error(errorCode);
|
|
}
|
|
|
|
// shutdown?
|
|
if(doShutdown) {
|
|
doShutdown = false;
|
|
return close();
|
|
}
|
|
|
|
if(!e.isNull()) {
|
|
// check for error
|
|
if(e.namespaceURI() == NS_ETHERX && e.tagName() == "error") {
|
|
extractStreamError(e);
|
|
return error(ErrStream);
|
|
}
|
|
}
|
|
|
|
if(ready) {
|
|
// stanzas written?
|
|
if(stanzasWritten > 0) {
|
|
--stanzasWritten;
|
|
event = EStanzaSent;
|
|
return true;
|
|
}
|
|
// send items?
|
|
if(!sendList.isEmpty()) {
|
|
SendItem i;
|
|
{
|
|
TQValueList<SendItem>::Iterator it = sendList.begin();
|
|
i = (*it);
|
|
sendList.remove(it);
|
|
}
|
|
|
|
// outgoing stanza?
|
|
if(!i.stanzaToSend.isNull()) {
|
|
++stanzasPending;
|
|
writeElement(i.stanzaToSend, TypeStanza, true);
|
|
event = ESend;
|
|
}
|
|
// direct send?
|
|
else if(!i.stringToSend.isEmpty()) {
|
|
writeString(i.stringToSend, TypeDirect, true);
|
|
event = ESend;
|
|
}
|
|
// whitespace keepalive?
|
|
else if(i.doWhitespace) {
|
|
writeString("\n", TypePing, false);
|
|
event = ESend;
|
|
}
|
|
return true;
|
|
}
|
|
else {
|
|
// if we have pending outgoing stanzas, ask for write notification
|
|
if(stanzasPending)
|
|
notify |= NSend;
|
|
}
|
|
}
|
|
|
|
return doStep2(e);
|
|
}
|
|
|
|
void BasicProtocol::itemWritten(int id, int)
|
|
{
|
|
if(id == TypeStanza) {
|
|
--stanzasPending;
|
|
++stanzasWritten;
|
|
}
|
|
}
|
|
|
|
TQString BasicProtocol::defaultNamespace()
|
|
{
|
|
// default none
|
|
return TQString();
|
|
}
|
|
|
|
TQStringList BasicProtocol::extraNamespaces()
|
|
{
|
|
// default none
|
|
return TQStringList();
|
|
}
|
|
|
|
void BasicProtocol::handleStreamOpen(const Parser::Event &)
|
|
{
|
|
// default does nothing
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// CoreProtocol
|
|
//----------------------------------------------------------------------------
|
|
CoreProtocol::CoreProtocol()
|
|
:BasicProtocol()
|
|
{
|
|
init();
|
|
}
|
|
|
|
CoreProtocol::~CoreProtocol()
|
|
{
|
|
}
|
|
|
|
void CoreProtocol::init()
|
|
{
|
|
step = Start;
|
|
|
|
// ??
|
|
server = false;
|
|
dialback = false;
|
|
dialback_verify = false;
|
|
|
|
// settings
|
|
jid = Jid();
|
|
password = TQString();
|
|
oldOnly = false;
|
|
allowPlain = false;
|
|
doTLS = true;
|
|
doAuth = true;
|
|
doBinding = true;
|
|
|
|
// input
|
|
user = TQString();
|
|
host = TQString();
|
|
|
|
// status
|
|
old = false;
|
|
digest = false;
|
|
tls_started = false;
|
|
sasl_started = false;
|
|
}
|
|
|
|
void CoreProtocol::reset()
|
|
{
|
|
BasicProtocol::reset();
|
|
init();
|
|
}
|
|
|
|
void CoreProtocol::startClientOut(const Jid &_jid, bool _oldOnly, bool tlsActive, bool _doAuth)
|
|
{
|
|
jid = _jid;
|
|
to = _jid.domain();
|
|
oldOnly = _oldOnly;
|
|
doAuth = _doAuth;
|
|
tls_started = tlsActive;
|
|
|
|
if(oldOnly)
|
|
version = Version(0,0);
|
|
startConnect();
|
|
}
|
|
|
|
void CoreProtocol::startServerOut(const TQString &_to)
|
|
{
|
|
server = true;
|
|
to = _to;
|
|
startConnect();
|
|
}
|
|
|
|
void CoreProtocol::startDialbackOut(const TQString &_to, const TQString &_from)
|
|
{
|
|
server = true;
|
|
dialback = true;
|
|
to = _to;
|
|
self_from = _from;
|
|
startConnect();
|
|
}
|
|
|
|
void CoreProtocol::startDialbackVerifyOut(const TQString &_to, const TQString &_from, const TQString &id, const TQString &key)
|
|
{
|
|
server = true;
|
|
dialback = true;
|
|
dialback_verify = true;
|
|
to = _to;
|
|
self_from = _from;
|
|
dialback_id = id;
|
|
dialback_key = key;
|
|
startConnect();
|
|
}
|
|
|
|
void CoreProtocol::startClientIn(const TQString &_id)
|
|
{
|
|
id = _id;
|
|
startAccept();
|
|
}
|
|
|
|
void CoreProtocol::startServerIn(const TQString &_id)
|
|
{
|
|
server = true;
|
|
id = _id;
|
|
startAccept();
|
|
}
|
|
|
|
void CoreProtocol::setLang(const TQString &s)
|
|
{
|
|
lang = s;
|
|
}
|
|
|
|
void CoreProtocol::setAllowTLS(bool b)
|
|
{
|
|
doTLS = b;
|
|
}
|
|
|
|
void CoreProtocol::setAllowBind(bool b)
|
|
{
|
|
doBinding = b;
|
|
}
|
|
|
|
void CoreProtocol::setAllowPlain(bool b)
|
|
{
|
|
allowPlain = b;
|
|
}
|
|
|
|
void CoreProtocol::setPassword(const TQString &s)
|
|
{
|
|
password = s;
|
|
}
|
|
|
|
void CoreProtocol::setFrom(const TQString &s)
|
|
{
|
|
from = s;
|
|
}
|
|
|
|
void CoreProtocol::setDialbackKey(const TQString &s)
|
|
{
|
|
dialback_key = s;
|
|
}
|
|
|
|
bool CoreProtocol::loginComplete()
|
|
{
|
|
setReady(true);
|
|
|
|
event = EReady;
|
|
step = Done;
|
|
return true;
|
|
}
|
|
|
|
int CoreProtocol::getOldErrorCode(const TQDomElement &e)
|
|
{
|
|
TQDomElement err = e.elementsByTagNameNS(NS_CLIENT, "error").item(0).toElement();
|
|
if(err.isNull() || !err.hasAttribute("code"))
|
|
return -1;
|
|
return err.attribute("code").toInt();
|
|
}
|
|
|
|
/*TQString CoreProtocol::xmlToString(const TQDomElement &e, bool clip)
|
|
{
|
|
// determine an appropriate 'fakeNS' to use
|
|
TQString ns;
|
|
if(e.prefix() == "stream")
|
|
ns = NS_ETHERX;
|
|
else if(e.prefix() == "db")
|
|
ns = NS_DIALBACK;
|
|
else
|
|
ns = NS_CLIENT;
|
|
return ::xmlToString(e, ns, "stream:stream", clip);
|
|
}*/
|
|
|
|
bool CoreProtocol::stepAdvancesParser() const
|
|
{
|
|
if(stepRequiresElement())
|
|
return true;
|
|
else if(isReady())
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
// all element-needing steps need to be registered here
|
|
bool CoreProtocol::stepRequiresElement() const
|
|
{
|
|
switch(step) {
|
|
case GetFeatures:
|
|
case GetTLSProceed:
|
|
case GetSASLChallenge:
|
|
case GetBindResponse:
|
|
case GetAuthGetResponse:
|
|
case GetAuthSetResponse:
|
|
case GetRequest:
|
|
case GetSASLResponse:
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CoreProtocol::stringSend(const TQString &s)
|
|
{
|
|
#ifdef XMPP_TEST
|
|
TD::outgoingTag(s);
|
|
#endif
|
|
}
|
|
|
|
void CoreProtocol::stringRecv(const TQString &s)
|
|
{
|
|
#ifdef XMPP_TEST
|
|
TD::incomingTag(s);
|
|
#endif
|
|
}
|
|
|
|
TQString CoreProtocol::defaultNamespace()
|
|
{
|
|
if(server)
|
|
return NS_SERVER;
|
|
else
|
|
return NS_CLIENT;
|
|
}
|
|
|
|
TQStringList CoreProtocol::extraNamespaces()
|
|
{
|
|
TQStringList list;
|
|
if(dialback) {
|
|
list += "db";
|
|
list += NS_DIALBACK;
|
|
}
|
|
return list;
|
|
}
|
|
|
|
void CoreProtocol::handleStreamOpen(const Parser::Event &pe)
|
|
{
|
|
if(isIncoming()) {
|
|
TQString ns = pe.nsprefix();
|
|
TQString db;
|
|
if(server) {
|
|
db = pe.nsprefix("db");
|
|
if(!db.isEmpty())
|
|
dialback = true;
|
|
}
|
|
|
|
// verify namespace
|
|
if((!server && ns != NS_CLIENT) || (server && ns != NS_SERVER) || (dialback && db != NS_DIALBACK)) {
|
|
delayErrorAndClose(InvalidNamespace);
|
|
return;
|
|
}
|
|
|
|
// verify version
|
|
if(version.major < 1 && !dialback) {
|
|
delayErrorAndClose(UnsupportedVersion);
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
if(!dialback) {
|
|
if(version.major >= 1 && !oldOnly)
|
|
old = false;
|
|
else
|
|
old = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CoreProtocol::elementSend(const TQDomElement &e)
|
|
{
|
|
#ifdef XMPP_TEST
|
|
TD::outgoingXml(e);
|
|
#endif
|
|
}
|
|
|
|
void CoreProtocol::elementRecv(const TQDomElement &e)
|
|
{
|
|
#ifdef XMPP_TEST
|
|
TD::incomingXml(e);
|
|
#endif
|
|
}
|
|
|
|
bool CoreProtocol::doStep2(const TQDomElement &e)
|
|
{
|
|
if(dialback)
|
|
return dialbackStep(e);
|
|
else
|
|
return normalStep(e);
|
|
}
|
|
|
|
bool CoreProtocol::isValidStanza(const TQDomElement &e) const
|
|
{
|
|
TQString s = e.tagName();
|
|
if(e.namespaceURI() == (server ? NS_SERVER : NS_CLIENT) && (s == "message" || s == "presence" || s == "iq"))
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool CoreProtocol::grabPendingItem(const Jid &to, const Jid &from, int type, DBItem *item)
|
|
{
|
|
for(TQValueList<DBItem>::Iterator it = dbpending.begin(); it != dbpending.end(); ++it) {
|
|
const DBItem &i = *it;
|
|
if(i.type == type && i.to.compare(to) && i.from.compare(from)) {
|
|
const DBItem &i = (*it);
|
|
*item = i;
|
|
dbpending.remove(it);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CoreProtocol::dialbackStep(const TQDomElement &e)
|
|
{
|
|
if(step == Start) {
|
|
setReady(true);
|
|
step = Done;
|
|
event = EReady;
|
|
return true;
|
|
}
|
|
|
|
if(!dbrequests.isEmpty()) {
|
|
// process a request
|
|
DBItem i;
|
|
{
|
|
TQValueList<DBItem>::Iterator it = dbrequests.begin();
|
|
i = (*it);
|
|
dbrequests.remove(it);
|
|
}
|
|
|
|
TQDomElement r;
|
|
if(i.type == DBItem::ResultRequest) {
|
|
r = doc.createElementNS(NS_DIALBACK, "db:result");
|
|
r.setAttribute("to", i.to.full());
|
|
r.setAttribute("from", i.from.full());
|
|
r.appendChild(doc.createTextNode(i.key));
|
|
dbpending += i;
|
|
}
|
|
else if(i.type == DBItem::ResultGrant) {
|
|
r = doc.createElementNS(NS_DIALBACK, "db:result");
|
|
r.setAttribute("to", i.to.full());
|
|
r.setAttribute("from", i.from.full());
|
|
r.setAttribute("type", i.ok ? "valid" : "invalid");
|
|
if(i.ok) {
|
|
i.type = DBItem::Validated;
|
|
dbvalidated += i;
|
|
}
|
|
else {
|
|
// TODO: disconnect after writing element
|
|
}
|
|
}
|
|
else if(i.type == DBItem::VerifyRequest) {
|
|
r = doc.createElementNS(NS_DIALBACK, "db:verify");
|
|
r.setAttribute("to", i.to.full());
|
|
r.setAttribute("from", i.from.full());
|
|
r.setAttribute("id", i.id);
|
|
r.appendChild(doc.createTextNode(i.key));
|
|
dbpending += i;
|
|
}
|
|
// VerifyGrant
|
|
else {
|
|
r = doc.createElementNS(NS_DIALBACK, "db:verify");
|
|
r.setAttribute("to", i.to.full());
|
|
r.setAttribute("from", i.from.full());
|
|
r.setAttribute("id", i.id);
|
|
r.setAttribute("type", i.ok ? "valid" : "invalid");
|
|
}
|
|
|
|
writeElement(r, TypeElement, false);
|
|
event = ESend;
|
|
return true;
|
|
}
|
|
|
|
if(!e.isNull()) {
|
|
if(e.namespaceURI() == NS_DIALBACK) {
|
|
if(e.tagName() == "result") {
|
|
Jid to, from;
|
|
to.set(e.attribute("to"), "");
|
|
from.set(e.attribute("from"), "");
|
|
if(isIncoming()) {
|
|
TQString key = e.text();
|
|
// TODO: report event
|
|
}
|
|
else {
|
|
bool ok = (e.attribute("type") == "valid") ? true: false;
|
|
DBItem i;
|
|
if(grabPendingItem(from, to, DBItem::ResultRequest, &i)) {
|
|
if(ok) {
|
|
i.type = DBItem::Validated;
|
|
i.ok = true;
|
|
dbvalidated += i;
|
|
// TODO: report event
|
|
}
|
|
else {
|
|
// TODO: report event
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(e.tagName() == "verify") {
|
|
Jid to, from;
|
|
to.set(e.attribute("to"), "");
|
|
from.set(e.attribute("from"), "");
|
|
TQString id = e.attribute("id");
|
|
if(isIncoming()) {
|
|
TQString key = e.text();
|
|
// TODO: report event
|
|
}
|
|
else {
|
|
bool ok = (e.attribute("type") == "valid") ? true: false;
|
|
DBItem i;
|
|
if(grabPendingItem(from, to, DBItem::VerifyRequest, &i)) {
|
|
if(ok) {
|
|
// TODO: report event
|
|
}
|
|
else {
|
|
// TODO: report event
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if(isReady()) {
|
|
if(isValidStanza(e)) {
|
|
// TODO: disconnect if stanza is from unverified sender
|
|
// TODO: ignore packets from receiving servers
|
|
stanzaToRecv = e;
|
|
event = EStanzaReady;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
need = NNotify;
|
|
notify |= NRecv;
|
|
return false;
|
|
}
|
|
|
|
bool CoreProtocol::normalStep(const TQDomElement &e)
|
|
{
|
|
if(step == Start) {
|
|
if(isIncoming()) {
|
|
need = NSASLMechs;
|
|
step = SendFeatures;
|
|
return false;
|
|
}
|
|
else {
|
|
if(old) {
|
|
if(doAuth)
|
|
step = HandleAuthGet;
|
|
else
|
|
return loginComplete();
|
|
}
|
|
else
|
|
step = GetFeatures;
|
|
|
|
return processStep();
|
|
}
|
|
}
|
|
else if(step == HandleFeatures) {
|
|
// deal with TLS?
|
|
if(doTLS && !tls_started && !sasl_authed && features.tls_supported) {
|
|
TQDomElement e = doc.createElementNS(NS_TLS, "starttls");
|
|
|
|
send(e, true);
|
|
event = ESend;
|
|
step = GetTLSProceed;
|
|
return true;
|
|
}
|
|
|
|
// deal with SASL?
|
|
if(!sasl_authed) {
|
|
if(!features.sasl_supported) {
|
|
// SASL MUST be supported
|
|
event = EError;
|
|
errorCode = ErrProtocol;
|
|
return true;
|
|
}
|
|
|
|
#ifdef XMPP_TEST
|
|
TD::msg("starting SASL authentication...");
|
|
#endif
|
|
need = NSASLFirst;
|
|
step = GetSASLFirst;
|
|
return false;
|
|
}
|
|
|
|
if(server) {
|
|
return loginComplete();
|
|
}
|
|
else {
|
|
if(!doBinding)
|
|
return loginComplete();
|
|
}
|
|
|
|
// deal with bind
|
|
if(!features.bind_supported) {
|
|
// bind MUST be supported
|
|
event = EError;
|
|
errorCode = ErrProtocol;
|
|
return true;
|
|
}
|
|
|
|
TQDomElement e = doc.createElement("iq");
|
|
e.setAttribute("type", "set");
|
|
e.setAttribute("id", "bind_1");
|
|
TQDomElement b = doc.createElementNS(NS_BIND, "bind");
|
|
|
|
// request specific resource?
|
|
TQString resource = jid.resource();
|
|
if(!resource.isEmpty()) {
|
|
TQDomElement r = doc.createElement("resource");
|
|
r.appendChild(doc.createTextNode(jid.resource()));
|
|
b.appendChild(r);
|
|
}
|
|
|
|
e.appendChild(b);
|
|
|
|
send(e);
|
|
event = ESend;
|
|
step = GetBindResponse;
|
|
return true;
|
|
}
|
|
else if(step == GetSASLFirst) {
|
|
TQDomElement e = doc.createElementNS(NS_SASL, "auth");
|
|
e.setAttribute("mechanism", sasl_mech);
|
|
if(!sasl_step.isEmpty()) {
|
|
#ifdef XMPP_TEST
|
|
TD::msg(TQString("SASL OUT: [%1]").arg(printArray(sasl_step)));
|
|
#endif
|
|
e.appendChild(doc.createTextNode(Base64::arrayToString(sasl_step)));
|
|
}
|
|
|
|
send(e, true);
|
|
event = ESend;
|
|
step = GetSASLChallenge;
|
|
return true;
|
|
}
|
|
else if(step == GetSASLNext) {
|
|
if(isIncoming()) {
|
|
if(sasl_authed) {
|
|
TQDomElement e = doc.createElementNS(NS_SASL, "success");
|
|
writeElement(e, TypeElement, false, true);
|
|
event = ESend;
|
|
step = IncHandleSASLSuccess;
|
|
return true;
|
|
}
|
|
else {
|
|
TQByteArray stepData = sasl_step;
|
|
TQDomElement e = doc.createElementNS(NS_SASL, "challenge");
|
|
if(!stepData.isEmpty())
|
|
e.appendChild(doc.createTextNode(Base64::arrayToString(stepData)));
|
|
|
|
writeElement(e, TypeElement, false, true);
|
|
event = ESend;
|
|
step = GetSASLResponse;
|
|
return true;
|
|
}
|
|
}
|
|
else {
|
|
TQByteArray stepData = sasl_step;
|
|
#ifdef XMPP_TEST
|
|
TD::msg(TQString("SASL OUT: [%1]").arg(printArray(sasl_step)));
|
|
#endif
|
|
TQDomElement e = doc.createElementNS(NS_SASL, "response");
|
|
if(!stepData.isEmpty())
|
|
e.appendChild(doc.createTextNode(Base64::arrayToString(stepData)));
|
|
|
|
send(e, true);
|
|
event = ESend;
|
|
step = GetSASLChallenge;
|
|
return true;
|
|
}
|
|
}
|
|
else if(step == HandleSASLSuccess) {
|
|
need = NSASLLayer;
|
|
spare = resetStream();
|
|
step = Start;
|
|
return false;
|
|
}
|
|
else if(step == HandleAuthGet) {
|
|
TQDomElement e = doc.createElement("iq");
|
|
e.setAttribute("to", to);
|
|
e.setAttribute("type", "get");
|
|
e.setAttribute("id", "auth_1");
|
|
TQDomElement q = doc.createElementNS("jabber:iq:auth", "query");
|
|
TQDomElement u = doc.createElement("username");
|
|
u.appendChild(doc.createTextNode(jid.node()));
|
|
q.appendChild(u);
|
|
e.appendChild(q);
|
|
|
|
send(e);
|
|
event = ESend;
|
|
step = GetAuthGetResponse;
|
|
return true;
|
|
}
|
|
else if(step == HandleAuthSet) {
|
|
TQDomElement e = doc.createElement("iq");
|
|
e.setAttribute("to", to);
|
|
e.setAttribute("type", "set");
|
|
e.setAttribute("id", "auth_2");
|
|
TQDomElement q = doc.createElementNS("jabber:iq:auth", "query");
|
|
TQDomElement u = doc.createElement("username");
|
|
u.appendChild(doc.createTextNode(jid.node()));
|
|
q.appendChild(u);
|
|
TQDomElement p;
|
|
if(digest) {
|
|
// need SHA1 here
|
|
if(!TQCA::isSupported(TQCA::CAP_SHA1))
|
|
TQCA::insertProvider(createProviderHash());
|
|
|
|
p = doc.createElement("digest");
|
|
TQCString cs = id.utf8() + password.utf8();
|
|
p.appendChild(doc.createTextNode(TQCA::SHA1::hashToString(cs)));
|
|
}
|
|
else {
|
|
p = doc.createElement("password");
|
|
p.appendChild(doc.createTextNode(password));
|
|
}
|
|
q.appendChild(p);
|
|
TQDomElement r = doc.createElement("resource");
|
|
r.appendChild(doc.createTextNode(jid.resource()));
|
|
q.appendChild(r);
|
|
e.appendChild(q);
|
|
|
|
send(e, true);
|
|
event = ESend;
|
|
step = GetAuthSetResponse;
|
|
return true;
|
|
}
|
|
// server
|
|
else if(step == SendFeatures) {
|
|
TQDomElement f = doc.createElementNS(NS_ETHERX, "stream:features");
|
|
if(!tls_started && !sasl_authed) { // don't offer tls if we are already sasl'd
|
|
TQDomElement tls = doc.createElementNS(NS_TLS, "starttls");
|
|
f.appendChild(tls);
|
|
}
|
|
|
|
if(sasl_authed) {
|
|
if(!server) {
|
|
TQDomElement bind = doc.createElementNS(NS_BIND, "bind");
|
|
f.appendChild(bind);
|
|
}
|
|
}
|
|
else {
|
|
TQDomElement mechs = doc.createElementNS(NS_SASL, "mechanisms");
|
|
for(TQStringList::ConstIterator it = sasl_mechlist.begin(); it != sasl_mechlist.end(); ++it) {
|
|
TQDomElement m = doc.createElement("mechanism");
|
|
m.appendChild(doc.createTextNode(*it));
|
|
mechs.appendChild(m);
|
|
}
|
|
f.appendChild(mechs);
|
|
}
|
|
|
|
writeElement(f, TypeElement, false);
|
|
event = ESend;
|
|
step = GetRequest;
|
|
return true;
|
|
}
|
|
// server
|
|
else if(step == HandleTLS) {
|
|
tls_started = true;
|
|
need = NStartTLS;
|
|
spare = resetStream();
|
|
step = Start;
|
|
return false;
|
|
}
|
|
// server
|
|
else if(step == IncHandleSASLSuccess) {
|
|
event = ESASLSuccess;
|
|
spare = resetStream();
|
|
step = Start;
|
|
printf("sasl success\n");
|
|
return true;
|
|
}
|
|
else if(step == GetFeatures) {
|
|
// we are waiting for stream features
|
|
if(e.namespaceURI() == NS_ETHERX && e.tagName() == "features") {
|
|
// extract features
|
|
StreamFeatures f;
|
|
TQDomElement s = e.elementsByTagNameNS(NS_TLS, "starttls").item(0).toElement();
|
|
if(!s.isNull()) {
|
|
f.tls_supported = true;
|
|
f.tls_required = s.elementsByTagNameNS(NS_TLS, "required").count() > 0;
|
|
}
|
|
TQDomElement m = e.elementsByTagNameNS(NS_SASL, "mechanisms").item(0).toElement();
|
|
if(!m.isNull()) {
|
|
f.sasl_supported = true;
|
|
TQDomNodeList l = m.elementsByTagNameNS(NS_SASL, "mechanism");
|
|
for(uint n = 0; n < l.count(); ++n)
|
|
f.sasl_mechs += l.item(n).toElement().text();
|
|
}
|
|
TQDomElement b = e.elementsByTagNameNS(NS_BIND, "bind").item(0).toElement();
|
|
if(!b.isNull())
|
|
f.bind_supported = true;
|
|
|
|
if(f.tls_supported) {
|
|
#ifdef XMPP_TEST
|
|
TQString s = "STARTTLS is available";
|
|
if(f.tls_required)
|
|
s += " (required)";
|
|
TD::msg(s);
|
|
#endif
|
|
}
|
|
if(f.sasl_supported) {
|
|
#ifdef XMPP_TEST
|
|
TQString s = "SASL mechs:";
|
|
for(TQStringList::ConstIterator it = f.sasl_mechs.begin(); it != f.sasl_mechs.end(); ++it)
|
|
s += TQString(" [%1]").arg((*it));
|
|
TD::msg(s);
|
|
#endif
|
|
}
|
|
|
|
if(doAuth) {
|
|
event = EFeatures;
|
|
features = f;
|
|
step = HandleFeatures;
|
|
return true;
|
|
}
|
|
else
|
|
return loginComplete();
|
|
}
|
|
else {
|
|
// ignore
|
|
}
|
|
}
|
|
else if(step == GetTLSProceed) {
|
|
// waiting for proceed to starttls
|
|
if(e.namespaceURI() == NS_TLS) {
|
|
if(e.tagName() == "proceed") {
|
|
#ifdef XMPP_TEST
|
|
TD::msg("Server wants us to proceed with ssl handshake");
|
|
#endif
|
|
tls_started = true;
|
|
need = NStartTLS;
|
|
spare = resetStream();
|
|
step = Start;
|
|
return false;
|
|
}
|
|
else if(e.tagName() == "failure") {
|
|
event = EError;
|
|
errorCode = ErrStartTLS;
|
|
return true;
|
|
}
|
|
else {
|
|
event = EError;
|
|
errorCode = ErrProtocol;
|
|
return true;
|
|
}
|
|
}
|
|
else {
|
|
// ignore
|
|
}
|
|
}
|
|
else if(step == GetSASLChallenge) {
|
|
// waiting for sasl challenge/success/fail
|
|
if(e.namespaceURI() == NS_SASL) {
|
|
if(e.tagName() == "challenge") {
|
|
TQByteArray a = Base64::stringToArray(e.text());
|
|
#ifdef XMPP_TEST
|
|
TD::msg(TQString("SASL IN: [%1]").arg(printArray(a)));
|
|
#endif
|
|
sasl_step = a;
|
|
need = NSASLNext;
|
|
step = GetSASLNext;
|
|
return false;
|
|
}
|
|
else if(e.tagName() == "success") {
|
|
sasl_authed = true;
|
|
event = ESASLSuccess;
|
|
step = HandleSASLSuccess;
|
|
return true;
|
|
}
|
|
else if(e.tagName() == "failure") {
|
|
TQDomElement t = firstChildElement(e);
|
|
if(t.isNull() || t.namespaceURI() != NS_SASL)
|
|
errCond = -1;
|
|
else
|
|
errCond = stringToSASLCond(t.tagName());
|
|
|
|
event = EError;
|
|
errorCode = ErrAuth;
|
|
return true;
|
|
}
|
|
else {
|
|
event = EError;
|
|
errorCode = ErrProtocol;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
else if(step == GetBindResponse) {
|
|
if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
|
|
TQString type(e.attribute("type"));
|
|
TQString id(e.attribute("id"));
|
|
|
|
if(id == "bind_1" && (type == "result" || type == "error")) {
|
|
if(type == "result") {
|
|
TQDomElement b = e.elementsByTagNameNS(NS_BIND, "bind").item(0).toElement();
|
|
Jid j;
|
|
if(!b.isNull()) {
|
|
TQDomElement je = e.elementsByTagName("jid").item(0).toElement();
|
|
j = je.text();
|
|
}
|
|
if(!j.isValid()) {
|
|
event = EError;
|
|
errorCode = ErrProtocol;
|
|
return true;
|
|
}
|
|
jid = j;
|
|
return loginComplete();
|
|
}
|
|
else {
|
|
errCond = -1;
|
|
|
|
TQDomElement err = e.elementsByTagNameNS(NS_CLIENT, "error").item(0).toElement();
|
|
if(!err.isNull()) {
|
|
// get error condition
|
|
TQDomNodeList nl = err.childNodes();
|
|
TQDomElement t;
|
|
for(uint n = 0; n < nl.count(); ++n) {
|
|
TQDomNode i = nl.item(n);
|
|
if(i.isElement()) {
|
|
t = i.toElement();
|
|
break;
|
|
}
|
|
}
|
|
if(!t.isNull() && t.namespaceURI() == NS_STANZAS) {
|
|
TQString cond = t.tagName();
|
|
if(cond == "not-allowed")
|
|
errCond = BindNotAllowed;
|
|
else if(cond == "conflict")
|
|
errCond = BindConflict;
|
|
}
|
|
}
|
|
|
|
event = EError;
|
|
errorCode = ErrBind;
|
|
return true;
|
|
}
|
|
}
|
|
else {
|
|
// ignore
|
|
}
|
|
}
|
|
else {
|
|
// ignore
|
|
}
|
|
}
|
|
else if(step == GetAuthGetResponse) {
|
|
// waiting for an iq
|
|
if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
|
|
Jid from(e.attribute("from"));
|
|
TQString type(e.attribute("type"));
|
|
TQString id(e.attribute("id"));
|
|
|
|
bool okfrom = (from.isEmpty() || from.compare(Jid(to)));
|
|
if(okfrom && id == "auth_1" && (type == "result" || type == "error")) {
|
|
if(type == "result") {
|
|
TQDomElement q = e.elementsByTagNameNS("jabber:iq:auth", "query").item(0).toElement();
|
|
if(q.isNull() || q.elementsByTagName("username").item(0).isNull() || q.elementsByTagName("resource").item(0).isNull()) {
|
|
event = EError;
|
|
errorCode = ErrProtocol;
|
|
return true;
|
|
}
|
|
bool plain_supported = !q.elementsByTagName("password").item(0).isNull();
|
|
bool digest_supported = !q.elementsByTagName("digest").item(0).isNull();
|
|
|
|
if(!digest_supported && !plain_supported) {
|
|
event = EError;
|
|
errorCode = ErrProtocol;
|
|
return true;
|
|
}
|
|
|
|
// plain text not allowed?
|
|
if(!digest_supported && !allowPlain) {
|
|
event = EError;
|
|
errorCode = ErrPlain;
|
|
return true;
|
|
}
|
|
|
|
digest = digest_supported;
|
|
need = NPassword;
|
|
step = HandleAuthSet;
|
|
return false;
|
|
}
|
|
else {
|
|
errCond = getOldErrorCode(e);
|
|
|
|
event = EError;
|
|
errorCode = ErrAuth;
|
|
return true;
|
|
}
|
|
}
|
|
else {
|
|
// ignore
|
|
}
|
|
}
|
|
else {
|
|
// ignore
|
|
}
|
|
}
|
|
else if(step == GetAuthSetResponse) {
|
|
// waiting for an iq
|
|
if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
|
|
Jid from(e.attribute("from"));
|
|
TQString type(e.attribute("type"));
|
|
TQString id(e.attribute("id"));
|
|
|
|
bool okfrom = (from.isEmpty() || from.compare(Jid(to)));
|
|
if(okfrom && id == "auth_2" && (type == "result" || type == "error")) {
|
|
if(type == "result") {
|
|
return loginComplete();
|
|
}
|
|
else {
|
|
errCond = getOldErrorCode(e);
|
|
|
|
event = EError;
|
|
errorCode = ErrAuth;
|
|
return true;
|
|
}
|
|
}
|
|
else {
|
|
// ignore
|
|
}
|
|
}
|
|
else {
|
|
// ignore
|
|
}
|
|
}
|
|
// server
|
|
else if(step == GetRequest) {
|
|
printf("get request: [%s], %s\n", e.namespaceURI().latin1(), e.tagName().latin1());
|
|
if(e.namespaceURI() == NS_TLS && e.localName() == "starttls") {
|
|
// TODO: don't let this be done twice
|
|
|
|
TQDomElement e = doc.createElementNS(NS_TLS, "proceed");
|
|
writeElement(e, TypeElement, false, true);
|
|
event = ESend;
|
|
step = HandleTLS;
|
|
return true;
|
|
}
|
|
if(e.namespaceURI() == NS_SASL) {
|
|
if(e.localName() == "auth") {
|
|
if(sasl_started) {
|
|
// TODO
|
|
printf("error\n");
|
|
return false;
|
|
}
|
|
|
|
sasl_started = true;
|
|
sasl_mech = e.attribute("mechanism");
|
|
// TODO: if child text missing, don't pass it
|
|
sasl_step = Base64::stringToArray(e.text());
|
|
need = NSASLFirst;
|
|
step = GetSASLNext;
|
|
return false;
|
|
}
|
|
else {
|
|
// TODO
|
|
printf("unknown sasl tag\n");
|
|
return false;
|
|
}
|
|
}
|
|
if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
|
|
TQDomElement b = e.elementsByTagNameNS(NS_BIND, "bind").item(0).toElement();
|
|
if(!b.isNull()) {
|
|
TQDomElement res = b.elementsByTagName("resource").item(0).toElement();
|
|
TQString resource = res.text();
|
|
|
|
TQDomElement r = doc.createElement("iq");
|
|
r.setAttribute("type", "result");
|
|
r.setAttribute("id", e.attribute("id"));
|
|
TQDomElement bind = doc.createElementNS(NS_BIND, "bind");
|
|
TQDomElement jid = doc.createElement("jid");
|
|
Jid j = user + '@' + host + '/' + resource;
|
|
jid.appendChild(doc.createTextNode(j.full()));
|
|
bind.appendChild(jid);
|
|
r.appendChild(bind);
|
|
|
|
writeElement(r, TypeElement, false);
|
|
event = ESend;
|
|
// TODO
|
|
return true;
|
|
}
|
|
else {
|
|
// TODO
|
|
}
|
|
}
|
|
}
|
|
else if(step == GetSASLResponse) {
|
|
if(e.namespaceURI() == NS_SASL && e.localName() == "response") {
|
|
sasl_step = Base64::stringToArray(e.text());
|
|
need = NSASLNext;
|
|
step = GetSASLNext;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(isReady()) {
|
|
if(!e.isNull() && isValidStanza(e)) {
|
|
stanzaToRecv = e;
|
|
event = EStanzaReady;
|
|
setIncomingAsExternal();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
need = NNotify;
|
|
notify |= NRecv;
|
|
return false;
|
|
}
|