2
* protocol.cpp - XMPP-Core protocol state machine
3
* Copyright (C) 2004 Justin Karneges
5
* This library is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU Lesser General Public
7
* License as published by the Free Software Foundation; either
8
* version 2.1 of the License, or (at your option) any later version.
10
* This library is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
* Lesser General Public License for more details.
15
* You should have received a copy of the GNU Lesser General Public
16
* License along with this library; if not, write to the Free Software
17
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
// TODO: let the app know if tls is required
22
// require mutual auth for server out/in
23
// report ErrProtocol if server uses wrong NS
24
// use send() instead of writeElement() in CoreProtocol
42
// This function prints out an array of bytes as latin characters, converting
43
// non-printable bytes into hex values as necessary. Useful for displaying
44
// QByteArrays for debugging purposes.
45
static QString printArray(const QByteArray &a)
48
for(int n = 0; n < a.size(); ++n) {
49
unsigned char c = (unsigned char)a[(int)n];
50
if(c < 32 || c >= 127) {
52
str.sprintf("[%02x]", c);
63
// Get an element's first child element
64
static QDomElement firstChildElement(const QDomElement &e)
66
for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
73
//----------------------------------------------------------------------------
75
//----------------------------------------------------------------------------
76
Version::Version(int maj, int min)
82
//----------------------------------------------------------------------------
84
//----------------------------------------------------------------------------
85
StreamFeatures::StreamFeatures()
87
tls_supported = false;
88
sasl_supported = false;
89
bind_supported = false;
91
compress_supported = false;
94
//----------------------------------------------------------------------------
96
//----------------------------------------------------------------------------
97
BasicProtocol::SASLCondEntry BasicProtocol::saslCondTable[] =
99
{ "aborted", Aborted },
100
{ "incorrect-encoding", IncorrectEncoding },
101
{ "invalid-authzid", InvalidAuthzid },
102
{ "invalid-mechanism", InvalidMech },
103
{ "mechanism-too-weak", MechTooWeak },
104
{ "not-authorized", NotAuthorized },
105
{ "temporary-auth-failure", TemporaryAuthFailure },
109
BasicProtocol::StreamCondEntry BasicProtocol::streamCondTable[] =
111
{ "bad-format", BadFormat },
112
{ "bad-namespace-prefix", BadNamespacePrefix },
113
{ "conflict", Conflict },
114
{ "connection-timeout", ConnectionTimeout },
115
{ "host-gone", HostGone },
116
{ "host-unknown", HostUnknown },
117
{ "improper-addressing", ImproperAddressing },
118
{ "internal-server-error", InternalServerError },
119
{ "invalid-from", InvalidFrom },
120
{ "invalid-id", InvalidId },
121
{ "invalid-namespace", InvalidNamespace },
122
{ "invalid-xml", InvalidXml },
123
{ "not-authorized", StreamNotAuthorized },
124
{ "policy-violation", PolicyViolation },
125
{ "remote-connection-failed", RemoteConnectionFailed },
126
{ "resource-constraint", ResourceConstraint },
127
{ "restricted-xml", RestrictedXml },
128
{ "see-other-host", SeeOtherHost },
129
{ "system-shutdown", SystemShutdown },
130
{ "undefined-condition", UndefinedCondition },
131
{ "unsupported-encoding", UnsupportedEncoding },
132
{ "unsupported-stanza-type", UnsupportedStanzaType },
133
{ "unsupported-version", UnsupportedVersion },
134
{ "xml-not-well-formed", XmlNotWellFormed },
138
BasicProtocol::BasicProtocol()
144
BasicProtocol::~BasicProtocol()
148
void BasicProtocol::init()
153
delayedError = false;
160
void BasicProtocol::reset()
162
XmlProtocol::reset();
169
version = Version(1,0);
171
errAppSpec = QDomElement();
172
otherHost = QString();
174
sasl_mech = QString();
175
sasl_mechlist.clear();
177
stanzaToRecv = QDomElement();
181
void BasicProtocol::sendStanza(const QDomElement &e)
188
void BasicProtocol::sendDirect(const QString &s)
195
void BasicProtocol::sendWhitespace()
198
i.doWhitespace = true;
202
QDomElement BasicProtocol::recvStanza()
204
QDomElement e = stanzaToRecv;
205
stanzaToRecv = QDomElement();
209
void BasicProtocol::shutdown()
214
void BasicProtocol::shutdownWithError(int cond, const QString &str)
217
delayErrorAndClose(cond);
220
bool BasicProtocol::isReady() const
225
void BasicProtocol::setReady(bool b)
230
QString BasicProtocol::saslMech() const
235
QByteArray BasicProtocol::saslStep() const
240
void BasicProtocol::setSASLMechList(const QStringList &list)
242
sasl_mechlist = list;
245
void BasicProtocol::setSASLFirst(const QString &mech, const QByteArray &step)
251
void BasicProtocol::setSASLNext(const QByteArray &step)
256
void BasicProtocol::setSASLAuthed()
261
int BasicProtocol::stringToSASLCond(const QString &s)
263
for(int n = 0; saslCondTable[n].str; ++n) {
264
if(s == saslCondTable[n].str)
265
return saslCondTable[n].cond;
270
int BasicProtocol::stringToStreamCond(const QString &s)
272
for(int n = 0; streamCondTable[n].str; ++n) {
273
if(s == streamCondTable[n].str)
274
return streamCondTable[n].cond;
279
QString BasicProtocol::saslCondToString(int x)
281
for(int n = 0; saslCondTable[n].str; ++n) {
282
if(x == saslCondTable[n].cond)
283
return saslCondTable[n].str;
288
QString BasicProtocol::streamCondToString(int x)
290
for(int n = 0; streamCondTable[n].str; ++n) {
291
if(x == streamCondTable[n].cond)
292
return streamCondTable[n].str;
297
void BasicProtocol::extractStreamError(const QDomElement &e)
302
QDomElement t = firstChildElement(e);
303
if(t.isNull() || t.namespaceURI() != NS_STREAMS) {
304
// probably old-style error
309
errCond = stringToStreamCond(t.tagName());
312
if(errCond == SeeOtherHost)
313
otherHost = t.text();
315
t = e.elementsByTagNameNS(NS_STREAMS, "text").item(0).toElement();
319
// find first non-standard namespaced element
320
QDomNodeList nl = e.childNodes();
321
for(int n = 0; n < nl.count(); ++n) {
322
QDomNode i = nl.item(n);
323
if(i.isElement() && i.namespaceURI() != NS_STREAMS) {
324
appSpec = i.toElement();
330
errAppSpec = appSpec;
334
void BasicProtocol::send(const QDomElement &e, bool clip)
336
writeElement(e, TypeElement, false, clip);
339
void BasicProtocol::sendStreamError(int cond, const QString &text, const QDomElement &appSpec)
341
QDomElement se = doc.createElementNS(NS_ETHERX, "stream:error");
342
QDomElement err = doc.createElementNS(NS_STREAMS, streamCondToString(cond));
343
if(!otherHost.isEmpty())
344
err.appendChild(doc.createTextNode(otherHost));
346
if(!text.isEmpty()) {
347
QDomElement te = doc.createElementNS(NS_STREAMS, "text");
348
te.setAttributeNS(NS_XML, "xml:lang", "en");
349
te.appendChild(doc.createTextNode(text));
352
se.appendChild(appSpec);
354
writeElement(se, 100, false);
357
void BasicProtocol::sendStreamError(const QString &text)
359
QDomElement se = doc.createElementNS(NS_ETHERX, "stream:error");
360
se.appendChild(doc.createTextNode(text));
362
writeElement(se, 100, false);
365
bool BasicProtocol::errorAndClose(int cond, const QString &text, const QDomElement &appSpec)
370
errAppSpec = appSpec;
371
sendStreamError(cond, text, appSpec);
375
bool BasicProtocol::error(int code)
382
void BasicProtocol::delayErrorAndClose(int cond, const QString &text, const QDomElement &appSpec)
384
errorCode = ErrStream;
387
errAppSpec = appSpec;
391
void BasicProtocol::delayError(int code)
397
QDomElement BasicProtocol::docElement()
399
// create the root element
400
QDomElement e = doc.createElementNS(NS_ETHERX, "stream:stream");
402
QString defns = defaultNamespace();
403
QStringList list = extraNamespaces();
405
// HACK: using attributes seems to be the only way to get additional namespaces in here
407
e.setAttribute("xmlns", defns);
408
for(QStringList::ConstIterator it = list.begin(); it != list.end();) {
409
QString prefix = *(it++);
410
QString uri = *(it++);
411
e.setAttribute(QString("xmlns:") + prefix, uri);
414
// additional attributes
415
if(!isIncoming() && !to.isEmpty())
416
e.setAttribute("to", to);
417
if(isIncoming() && !from.isEmpty())
418
e.setAttribute("from", from);
420
e.setAttribute("id", id);
422
e.setAttributeNS(NS_XML, "xml:lang", lang);
423
if(version.major > 0 || version.minor > 0)
424
e.setAttribute("version", QString::number(version.major) + '.' + QString::number(version.minor));
429
void BasicProtocol::handleDocOpen(const Parser::Event &pe)
432
if(xmlEncoding() != "UTF-8") {
433
delayErrorAndClose(UnsupportedEncoding);
438
if(pe.namespaceURI() == NS_ETHERX && pe.localName() == "stream") {
439
QXmlAttributes atts = pe.atts();
444
QString verstr = atts.value("version");
445
if(!verstr.isEmpty()) {
446
int n = verstr.find('.');
448
major = verstr.mid(0, n).toInt();
449
minor = verstr.mid(n+1).toInt();
452
major = verstr.toInt();
456
version = Version(major, minor);
459
to = atts.value("to");
460
QString peerLang = atts.value(NS_XML, "lang");
461
if(!peerLang.isEmpty())
466
from = atts.value("from");
467
lang = atts.value(NS_XML, "lang");
468
id = atts.value("id");
471
handleStreamOpen(pe);
475
delayErrorAndClose(BadFormat);
477
delayError(ErrProtocol);
481
bool BasicProtocol::handleError()
484
return errorAndClose(XmlNotWellFormed);
486
return error(ErrParse);
489
bool BasicProtocol::handleCloseFinished()
493
errorCode = ErrStream;
494
// note: errCond and friends are already set at this point
501
bool BasicProtocol::doStep(const QDomElement &e)
503
// handle pending error
506
return errorAndClose(errCond, errText, errAppSpec);
508
return error(errorCode);
519
if(e.namespaceURI() == NS_ETHERX && e.tagName() == "error") {
520
extractStreamError(e);
521
return error(ErrStream);
527
if(stanzasWritten > 0) {
533
if(!sendList.isEmpty()) {
536
QList<SendItem>::Iterator it = sendList.begin();
542
if(!i.stanzaToSend.isNull()) {
544
writeElement(i.stanzaToSend, TypeStanza, true);
548
else if(!i.stringToSend.isEmpty()) {
549
writeString(i.stringToSend, TypeDirect, true);
552
// whitespace keepalive?
553
else if(i.doWhitespace) {
554
writeString("\n", TypePing, false);
560
// if we have pending outgoing stanzas, ask for write notification
569
void BasicProtocol::itemWritten(int id, int)
571
if(id == TypeStanza) {
577
QString BasicProtocol::defaultNamespace()
583
QStringList BasicProtocol::extraNamespaces()
586
return QStringList();
589
void BasicProtocol::handleStreamOpen(const Parser::Event &)
591
// default does nothing
594
//----------------------------------------------------------------------------
596
//----------------------------------------------------------------------------
597
CoreProtocol::CoreProtocol()
603
CoreProtocol::~CoreProtocol()
607
void CoreProtocol::init()
614
dialback_verify = false;
618
password = QString();
634
sasl_started = false;
635
compress_started = false;
638
void CoreProtocol::reset()
640
BasicProtocol::reset();
644
void CoreProtocol::startClientOut(const Jid &_jid, bool _oldOnly, bool tlsActive, bool _doAuth, bool _doCompress)
650
doCompress = _doCompress;
651
tls_started = tlsActive;
654
version = Version(0,0);
658
void CoreProtocol::startServerOut(const QString &_to)
665
void CoreProtocol::startDialbackOut(const QString &_to, const QString &_from)
674
void CoreProtocol::startDialbackVerifyOut(const QString &_to, const QString &_from, const QString &id, const QString &key)
678
dialback_verify = true;
686
void CoreProtocol::startClientIn(const QString &_id)
692
void CoreProtocol::startServerIn(const QString &_id)
699
void CoreProtocol::setLang(const QString &s)
704
void CoreProtocol::setAllowTLS(bool b)
709
void CoreProtocol::setAllowBind(bool b)
714
void CoreProtocol::setAllowPlain(bool b)
719
const Jid& CoreProtocol::jid() const
724
void CoreProtocol::setPassword(const QString &s)
729
void CoreProtocol::setFrom(const QString &s)
734
void CoreProtocol::setDialbackKey(const QString &s)
739
bool CoreProtocol::loginComplete()
748
int CoreProtocol::getOldErrorCode(const QDomElement &e)
750
QDomElement err = e.elementsByTagNameNS(NS_CLIENT, "error").item(0).toElement();
751
if(err.isNull() || !err.hasAttribute("code"))
753
return err.attribute("code").toInt();
756
/*QString CoreProtocol::xmlToString(const QDomElement &e, bool clip)
758
// determine an appropriate 'fakeNS' to use
760
if(e.prefix() == "stream")
762
else if(e.prefix() == "db")
766
return ::xmlToString(e, ns, "stream:stream", clip);
769
bool CoreProtocol::stepAdvancesParser() const
771
if(stepRequiresElement())
778
// all element-needing steps need to be registered here
779
bool CoreProtocol::stepRequiresElement() const
784
case GetCompressProceed:
785
case GetSASLChallenge:
786
case GetBindResponse:
787
case GetAuthGetResponse:
788
case GetAuthSetResponse:
790
case GetSASLResponse:
796
void CoreProtocol::stringSend(const QString &s)
803
void CoreProtocol::stringRecv(const QString &s)
810
QString CoreProtocol::defaultNamespace()
818
QStringList CoreProtocol::extraNamespaces()
828
void CoreProtocol::handleStreamOpen(const Parser::Event &pe)
831
QString ns = pe.nsprefix();
834
db = pe.nsprefix("db");
840
if((!server && ns != NS_CLIENT) || (server && ns != NS_SERVER) || (dialback && db != NS_DIALBACK)) {
841
delayErrorAndClose(InvalidNamespace);
846
if(version.major < 1 && !dialback) {
847
delayErrorAndClose(UnsupportedVersion);
853
if(version.major >= 1 && !oldOnly)
861
void CoreProtocol::elementSend(const QDomElement &e)
868
void CoreProtocol::elementRecv(const QDomElement &e)
875
bool CoreProtocol::doStep2(const QDomElement &e)
878
return dialbackStep(e);
880
return normalStep(e);
883
bool CoreProtocol::isValidStanza(const QDomElement &e) const
885
QString s = e.tagName();
886
if(e.namespaceURI() == (server ? NS_SERVER : NS_CLIENT) && (s == "message" || s == "presence" || s == "iq"))
892
bool CoreProtocol::grabPendingItem(const Jid &to, const Jid &from, int type, DBItem *item)
894
for(QList<DBItem>::Iterator it = dbpending.begin(); it != dbpending.end(); ++it) {
895
const DBItem &i = *it;
896
if(i.type == type && i.to.compare(to) && i.from.compare(from)) {
897
const DBItem &i = (*it);
899
dbpending.remove(it);
906
bool CoreProtocol::dialbackStep(const QDomElement &e)
915
if(!dbrequests.isEmpty()) {
919
QList<DBItem>::Iterator it = dbrequests.begin();
921
dbrequests.remove(it);
925
if(i.type == DBItem::ResultRequest) {
926
r = doc.createElementNS(NS_DIALBACK, "db:result");
927
r.setAttribute("to", i.to.full());
928
r.setAttribute("from", i.from.full());
929
r.appendChild(doc.createTextNode(i.key));
932
else if(i.type == DBItem::ResultGrant) {
933
r = doc.createElementNS(NS_DIALBACK, "db:result");
934
r.setAttribute("to", i.to.full());
935
r.setAttribute("from", i.from.full());
936
r.setAttribute("type", i.ok ? "valid" : "invalid");
938
i.type = DBItem::Validated;
942
// TODO: disconnect after writing element
945
else if(i.type == DBItem::VerifyRequest) {
946
r = doc.createElementNS(NS_DIALBACK, "db:verify");
947
r.setAttribute("to", i.to.full());
948
r.setAttribute("from", i.from.full());
949
r.setAttribute("id", i.id);
950
r.appendChild(doc.createTextNode(i.key));
955
r = doc.createElementNS(NS_DIALBACK, "db:verify");
956
r.setAttribute("to", i.to.full());
957
r.setAttribute("from", i.from.full());
958
r.setAttribute("id", i.id);
959
r.setAttribute("type", i.ok ? "valid" : "invalid");
962
writeElement(r, TypeElement, false);
968
if(e.namespaceURI() == NS_DIALBACK) {
969
if(e.tagName() == "result") {
971
to.set(e.attribute("to"), "");
972
from.set(e.attribute("from"), "");
974
QString key = e.text();
975
// TODO: report event
978
bool ok = (e.attribute("type") == "valid") ? true: false;
980
if(grabPendingItem(from, to, DBItem::ResultRequest, &i)) {
982
i.type = DBItem::Validated;
985
// TODO: report event
988
// TODO: report event
993
else if(e.tagName() == "verify") {
995
to.set(e.attribute("to"), "");
996
from.set(e.attribute("from"), "");
997
QString id = e.attribute("id");
999
QString key = e.text();
1000
// TODO: report event
1003
bool ok = (e.attribute("type") == "valid") ? true: false;
1005
if(grabPendingItem(from, to, DBItem::VerifyRequest, &i)) {
1007
// TODO: report event
1010
// TODO: report event
1018
if(isValidStanza(e)) {
1019
// TODO: disconnect if stanza is from unverified sender
1020
// TODO: ignore packets from receiving servers
1022
event = EStanzaReady;
1034
bool CoreProtocol::normalStep(const QDomElement &e)
1039
step = SendFeatures;
1045
step = HandleAuthGet;
1047
return loginComplete();
1052
return processStep();
1055
else if(step == HandleFeatures) {
1057
if(doTLS && !tls_started && !sasl_authed && features.tls_supported) {
1058
QDomElement e = doc.createElementNS(NS_TLS, "starttls");
1062
step = GetTLSProceed;
1066
// Should we go further ?
1068
return loginComplete();
1070
// Deal with compression
1071
if (doCompress && !compress_started && features.compress_supported && features.compression_mechs.contains("zlib")) {
1072
QDomElement e = doc.createElementNS(NS_COMPRESS_PROTOCOL, "compress");
1073
QDomElement m = doc.createElementNS(NS_COMPRESS_PROTOCOL, "method");
1074
m.appendChild(doc.createTextNode("zlib"));
1078
step = GetCompressProceed;
1084
if(!features.sasl_supported) {
1085
// SASL MUST be supported
1087
//errorCode = ErrProtocol;
1090
// Fall back on auth for non-compliant servers
1091
step = HandleAuthGet;
1097
TD::msg("starting SASL authentication...");
1100
step = GetSASLFirst;
1105
return loginComplete();
1109
return loginComplete();
1113
if(!features.bind_supported) {
1114
// bind MUST be supported
1116
errorCode = ErrProtocol;
1120
QDomElement e = doc.createElement("iq");
1121
e.setAttribute("type", "set");
1122
e.setAttribute("id", "bind_1");
1123
QDomElement b = doc.createElementNS(NS_BIND, "bind");
1125
// request specific resource?
1126
QString resource = jid_.resource();
1127
if(!resource.isEmpty()) {
1128
QDomElement r = doc.createElement("resource");
1129
r.appendChild(doc.createTextNode(jid_.resource()));
1137
step = GetBindResponse;
1140
else if(step == GetSASLFirst) {
1141
QDomElement e = doc.createElementNS(NS_SASL, "auth");
1142
e.setAttribute("mechanism", sasl_mech);
1143
if(!sasl_step.isEmpty()) {
1145
TD::msg(QString("SASL OUT: [%1]").arg(printArray(sasl_step)));
1147
e.appendChild(doc.createTextNode(QCA::Base64().arrayToString(sasl_step)));
1152
step = GetSASLChallenge;
1155
else if(step == GetSASLNext) {
1158
QDomElement e = doc.createElementNS(NS_SASL, "success");
1159
writeElement(e, TypeElement, false, true);
1161
step = IncHandleSASLSuccess;
1165
QByteArray stepData = sasl_step;
1166
QDomElement e = doc.createElementNS(NS_SASL, "challenge");
1167
if(!stepData.isEmpty())
1168
e.appendChild(doc.createTextNode(QCA::Base64().arrayToString(stepData)));
1170
writeElement(e, TypeElement, false, true);
1172
step = GetSASLResponse;
1177
// already authed? then ignore last client step
1178
// (this happens if "additional data with success"
1182
event = ESASLSuccess;
1183
step = HandleSASLSuccess;
1187
QByteArray stepData = sasl_step;
1189
TD::msg(QString("SASL OUT: [%1]").arg(printArray(sasl_step)));
1191
QDomElement e = doc.createElementNS(NS_SASL, "response");
1192
if(!stepData.isEmpty())
1193
e.appendChild(doc.createTextNode(QCA::Base64().arrayToString(stepData)));
1197
step = GetSASLChallenge;
1201
else if(step == HandleSASLSuccess) {
1203
spare = resetStream();
1207
else if(step == HandleAuthGet) {
1208
QDomElement e = doc.createElement("iq");
1209
e.setAttribute("to", to);
1210
e.setAttribute("type", "get");
1211
e.setAttribute("id", "auth_1");
1212
QDomElement q = doc.createElementNS("jabber:iq:auth", "query");
1213
QDomElement u = doc.createElement("username");
1214
u.appendChild(doc.createTextNode(jid_.node()));
1220
step = GetAuthGetResponse;
1223
else if(step == HandleAuthSet) {
1224
QDomElement e = doc.createElement("iq");
1225
e.setAttribute("to", to);
1226
e.setAttribute("type", "set");
1227
e.setAttribute("id", "auth_2");
1228
QDomElement q = doc.createElementNS("jabber:iq:auth", "query");
1229
QDomElement u = doc.createElement("username");
1230
u.appendChild(doc.createTextNode(jid_.node()));
1235
//if(!QCA::isSupported(QCA::CAP_SHA1))
1236
// QCA::insertProvider(createProviderHash());
1238
p = doc.createElement("digest");
1239
Q3CString cs = id.utf8() + password.utf8();
1240
p.appendChild(doc.createTextNode(QCA::Hash("sha1").hashToString(cs)));
1243
p = doc.createElement("password");
1244
p.appendChild(doc.createTextNode(password));
1247
QDomElement r = doc.createElement("resource");
1248
r.appendChild(doc.createTextNode(jid_.resource()));
1254
step = GetAuthSetResponse;
1258
else if(step == SendFeatures) {
1259
QDomElement f = doc.createElementNS(NS_ETHERX, "stream:features");
1260
if(!tls_started && !sasl_authed) { // don't offer tls if we are already sasl'd
1261
QDomElement tls = doc.createElementNS(NS_TLS, "starttls");
1267
QDomElement bind = doc.createElementNS(NS_BIND, "bind");
1268
f.appendChild(bind);
1272
QDomElement mechs = doc.createElementNS(NS_SASL, "mechanisms");
1273
for(QStringList::ConstIterator it = sasl_mechlist.begin(); it != sasl_mechlist.end(); ++it) {
1274
QDomElement m = doc.createElement("mechanism");
1275
m.appendChild(doc.createTextNode(*it));
1276
mechs.appendChild(m);
1278
f.appendChild(mechs);
1281
writeElement(f, TypeElement, false);
1287
else if(step == HandleTLS) {
1290
spare = resetStream();
1295
else if(step == IncHandleSASLSuccess) {
1296
event = ESASLSuccess;
1297
spare = resetStream();
1299
printf("sasl success\n");
1302
else if(step == GetFeatures) {
1303
// we are waiting for stream features
1304
if(e.namespaceURI() == NS_ETHERX && e.tagName() == "features") {
1307
QDomElement s = e.elementsByTagNameNS(NS_TLS, "starttls").item(0).toElement();
1309
f.tls_supported = true;
1310
f.tls_required = s.elementsByTagNameNS(NS_TLS, "required").count() > 0;
1312
QDomElement m = e.elementsByTagNameNS(NS_SASL, "mechanisms").item(0).toElement();
1314
f.sasl_supported = true;
1315
QDomNodeList l = m.elementsByTagNameNS(NS_SASL, "mechanism");
1316
for(int n = 0; n < l.count(); ++n)
1317
f.sasl_mechs += l.item(n).toElement().text();
1319
QDomElement c = e.elementsByTagNameNS(NS_COMPRESS_FEATURE, "compression").item(0).toElement();
1321
f.compress_supported = true;
1322
QDomNodeList l = c.elementsByTagNameNS(NS_COMPRESS_FEATURE, "method");
1323
for(int n = 0; n < l.count(); ++n)
1324
f.compression_mechs += l.item(n).toElement().text();
1326
QDomElement b = e.elementsByTagNameNS(NS_BIND, "bind").item(0).toElement();
1328
f.bind_supported = true;
1330
if(f.tls_supported) {
1332
QString s = "STARTTLS is available";
1338
if(f.sasl_supported) {
1340
QString s = "SASL mechs:";
1341
for(QStringList::ConstIterator it = f.sasl_mechs.begin(); it != f.sasl_mechs.end(); ++it)
1342
s += QString(" [%1]").arg((*it));
1346
if(f.compress_supported) {
1348
QString s = "Compression mechs:";
1349
for(QStringList::ConstIterator it = f.compression_mechs.begin(); it != f.compression_mechs.end(); ++it)
1350
s += QString(" [%1]").arg((*it));
1357
step = HandleFeatures;
1364
else if(step == GetTLSProceed) {
1365
// waiting for proceed to starttls
1366
if(e.namespaceURI() == NS_TLS) {
1367
if(e.tagName() == "proceed") {
1369
TD::msg("Server wants us to proceed with ssl handshake");
1373
spare = resetStream();
1377
else if(e.tagName() == "failure") {
1379
errorCode = ErrStartTLS;
1384
errorCode = ErrProtocol;
1392
else if(step == GetCompressProceed) {
1393
// waiting for proceed to compression
1394
if(e.namespaceURI() == NS_COMPRESS_PROTOCOL) {
1395
if(e.tagName() == "compressed") {
1397
TD::msg("Server wants us to proceed with compression");
1399
compress_started = true;
1401
spare = resetStream();
1405
else if(e.tagName() == "failure") {
1407
errorCode = ErrCompress;
1412
errorCode = ErrProtocol;
1420
else if(step == GetSASLChallenge) {
1421
// waiting for sasl challenge/success/fail
1422
if(e.namespaceURI() == NS_SASL) {
1423
if(e.tagName() == "challenge") {
1424
QByteArray a = QCA::Base64().stringToArray(e.text()).toByteArray();
1426
TD::msg(QString("SASL IN: [%1]").arg(printArray(a)));
1433
else if(e.tagName() == "success") {
1434
QString str = e.text();
1435
// "additional data with success" ?
1438
QByteArray a = QCA::Base64().stringToArray(str).toByteArray();
1447
event = ESASLSuccess;
1448
step = HandleSASLSuccess;
1451
else if(e.tagName() == "failure") {
1452
QDomElement t = firstChildElement(e);
1453
if(t.isNull() || t.namespaceURI() != NS_SASL)
1456
errCond = stringToSASLCond(t.tagName());
1459
errorCode = ErrAuth;
1464
errorCode = ErrProtocol;
1469
else if(step == GetBindResponse) {
1470
if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
1471
QString type(e.attribute("type"));
1472
QString id(e.attribute("id"));
1474
if(id == "bind_1" && (type == "result" || type == "error")) {
1475
if(type == "result") {
1476
QDomElement b = e.elementsByTagNameNS(NS_BIND, "bind").item(0).toElement();
1479
QDomElement je = e.elementsByTagName("jid").item(0).toElement();
1484
errorCode = ErrProtocol;
1488
return loginComplete();
1493
QDomElement err = e.elementsByTagNameNS(NS_CLIENT, "error").item(0).toElement();
1495
// get error condition
1496
QDomNodeList nl = err.childNodes();
1498
for(int n = 0; n < nl.count(); ++n) {
1499
QDomNode i = nl.item(n);
1505
if(!t.isNull() && t.namespaceURI() == NS_STANZAS) {
1506
QString cond = t.tagName();
1507
if(cond == "not-allowed")
1508
errCond = BindNotAllowed;
1509
else if(cond == "conflict")
1510
errCond = BindConflict;
1515
errorCode = ErrBind;
1527
else if(step == GetAuthGetResponse) {
1528
// waiting for an iq
1529
if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
1530
Jid from(e.attribute("from"));
1531
QString type(e.attribute("type"));
1532
QString id(e.attribute("id"));
1534
bool okfrom = (from.isEmpty() || from.compare(Jid(to)));
1535
if(okfrom && id == "auth_1" && (type == "result" || type == "error")) {
1536
if(type == "result") {
1537
QDomElement q = e.elementsByTagNameNS("jabber:iq:auth", "query").item(0).toElement();
1538
if(q.isNull() || q.elementsByTagName("username").item(0).isNull() || q.elementsByTagName("resource").item(0).isNull()) {
1540
errorCode = ErrProtocol;
1543
bool plain_supported = !q.elementsByTagName("password").item(0).isNull();
1544
bool digest_supported = !q.elementsByTagName("digest").item(0).isNull();
1546
if(!digest_supported && !plain_supported) {
1548
errorCode = ErrProtocol;
1552
// plain text not allowed?
1553
if(!digest_supported && !allowPlain) {
1555
errorCode = ErrPlain;
1559
digest = digest_supported;
1561
step = HandleAuthSet;
1565
errCond = getOldErrorCode(e);
1568
errorCode = ErrAuth;
1580
else if(step == GetAuthSetResponse) {
1581
// waiting for an iq
1582
if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
1583
Jid from(e.attribute("from"));
1584
QString type(e.attribute("type"));
1585
QString id(e.attribute("id"));
1587
bool okfrom = (from.isEmpty() || from.compare(Jid(to)));
1588
if(okfrom && id == "auth_2" && (type == "result" || type == "error")) {
1589
if(type == "result") {
1590
return loginComplete();
1593
errCond = getOldErrorCode(e);
1596
errorCode = ErrAuth;
1609
else if(step == GetRequest) {
1610
printf("get request: [%s], %s\n", e.namespaceURI().latin1(), e.tagName().latin1());
1611
if(e.namespaceURI() == NS_TLS && e.localName() == "starttls") {
1612
// TODO: don't let this be done twice
1614
QDomElement e = doc.createElementNS(NS_TLS, "proceed");
1615
writeElement(e, TypeElement, false, true);
1620
if(e.namespaceURI() == NS_SASL) {
1621
if(e.localName() == "auth") {
1628
sasl_started = true;
1629
sasl_mech = e.attribute("mechanism");
1630
// TODO: if child text missing, don't pass it
1631
sasl_step = QCA::Base64().stringToArray(e.text()).toByteArray();
1638
printf("unknown sasl tag\n");
1642
if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
1643
QDomElement b = e.elementsByTagNameNS(NS_BIND, "bind").item(0).toElement();
1645
QDomElement res = b.elementsByTagName("resource").item(0).toElement();
1646
QString resource = res.text();
1648
QDomElement r = doc.createElement("iq");
1649
r.setAttribute("type", "result");
1650
r.setAttribute("id", e.attribute("id"));
1651
QDomElement bind = doc.createElementNS(NS_BIND, "bind");
1652
QDomElement jid = doc.createElement("jid");
1653
Jid j = user + '@' + host + '/' + resource;
1654
jid.appendChild(doc.createTextNode(j.full()));
1655
bind.appendChild(jid);
1656
r.appendChild(bind);
1658
writeElement(r, TypeElement, false);
1668
else if(step == GetSASLResponse) {
1669
if(e.namespaceURI() == NS_SASL && e.localName() == "response") {
1670
sasl_step = QCA::Base64().stringToArray(e.text()).toByteArray();
1678
if(!e.isNull() && isValidStanza(e)) {
1680
event = EStanzaReady;
1681
setIncomingAsExternal();