~ubuntu-branches/ubuntu/utopic/psi/utopic

« back to all changes in this revision

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