File indexing completed on 2024-06-23 04:03:34

0001 /*
0002  * protocol.cpp - XMPP-Core protocol state machine
0003  * Copyright (C) 2004  Justin Karneges
0004  *
0005  * This library is free software; you can redistribute it and/or
0006  * modify it under the terms of the GNU Lesser General Public
0007  * License as published by the Free Software Foundation; either
0008  * either version 2
0009    of the License, or (at your option) any later version.1 of the License, or (at your option) any later version.
0010  *
0011  * This library is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014  * Lesser General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU Lesser General Public
0017  * License along with this library; if not, write to the Free Software
0018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
0019  *
0020  */
0021 
0022 // TODO: let the app know if tls is required
0023 //       require mutual auth for server out/in
0024 //       report ErrProtocol if server uses wrong NS
0025 //       use send() instead of writeElement() in CoreProtocol
0026 
0027 #include "protocol.h"
0028 
0029 #include <qca.h>
0030 #include <QByteArray>
0031 #include <QtCrypto>
0032 
0033 #ifdef XMPP_TEST
0034 #include "td.h"
0035 #endif
0036 
0037 using namespace XMPP;
0038 
0039 // printArray
0040 //
0041 // This function prints out an array of bytes as latin characters, converting
0042 // non-printable bytes into hex values as necessary.  Useful for displaying
0043 // QByteArrays for debugging purposes.
0044 static QString printArray(const QByteArray &a)
0045 {
0046     QString s;
0047     for(int n = 0; n < a.size(); ++n) {
0048         unsigned char c = (unsigned char)a[(int)n];
0049         if(c < 32 || c >= 127) {
0050             const QString str = QString::asprintf("[%02x]", c);
0051             s += str;
0052         }
0053         else
0054             s += QChar(c);
0055     }
0056     return s;
0057 }
0058 
0059 // firstChildElement
0060 //
0061 // Get an element's first child element
0062 static QDomElement firstChildElement(const QDomElement &e)
0063 {
0064     for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
0065         if(n.isElement())
0066             return n.toElement();
0067     }
0068     return QDomElement();
0069 }
0070 
0071 //----------------------------------------------------------------------------
0072 // Version
0073 //----------------------------------------------------------------------------
0074 Version::Version(int maj, int min)
0075 {
0076     major = maj;
0077     minor = min;
0078 }
0079 
0080 //----------------------------------------------------------------------------
0081 // StreamFeatures
0082 //----------------------------------------------------------------------------
0083 StreamFeatures::StreamFeatures()
0084 {
0085     tls_supported = false;
0086     sasl_supported = false;
0087     bind_supported = false;
0088     tls_required = false;
0089     compress_supported = false;
0090 }
0091 
0092 //----------------------------------------------------------------------------
0093 // BasicProtocol
0094 //----------------------------------------------------------------------------
0095 BasicProtocol::SASLCondEntry BasicProtocol::saslCondTable[] =
0096 {
0097     { "aborted",                Aborted },
0098     { "incorrect-encoding",     IncorrectEncoding },
0099     { "invalid-authzid",        InvalidAuthzid },
0100     { "invalid-mechanism",      InvalidMech },
0101     { "mechanism-too-weak",     MechTooWeak },
0102     { "not-authorized",         NotAuthorized },
0103     { "temporary-auth-failure", TemporaryAuthFailure },
0104     { 0, 0 },
0105 };
0106 
0107 BasicProtocol::StreamCondEntry BasicProtocol::streamCondTable[] =
0108 {
0109     { "bad-format",               BadFormat },
0110     { "bad-namespace-prefix",     BadNamespacePrefix },
0111     { "conflict",                 Conflict },
0112     { "connection-timeout",       ConnectionTimeout },
0113     { "host-gone",                HostGone },
0114     { "host-unknown",             HostUnknown },
0115     { "improper-addressing",      ImproperAddressing },
0116     { "internal-server-error",    InternalServerError },
0117     { "invalid-from",             InvalidFrom },
0118     { "invalid-id",               InvalidId },
0119     { "invalid-namespace",        InvalidNamespace },
0120     { "invalid-xml",              InvalidXml },
0121     { "not-authorized",           StreamNotAuthorized },
0122     { "policy-violation",         PolicyViolation },
0123     { "remote-connection-failed", RemoteConnectionFailed },
0124     { "resource-constraint",      ResourceConstraint },
0125     { "restricted-xml",           RestrictedXml },
0126     { "see-other-host",           SeeOtherHost },
0127     { "system-shutdown",          SystemShutdown },
0128     { "undefined-condition",      UndefinedCondition },
0129     { "unsupported-encoding",     UnsupportedEncoding },
0130     { "unsupported-stanza-type",  UnsupportedStanzaType },
0131     { "unsupported-version",      UnsupportedVersion },
0132     { "xml-not-well-formed",      XmlNotWellFormed },
0133     { 0, 0 },
0134 };
0135 
0136 BasicProtocol::BasicProtocol()
0137 :XmlProtocol()
0138 {
0139     init();
0140 }
0141 
0142 BasicProtocol::~BasicProtocol()
0143 {
0144 }
0145 
0146 void BasicProtocol::init()
0147 {
0148     errCond = -1;
0149     sasl_authed = false;
0150     doShutdown = false;
0151     delayedError = false;
0152     closeError = false;
0153     ready = false;
0154     stanzasPending = 0;
0155     stanzasWritten = 0;
0156 }
0157 
0158 void BasicProtocol::reset()
0159 {
0160     XmlProtocol::reset();
0161     init();
0162 
0163     to = QString();
0164     from = QString();
0165     id = QString();
0166     lang = QString();
0167     version = Version(1,0);
0168     errText = QString();
0169     errAppSpec = QDomElement();
0170     otherHost = QString();
0171     spare.resize(0);
0172     sasl_mech = QString();
0173     sasl_mechlist.clear();
0174     sasl_step.resize(0);
0175     stanzaToRecv = QDomElement();
0176     sendList.clear();
0177 }
0178 
0179 void BasicProtocol::sendStanza(const QDomElement &e)
0180 {
0181     SendItem i;
0182     i.stanzaToSend = e;
0183     sendList += i;
0184 }
0185 
0186 void BasicProtocol::sendDirect(const QString &s)
0187 {
0188     SendItem i;
0189     i.stringToSend = s;
0190     sendList += i;
0191 }
0192 
0193 void BasicProtocol::sendWhitespace()
0194 {
0195     SendItem i;
0196     i.doWhitespace = true;
0197     sendList += i;
0198 }
0199 
0200 QDomElement BasicProtocol::recvStanza()
0201 {
0202     QDomElement e = stanzaToRecv;
0203     stanzaToRecv = QDomElement();
0204     return e;
0205 }
0206 
0207 void BasicProtocol::shutdown()
0208 {
0209     doShutdown = true;
0210 }
0211 
0212 void BasicProtocol::shutdownWithError(int cond, const QString &str)
0213 {
0214     otherHost = str;
0215     delayErrorAndClose(cond);
0216 }
0217 
0218 bool BasicProtocol::isReady() const
0219 {
0220     return ready;
0221 }
0222 
0223 void BasicProtocol::setReady(bool b)
0224 {
0225     ready = b;
0226 }
0227 
0228 QString BasicProtocol::saslMech() const
0229 {
0230     return sasl_mech;
0231 }
0232 
0233 QByteArray BasicProtocol::saslStep() const
0234 {
0235     return sasl_step;
0236 }
0237 
0238 void BasicProtocol::setSASLMechList(const QStringList &list)
0239 {
0240     sasl_mechlist = list;
0241 }
0242 
0243 void BasicProtocol::setSASLFirst(const QString &mech, const QByteArray &step)
0244 {
0245     sasl_mech = mech;
0246     sasl_step = step;
0247 }
0248 
0249 void BasicProtocol::setSASLNext(const QByteArray &step)
0250 {
0251     sasl_step = step;
0252 }
0253 
0254 void BasicProtocol::setSASLAuthed()
0255 {
0256     sasl_authed = true;
0257 }
0258 
0259 int BasicProtocol::stringToSASLCond(const QString &s)
0260 {
0261     for(int n = 0; saslCondTable[n].str; ++n) {
0262         if(s == saslCondTable[n].str)
0263             return saslCondTable[n].cond;
0264     }
0265     return -1;
0266 }
0267 
0268 int BasicProtocol::stringToStreamCond(const QString &s)
0269 {
0270     for(int n = 0; streamCondTable[n].str; ++n) {
0271         if(s == streamCondTable[n].str)
0272             return streamCondTable[n].cond;
0273     }
0274     return -1;
0275 }
0276 
0277 QString BasicProtocol::saslCondToString(int x)
0278 {
0279     for(int n = 0; saslCondTable[n].str; ++n) {
0280         if(x == saslCondTable[n].cond)
0281             return saslCondTable[n].str;
0282     }
0283     return QString();
0284 }
0285 
0286 QString BasicProtocol::streamCondToString(int x)
0287 {
0288     for(int n = 0; streamCondTable[n].str; ++n) {
0289         if(x == streamCondTable[n].cond)
0290             return streamCondTable[n].str;
0291     }
0292     return QString();
0293 }
0294 
0295 void BasicProtocol::extractStreamError(const QDomElement &e)
0296 {
0297     QString text;
0298     QDomElement appSpec;
0299 
0300     QDomElement t = firstChildElement(e);
0301     if(t.isNull() || t.namespaceURI() != NS_STREAMS) {
0302         // probably old-style error
0303         errCond = -1;
0304         errText = e.text();
0305     }
0306     else
0307         errCond = stringToStreamCond(t.tagName());
0308 
0309     if(errCond != -1) {
0310         if(errCond == SeeOtherHost)
0311             otherHost = t.text();
0312 
0313         t = e.elementsByTagNameNS(NS_STREAMS, "text").item(0).toElement();
0314         if(!t.isNull())
0315             text = t.text();
0316 
0317         // find first non-standard namespaced element
0318         QDomNodeList nl = e.childNodes();
0319         for(int n = 0; n < nl.count(); ++n) {
0320             QDomNode i = nl.item(n);
0321             if(i.isElement() && i.namespaceURI() != NS_STREAMS) {
0322                 appSpec = i.toElement();
0323                 break;
0324             }
0325         }
0326 
0327         errText = text;
0328         errAppSpec = appSpec;
0329     }
0330 }
0331 
0332 void BasicProtocol::send(const QDomElement &e, bool clip)
0333 {
0334     writeElement(e, TypeElement, false, clip);
0335 }
0336 
0337 void BasicProtocol::sendStreamError(int cond, const QString &text, const QDomElement &appSpec)
0338 {
0339     QDomElement se = doc.createElementNS(NS_ETHERX, "stream:error");
0340     QDomElement err = doc.createElementNS(NS_STREAMS, streamCondToString(cond));
0341     if(!otherHost.isEmpty())
0342         err.appendChild(doc.createTextNode(otherHost));
0343     se.appendChild(err);
0344     if(!text.isEmpty()) {
0345         QDomElement te = doc.createElementNS(NS_STREAMS, "text");
0346         te.setAttributeNS(NS_XML, "xml:lang", "en");
0347         te.appendChild(doc.createTextNode(text));
0348         se.appendChild(te);
0349     }
0350     se.appendChild(appSpec);
0351 
0352     writeElement(se, 100, false);
0353 }
0354 
0355 void BasicProtocol::sendStreamError(const QString &text)
0356 {
0357     QDomElement se = doc.createElementNS(NS_ETHERX, "stream:error");
0358     se.appendChild(doc.createTextNode(text));
0359 
0360     writeElement(se, 100, false);
0361 }
0362 
0363 bool BasicProtocol::errorAndClose(int cond, const QString &text, const QDomElement &appSpec)
0364 {
0365     closeError = true;
0366     errCond = cond;
0367     errText = text;
0368     errAppSpec = appSpec;
0369     sendStreamError(cond, text, appSpec);
0370     return close();
0371 }
0372 
0373 bool BasicProtocol::error(int code)
0374 {
0375     event = EError;
0376     errorCode = code;
0377     return true;
0378 }
0379 
0380 void BasicProtocol::delayErrorAndClose(int cond, const QString &text, const QDomElement &appSpec)
0381 {
0382     errorCode = ErrStream;
0383     errCond = cond;
0384     errText = text;
0385     errAppSpec = appSpec;
0386     delayedError = true;
0387 }
0388 
0389 void BasicProtocol::delayError(int code)
0390 {
0391     errorCode = code;
0392     delayedError = true;
0393 }
0394 
0395 QDomElement BasicProtocol::docElement()
0396 {
0397     // create the root element
0398     QDomElement e = doc.createElementNS(NS_ETHERX, "stream:stream");
0399 
0400     QString defns = defaultNamespace();
0401     QStringList list = extraNamespaces();
0402 
0403     // HACK: using attributes seems to be the only way to get additional namespaces in here
0404     if(!defns.isEmpty())
0405         e.setAttribute("xmlns", defns);
0406     for(QStringList::ConstIterator it = list.constBegin(); it != list.constEnd();) {
0407         QString prefix = *(it++);
0408         QString uri = *(it++);
0409         e.setAttribute(QString("xmlns:") + prefix, uri);
0410     }
0411 
0412     // additional attributes
0413     if(!isIncoming() && !to.isEmpty())
0414         e.setAttribute("to", to);
0415     if(isIncoming() && !from.isEmpty())
0416         e.setAttribute("from", from);
0417     if(!id.isEmpty())
0418         e.setAttribute("id", id);
0419     if(!lang.isEmpty())
0420         e.setAttributeNS(NS_XML, "xml:lang", lang);
0421     if(version.major > 0 || version.minor > 0)
0422         e.setAttribute("version", QString::number(version.major) + '.' + QString::number(version.minor));
0423 
0424     return e;
0425 }
0426 
0427 void BasicProtocol::handleDocOpen(const Parser::Event &pe)
0428 {
0429     if(isIncoming()) {
0430         if(xmlEncoding() != "UTF-8") {
0431             delayErrorAndClose(UnsupportedEncoding);
0432             return;
0433         }
0434     }
0435 
0436     if(pe.namespaceURI() == NS_ETHERX && pe.localName() == "stream") {
0437         QXmlAttributes atts = pe.atts();
0438 
0439         // grab the version
0440         int major = 0;
0441         int minor = 0;
0442         QString verstr = atts.value("version");
0443         if(!verstr.isEmpty()) {
0444             int n = verstr.indexOf('.');
0445             if(n != -1) {
0446                 major = QStringView(verstr).mid(0, n).toInt();
0447                 minor = QStringView(verstr).mid(n+1).toInt();
0448             }
0449             else {
0450                 major = verstr.toInt();
0451                 minor = 0;
0452             }
0453         }
0454         version = Version(major, minor);
0455 
0456         if(isIncoming()) {
0457             to = atts.value("to");
0458             QString peerLang = atts.value(NS_XML, "lang");
0459             if(!peerLang.isEmpty())
0460                 lang = peerLang;
0461         }
0462         // outgoing
0463         else {
0464             from = atts.value("from");
0465             lang = atts.value(NS_XML, "lang");
0466             id = atts.value("id");
0467         }
0468 
0469         handleStreamOpen(pe);
0470     }
0471     else {
0472         if(isIncoming())
0473             delayErrorAndClose(BadFormat);
0474         else
0475             delayError(ErrProtocol);
0476     }
0477 }
0478 
0479 bool BasicProtocol::handleError()
0480 {
0481     if(isIncoming())
0482         return errorAndClose(XmlNotWellFormed);
0483     else
0484         return error(ErrParse);
0485 }
0486 
0487 bool BasicProtocol::handleCloseFinished()
0488 {
0489     if(closeError) {
0490         event = EError;
0491         errorCode = ErrStream;
0492         // note: errCond and friends are already set at this point
0493     }
0494     else
0495         event = EClosed;
0496     return true;
0497 }
0498 
0499 bool BasicProtocol::doStep(const QDomElement &e)
0500 {
0501     // handle pending error
0502     if(delayedError) {
0503         if(isIncoming())
0504             return errorAndClose(errCond, errText, errAppSpec);
0505         else
0506             return error(errorCode);
0507     }
0508 
0509     // shutdown?
0510     if(doShutdown) {
0511         doShutdown = false;
0512         return close();
0513     }
0514 
0515     if(!e.isNull()) {
0516         // check for error
0517         if(e.namespaceURI() == NS_ETHERX && e.tagName() == "error") {
0518             extractStreamError(e);
0519             return error(ErrStream);
0520         }
0521     }
0522 
0523     if(ready) {
0524         // stanzas written?
0525         if(stanzasWritten > 0) {
0526             --stanzasWritten;
0527             event = EStanzaSent;
0528             return true;
0529         }
0530         // send items?
0531         if(!sendList.isEmpty()) {
0532             SendItem i;
0533             {
0534                 QList<SendItem>::Iterator it = sendList.begin();
0535                 i = (*it);
0536                 sendList.erase(it);
0537             }
0538 
0539             // outgoing stanza?
0540             if(!i.stanzaToSend.isNull()) {
0541                 ++stanzasPending;
0542                 writeElement(i.stanzaToSend, TypeStanza, true);
0543                 event = ESend;
0544             }
0545             // direct send?
0546             else if(!i.stringToSend.isEmpty()) {
0547                 writeString(i.stringToSend, TypeDirect, true);
0548                 event = ESend;
0549             }
0550             // whitespace keepalive?
0551             else if(i.doWhitespace) {
0552                 writeString("\n", TypePing, false);
0553                 event = ESend;
0554             }
0555             return true;
0556         }
0557         else {
0558             // if we have pending outgoing stanzas, ask for write notification
0559             if(stanzasPending)
0560                 notify |= NSend;
0561         }
0562     }
0563 
0564     return doStep2(e);
0565 }
0566 
0567 void BasicProtocol::itemWritten(int id, int)
0568 {
0569     if(id == TypeStanza) {
0570         --stanzasPending;
0571         ++stanzasWritten;
0572     }
0573 }
0574 
0575 QString BasicProtocol::defaultNamespace()
0576 {
0577     // default none
0578     return QString();
0579 }
0580 
0581 QStringList BasicProtocol::extraNamespaces()
0582 {
0583     // default none
0584     return QStringList();
0585 }
0586 
0587 void BasicProtocol::handleStreamOpen(const Parser::Event &)
0588 {
0589     // default does nothing
0590 }
0591 
0592 //----------------------------------------------------------------------------
0593 // CoreProtocol
0594 //----------------------------------------------------------------------------
0595 CoreProtocol::CoreProtocol()
0596 :BasicProtocol()
0597 {
0598     init();
0599 }
0600 
0601 CoreProtocol::~CoreProtocol()
0602 {
0603 }
0604 
0605 void CoreProtocol::init()
0606 {
0607     step = Start;
0608 
0609     // ??
0610     server = false;
0611     dialback = false;
0612     dialback_verify = false;
0613 
0614     // settings
0615     jid_ = Jid();
0616     password = QString();
0617     oldOnly = false;
0618     allowPlain = false;
0619     doTLS = true;
0620     doAuth = true;
0621     doCompress = true;
0622     doBinding = true;
0623 
0624     // input
0625     user = QString();
0626     host = QString();
0627 
0628     // status
0629     old = false;
0630     digest = false;
0631     tls_started = false;
0632     sasl_started = false;
0633     compress_started = false;
0634 }
0635 
0636 void CoreProtocol::reset()
0637 {
0638     BasicProtocol::reset();
0639     init();
0640 }
0641 
0642 void CoreProtocol::startClientOut(const Jid &_jid, bool _oldOnly, bool tlsActive, bool _doAuth, bool _doCompress)
0643 {
0644     jid_ = _jid;
0645     to = _jid.domain();
0646     oldOnly = _oldOnly;
0647     doAuth = _doAuth;
0648     doCompress = _doCompress;
0649     tls_started = tlsActive;
0650 
0651     if(oldOnly)
0652         version = Version(0,0);
0653     startConnect();
0654 }
0655 
0656 void CoreProtocol::startServerOut(const QString &_to)
0657 {
0658     server = true;
0659     to = _to;
0660     startConnect();
0661 }
0662 
0663 void CoreProtocol::startDialbackOut(const QString &_to, const QString &_from)
0664 {
0665     server = true;
0666     dialback = true;
0667     to = _to;
0668     self_from = _from;
0669     startConnect();
0670 }
0671 
0672 void CoreProtocol::startDialbackVerifyOut(const QString &_to, const QString &_from, const QString &id, const QString &key)
0673 {
0674     server = true;
0675     dialback = true;
0676     dialback_verify = true;
0677     to = _to;
0678     self_from = _from;
0679     dialback_id = id;
0680     dialback_key = key;
0681     startConnect();
0682 }
0683 
0684 void CoreProtocol::startClientIn(const QString &_id)
0685 {
0686     id = _id;
0687     startAccept();
0688 }
0689 
0690 void CoreProtocol::startServerIn(const QString &_id)
0691 {
0692     server = true;
0693     id = _id;
0694     startAccept();
0695 }
0696 
0697 void CoreProtocol::setLang(const QString &s)
0698 {
0699     lang = s;
0700 }
0701 
0702 void CoreProtocol::setAllowTLS(bool b)
0703 {
0704     doTLS = b;
0705 }
0706 
0707 void CoreProtocol::setAllowBind(bool b)
0708 {
0709     doBinding = b;
0710 }
0711 
0712 void CoreProtocol::setAllowPlain(bool b)
0713 {
0714     allowPlain = b;
0715 }
0716 
0717 const Jid& CoreProtocol::jid() const
0718 {
0719     return jid_;
0720 }
0721 
0722 void CoreProtocol::setPassword(const QString &s)
0723 {
0724     password = s;
0725 }
0726 
0727 void CoreProtocol::setFrom(const QString &s)
0728 {
0729     from = s;
0730 }
0731 
0732 void CoreProtocol::setDialbackKey(const QString &s)
0733 {
0734     dialback_key = s;
0735 }
0736 
0737 bool CoreProtocol::loginComplete()
0738 {
0739     setReady(true);
0740 
0741     event = EReady;
0742     step = Done;
0743     return true;
0744 }
0745 
0746 int CoreProtocol::getOldErrorCode(const QDomElement &e)
0747 {
0748     QDomElement err = e.elementsByTagNameNS(NS_CLIENT, "error").item(0).toElement();
0749     if(err.isNull() || !err.hasAttribute("code"))
0750         return -1;
0751     return err.attribute("code").toInt();
0752 }
0753 
0754 /*QString CoreProtocol::xmlToString(const QDomElement &e, bool clip)
0755 {
0756     // determine an appropriate 'fakeNS' to use
0757     QString ns;
0758     if(e.prefix() == "stream")
0759         ns = NS_ETHERX;
0760     else if(e.prefix() == "db")
0761         ns = NS_DIALBACK;
0762     else
0763         ns = NS_CLIENT;
0764     return ::xmlToString(e, ns, "stream:stream", clip);
0765 }*/
0766 
0767 bool CoreProtocol::stepAdvancesParser() const
0768 {
0769     if(stepRequiresElement())
0770         return true;
0771     else if(isReady())
0772         return true;
0773     return false;
0774 }
0775 
0776 // all element-needing steps need to be registered here
0777 bool CoreProtocol::stepRequiresElement() const
0778 {
0779     switch(step) {
0780         case GetFeatures:
0781         case GetTLSProceed:
0782         case GetCompressProceed:
0783         case GetSASLChallenge:
0784         case GetBindResponse:
0785         case GetAuthGetResponse:
0786         case GetAuthSetResponse:
0787         case GetRequest:
0788         case GetSASLResponse:
0789             return true;
0790     }
0791     return false;
0792 }
0793 
0794 void CoreProtocol::stringSend(const QString &s)
0795 {
0796 #ifdef XMPP_TEST
0797     TD::outgoingTag(s);
0798 #endif
0799 }
0800 
0801 void CoreProtocol::stringRecv(const QString &s)
0802 {
0803 #ifdef XMPP_TEST
0804     TD::incomingTag(s);
0805 #endif
0806 }
0807 
0808 QString CoreProtocol::defaultNamespace()
0809 {
0810     if(server)
0811         return NS_SERVER;
0812     else
0813         return NS_CLIENT;
0814 }
0815 
0816 QStringList CoreProtocol::extraNamespaces()
0817 {
0818     QStringList list;
0819     if(dialback) {
0820         list += "db";
0821         list += NS_DIALBACK;
0822     }
0823     return list;
0824 }
0825 
0826 void CoreProtocol::handleStreamOpen(const Parser::Event &pe)
0827 {
0828     if(isIncoming()) {
0829         QString ns = pe.nsprefix();
0830         QString db;
0831         if(server) {
0832             db = pe.nsprefix("db");
0833             if(!db.isEmpty())
0834                 dialback = true;
0835         }
0836 
0837         // verify namespace
0838         if((!server && ns != NS_CLIENT) || (server && ns != NS_SERVER) || (dialback && db != NS_DIALBACK)) {
0839             delayErrorAndClose(InvalidNamespace);
0840             return;
0841         }
0842 
0843         // verify version
0844         if(version.major < 1 && !dialback) {
0845             delayErrorAndClose(UnsupportedVersion);
0846             return;
0847         }
0848     }
0849     else {
0850         if(!dialback) {
0851             if(version.major >= 1 && !oldOnly)
0852                 old = false;
0853             else
0854                 old = true;
0855         }
0856     }
0857 }
0858 
0859 void CoreProtocol::elementSend(const QDomElement &e)
0860 {
0861 #ifdef XMPP_TEST
0862     TD::outgoingXml(e);
0863 #endif
0864 }
0865 
0866 void CoreProtocol::elementRecv(const QDomElement &e)
0867 {
0868 #ifdef XMPP_TEST
0869     TD::incomingXml(e);
0870 #endif
0871 }
0872 
0873 bool CoreProtocol::doStep2(const QDomElement &e)
0874 {
0875     if(dialback)
0876         return dialbackStep(e);
0877     else
0878         return normalStep(e);
0879 }
0880 
0881 bool CoreProtocol::isValidStanza(const QDomElement &e) const
0882 {
0883     QString s = e.tagName();
0884     if(e.namespaceURI() == (server ? NS_SERVER : NS_CLIENT) && (s == "message" || s == "presence" || s == "iq"))
0885         return true;
0886     else
0887         return false;
0888 }
0889 
0890 bool CoreProtocol::grabPendingItem(const Jid &to, const Jid &from, int type, DBItem *item)
0891 {
0892     for(QList<DBItem>::Iterator it = dbpending.begin(); it != dbpending.end(); ++it) {
0893         const DBItem &i = *it;
0894         if(i.type == type && i.to.compare(to) && i.from.compare(from)) {
0895             const DBItem &i = (*it);
0896             *item = i;
0897             dbpending.erase(it);
0898             return true;
0899         }
0900     }
0901     return false;
0902 }
0903 
0904 bool CoreProtocol::dialbackStep(const QDomElement &e)
0905 {
0906     if(step == Start) {
0907         setReady(true);
0908         step = Done;
0909         event = EReady;
0910         return true;
0911     }
0912 
0913     if(!dbrequests.isEmpty()) {
0914         // process a request
0915         DBItem i;
0916         {
0917             QList<DBItem>::Iterator it = dbrequests.begin();
0918             i = (*it);
0919             dbrequests.erase(it);
0920         }
0921 
0922         QDomElement r;
0923         if(i.type == DBItem::ResultRequest) {
0924             r = doc.createElementNS(NS_DIALBACK, "db:result");
0925             r.setAttribute("to", i.to.full());
0926             r.setAttribute("from", i.from.full());
0927             r.appendChild(doc.createTextNode(i.key));
0928             dbpending += i;
0929         }
0930         else if(i.type == DBItem::ResultGrant) {
0931             r = doc.createElementNS(NS_DIALBACK, "db:result");
0932             r.setAttribute("to", i.to.full());
0933             r.setAttribute("from", i.from.full());
0934             r.setAttribute("type", i.ok ? "valid" : "invalid");
0935             if(i.ok) {
0936                 i.type = DBItem::Validated;
0937                 dbvalidated += i;
0938             }
0939             else {
0940                 // TODO: disconnect after writing element
0941             }
0942         }
0943         else if(i.type == DBItem::VerifyRequest) {
0944             r = doc.createElementNS(NS_DIALBACK, "db:verify");
0945             r.setAttribute("to", i.to.full());
0946             r.setAttribute("from", i.from.full());
0947             r.setAttribute("id", i.id);
0948             r.appendChild(doc.createTextNode(i.key));
0949             dbpending += i;
0950         }
0951         // VerifyGrant
0952         else {
0953             r = doc.createElementNS(NS_DIALBACK, "db:verify");
0954             r.setAttribute("to", i.to.full());
0955             r.setAttribute("from", i.from.full());
0956             r.setAttribute("id", i.id);
0957             r.setAttribute("type", i.ok ? "valid" : "invalid");
0958         }
0959 
0960         writeElement(r, TypeElement, false);
0961         event = ESend;
0962         return true;
0963     }
0964 
0965     if(!e.isNull()) {
0966         if(e.namespaceURI() == NS_DIALBACK) {
0967             if(e.tagName() == "result") {
0968                 Jid to(Jid(e.attribute("to")).domain());
0969                 Jid from(Jid(e.attribute("from")).domain());
0970                 if(isIncoming()) {
0971                     QString key = e.text();
0972                     // TODO: report event
0973                 }
0974                 else {
0975                     bool ok = (e.attribute("type") == "valid") ? true: false;
0976                     DBItem i;
0977                     if(grabPendingItem(from, to, DBItem::ResultRequest, &i)) {
0978                         if(ok) {
0979                             i.type = DBItem::Validated;
0980                             i.ok = true;
0981                             dbvalidated += i;
0982                             // TODO: report event
0983                         }
0984                         else {
0985                             // TODO: report event
0986                         }
0987                     }
0988                 }
0989             }
0990             else if(e.tagName() == "verify") {
0991                 Jid to(Jid(e.attribute("to")).domain());
0992                 Jid from(Jid(e.attribute("from")).domain());
0993                 QString id = e.attribute("id");
0994                 if(isIncoming()) {
0995                     QString key = e.text();
0996                     // TODO: report event
0997                 }
0998                 else {
0999                     bool ok = (e.attribute("type") == "valid") ? true: false;
1000                     DBItem i;
1001                     if(grabPendingItem(from, to, DBItem::VerifyRequest, &i)) {
1002                         if(ok) {
1003                             // TODO: report event
1004                         }
1005                         else {
1006                             // TODO: report event
1007                         }
1008                     }
1009                 }
1010             }
1011         }
1012         else {
1013             if(isReady()) {
1014                 if(isValidStanza(e)) {
1015                     // TODO: disconnect if stanza is from unverified sender
1016                     // TODO: ignore packets from receiving servers
1017                     stanzaToRecv = e;
1018                     event = EStanzaReady;
1019                     return true;
1020                 }
1021             }
1022         }
1023     }
1024 
1025     need = NNotify;
1026     notify |= NRecv;
1027     return false;
1028 }
1029 
1030 bool CoreProtocol::normalStep(const QDomElement &e)
1031 {
1032     if(step == Start) {
1033         if(isIncoming()) {
1034             need = NSASLMechs;
1035             step = SendFeatures;
1036             return false;
1037         }
1038         else {
1039             if(old) {
1040                 if(doAuth)
1041                     step = HandleAuthGet;
1042                 else
1043                     return loginComplete();
1044             }
1045             else
1046                 step = GetFeatures;
1047 
1048             return processStep();
1049         }
1050     }
1051     else if(step == HandleFeatures) {
1052         // deal with TLS?
1053         if(doTLS && !tls_started && !sasl_authed && features.tls_supported) {
1054             QDomElement e = doc.createElementNS(NS_TLS, "starttls");
1055 
1056             send(e, true);
1057             event = ESend;
1058             step = GetTLSProceed;
1059             return true;
1060         }
1061 
1062         // Should we go further ?
1063         if (!doAuth)
1064             return loginComplete();
1065         
1066         // Deal with compression
1067         if (doCompress && !compress_started && features.compress_supported && features.compression_mechs.contains("zlib")) {
1068             QDomElement e = doc.createElementNS(NS_COMPRESS_PROTOCOL, "compress");
1069             QDomElement m = doc.createElementNS(NS_COMPRESS_PROTOCOL, "method");
1070             m.appendChild(doc.createTextNode("zlib"));
1071             e.appendChild(m);
1072             send(e,true);
1073             event = ESend;
1074             step = GetCompressProceed;
1075             return true;
1076         }
1077 
1078         // deal with SASL?
1079         if(!sasl_authed) {
1080             if(!features.sasl_supported) {
1081                 // SASL MUST be supported
1082                 //event = EError;
1083                 //errorCode = ErrProtocol;
1084                 //return true;
1085                 
1086                 // Fall back on auth for non-compliant servers 
1087                 step = HandleAuthGet;
1088                 old = true;
1089                 return true;
1090             }
1091 
1092 #ifdef XMPP_TEST
1093             TD::msg("starting SASL authentication...");
1094 #endif
1095             need = NSASLFirst;
1096             step = GetSASLFirst;
1097             return false;
1098         }
1099 
1100         if(server) {
1101             return loginComplete();
1102         }
1103         else {
1104             if(!doBinding)
1105                 return loginComplete();
1106         }
1107 
1108         // deal with bind
1109         if(!features.bind_supported) {
1110             // bind MUST be supported
1111             event = EError;
1112             errorCode = ErrProtocol;
1113             return true;
1114         }
1115 
1116         QDomElement e = doc.createElement("iq");
1117         e.setAttribute("type", "set");
1118         e.setAttribute("id", "bind_1");
1119         QDomElement b = doc.createElementNS(NS_BIND, "bind");
1120 
1121         // request specific resource?
1122         QString resource = jid_.resource();
1123         if(!resource.isEmpty()) {
1124             QDomElement r = doc.createElement("resource");
1125             r.appendChild(doc.createTextNode(jid_.resource()));
1126             b.appendChild(r);
1127         }
1128 
1129         e.appendChild(b);
1130 
1131         send(e);
1132         event = ESend;
1133         step = GetBindResponse;
1134         return true;
1135     }
1136     else if(step == GetSASLFirst) {
1137         QDomElement e = doc.createElementNS(NS_SASL, "auth");
1138         e.setAttribute("mechanism", sasl_mech);
1139         if(!sasl_step.isEmpty()) {
1140 #ifdef XMPP_TEST
1141             TD::msg(QString("SASL OUT: [%1]").arg(printArray(sasl_step)));
1142 #endif
1143             e.appendChild(doc.createTextNode(QCA::Base64().arrayToString(sasl_step)));
1144         }
1145 
1146         send(e, true);
1147         event = ESend;
1148         step = GetSASLChallenge;
1149         return true;
1150     }
1151     else if(step == GetSASLNext) {
1152         if(isIncoming()) {
1153             if(sasl_authed) {
1154                 QDomElement e = doc.createElementNS(NS_SASL, "success");
1155                 writeElement(e, TypeElement, false, true);
1156                 event = ESend;
1157                 step = IncHandleSASLSuccess;
1158                 return true;
1159             }
1160             else {
1161                 QByteArray stepData = sasl_step;
1162                 QDomElement e = doc.createElementNS(NS_SASL, "challenge");
1163                 if(!stepData.isEmpty())
1164                     e.appendChild(doc.createTextNode(QCA::Base64().arrayToString(stepData)));
1165 
1166                 writeElement(e, TypeElement, false, true);
1167                 event = ESend;
1168                 step = GetSASLResponse;
1169                 return true;
1170             }
1171         }
1172         else {
1173             // already authed?  then ignore last client step
1174             //   (this happens if "additional data with success"
1175             //   is used)
1176             if(sasl_authed)
1177             {
1178                 event = ESASLSuccess;
1179                 step = HandleSASLSuccess;
1180                 return true;
1181             }
1182 
1183             QByteArray stepData = sasl_step;
1184 #ifdef XMPP_TEST
1185             TD::msg(QString("SASL OUT: [%1]").arg(printArray(sasl_step)));
1186 #endif
1187             QDomElement e = doc.createElementNS(NS_SASL, "response");
1188             if(!stepData.isEmpty())
1189                 e.appendChild(doc.createTextNode(QCA::Base64().arrayToString(stepData)));
1190 
1191             send(e, true);
1192             event = ESend;
1193             step = GetSASLChallenge;
1194             return true;
1195         }
1196     }
1197     else if(step == HandleSASLSuccess) {
1198         need = NSASLLayer;
1199         spare = resetStream();
1200         step = Start;
1201         return false;
1202     }
1203     else if(step == HandleAuthGet) {
1204         QDomElement e = doc.createElement("iq");
1205         e.setAttribute("to", to);
1206         e.setAttribute("type", "get");
1207         e.setAttribute("id", "auth_1");
1208         QDomElement q = doc.createElementNS("jabber:iq:auth", "query");
1209         QDomElement u = doc.createElement("username");
1210         u.appendChild(doc.createTextNode(jid_.node()));
1211         q.appendChild(u);
1212         e.appendChild(q);
1213 
1214         send(e);
1215         event = ESend;
1216         step = GetAuthGetResponse;
1217         return true;
1218     }
1219     else if(step == HandleAuthSet) {
1220         QDomElement e = doc.createElement("iq");
1221         e.setAttribute("to", to);
1222         e.setAttribute("type", "set");
1223         e.setAttribute("id", "auth_2");
1224         QDomElement q = doc.createElementNS("jabber:iq:auth", "query");
1225         QDomElement u = doc.createElement("username");
1226         u.appendChild(doc.createTextNode(jid_.node()));
1227         q.appendChild(u);
1228         QDomElement p;
1229         if(digest) {
1230             // need SHA1 here
1231             //if(!QCA::isSupported(QCA::CAP_SHA1))
1232             //  QCA::insertProvider(createProviderHash());
1233 
1234             p = doc.createElement("digest");
1235             QByteArray cs = id.toUtf8() + password.toUtf8();
1236             p.appendChild(doc.createTextNode(QCA::Hash("sha1").hashToString(cs)));
1237         }
1238         else {
1239             p = doc.createElement("password");
1240             p.appendChild(doc.createTextNode(password));
1241         }
1242         q.appendChild(p);
1243         QDomElement r = doc.createElement("resource");
1244         r.appendChild(doc.createTextNode(jid_.resource()));
1245         q.appendChild(r);
1246         e.appendChild(q);
1247 
1248         send(e, true);
1249         event = ESend;
1250         step = GetAuthSetResponse;
1251         return true;
1252     }
1253     // server
1254     else if(step == SendFeatures) {
1255         QDomElement f = doc.createElementNS(NS_ETHERX, "stream:features");
1256         if(!tls_started && !sasl_authed) { // don't offer tls if we are already sasl'd
1257             QDomElement tls = doc.createElementNS(NS_TLS, "starttls");
1258             f.appendChild(tls);
1259         }
1260 
1261         if(sasl_authed) {
1262             if(!server) {
1263                 QDomElement bind = doc.createElementNS(NS_BIND, "bind");
1264                 f.appendChild(bind);
1265             }
1266         }
1267         else {
1268             QDomElement mechs = doc.createElementNS(NS_SASL, "mechanisms");
1269             for(QStringList::ConstIterator it = sasl_mechlist.constBegin(); it != sasl_mechlist.constEnd(); ++it) {
1270                 QDomElement m = doc.createElement("mechanism");
1271                 m.appendChild(doc.createTextNode(*it));
1272                 mechs.appendChild(m);
1273             }
1274             f.appendChild(mechs);
1275         }
1276 
1277         writeElement(f, TypeElement, false);
1278         event = ESend;
1279         step = GetRequest;
1280         return true;
1281     }
1282     // server
1283     else if(step == HandleTLS) {
1284         tls_started = true;
1285         need = NStartTLS;
1286         spare = resetStream();
1287         step = Start;
1288         return false;
1289     }
1290     // server
1291     else if(step == IncHandleSASLSuccess) {
1292         event = ESASLSuccess;
1293         spare = resetStream();
1294         step = Start;
1295         printf("sasl success\n");
1296         return true;
1297     }
1298     else if(step == GetFeatures) {
1299         // we are waiting for stream features
1300         if(e.namespaceURI() == NS_ETHERX && e.tagName() == "features") {
1301             // extract features
1302             StreamFeatures f;
1303             QDomElement s = e.elementsByTagNameNS(NS_TLS, "starttls").item(0).toElement();
1304             if(!s.isNull()) {
1305                 f.tls_supported = true;
1306                 f.tls_required = s.elementsByTagNameNS(NS_TLS, "required").count() > 0;
1307             }
1308             QDomElement m = e.elementsByTagNameNS(NS_SASL, "mechanisms").item(0).toElement();
1309             if(!m.isNull()) {
1310                 f.sasl_supported = true;
1311                 QDomNodeList l = m.elementsByTagNameNS(NS_SASL, "mechanism");
1312                 for(int n = 0; n < l.count(); ++n)
1313                     f.sasl_mechs += l.item(n).toElement().text();
1314             }
1315             QDomElement c = e.elementsByTagNameNS(NS_COMPRESS_FEATURE, "compression").item(0).toElement();
1316             if(!c.isNull()) {
1317                 f.compress_supported = true;
1318                 QDomNodeList l = c.elementsByTagNameNS(NS_COMPRESS_FEATURE, "method");
1319                 for(int n = 0; n < l.count(); ++n)
1320                     f.compression_mechs += l.item(n).toElement().text();
1321             }
1322             QDomElement b = e.elementsByTagNameNS(NS_BIND, "bind").item(0).toElement();
1323             if(!b.isNull())
1324                 f.bind_supported = true;
1325             QDomElement h = e.elementsByTagNameNS(NS_HOSTS, "hosts").item(0).toElement();
1326             if(!h.isNull()) {
1327                 QDomNodeList l = h.elementsByTagNameNS(NS_HOSTS, "host");
1328                 for(int n = 0; n < l.count(); ++n)
1329                     f.hosts += l.item(n).toElement().text();
1330                 hosts += f.hosts;
1331             }
1332 
1333             if(f.tls_supported) {
1334 #ifdef XMPP_TEST
1335                 QString s = "STARTTLS is available";
1336                 if(f.tls_required)
1337                     s += " (required)";
1338                 TD::msg(s);
1339 #endif
1340             }
1341             if(f.sasl_supported) {
1342 #ifdef XMPP_TEST
1343                 QString s = "SASL mechs:";
1344                 for(QStringList::ConstIterator it = f.sasl_mechs.begin(); it != f.sasl_mechs.end(); ++it)
1345                     s += QString(" [%1]").arg((*it));
1346                 TD::msg(s);
1347 #endif
1348             }
1349             if(f.compress_supported) {
1350 #ifdef XMPP_TEST
1351                 QString s = "Compression mechs:";
1352                 for(QStringList::ConstIterator it = f.compression_mechs.begin(); it != f.compression_mechs.end(); ++it)
1353                     s += QString(" [%1]").arg((*it));
1354                 TD::msg(s);
1355 #endif
1356             }
1357 
1358             event = EFeatures;
1359             features = f;
1360             step = HandleFeatures;
1361             return true;
1362         }
1363         else {
1364             // ignore
1365         }
1366     }
1367     else if(step == GetTLSProceed) {
1368         // waiting for proceed to starttls
1369         if(e.namespaceURI() == NS_TLS) {
1370             if(e.tagName() == "proceed") {
1371 #ifdef XMPP_TEST
1372                 TD::msg("Server wants us to proceed with ssl handshake");
1373 #endif
1374                 tls_started = true;
1375                 need = NStartTLS;
1376                 spare = resetStream();
1377                 step = Start;
1378                 return false;
1379             }
1380             else if(e.tagName() == "failure") {
1381                 event = EError;
1382                 errorCode = ErrStartTLS;
1383                 return true;
1384             }
1385             else {
1386                 event = EError;
1387                 errorCode = ErrProtocol;
1388                 return true;
1389             }
1390         }
1391         else {
1392             // ignore
1393         }
1394     }
1395     else if(step == GetCompressProceed) {
1396         // waiting for proceed to compression
1397         if(e.namespaceURI() == NS_COMPRESS_PROTOCOL) {
1398             if(e.tagName() == "compressed") {
1399 #ifdef XMPP_TEST
1400                 TD::msg("Server wants us to proceed with compression");
1401 #endif
1402                 compress_started = true;
1403                 need = NCompress;
1404                 spare = resetStream();
1405                 step = Start;
1406                 return false;
1407             }
1408             else if(e.tagName() == "failure") {
1409                 event = EError;
1410                 errorCode = ErrCompress;
1411                 return true;
1412             }
1413             else {
1414                 event = EError;
1415                 errorCode = ErrProtocol;
1416                 return true;
1417             }
1418         }
1419         else {
1420             // ignore
1421         }
1422     }
1423     else if(step == GetSASLChallenge) {
1424         // waiting for sasl challenge/success/fail
1425         if(e.namespaceURI() == NS_SASL) {
1426             if(e.tagName() == "challenge") {
1427                 QByteArray a = QCA::Base64().stringToArray(e.text()).toByteArray();
1428 #ifdef XMPP_TEST
1429                 TD::msg(QString("SASL IN: [%1]").arg(printArray(a)));
1430 #endif
1431                 sasl_step = a;
1432                 need = NSASLNext;
1433                 step = GetSASLNext;
1434                 return false;
1435             }
1436             else if(e.tagName() == "success") {
1437                 QString str = e.text();
1438                 // "additional data with success" ?
1439                 if(!str.isEmpty())
1440                 {
1441                     QByteArray a = QCA::Base64().stringToArray(str).toByteArray();
1442                     sasl_step = a;
1443                     sasl_authed = true;
1444                     need = NSASLNext;
1445                     step = GetSASLNext;
1446                     return false;
1447                 }
1448 
1449                 sasl_authed = true;
1450                 event = ESASLSuccess;
1451                 step = HandleSASLSuccess;
1452                 return true;
1453             }
1454             else if(e.tagName() == "failure") {
1455                 QDomElement t = firstChildElement(e);
1456                 if(t.isNull() || t.namespaceURI() != NS_SASL)
1457                     errCond = -1;
1458                 else
1459                     errCond = stringToSASLCond(t.tagName());
1460 
1461                 event = EError;
1462                 errorCode = ErrAuth;
1463                 return true;
1464             }
1465             else {
1466                 event = EError;
1467                 errorCode = ErrProtocol;
1468                 return true;
1469             }
1470         }
1471     }
1472     else if(step == GetBindResponse) {
1473         if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
1474             QString type(e.attribute("type"));
1475             QString id(e.attribute("id"));
1476 
1477             if(id == "bind_1" && (type == "result" || type == "error")) {
1478                 if(type == "result") {
1479                     QDomElement b = e.elementsByTagNameNS(NS_BIND, "bind").item(0).toElement();
1480                     Jid j;
1481                     if(!b.isNull()) {
1482                         QDomElement je = e.elementsByTagName("jid").item(0).toElement();
1483                         j = je.text();
1484                     }
1485                     if(!j.isValid()) {
1486                         event = EError;
1487                         errorCode = ErrProtocol;
1488                         return true;
1489                     }
1490                     jid_ = j;
1491                     return loginComplete();
1492                 }
1493                 else {
1494                     errCond = -1;
1495 
1496                     QDomElement err = e.elementsByTagNameNS(NS_CLIENT, "error").item(0).toElement();
1497                     if(!err.isNull()) {
1498                         // get error condition
1499                         QDomNodeList nl = err.childNodes();
1500                         QDomElement t;
1501                         for(int n = 0; n < nl.count(); ++n) {
1502                             QDomNode i = nl.item(n);
1503                             if(i.isElement()) {
1504                                 t = i.toElement();
1505                                 break;
1506                             }
1507                         }
1508                         if(!t.isNull() && t.namespaceURI() == NS_STANZAS) {
1509                             QString cond = t.tagName();
1510                             if(cond == "not-allowed")
1511                                 errCond = BindNotAllowed;
1512                             else if(cond == "conflict")
1513                                 errCond = BindConflict;
1514                         }
1515                     }
1516 
1517                     event = EError;
1518                     errorCode = ErrBind;
1519                     return true;
1520                 }
1521             }
1522             else {
1523                 // ignore
1524             }
1525         }
1526         else {
1527             // ignore
1528         }
1529     }
1530     else if(step == GetAuthGetResponse) {
1531         // waiting for an iq
1532         if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
1533             Jid from(e.attribute("from"));
1534             QString type(e.attribute("type"));
1535             QString id(e.attribute("id"));
1536 
1537             bool okfrom = (from.isEmpty() || from.compare(Jid(to)));
1538             if(okfrom && id == "auth_1" && (type == "result" || type == "error")) {
1539                 if(type == "result") {
1540                     QDomElement q = e.elementsByTagNameNS("jabber:iq:auth", "query").item(0).toElement();
1541                     if(q.isNull() || q.elementsByTagName("username").item(0).isNull() || q.elementsByTagName("resource").item(0).isNull()) {
1542                         event = EError;
1543                         errorCode = ErrProtocol;
1544                         return true;
1545                     }
1546                     bool plain_supported = !q.elementsByTagName("password").item(0).isNull();
1547                     bool digest_supported = !q.elementsByTagName("digest").item(0).isNull();
1548 
1549                     if(!digest_supported && !plain_supported) {
1550                         event = EError;
1551                         errorCode = ErrProtocol;
1552                         return true;
1553                     }
1554 
1555                     // plain text not allowed?
1556                     if(!digest_supported && !allowPlain) {
1557                         event = EError;
1558                         errorCode = ErrPlain;
1559                         return true;
1560                     }
1561 
1562                     digest = digest_supported;
1563                     need = NPassword;
1564                     step = HandleAuthSet;
1565                     return false;
1566                 }
1567                 else {
1568                     errCond = getOldErrorCode(e);
1569 
1570                     event = EError;
1571                     errorCode = ErrAuth;
1572                     return true;
1573                 }
1574             }
1575             else {
1576                 // ignore
1577             }
1578         }
1579         else {
1580             // ignore
1581         }
1582     }
1583     else if(step == GetAuthSetResponse) {
1584         // waiting for an iq
1585         if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
1586             Jid from(e.attribute("from"));
1587             QString type(e.attribute("type"));
1588             QString id(e.attribute("id"));
1589 
1590             bool okfrom = (from.isEmpty() || from.compare(Jid(to)));
1591             if(okfrom && id == "auth_2" && (type == "result" || type == "error")) {
1592                 if(type == "result") {
1593                     return loginComplete();
1594                 }
1595                 else {
1596                     errCond = getOldErrorCode(e);
1597 
1598                     event = EError;
1599                     errorCode = ErrAuth;
1600                     return true;
1601                 }
1602             }
1603             else {
1604                 // ignore
1605             }
1606         }
1607         else {
1608             // ignore
1609         }
1610     }
1611     // server
1612     else if(step == GetRequest) {
1613         printf("get request: [%s], %s\n", e.namespaceURI().toLatin1().data(), e.tagName().toLatin1().data());
1614         if(e.namespaceURI() == NS_TLS && e.localName() == "starttls") {
1615             // TODO: don't let this be done twice
1616 
1617             QDomElement e = doc.createElementNS(NS_TLS, "proceed");
1618             writeElement(e, TypeElement, false, true);
1619             event = ESend;
1620             step = HandleTLS;
1621             return true;
1622         }
1623         if(e.namespaceURI() == NS_SASL) {
1624             if(e.localName() == "auth") {
1625                 if(sasl_started) {
1626                     // TODO
1627                     printf("error\n");
1628                     return false;
1629                 }
1630 
1631                 sasl_started = true;
1632                 sasl_mech = e.attribute("mechanism");
1633                 // TODO: if child text missing, don't pass it
1634                 sasl_step = QCA::Base64().stringToArray(e.text()).toByteArray();
1635                 need = NSASLFirst;
1636                 step = GetSASLNext;
1637                 return false;
1638             }
1639             else {
1640                 // TODO
1641                 printf("unknown sasl tag\n");
1642                 return false;
1643             }
1644         }
1645         if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
1646             QDomElement b = e.elementsByTagNameNS(NS_BIND, "bind").item(0).toElement();
1647             if(!b.isNull()) {
1648                 QDomElement res = b.elementsByTagName("resource").item(0).toElement();
1649                 QString resource = res.text();
1650 
1651                 QDomElement r = doc.createElement("iq");
1652                 r.setAttribute("type", "result");
1653                 r.setAttribute("id", e.attribute("id"));
1654                 QDomElement bind = doc.createElementNS(NS_BIND, "bind");
1655                 QDomElement jid = doc.createElement("jid");
1656                 Jid j = QString(user + QLatin1Char('@') + host + QLatin1Char('/') + resource);
1657                 jid.appendChild(doc.createTextNode(j.full()));
1658                 bind.appendChild(jid);
1659                 r.appendChild(bind);
1660 
1661                 writeElement(r, TypeElement, false);
1662                 event = ESend;
1663                 // TODO
1664                 return true;
1665             }
1666             else {
1667                 // TODO
1668             }
1669         }
1670     }
1671     else if(step == GetSASLResponse) {
1672         if(e.namespaceURI() == NS_SASL && e.localName() == "response") {
1673             sasl_step = QCA::Base64().stringToArray(e.text()).toByteArray();
1674             need = NSASLNext;
1675             step = GetSASLNext;
1676             return false;
1677         }
1678     }
1679 
1680     if(isReady()) {
1681         if(!e.isNull() && isValidStanza(e)) {
1682             stanzaToRecv = e;
1683             event = EStanzaReady;
1684             setIncomingAsExternal();
1685             return true;
1686         }
1687     }
1688 
1689     need = NNotify;
1690     notify |= NRecv;
1691     return false;
1692 }