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 }