~ubuntu-branches/ubuntu/quantal/psi/quantal

« back to all changes in this revision

Viewing changes to iris/src/xmpp/xmpp-core/protocol.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jan Niehusmann
  • Date: 2009-09-25 17:49:51 UTC
  • mfrom: (6.1.3 sid)
  • Revision ID: james.westby@ubuntu.com-20090925174951-lvm7kdap82o8xhn3
Tags: 0.13-1
* Updated to upstream version 0.13
* Set Standards-Version to 3.8.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * protocol.cpp - XMPP-Core protocol state machine
 
3
 * Copyright (C) 2004  Justin Karneges
 
4
 *
 
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.
 
9
 *
 
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.
 
14
 *
 
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
 
18
 *
 
19
 */
 
20
 
 
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
 
25
 
 
26
#include "protocol.h"
 
27
 
 
28
#include <qca.h>
 
29
#include <QList>
 
30
#include <QByteArray>
 
31
#include <QtCrypto>
 
32
 
 
33
#ifdef XMPP_TEST
 
34
#include "td.h"
 
35
#endif
 
36
 
 
37
using namespace XMPP;
 
38
 
 
39
// printArray
 
40
//
 
41
// This function prints out an array of bytes as latin characters, converting
 
42
// non-printable bytes into hex values as necessary.  Useful for displaying
 
43
// QByteArrays for debugging purposes.
 
44
static QString printArray(const QByteArray &a)
 
45
{
 
46
        QString s;
 
47
        for(int n = 0; n < a.size(); ++n) {
 
48
                unsigned char c = (unsigned char)a[(int)n];
 
49
                if(c < 32 || c >= 127) {
 
50
                        QString str;
 
51
                        str.sprintf("[%02x]", c);
 
52
                        s += str;
 
53
                }
 
54
                else
 
55
                        s += c;
 
56
        }
 
57
        return s;
 
58
}
 
59
 
 
60
// firstChildElement
 
61
//
 
62
// Get an element's first child element
 
63
static QDomElement firstChildElement(const QDomElement &e)
 
64
{
 
65
        for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
 
66
                if(n.isElement())
 
67
                        return n.toElement();
 
68
        }
 
69
        return QDomElement();
 
70
}
 
71
 
 
72
//----------------------------------------------------------------------------
 
73
// Version
 
74
//----------------------------------------------------------------------------
 
75
Version::Version(int maj, int min)
 
76
{
 
77
        major = maj;
 
78
        minor = min;
 
79
}
 
80
 
 
81
//----------------------------------------------------------------------------
 
82
// StreamFeatures
 
83
//----------------------------------------------------------------------------
 
84
StreamFeatures::StreamFeatures()
 
85
{
 
86
        tls_supported = false;
 
87
        sasl_supported = false;
 
88
        bind_supported = false;
 
89
        tls_required = false;
 
90
        compress_supported = false;
 
91
}
 
92
 
 
93
//----------------------------------------------------------------------------
 
94
// BasicProtocol
 
95
//----------------------------------------------------------------------------
 
96
BasicProtocol::SASLCondEntry BasicProtocol::saslCondTable[] =
 
97
{
 
98
        { "aborted",                Aborted },
 
99
        { "incorrect-encoding",     IncorrectEncoding },
 
100
        { "invalid-authzid",        InvalidAuthzid },
 
101
        { "invalid-mechanism",      InvalidMech },
 
102
        { "mechanism-too-weak",     MechTooWeak },
 
103
        { "not-authorized",         NotAuthorized },
 
104
        { "temporary-auth-failure", TemporaryAuthFailure },
 
105
        { 0, 0 },
 
106
};
 
107
 
 
108
BasicProtocol::StreamCondEntry BasicProtocol::streamCondTable[] =
 
109
{
 
110
        { "bad-format",               BadFormat },
 
111
        { "bad-namespace-prefix",     BadNamespacePrefix },
 
112
        { "conflict",                 Conflict },
 
113
        { "connection-timeout",       ConnectionTimeout },
 
114
        { "host-gone",                HostGone },
 
115
        { "host-unknown",             HostUnknown },
 
116
        { "improper-addressing",      ImproperAddressing },
 
117
        { "internal-server-error",    InternalServerError },
 
118
        { "invalid-from",             InvalidFrom },
 
119
        { "invalid-id",               InvalidId },
 
120
        { "invalid-namespace",        InvalidNamespace },
 
121
        { "invalid-xml",              InvalidXml },
 
122
        { "not-authorized",           StreamNotAuthorized },
 
123
        { "policy-violation",         PolicyViolation },
 
124
        { "remote-connection-failed", RemoteConnectionFailed },
 
125
        { "resource-constraint",      ResourceConstraint },
 
126
        { "restricted-xml",           RestrictedXml },
 
127
        { "see-other-host",           SeeOtherHost },
 
128
        { "system-shutdown",          SystemShutdown },
 
129
        { "undefined-condition",      UndefinedCondition },
 
130
        { "unsupported-encoding",     UnsupportedEncoding },
 
131
        { "unsupported-stanza-type",  UnsupportedStanzaType },
 
132
        { "unsupported-version",      UnsupportedVersion },
 
133
        { "xml-not-well-formed",      XmlNotWellFormed },
 
134
        { 0, 0 },
 
135
};
 
136
 
 
137
BasicProtocol::BasicProtocol()
 
138
:XmlProtocol()
 
139
{
 
140
        init();
 
141
}
 
142
 
 
143
BasicProtocol::~BasicProtocol()
 
144
{
 
145
}
 
146
 
 
147
void BasicProtocol::init()
 
148
{
 
149
        errCond = -1;
 
150
        sasl_authed = false;
 
151
        doShutdown = false;
 
152
        delayedError = false;
 
153
        closeError = false;
 
154
        ready = false;
 
155
        stanzasPending = 0;
 
156
        stanzasWritten = 0;
 
157
}
 
158
 
 
159
void BasicProtocol::reset()
 
160
{
 
161
        XmlProtocol::reset();
 
162
        init();
 
163
 
 
164
        to = QString();
 
165
        from = QString();
 
166
        id = QString();
 
167
        lang = QString();
 
168
        version = Version(1,0);
 
169
        errText = QString();
 
170
        errAppSpec = QDomElement();
 
171
        otherHost = QString();
 
172
        spare.resize(0);
 
173
        sasl_mech = QString();
 
174
        sasl_mechlist.clear();
 
175
        sasl_step.resize(0);
 
176
        stanzaToRecv = QDomElement();
 
177
        sendList.clear();
 
178
}
 
179
 
 
180
void BasicProtocol::sendStanza(const QDomElement &e)
 
181
{
 
182
        SendItem i;
 
183
        i.stanzaToSend = e;
 
184
        sendList += i;
 
185
}
 
186
 
 
187
void BasicProtocol::sendDirect(const QString &s)
 
188
{
 
189
        SendItem i;
 
190
        i.stringToSend = s;
 
191
        sendList += i;
 
192
}
 
193
 
 
194
void BasicProtocol::sendWhitespace()
 
195
{
 
196
        SendItem i;
 
197
        i.doWhitespace = true;
 
198
        sendList += i;
 
199
}
 
200
 
 
201
QDomElement BasicProtocol::recvStanza()
 
202
{
 
203
        QDomElement e = stanzaToRecv;
 
204
        stanzaToRecv = QDomElement();
 
205
        return e;
 
206
}
 
207
 
 
208
void BasicProtocol::shutdown()
 
209
{
 
210
        doShutdown = true;
 
211
}
 
212
 
 
213
void BasicProtocol::shutdownWithError(int cond, const QString &str)
 
214
{
 
215
        otherHost = str;
 
216
        delayErrorAndClose(cond);
 
217
}
 
218
 
 
219
bool BasicProtocol::isReady() const
 
220
{
 
221
        return ready;
 
222
}
 
223
 
 
224
void BasicProtocol::setReady(bool b)
 
225
{
 
226
        ready = b;
 
227
}
 
228
 
 
229
QString BasicProtocol::saslMech() const
 
230
{
 
231
        return sasl_mech;
 
232
}
 
233
 
 
234
QByteArray BasicProtocol::saslStep() const
 
235
{
 
236
        return sasl_step;
 
237
}
 
238
 
 
239
void BasicProtocol::setSASLMechList(const QStringList &list)
 
240
{
 
241
        sasl_mechlist = list;
 
242
}
 
243
 
 
244
void BasicProtocol::setSASLFirst(const QString &mech, const QByteArray &step)
 
245
{
 
246
        sasl_mech = mech;
 
247
        sasl_step = step;
 
248
}
 
249
 
 
250
void BasicProtocol::setSASLNext(const QByteArray &step)
 
251
{
 
252
        sasl_step = step;
 
253
}
 
254
 
 
255
void BasicProtocol::setSASLAuthed()
 
256
{
 
257
        sasl_authed = true;
 
258
}
 
259
 
 
260
int BasicProtocol::stringToSASLCond(const QString &s)
 
261
{
 
262
        for(int n = 0; saslCondTable[n].str; ++n) {
 
263
                if(s == saslCondTable[n].str)
 
264
                        return saslCondTable[n].cond;
 
265
        }
 
266
        return -1;
 
267
}
 
268
 
 
269
int BasicProtocol::stringToStreamCond(const QString &s)
 
270
{
 
271
        for(int n = 0; streamCondTable[n].str; ++n) {
 
272
                if(s == streamCondTable[n].str)
 
273
                        return streamCondTable[n].cond;
 
274
        }
 
275
        return -1;
 
276
}
 
277
 
 
278
QString BasicProtocol::saslCondToString(int x)
 
279
{
 
280
        for(int n = 0; saslCondTable[n].str; ++n) {
 
281
                if(x == saslCondTable[n].cond)
 
282
                        return saslCondTable[n].str;
 
283
        }
 
284
        return QString();
 
285
}
 
286
 
 
287
QString BasicProtocol::streamCondToString(int x)
 
288
{
 
289
        for(int n = 0; streamCondTable[n].str; ++n) {
 
290
                if(x == streamCondTable[n].cond)
 
291
                        return streamCondTable[n].str;
 
292
        }
 
293
        return QString();
 
294
}
 
295
 
 
296
void BasicProtocol::extractStreamError(const QDomElement &e)
 
297
{
 
298
        QString text;
 
299
        QDomElement appSpec;
 
300
 
 
301
        QDomElement t = firstChildElement(e);
 
302
        if(t.isNull() || t.namespaceURI() != NS_STREAMS) {
 
303
                // probably old-style error
 
304
                errCond = -1;
 
305
                errText = e.text();
 
306
        }
 
307
        else
 
308
                errCond = stringToStreamCond(t.tagName());
 
309
 
 
310
        if(errCond != -1) {
 
311
                if(errCond == SeeOtherHost)
 
312
                        otherHost = t.text();
 
313
 
 
314
                t = e.elementsByTagNameNS(NS_STREAMS, "text").item(0).toElement();
 
315
                if(!t.isNull())
 
316
                        text = t.text();
 
317
 
 
318
                // find first non-standard namespaced element
 
319
                QDomNodeList nl = e.childNodes();
 
320
                for(int n = 0; n < nl.count(); ++n) {
 
321
                        QDomNode i = nl.item(n);
 
322
                        if(i.isElement() && i.namespaceURI() != NS_STREAMS) {
 
323
                                appSpec = i.toElement();
 
324
                                break;
 
325
                        }
 
326
                }
 
327
 
 
328
                errText = text;
 
329
                errAppSpec = appSpec;
 
330
        }
 
331
}
 
332
 
 
333
void BasicProtocol::send(const QDomElement &e, bool clip)
 
334
{
 
335
        writeElement(e, TypeElement, false, clip);
 
336
}
 
337
 
 
338
void BasicProtocol::sendStreamError(int cond, const QString &text, const QDomElement &appSpec)
 
339
{
 
340
        QDomElement se = doc.createElementNS(NS_ETHERX, "stream:error");
 
341
        QDomElement err = doc.createElementNS(NS_STREAMS, streamCondToString(cond));
 
342
        if(!otherHost.isEmpty())
 
343
                err.appendChild(doc.createTextNode(otherHost));
 
344
        se.appendChild(err);
 
345
        if(!text.isEmpty()) {
 
346
                QDomElement te = doc.createElementNS(NS_STREAMS, "text");
 
347
                te.setAttributeNS(NS_XML, "xml:lang", "en");
 
348
                te.appendChild(doc.createTextNode(text));
 
349
                se.appendChild(te);
 
350
        }
 
351
        se.appendChild(appSpec);
 
352
 
 
353
        writeElement(se, 100, false);
 
354
}
 
355
 
 
356
void BasicProtocol::sendStreamError(const QString &text)
 
357
{
 
358
        QDomElement se = doc.createElementNS(NS_ETHERX, "stream:error");
 
359
        se.appendChild(doc.createTextNode(text));
 
360
 
 
361
        writeElement(se, 100, false);
 
362
}
 
363
 
 
364
bool BasicProtocol::errorAndClose(int cond, const QString &text, const QDomElement &appSpec)
 
365
{
 
366
        closeError = true;
 
367
        errCond = cond;
 
368
        errText = text;
 
369
        errAppSpec = appSpec;
 
370
        sendStreamError(cond, text, appSpec);
 
371
        return close();
 
372
}
 
373
 
 
374
bool BasicProtocol::error(int code)
 
375
{
 
376
        event = EError;
 
377
        errorCode = code;
 
378
        return true;
 
379
}
 
380
 
 
381
void BasicProtocol::delayErrorAndClose(int cond, const QString &text, const QDomElement &appSpec)
 
382
{
 
383
        errorCode = ErrStream;
 
384
        errCond = cond;
 
385
        errText = text;
 
386
        errAppSpec = appSpec;
 
387
        delayedError = true;
 
388
}
 
389
 
 
390
void BasicProtocol::delayError(int code)
 
391
{
 
392
        errorCode = code;
 
393
        delayedError = true;
 
394
}
 
395
 
 
396
QDomElement BasicProtocol::docElement()
 
397
{
 
398
        // create the root element
 
399
        QDomElement e = doc.createElementNS(NS_ETHERX, "stream:stream");
 
400
 
 
401
        QString defns = defaultNamespace();
 
402
        QStringList list = extraNamespaces();
 
403
 
 
404
        // HACK: using attributes seems to be the only way to get additional namespaces in here
 
405
        if(!defns.isEmpty())
 
406
                e.setAttribute("xmlns", defns);
 
407
        for(QStringList::ConstIterator it = list.begin(); it != list.end();) {
 
408
                QString prefix = *(it++);
 
409
                QString uri = *(it++);
 
410
                e.setAttribute(QString("xmlns:") + prefix, uri);
 
411
        }
 
412
 
 
413
        // additional attributes
 
414
        if(!isIncoming() && !to.isEmpty())
 
415
                e.setAttribute("to", to);
 
416
        if(isIncoming() && !from.isEmpty())
 
417
                e.setAttribute("from", from);
 
418
        if(!id.isEmpty())
 
419
                e.setAttribute("id", id);
 
420
        if(!lang.isEmpty())
 
421
                e.setAttributeNS(NS_XML, "xml:lang", lang);
 
422
        if(version.major > 0 || version.minor > 0)
 
423
                e.setAttribute("version", QString::number(version.major) + '.' + QString::number(version.minor));
 
424
 
 
425
        return e;
 
426
}
 
427
 
 
428
void BasicProtocol::handleDocOpen(const Parser::Event &pe)
 
429
{
 
430
        if(isIncoming()) {
 
431
                if(xmlEncoding() != "UTF-8") {
 
432
                        delayErrorAndClose(UnsupportedEncoding);
 
433
                        return;
 
434
                }
 
435
        }
 
436
 
 
437
        if(pe.namespaceURI() == NS_ETHERX && pe.localName() == "stream") {
 
438
                QXmlAttributes atts = pe.atts();
 
439
 
 
440
                // grab the version
 
441
                int major = 0;
 
442
                int minor = 0;
 
443
                QString verstr = atts.value("version");
 
444
                if(!verstr.isEmpty()) {
 
445
                        int n = verstr.indexOf('.');
 
446
                        if(n != -1) {
 
447
                                major = verstr.mid(0, n).toInt();
 
448
                                minor = verstr.mid(n+1).toInt();
 
449
                        }
 
450
                        else {
 
451
                                major = verstr.toInt();
 
452
                                minor = 0;
 
453
                        }
 
454
                }
 
455
                version = Version(major, minor);
 
456
 
 
457
                if(isIncoming()) {
 
458
                        to = atts.value("to");
 
459
                        QString peerLang = atts.value(NS_XML, "lang");
 
460
                        if(!peerLang.isEmpty())
 
461
                                lang = peerLang;
 
462
                }
 
463
                // outgoing
 
464
                else {
 
465
                        from = atts.value("from");
 
466
                        lang = atts.value(NS_XML, "lang");
 
467
                        id = atts.value("id");
 
468
                }
 
469
 
 
470
                handleStreamOpen(pe);
 
471
        }
 
472
        else {
 
473
                if(isIncoming())
 
474
                        delayErrorAndClose(BadFormat);
 
475
                else
 
476
                        delayError(ErrProtocol);
 
477
        }
 
478
}
 
479
 
 
480
bool BasicProtocol::handleError()
 
481
{
 
482
        if(isIncoming())
 
483
                return errorAndClose(XmlNotWellFormed);
 
484
        else
 
485
                return error(ErrParse);
 
486
}
 
487
 
 
488
bool BasicProtocol::handleCloseFinished()
 
489
{
 
490
        if(closeError) {
 
491
                event = EError;
 
492
                errorCode = ErrStream;
 
493
                // note: errCond and friends are already set at this point
 
494
        }
 
495
        else
 
496
                event = EClosed;
 
497
        return true;
 
498
}
 
499
 
 
500
bool BasicProtocol::doStep(const QDomElement &e)
 
501
{
 
502
        // handle pending error
 
503
        if(delayedError) {
 
504
                if(isIncoming())
 
505
                        return errorAndClose(errCond, errText, errAppSpec);
 
506
                else
 
507
                        return error(errorCode);
 
508
        }
 
509
 
 
510
        // shutdown?
 
511
        if(doShutdown) {
 
512
                doShutdown = false;
 
513
                return close();
 
514
        }
 
515
 
 
516
        if(!e.isNull()) {
 
517
                // check for error
 
518
                if(e.namespaceURI() == NS_ETHERX && e.tagName() == "error") {
 
519
                        extractStreamError(e);
 
520
                        return error(ErrStream);
 
521
                }
 
522
        }
 
523
 
 
524
        if(ready) {
 
525
                // stanzas written?
 
526
                if(stanzasWritten > 0) {
 
527
                        --stanzasWritten;
 
528
                        event = EStanzaSent;
 
529
                        return true;
 
530
                }
 
531
                // send items?
 
532
                if(!sendList.isEmpty()) {
 
533
                        SendItem i;
 
534
                        {
 
535
                                QList<SendItem>::Iterator it = sendList.begin();
 
536
                                i = (*it);
 
537
                                sendList.erase(it);
 
538
                        }
 
539
 
 
540
                        // outgoing stanza?
 
541
                        if(!i.stanzaToSend.isNull()) {
 
542
                                ++stanzasPending;
 
543
                                writeElement(i.stanzaToSend, TypeStanza, true);
 
544
                                event = ESend;
 
545
                        }
 
546
                        // direct send?
 
547
                        else if(!i.stringToSend.isEmpty()) {
 
548
                                writeString(i.stringToSend, TypeDirect, true);
 
549
                                event = ESend;
 
550
                        }
 
551
                        // whitespace keepalive?
 
552
                        else if(i.doWhitespace) {
 
553
                                writeString("\n", TypePing, false);
 
554
                                event = ESend;
 
555
                        }
 
556
                        return true;
 
557
                }
 
558
                else {
 
559
                        // if we have pending outgoing stanzas, ask for write notification
 
560
                        if(stanzasPending)
 
561
                                notify |= NSend;
 
562
                }
 
563
        }
 
564
 
 
565
        return doStep2(e);
 
566
}
 
567
 
 
568
void BasicProtocol::itemWritten(int id, int)
 
569
{
 
570
        if(id == TypeStanza) {
 
571
                --stanzasPending;
 
572
                ++stanzasWritten;
 
573
        }
 
574
}
 
575
 
 
576
QString BasicProtocol::defaultNamespace()
 
577
{
 
578
        // default none
 
579
        return QString();
 
580
}
 
581
 
 
582
QStringList BasicProtocol::extraNamespaces()
 
583
{
 
584
        // default none
 
585
        return QStringList();
 
586
}
 
587
 
 
588
void BasicProtocol::handleStreamOpen(const Parser::Event &)
 
589
{
 
590
        // default does nothing
 
591
}
 
592
 
 
593
//----------------------------------------------------------------------------
 
594
// CoreProtocol
 
595
//----------------------------------------------------------------------------
 
596
CoreProtocol::CoreProtocol()
 
597
:BasicProtocol()
 
598
{
 
599
        init();
 
600
}
 
601
 
 
602
CoreProtocol::~CoreProtocol()
 
603
{
 
604
}
 
605
 
 
606
void CoreProtocol::init()
 
607
{
 
608
        step = Start;
 
609
 
 
610
        // ??
 
611
        server = false;
 
612
        dialback = false;
 
613
        dialback_verify = false;
 
614
 
 
615
        // settings
 
616
        jid_ = Jid();
 
617
        password = QString();
 
618
        oldOnly = false;
 
619
        allowPlain = false;
 
620
        doTLS = true;
 
621
        doAuth = true;
 
622
        doCompress = true;
 
623
        doBinding = true;
 
624
 
 
625
        // input
 
626
        user = QString();
 
627
        host = QString();
 
628
 
 
629
        // status
 
630
        old = false;
 
631
        digest = false;
 
632
        tls_started = false;
 
633
        sasl_started = false;
 
634
        compress_started = false;
 
635
}
 
636
 
 
637
void CoreProtocol::reset()
 
638
{
 
639
        BasicProtocol::reset();
 
640
        init();
 
641
}
 
642
 
 
643
void CoreProtocol::startClientOut(const Jid &_jid, bool _oldOnly, bool tlsActive, bool _doAuth, bool _doCompress)
 
644
{
 
645
        jid_ = _jid;
 
646
        to = _jid.domain();
 
647
        oldOnly = _oldOnly;
 
648
        doAuth = _doAuth;
 
649
        doCompress = _doCompress;
 
650
        tls_started = tlsActive;
 
651
 
 
652
        if(oldOnly)
 
653
                version = Version(0,0);
 
654
        startConnect();
 
655
}
 
656
 
 
657
void CoreProtocol::startServerOut(const QString &_to)
 
658
{
 
659
        server = true;
 
660
        to = _to;
 
661
        startConnect();
 
662
}
 
663
 
 
664
void CoreProtocol::startDialbackOut(const QString &_to, const QString &_from)
 
665
{
 
666
        server = true;
 
667
        dialback = true;
 
668
        to = _to;
 
669
        self_from = _from;
 
670
        startConnect();
 
671
}
 
672
 
 
673
void CoreProtocol::startDialbackVerifyOut(const QString &_to, const QString &_from, const QString &id, const QString &key)
 
674
{
 
675
        server = true;
 
676
        dialback = true;
 
677
        dialback_verify = true;
 
678
        to = _to;
 
679
        self_from = _from;
 
680
        dialback_id = id;
 
681
        dialback_key = key;
 
682
        startConnect();
 
683
}
 
684
 
 
685
void CoreProtocol::startClientIn(const QString &_id)
 
686
{
 
687
        id = _id;
 
688
        startAccept();
 
689
}
 
690
 
 
691
void CoreProtocol::startServerIn(const QString &_id)
 
692
{
 
693
        server = true;
 
694
        id = _id;
 
695
        startAccept();
 
696
}
 
697
 
 
698
void CoreProtocol::setLang(const QString &s)
 
699
{
 
700
        lang = s;
 
701
}
 
702
 
 
703
void CoreProtocol::setAllowTLS(bool b)
 
704
{
 
705
        doTLS = b;
 
706
}
 
707
 
 
708
void CoreProtocol::setAllowBind(bool b)
 
709
{
 
710
        doBinding = b;
 
711
}
 
712
 
 
713
void CoreProtocol::setAllowPlain(bool b)
 
714
{
 
715
        allowPlain = b;
 
716
}
 
717
 
 
718
const Jid& CoreProtocol::jid() const
 
719
{
 
720
        return jid_;
 
721
}
 
722
 
 
723
void CoreProtocol::setPassword(const QString &s)
 
724
{
 
725
        password = s;
 
726
}
 
727
 
 
728
void CoreProtocol::setFrom(const QString &s)
 
729
{
 
730
        from = s;
 
731
}
 
732
 
 
733
void CoreProtocol::setDialbackKey(const QString &s)
 
734
{
 
735
        dialback_key = s;
 
736
}
 
737
 
 
738
bool CoreProtocol::loginComplete()
 
739
{
 
740
        setReady(true);
 
741
 
 
742
        event = EReady;
 
743
        step = Done;
 
744
        return true;
 
745
}
 
746
 
 
747
int CoreProtocol::getOldErrorCode(const QDomElement &e)
 
748
{
 
749
        QDomElement err = e.elementsByTagNameNS(NS_CLIENT, "error").item(0).toElement();
 
750
        if(err.isNull() || !err.hasAttribute("code"))
 
751
                return -1;
 
752
        return err.attribute("code").toInt();
 
753
}
 
754
 
 
755
/*QString CoreProtocol::xmlToString(const QDomElement &e, bool clip)
 
756
{
 
757
        // determine an appropriate 'fakeNS' to use
 
758
        QString ns;
 
759
        if(e.prefix() == "stream")
 
760
                ns = NS_ETHERX;
 
761
        else if(e.prefix() == "db")
 
762
                ns = NS_DIALBACK;
 
763
        else
 
764
                ns = NS_CLIENT;
 
765
        return ::xmlToString(e, ns, "stream:stream", clip);
 
766
}*/
 
767
 
 
768
bool CoreProtocol::stepAdvancesParser() const
 
769
{
 
770
        if(stepRequiresElement())
 
771
                return true;
 
772
        else if(isReady())
 
773
                return true;
 
774
        return false;
 
775
}
 
776
 
 
777
// all element-needing steps need to be registered here
 
778
bool CoreProtocol::stepRequiresElement() const
 
779
{
 
780
        switch(step) {
 
781
                case GetFeatures:
 
782
                case GetTLSProceed:
 
783
                case GetCompressProceed:
 
784
                case GetSASLChallenge:
 
785
                case GetBindResponse:
 
786
                case GetAuthGetResponse:
 
787
                case GetAuthSetResponse:
 
788
                case GetRequest:
 
789
                case GetSASLResponse:
 
790
                        return true;
 
791
        }
 
792
        return false;
 
793
}
 
794
 
 
795
void CoreProtocol::stringSend(const QString &s)
 
796
{
 
797
#ifdef XMPP_TEST
 
798
        TD::outgoingTag(s);
 
799
#endif
 
800
}
 
801
 
 
802
void CoreProtocol::stringRecv(const QString &s)
 
803
{
 
804
#ifdef XMPP_TEST
 
805
        TD::incomingTag(s);
 
806
#endif
 
807
}
 
808
 
 
809
QString CoreProtocol::defaultNamespace()
 
810
{
 
811
        if(server)
 
812
                return NS_SERVER;
 
813
        else
 
814
                return NS_CLIENT;
 
815
}
 
816
 
 
817
QStringList CoreProtocol::extraNamespaces()
 
818
{
 
819
        QStringList list;
 
820
        if(dialback) {
 
821
                list += "db";
 
822
                list += NS_DIALBACK;
 
823
        }
 
824
        return list;
 
825
}
 
826
 
 
827
void CoreProtocol::handleStreamOpen(const Parser::Event &pe)
 
828
{
 
829
        if(isIncoming()) {
 
830
                QString ns = pe.nsprefix();
 
831
                QString db;
 
832
                if(server) {
 
833
                        db = pe.nsprefix("db");
 
834
                        if(!db.isEmpty())
 
835
                                dialback = true;
 
836
                }
 
837
 
 
838
                // verify namespace
 
839
                if((!server && ns != NS_CLIENT) || (server && ns != NS_SERVER) || (dialback && db != NS_DIALBACK)) {
 
840
                        delayErrorAndClose(InvalidNamespace);
 
841
                        return;
 
842
                }
 
843
 
 
844
                // verify version
 
845
                if(version.major < 1 && !dialback) {
 
846
                        delayErrorAndClose(UnsupportedVersion);
 
847
                        return;
 
848
                }
 
849
        }
 
850
        else {
 
851
                if(!dialback) {
 
852
                        if(version.major >= 1 && !oldOnly)
 
853
                                old = false;
 
854
                        else
 
855
                                old = true;
 
856
                }
 
857
        }
 
858
}
 
859
 
 
860
void CoreProtocol::elementSend(const QDomElement &e)
 
861
{
 
862
#ifdef XMPP_TEST
 
863
        TD::outgoingXml(e);
 
864
#endif
 
865
}
 
866
 
 
867
void CoreProtocol::elementRecv(const QDomElement &e)
 
868
{
 
869
#ifdef XMPP_TEST
 
870
        TD::incomingXml(e);
 
871
#endif
 
872
}
 
873
 
 
874
bool CoreProtocol::doStep2(const QDomElement &e)
 
875
{
 
876
        if(dialback)
 
877
                return dialbackStep(e);
 
878
        else
 
879
                return normalStep(e);
 
880
}
 
881
 
 
882
bool CoreProtocol::isValidStanza(const QDomElement &e) const
 
883
{
 
884
        QString s = e.tagName();
 
885
        if(e.namespaceURI() == (server ? NS_SERVER : NS_CLIENT) && (s == "message" || s == "presence" || s == "iq"))
 
886
                return true;
 
887
        else
 
888
                return false;
 
889
}
 
890
 
 
891
bool CoreProtocol::grabPendingItem(const Jid &to, const Jid &from, int type, DBItem *item)
 
892
{
 
893
        for(QList<DBItem>::Iterator it = dbpending.begin(); it != dbpending.end(); ++it) {
 
894
                const DBItem &i = *it;
 
895
                if(i.type == type && i.to.compare(to) && i.from.compare(from)) {
 
896
                        const DBItem &i = (*it);
 
897
                        *item = i;
 
898
                        dbpending.erase(it);
 
899
                        return true;
 
900
                }
 
901
        }
 
902
        return false;
 
903
}
 
904
 
 
905
bool CoreProtocol::dialbackStep(const QDomElement &e)
 
906
{
 
907
        if(step == Start) {
 
908
                setReady(true);
 
909
                step = Done;
 
910
                event = EReady;
 
911
                return true;
 
912
        }
 
913
 
 
914
        if(!dbrequests.isEmpty()) {
 
915
                // process a request
 
916
                DBItem i;
 
917
                {
 
918
                        QList<DBItem>::Iterator it = dbrequests.begin();
 
919
                        i = (*it);
 
920
                        dbrequests.erase(it);
 
921
                }
 
922
 
 
923
                QDomElement r;
 
924
                if(i.type == DBItem::ResultRequest) {
 
925
                        r = doc.createElementNS(NS_DIALBACK, "db:result");
 
926
                        r.setAttribute("to", i.to.full());
 
927
                        r.setAttribute("from", i.from.full());
 
928
                        r.appendChild(doc.createTextNode(i.key));
 
929
                        dbpending += i;
 
930
                }
 
931
                else if(i.type == DBItem::ResultGrant) {
 
932
                        r = doc.createElementNS(NS_DIALBACK, "db:result");
 
933
                        r.setAttribute("to", i.to.full());
 
934
                        r.setAttribute("from", i.from.full());
 
935
                        r.setAttribute("type", i.ok ? "valid" : "invalid");
 
936
                        if(i.ok) {
 
937
                                i.type = DBItem::Validated;
 
938
                                dbvalidated += i;
 
939
                        }
 
940
                        else {
 
941
                                // TODO: disconnect after writing element
 
942
                        }
 
943
                }
 
944
                else if(i.type == DBItem::VerifyRequest) {
 
945
                        r = doc.createElementNS(NS_DIALBACK, "db:verify");
 
946
                        r.setAttribute("to", i.to.full());
 
947
                        r.setAttribute("from", i.from.full());
 
948
                        r.setAttribute("id", i.id);
 
949
                        r.appendChild(doc.createTextNode(i.key));
 
950
                        dbpending += i;
 
951
                }
 
952
                // VerifyGrant
 
953
                else {
 
954
                        r = doc.createElementNS(NS_DIALBACK, "db:verify");
 
955
                        r.setAttribute("to", i.to.full());
 
956
                        r.setAttribute("from", i.from.full());
 
957
                        r.setAttribute("id", i.id);
 
958
                        r.setAttribute("type", i.ok ? "valid" : "invalid");
 
959
                }
 
960
 
 
961
                writeElement(r, TypeElement, false);
 
962
                event = ESend;
 
963
                return true;
 
964
        }
 
965
 
 
966
        if(!e.isNull()) {
 
967
                if(e.namespaceURI() == NS_DIALBACK) {
 
968
                        if(e.tagName() == "result") {
 
969
                                Jid to(Jid(e.attribute("to")).domain());
 
970
                                Jid from(Jid(e.attribute("from")).domain());
 
971
                                if(isIncoming()) {
 
972
                                        QString key = e.text();
 
973
                                        // TODO: report event
 
974
                                }
 
975
                                else {
 
976
                                        bool ok = (e.attribute("type") == "valid") ? true: false;
 
977
                                        DBItem i;
 
978
                                        if(grabPendingItem(from, to, DBItem::ResultRequest, &i)) {
 
979
                                                if(ok) {
 
980
                                                        i.type = DBItem::Validated;
 
981
                                                        i.ok = true;
 
982
                                                        dbvalidated += i;
 
983
                                                        // TODO: report event
 
984
                                                }
 
985
                                                else {
 
986
                                                        // TODO: report event
 
987
                                                }
 
988
                                        }
 
989
                                }
 
990
                        }
 
991
                        else if(e.tagName() == "verify") {
 
992
                                Jid to(Jid(e.attribute("to")).domain());
 
993
                                Jid from(Jid(e.attribute("from")).domain());
 
994
                                QString id = e.attribute("id");
 
995
                                if(isIncoming()) {
 
996
                                        QString key = e.text();
 
997
                                        // TODO: report event
 
998
                                }
 
999
                                else {
 
1000
                                        bool ok = (e.attribute("type") == "valid") ? true: false;
 
1001
                                        DBItem i;
 
1002
                                        if(grabPendingItem(from, to, DBItem::VerifyRequest, &i)) {
 
1003
                                                if(ok) {
 
1004
                                                        // TODO: report event
 
1005
                                                }
 
1006
                                                else {
 
1007
                                                        // TODO: report event
 
1008
                                                }
 
1009
                                        }
 
1010
                                }
 
1011
                        }
 
1012
                }
 
1013
                else {
 
1014
                        if(isReady()) {
 
1015
                                if(isValidStanza(e)) {
 
1016
                                        // TODO: disconnect if stanza is from unverified sender
 
1017
                                        // TODO: ignore packets from receiving servers
 
1018
                                        stanzaToRecv = e;
 
1019
                                        event = EStanzaReady;
 
1020
                                        return true;
 
1021
                                }
 
1022
                        }
 
1023
                }
 
1024
        }
 
1025
 
 
1026
        need = NNotify;
 
1027
        notify |= NRecv;
 
1028
        return false;
 
1029
}
 
1030
 
 
1031
bool CoreProtocol::normalStep(const QDomElement &e)
 
1032
{
 
1033
        if(step == Start) {
 
1034
                if(isIncoming()) {
 
1035
                        need = NSASLMechs;
 
1036
                        step = SendFeatures;
 
1037
                        return false;
 
1038
                }
 
1039
                else {
 
1040
                        if(old) {
 
1041
                                if(doAuth)
 
1042
                                        step = HandleAuthGet;
 
1043
                                else
 
1044
                                        return loginComplete();
 
1045
                        }
 
1046
                        else
 
1047
                                step = GetFeatures;
 
1048
 
 
1049
                        return processStep();
 
1050
                }
 
1051
        }
 
1052
        else if(step == HandleFeatures) {
 
1053
                // deal with TLS?
 
1054
                if(doTLS && !tls_started && !sasl_authed && features.tls_supported) {
 
1055
                        QDomElement e = doc.createElementNS(NS_TLS, "starttls");
 
1056
 
 
1057
                        send(e, true);
 
1058
                        event = ESend;
 
1059
                        step = GetTLSProceed;
 
1060
                        return true;
 
1061
                }
 
1062
 
 
1063
                // Should we go further ?
 
1064
                if (!doAuth)
 
1065
                        return loginComplete();
 
1066
                
 
1067
                // Deal with compression
 
1068
                if (doCompress && !compress_started && features.compress_supported && features.compression_mechs.contains("zlib")) {
 
1069
                        QDomElement e = doc.createElementNS(NS_COMPRESS_PROTOCOL, "compress");
 
1070
                        QDomElement m = doc.createElementNS(NS_COMPRESS_PROTOCOL, "method");
 
1071
                        m.appendChild(doc.createTextNode("zlib"));
 
1072
                        e.appendChild(m);
 
1073
                        send(e,true);
 
1074
                        event = ESend;
 
1075
                        step = GetCompressProceed;
 
1076
                        return true;
 
1077
                }
 
1078
 
 
1079
                // deal with SASL?
 
1080
                if(!sasl_authed) {
 
1081
                        if(!features.sasl_supported) {
 
1082
                                // SASL MUST be supported
 
1083
                                //event = EError;
 
1084
                                //errorCode = ErrProtocol;
 
1085
                                //return true;
 
1086
                                
 
1087
                                // Fall back on auth for non-compliant servers 
 
1088
                                step = HandleAuthGet;
 
1089
                                old = true;
 
1090
                                return true;
 
1091
                        }
 
1092
 
 
1093
#ifdef XMPP_TEST
 
1094
                        TD::msg("starting SASL authentication...");
 
1095
#endif
 
1096
                        need = NSASLFirst;
 
1097
                        step = GetSASLFirst;
 
1098
                        return false;
 
1099
                }
 
1100
 
 
1101
                if(server) {
 
1102
                        return loginComplete();
 
1103
                }
 
1104
                else {
 
1105
                        if(!doBinding)
 
1106
                                return loginComplete();
 
1107
                }
 
1108
 
 
1109
                // deal with bind
 
1110
                if(!features.bind_supported) {
 
1111
                        // bind MUST be supported
 
1112
                        event = EError;
 
1113
                        errorCode = ErrProtocol;
 
1114
                        return true;
 
1115
                }
 
1116
 
 
1117
                QDomElement e = doc.createElement("iq");
 
1118
                e.setAttribute("type", "set");
 
1119
                e.setAttribute("id", "bind_1");
 
1120
                QDomElement b = doc.createElementNS(NS_BIND, "bind");
 
1121
 
 
1122
                // request specific resource?
 
1123
                QString resource = jid_.resource();
 
1124
                if(!resource.isEmpty()) {
 
1125
                        QDomElement r = doc.createElement("resource");
 
1126
                        r.appendChild(doc.createTextNode(jid_.resource()));
 
1127
                        b.appendChild(r);
 
1128
                }
 
1129
 
 
1130
                e.appendChild(b);
 
1131
 
 
1132
                send(e);
 
1133
                event = ESend;
 
1134
                step = GetBindResponse;
 
1135
                return true;
 
1136
        }
 
1137
        else if(step == GetSASLFirst) {
 
1138
                QDomElement e = doc.createElementNS(NS_SASL, "auth");
 
1139
                e.setAttribute("mechanism", sasl_mech);
 
1140
                if(!sasl_step.isEmpty()) {
 
1141
#ifdef XMPP_TEST
 
1142
                        TD::msg(QString("SASL OUT: [%1]").arg(printArray(sasl_step)));
 
1143
#endif
 
1144
                        e.appendChild(doc.createTextNode(QCA::Base64().arrayToString(sasl_step)));
 
1145
                }
 
1146
 
 
1147
                send(e, true);
 
1148
                event = ESend;
 
1149
                step = GetSASLChallenge;
 
1150
                return true;
 
1151
        }
 
1152
        else if(step == GetSASLNext) {
 
1153
                if(isIncoming()) {
 
1154
                        if(sasl_authed) {
 
1155
                                QDomElement e = doc.createElementNS(NS_SASL, "success");
 
1156
                                writeElement(e, TypeElement, false, true);
 
1157
                                event = ESend;
 
1158
                                step = IncHandleSASLSuccess;
 
1159
                                return true;
 
1160
                        }
 
1161
                        else {
 
1162
                                QByteArray stepData = sasl_step;
 
1163
                                QDomElement e = doc.createElementNS(NS_SASL, "challenge");
 
1164
                                if(!stepData.isEmpty())
 
1165
                                        e.appendChild(doc.createTextNode(QCA::Base64().arrayToString(stepData)));
 
1166
 
 
1167
                                writeElement(e, TypeElement, false, true);
 
1168
                                event = ESend;
 
1169
                                step = GetSASLResponse;
 
1170
                                return true;
 
1171
                        }
 
1172
                }
 
1173
                else {
 
1174
                        // already authed?  then ignore last client step
 
1175
                        //   (this happens if "additional data with success"
 
1176
                        //   is used)
 
1177
                        if(sasl_authed)
 
1178
                        {
 
1179
                                event = ESASLSuccess;
 
1180
                                step = HandleSASLSuccess;
 
1181
                                return true;
 
1182
                        }
 
1183
 
 
1184
                        QByteArray stepData = sasl_step;
 
1185
#ifdef XMPP_TEST
 
1186
                        TD::msg(QString("SASL OUT: [%1]").arg(printArray(sasl_step)));
 
1187
#endif
 
1188
                        QDomElement e = doc.createElementNS(NS_SASL, "response");
 
1189
                        if(!stepData.isEmpty())
 
1190
                                e.appendChild(doc.createTextNode(QCA::Base64().arrayToString(stepData)));
 
1191
 
 
1192
                        send(e, true);
 
1193
                        event = ESend;
 
1194
                        step = GetSASLChallenge;
 
1195
                        return true;
 
1196
                }
 
1197
        }
 
1198
        else if(step == HandleSASLSuccess) {
 
1199
                need = NSASLLayer;
 
1200
                spare = resetStream();
 
1201
                step = Start;
 
1202
                return false;
 
1203
        }
 
1204
        else if(step == HandleAuthGet) {
 
1205
                QDomElement e = doc.createElement("iq");
 
1206
                e.setAttribute("to", to);
 
1207
                e.setAttribute("type", "get");
 
1208
                e.setAttribute("id", "auth_1");
 
1209
                QDomElement q = doc.createElementNS("jabber:iq:auth", "query");
 
1210
                QDomElement u = doc.createElement("username");
 
1211
                u.appendChild(doc.createTextNode(jid_.node()));
 
1212
                q.appendChild(u);
 
1213
                e.appendChild(q);
 
1214
 
 
1215
                send(e);
 
1216
                event = ESend;
 
1217
                step = GetAuthGetResponse;
 
1218
                return true;
 
1219
        }
 
1220
        else if(step == HandleAuthSet) {
 
1221
                QDomElement e = doc.createElement("iq");
 
1222
                e.setAttribute("to", to);
 
1223
                e.setAttribute("type", "set");
 
1224
                e.setAttribute("id", "auth_2");
 
1225
                QDomElement q = doc.createElementNS("jabber:iq:auth", "query");
 
1226
                QDomElement u = doc.createElement("username");
 
1227
                u.appendChild(doc.createTextNode(jid_.node()));
 
1228
                q.appendChild(u);
 
1229
                QDomElement p;
 
1230
                if(digest) {
 
1231
                        // need SHA1 here
 
1232
                        //if(!QCA::isSupported(QCA::CAP_SHA1))
 
1233
                        //      QCA::insertProvider(createProviderHash());
 
1234
 
 
1235
                        p = doc.createElement("digest");
 
1236
                        QByteArray cs = id.toUtf8() + password.toUtf8();
 
1237
                        p.appendChild(doc.createTextNode(QCA::Hash("sha1").hashToString(cs)));
 
1238
                }
 
1239
                else {
 
1240
                        p = doc.createElement("password");
 
1241
                        p.appendChild(doc.createTextNode(password));
 
1242
                }
 
1243
                q.appendChild(p);
 
1244
                QDomElement r = doc.createElement("resource");
 
1245
                r.appendChild(doc.createTextNode(jid_.resource()));
 
1246
                q.appendChild(r);
 
1247
                e.appendChild(q);
 
1248
 
 
1249
                send(e, true);
 
1250
                event = ESend;
 
1251
                step = GetAuthSetResponse;
 
1252
                return true;
 
1253
        }
 
1254
        // server
 
1255
        else if(step == SendFeatures) {
 
1256
                QDomElement f = doc.createElementNS(NS_ETHERX, "stream:features");
 
1257
                if(!tls_started && !sasl_authed) { // don't offer tls if we are already sasl'd
 
1258
                        QDomElement tls = doc.createElementNS(NS_TLS, "starttls");
 
1259
                        f.appendChild(tls);
 
1260
                }
 
1261
 
 
1262
                if(sasl_authed) {
 
1263
                        if(!server) {
 
1264
                                QDomElement bind = doc.createElementNS(NS_BIND, "bind");
 
1265
                                f.appendChild(bind);
 
1266
                        }
 
1267
                }
 
1268
                else {
 
1269
                        QDomElement mechs = doc.createElementNS(NS_SASL, "mechanisms");
 
1270
                        for(QStringList::ConstIterator it = sasl_mechlist.begin(); it != sasl_mechlist.end(); ++it) {
 
1271
                                QDomElement m = doc.createElement("mechanism");
 
1272
                                m.appendChild(doc.createTextNode(*it));
 
1273
                                mechs.appendChild(m);
 
1274
                        }
 
1275
                        f.appendChild(mechs);
 
1276
                }
 
1277
 
 
1278
                writeElement(f, TypeElement, false);
 
1279
                event = ESend;
 
1280
                step = GetRequest;
 
1281
                return true;
 
1282
        }
 
1283
        // server
 
1284
        else if(step == HandleTLS) {
 
1285
                tls_started = true;
 
1286
                need = NStartTLS;
 
1287
                spare = resetStream();
 
1288
                step = Start;
 
1289
                return false;
 
1290
        }
 
1291
        // server
 
1292
        else if(step == IncHandleSASLSuccess) {
 
1293
                event = ESASLSuccess;
 
1294
                spare = resetStream();
 
1295
                step = Start;
 
1296
                printf("sasl success\n");
 
1297
                return true;
 
1298
        }
 
1299
        else if(step == GetFeatures) {
 
1300
                // we are waiting for stream features
 
1301
                if(e.namespaceURI() == NS_ETHERX && e.tagName() == "features") {
 
1302
                        // extract features
 
1303
                        StreamFeatures f;
 
1304
                        QDomElement s = e.elementsByTagNameNS(NS_TLS, "starttls").item(0).toElement();
 
1305
                        if(!s.isNull()) {
 
1306
                                f.tls_supported = true;
 
1307
                                f.tls_required = s.elementsByTagNameNS(NS_TLS, "required").count() > 0;
 
1308
                        }
 
1309
                        QDomElement m = e.elementsByTagNameNS(NS_SASL, "mechanisms").item(0).toElement();
 
1310
                        if(!m.isNull()) {
 
1311
                                f.sasl_supported = true;
 
1312
                                QDomNodeList l = m.elementsByTagNameNS(NS_SASL, "mechanism");
 
1313
                                for(int n = 0; n < l.count(); ++n)
 
1314
                                        f.sasl_mechs += l.item(n).toElement().text();
 
1315
                        }
 
1316
                        QDomElement c = e.elementsByTagNameNS(NS_COMPRESS_FEATURE, "compression").item(0).toElement();
 
1317
                        if(!c.isNull()) {
 
1318
                                f.compress_supported = true;
 
1319
                                QDomNodeList l = c.elementsByTagNameNS(NS_COMPRESS_FEATURE, "method");
 
1320
                                for(int n = 0; n < l.count(); ++n)
 
1321
                                        f.compression_mechs += l.item(n).toElement().text();
 
1322
                        }
 
1323
                        QDomElement b = e.elementsByTagNameNS(NS_BIND, "bind").item(0).toElement();
 
1324
                        if(!b.isNull())
 
1325
                                f.bind_supported = true;
 
1326
                        QDomElement h = e.elementsByTagNameNS(NS_HOSTS, "hosts").item(0).toElement();
 
1327
                        if(!h.isNull()) {
 
1328
                                QDomNodeList l = h.elementsByTagNameNS(NS_HOSTS, "host");
 
1329
                                for(int n = 0; n < l.count(); ++n)
 
1330
                                        f.hosts += l.item(n).toElement().text();
 
1331
                                hosts += f.hosts;
 
1332
                        }
 
1333
 
 
1334
                        if(f.tls_supported) {
 
1335
#ifdef XMPP_TEST
 
1336
                                QString s = "STARTTLS is available";
 
1337
                                if(f.tls_required)
 
1338
                                        s += " (required)";
 
1339
                                TD::msg(s);
 
1340
#endif
 
1341
                        }
 
1342
                        if(f.sasl_supported) {
 
1343
#ifdef XMPP_TEST
 
1344
                                QString s = "SASL mechs:";
 
1345
                                for(QStringList::ConstIterator it = f.sasl_mechs.begin(); it != f.sasl_mechs.end(); ++it)
 
1346
                                        s += QString(" [%1]").arg((*it));
 
1347
                                TD::msg(s);
 
1348
#endif
 
1349
                        }
 
1350
                        if(f.compress_supported) {
 
1351
#ifdef XMPP_TEST
 
1352
                                QString s = "Compression mechs:";
 
1353
                                for(QStringList::ConstIterator it = f.compression_mechs.begin(); it != f.compression_mechs.end(); ++it)
 
1354
                                        s += QString(" [%1]").arg((*it));
 
1355
                                TD::msg(s);
 
1356
#endif
 
1357
                        }
 
1358
 
 
1359
                        event = EFeatures;
 
1360
                        features = f;
 
1361
                        step = HandleFeatures;
 
1362
                        return true;
 
1363
                }
 
1364
                else {
 
1365
                        // ignore
 
1366
                }
 
1367
        }
 
1368
        else if(step == GetTLSProceed) {
 
1369
                // waiting for proceed to starttls
 
1370
                if(e.namespaceURI() == NS_TLS) {
 
1371
                        if(e.tagName() == "proceed") {
 
1372
#ifdef XMPP_TEST
 
1373
                                TD::msg("Server wants us to proceed with ssl handshake");
 
1374
#endif
 
1375
                                tls_started = true;
 
1376
                                need = NStartTLS;
 
1377
                                spare = resetStream();
 
1378
                                step = Start;
 
1379
                                return false;
 
1380
                        }
 
1381
                        else if(e.tagName() == "failure") {
 
1382
                                event = EError;
 
1383
                                errorCode = ErrStartTLS;
 
1384
                                return true;
 
1385
                        }
 
1386
                        else {
 
1387
                                event = EError;
 
1388
                                errorCode = ErrProtocol;
 
1389
                                return true;
 
1390
                        }
 
1391
                }
 
1392
                else {
 
1393
                        // ignore
 
1394
                }
 
1395
        }
 
1396
        else if(step == GetCompressProceed) {
 
1397
                // waiting for proceed to compression
 
1398
                if(e.namespaceURI() == NS_COMPRESS_PROTOCOL) {
 
1399
                        if(e.tagName() == "compressed") {
 
1400
#ifdef XMPP_TEST
 
1401
                                TD::msg("Server wants us to proceed with compression");
 
1402
#endif
 
1403
                                compress_started = true;
 
1404
                                need = NCompress;
 
1405
                                spare = resetStream();
 
1406
                                step = Start;
 
1407
                                return false;
 
1408
                        }
 
1409
                        else if(e.tagName() == "failure") {
 
1410
                                event = EError;
 
1411
                                errorCode = ErrCompress;
 
1412
                                return true;
 
1413
                        }
 
1414
                        else {
 
1415
                                event = EError;
 
1416
                                errorCode = ErrProtocol;
 
1417
                                return true;
 
1418
                        }
 
1419
                }
 
1420
                else {
 
1421
                        // ignore
 
1422
                }
 
1423
        }
 
1424
        else if(step == GetSASLChallenge) {
 
1425
                // waiting for sasl challenge/success/fail
 
1426
                if(e.namespaceURI() == NS_SASL) {
 
1427
                        if(e.tagName() == "challenge") {
 
1428
                                QByteArray a = QCA::Base64().stringToArray(e.text()).toByteArray();
 
1429
#ifdef XMPP_TEST
 
1430
                                TD::msg(QString("SASL IN: [%1]").arg(printArray(a)));
 
1431
#endif
 
1432
                                sasl_step = a;
 
1433
                                need = NSASLNext;
 
1434
                                step = GetSASLNext;
 
1435
                                return false;
 
1436
                        }
 
1437
                        else if(e.tagName() == "success") {
 
1438
                                QString str = e.text();
 
1439
                                // "additional data with success" ?
 
1440
                                if(!str.isEmpty())
 
1441
                                {
 
1442
                                        QByteArray a = QCA::Base64().stringToArray(str).toByteArray();
 
1443
                                        sasl_step = a;
 
1444
                                        sasl_authed = true;
 
1445
                                        need = NSASLNext;
 
1446
                                        step = GetSASLNext;
 
1447
                                        return false;
 
1448
                                }
 
1449
 
 
1450
                                sasl_authed = true;
 
1451
                                event = ESASLSuccess;
 
1452
                                step = HandleSASLSuccess;
 
1453
                                return true;
 
1454
                        }
 
1455
                        else if(e.tagName() == "failure") {
 
1456
                                QDomElement t = firstChildElement(e);
 
1457
                                if(t.isNull() || t.namespaceURI() != NS_SASL)
 
1458
                                        errCond = -1;
 
1459
                                else
 
1460
                                        errCond = stringToSASLCond(t.tagName());
 
1461
 
 
1462
                                event = EError;
 
1463
                                errorCode = ErrAuth;
 
1464
                                return true;
 
1465
                        }
 
1466
                        else {
 
1467
                                event = EError;
 
1468
                                errorCode = ErrProtocol;
 
1469
                                return true;
 
1470
                        }
 
1471
                }
 
1472
        }
 
1473
        else if(step == GetBindResponse) {
 
1474
                if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
 
1475
                        QString type(e.attribute("type"));
 
1476
                        QString id(e.attribute("id"));
 
1477
 
 
1478
                        if(id == "bind_1" && (type == "result" || type == "error")) {
 
1479
                                if(type == "result") {
 
1480
                                        QDomElement b = e.elementsByTagNameNS(NS_BIND, "bind").item(0).toElement();
 
1481
                                        Jid j;
 
1482
                                        if(!b.isNull()) {
 
1483
                                                QDomElement je = e.elementsByTagName("jid").item(0).toElement();
 
1484
                                                j = je.text();
 
1485
                                        }
 
1486
                                        if(!j.isValid()) {
 
1487
                                                event = EError;
 
1488
                                                errorCode = ErrProtocol;
 
1489
                                                return true;
 
1490
                                        }
 
1491
                                        jid_ = j;
 
1492
                                        return loginComplete();
 
1493
                                }
 
1494
                                else {
 
1495
                                        errCond = -1;
 
1496
 
 
1497
                                        QDomElement err = e.elementsByTagNameNS(NS_CLIENT, "error").item(0).toElement();
 
1498
                                        if(!err.isNull()) {
 
1499
                                                // get error condition
 
1500
                                                QDomNodeList nl = err.childNodes();
 
1501
                                                QDomElement t;
 
1502
                                                for(int n = 0; n < nl.count(); ++n) {
 
1503
                                                        QDomNode i = nl.item(n);
 
1504
                                                        if(i.isElement()) {
 
1505
                                                                t = i.toElement();
 
1506
                                                                break;
 
1507
                                                        }
 
1508
                                                }
 
1509
                                                if(!t.isNull() && t.namespaceURI() == NS_STANZAS) {
 
1510
                                                        QString cond = t.tagName();
 
1511
                                                        if(cond == "not-allowed")
 
1512
                                                                errCond = BindNotAllowed;
 
1513
                                                        else if(cond == "conflict")
 
1514
                                                                errCond = BindConflict;
 
1515
                                                }
 
1516
                                        }
 
1517
 
 
1518
                                        event = EError;
 
1519
                                        errorCode = ErrBind;
 
1520
                                        return true;
 
1521
                                }
 
1522
                        }
 
1523
                        else {
 
1524
                                // ignore
 
1525
                        }
 
1526
                }
 
1527
                else {
 
1528
                        // ignore
 
1529
                }
 
1530
        }
 
1531
        else if(step == GetAuthGetResponse) {
 
1532
                // waiting for an iq
 
1533
                if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
 
1534
                        Jid from(e.attribute("from"));
 
1535
                        QString type(e.attribute("type"));
 
1536
                        QString id(e.attribute("id"));
 
1537
 
 
1538
                        bool okfrom = (from.isEmpty() || from.compare(Jid(to)));
 
1539
                        if(okfrom && id == "auth_1" && (type == "result" || type == "error")) {
 
1540
                                if(type == "result") {
 
1541
                                        QDomElement q = e.elementsByTagNameNS("jabber:iq:auth", "query").item(0).toElement();
 
1542
                                        if(q.isNull() || q.elementsByTagName("username").item(0).isNull() || q.elementsByTagName("resource").item(0).isNull()) {
 
1543
                                                event = EError;
 
1544
                                                errorCode = ErrProtocol;
 
1545
                                                return true;
 
1546
                                        }
 
1547
                                        bool plain_supported = !q.elementsByTagName("password").item(0).isNull();
 
1548
                                        bool digest_supported = !q.elementsByTagName("digest").item(0).isNull();
 
1549
 
 
1550
                                        if(!digest_supported && !plain_supported) {
 
1551
                                                event = EError;
 
1552
                                                errorCode = ErrProtocol;
 
1553
                                                return true;
 
1554
                                        }
 
1555
 
 
1556
                                        // plain text not allowed?
 
1557
                                        if(!digest_supported && !allowPlain) {
 
1558
                                                event = EError;
 
1559
                                                errorCode = ErrPlain;
 
1560
                                                return true;
 
1561
                                        }
 
1562
 
 
1563
                                        digest = digest_supported;
 
1564
                                        need = NPassword;
 
1565
                                        step = HandleAuthSet;
 
1566
                                        return false;
 
1567
                                }
 
1568
                                else {
 
1569
                                        errCond = getOldErrorCode(e);
 
1570
 
 
1571
                                        event = EError;
 
1572
                                        errorCode = ErrAuth;
 
1573
                                        return true;
 
1574
                                }
 
1575
                        }
 
1576
                        else {
 
1577
                                // ignore
 
1578
                        }
 
1579
                }
 
1580
                else {
 
1581
                        // ignore
 
1582
                }
 
1583
        }
 
1584
        else if(step == GetAuthSetResponse) {
 
1585
                // waiting for an iq
 
1586
                if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
 
1587
                        Jid from(e.attribute("from"));
 
1588
                        QString type(e.attribute("type"));
 
1589
                        QString id(e.attribute("id"));
 
1590
 
 
1591
                        bool okfrom = (from.isEmpty() || from.compare(Jid(to)));
 
1592
                        if(okfrom && id == "auth_2" && (type == "result" || type == "error")) {
 
1593
                                if(type == "result") {
 
1594
                                        return loginComplete();
 
1595
                                }
 
1596
                                else {
 
1597
                                        errCond = getOldErrorCode(e);
 
1598
 
 
1599
                                        event = EError;
 
1600
                                        errorCode = ErrAuth;
 
1601
                                        return true;
 
1602
                                }
 
1603
                        }
 
1604
                        else {
 
1605
                                // ignore
 
1606
                        }
 
1607
                }
 
1608
                else {
 
1609
                        // ignore
 
1610
                }
 
1611
        }
 
1612
        // server
 
1613
        else if(step == GetRequest) {
 
1614
                printf("get request: [%s], %s\n", e.namespaceURI().toLatin1().data(), e.tagName().toLatin1().data());
 
1615
                if(e.namespaceURI() == NS_TLS && e.localName() == "starttls") {
 
1616
                        // TODO: don't let this be done twice
 
1617
 
 
1618
                        QDomElement e = doc.createElementNS(NS_TLS, "proceed");
 
1619
                        writeElement(e, TypeElement, false, true);
 
1620
                        event = ESend;
 
1621
                        step = HandleTLS;
 
1622
                        return true;
 
1623
                }
 
1624
                if(e.namespaceURI() == NS_SASL) {
 
1625
                        if(e.localName() == "auth") {
 
1626
                                if(sasl_started) {
 
1627
                                        // TODO
 
1628
                                        printf("error\n");
 
1629
                                        return false;
 
1630
                                }
 
1631
 
 
1632
                                sasl_started = true;
 
1633
                                sasl_mech = e.attribute("mechanism");
 
1634
                                // TODO: if child text missing, don't pass it
 
1635
                                sasl_step = QCA::Base64().stringToArray(e.text()).toByteArray();
 
1636
                                need = NSASLFirst;
 
1637
                                step = GetSASLNext;
 
1638
                                return false;
 
1639
                        }
 
1640
                        else {
 
1641
                                // TODO
 
1642
                                printf("unknown sasl tag\n");
 
1643
                                return false;
 
1644
                        }
 
1645
                }
 
1646
                if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
 
1647
                        QDomElement b = e.elementsByTagNameNS(NS_BIND, "bind").item(0).toElement();
 
1648
                        if(!b.isNull()) {
 
1649
                                QDomElement res = b.elementsByTagName("resource").item(0).toElement();
 
1650
                                QString resource = res.text();
 
1651
 
 
1652
                                QDomElement r = doc.createElement("iq");
 
1653
                                r.setAttribute("type", "result");
 
1654
                                r.setAttribute("id", e.attribute("id"));
 
1655
                                QDomElement bind = doc.createElementNS(NS_BIND, "bind");
 
1656
                                QDomElement jid = doc.createElement("jid");
 
1657
                                Jid j = user + '@' + host + '/' + resource;
 
1658
                                jid.appendChild(doc.createTextNode(j.full()));
 
1659
                                bind.appendChild(jid);
 
1660
                                r.appendChild(bind);
 
1661
 
 
1662
                                writeElement(r, TypeElement, false);
 
1663
                                event = ESend;
 
1664
                                // TODO
 
1665
                                return true;
 
1666
                        }
 
1667
                        else {
 
1668
                                // TODO
 
1669
                        }
 
1670
                }
 
1671
        }
 
1672
        else if(step == GetSASLResponse) {
 
1673
                if(e.namespaceURI() == NS_SASL && e.localName() == "response") {
 
1674
                        sasl_step = QCA::Base64().stringToArray(e.text()).toByteArray();
 
1675
                        need = NSASLNext;
 
1676
                        step = GetSASLNext;
 
1677
                        return false;
 
1678
                }
 
1679
        }
 
1680
 
 
1681
        if(isReady()) {
 
1682
                if(!e.isNull() && isValidStanza(e)) {
 
1683
                        stanzaToRecv = e;
 
1684
                        event = EStanzaReady;
 
1685
                        setIncomingAsExternal();
 
1686
                        return true;
 
1687
                }
 
1688
        }
 
1689
 
 
1690
        need = NNotify;
 
1691
        notify |= NRecv;
 
1692
        return false;
 
1693
}