~ubuntu-branches/ubuntu/feisty/psi/feisty

« back to all changes in this revision

Viewing changes to src/jabstream.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jan Niehusmann
  • Date: 2002-04-19 02:28:44 UTC
  • Revision ID: james.westby@ubuntu.com-20020419022844-za7xgai5qyfd9xv6
Tags: upstream-0.8.5
ImportĀ upstreamĀ versionĀ 0.8.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
** jabstream.cpp - handles a Jabber XML stream
 
3
** Copyright (C) 2001, 2002  Justin Karneges
 
4
**
 
5
** This program is free software; you can redistribute it and/or
 
6
** modify it under the terms of the GNU General Public License
 
7
** as published by the Free Software Foundation; either version 2
 
8
** of the License, or (at your option) any later version.
 
9
**
 
10
** This program 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
 
13
** GNU General Public License for more details.
 
14
**
 
15
** You should have received a copy of the GNU General Public License
 
16
** along with this program; if not, write to the Free Software
 
17
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
 
18
**
 
19
****************************************************************************/
 
20
 
 
21
#include"jabstream.h"
 
22
#include<qtextstream.h>
 
23
#include<qregexp.h>
 
24
 
 
25
 
 
26
/****************************************************************************
 
27
  JabStream
 
28
****************************************************************************/
 
29
JabStream::JabStream()
 
30
{
 
31
        ssl = 0;
 
32
        ssl = new SSLFilter;
 
33
        use_ssl = FALSE;
 
34
 
 
35
        sock = 0;
 
36
        doc = 0;
 
37
        src = 0;
 
38
        reader = 0;
 
39
        handler = 0;
 
40
        v_isConnected = v_isHandShaken = first_time = FALSE;
 
41
 
 
42
        t = 0;
 
43
        noop_time = 0;
 
44
        in.setAutoDelete(TRUE);
 
45
 
 
46
        if(ssl->isSupported()) {
 
47
                connect(ssl, SIGNAL(outgoingSSLDataReady()), SLOT(ssl_outgoingReady()));
 
48
                connect(ssl, SIGNAL(readyRead()), SLOT(ssl_readyRead()));
 
49
                connect(ssl, SIGNAL(error()), SLOT(ssl_error()));
 
50
                //printf("jabstream: SSL supported.\n");
 
51
        }
 
52
        else {
 
53
                delete ssl;
 
54
                ssl = 0;
 
55
                //printf("jabstream: SSL not available.\n");
 
56
        }
 
57
}
 
58
 
 
59
JabStream::~JabStream()
 
60
{
 
61
        disc();
 
62
 
 
63
        if(ssl)
 
64
                delete ssl;
 
65
}
 
66
 
 
67
void JabStream::connectToHost(const QString &_host, int _port)
 
68
{
 
69
        host = _host;
 
70
        port = _port;
 
71
 
 
72
        disc();
 
73
 
 
74
        sock = new QSocket;
 
75
        connect(sock, SIGNAL(connected()),        SLOT(sock_connected()));
 
76
        connect(sock, SIGNAL(connectionClosed()), SLOT(sock_disconnected()));
 
77
        connect(sock, SIGNAL(readyRead()),        SLOT(sock_readyRead()));
 
78
        connect(sock, SIGNAL(error(int)),         SLOT(sock_error(int)));
 
79
 
 
80
        sock->connectToHost(host, port);
 
81
}
 
82
 
 
83
void JabStream::disc()
 
84
{
 
85
        if(t) {
 
86
                delete t;
 
87
                t = 0;
 
88
        }
 
89
 
 
90
        if(sock) {
 
91
                if(v_isConnected)
 
92
                        sendString("</stream:stream>\n");
 
93
 
 
94
                sock->close();
 
95
                delete sock;
 
96
                sock = 0;
 
97
 
 
98
                if(v_isConnected) {
 
99
                        delete reader;
 
100
                        delete src;
 
101
                        delete handler;
 
102
                        delete doc;
 
103
                        doc = 0;
 
104
                        src = 0;
 
105
                        reader = 0;
 
106
                        handler = 0;
 
107
                }
 
108
        }
 
109
 
 
110
        if(use_ssl)
 
111
                ssl->reset();
 
112
 
 
113
        v_isConnected = v_isHandShaken = first_time = FALSE;
 
114
}
 
115
 
 
116
void JabStream::setNoop(int mills)
 
117
{
 
118
        noop_time = mills;
 
119
 
 
120
        if(!v_isHandShaken)
 
121
                return;
 
122
 
 
123
        if(noop_time == 0) {
 
124
                if(t) {
 
125
                        delete t;
 
126
                        t = 0;
 
127
                }
 
128
                return;
 
129
        }
 
130
 
 
131
        if(!t) {
 
132
                t = new QTimer(this);
 
133
                connect(t, SIGNAL(timeout()), SLOT(doNoop()));
 
134
        }
 
135
 
 
136
        t->start(noop_time);
 
137
}
 
138
 
 
139
bool JabStream::isSSLSupported()
 
140
{
 
141
        return ssl ? TRUE: FALSE;
 
142
}
 
143
 
 
144
void JabStream::setSSLEnabled(bool use)
 
145
{
 
146
        if(v_isConnected)
 
147
                return;
 
148
 
 
149
        if(use && ssl)
 
150
                use_ssl = TRUE;
 
151
        else
 
152
                use_ssl = FALSE;
 
153
}
 
154
 
 
155
void JabStream::sendPacket(const QDomElement &e)
 
156
{
 
157
        sendString(elemToString(e));
 
158
}
 
159
 
 
160
void JabStream::sendString(const QCString &str)
 
161
{
 
162
        if(v_isConnected) {
 
163
                if(use_ssl) {
 
164
                        QByteArray a = str;
 
165
                        a.detach();
 
166
                        a.resize(a.size()-1); // kick off the trailing zero
 
167
                        ssl->send(a);
 
168
                }
 
169
                else {
 
170
                        sock->writeBlock(str, str.length());
 
171
                }
 
172
        }
 
173
}
 
174
 
 
175
void JabStream::sock_connected()
 
176
{
 
177
        if(use_ssl)
 
178
                ssl->begin();
 
179
 
 
180
        v_isConnected = TRUE;
 
181
 
 
182
        // start an XML document
 
183
        doc = new QDomDocument;
 
184
 
 
185
        // setup the input source
 
186
        src = new QXmlInputSource;
 
187
        first_time = TRUE;
 
188
 
 
189
        // setup the reader and handler
 
190
        reader = new QXmlSimpleReader;
 
191
        handler = new JabXmlHandler(doc);
 
192
        connect(handler, SIGNAL(packetReady(const QDomElement &)), SLOT(packetReady(const QDomElement &)));
 
193
        connect(handler, SIGNAL(handshake(bool, const QString &)), SLOT(handshake(bool, const QString &)));
 
194
        reader->setContentHandler(handler);
 
195
 
 
196
        // Start the handshake
 
197
        QCString str;
 
198
        str.sprintf("<stream:stream to=\"%s\" xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\">\n", encodeXML(host).data());
 
199
        sendString(str);
 
200
}
 
201
 
 
202
void JabStream::sock_disconnected()
 
203
{
 
204
        errType = JABSTREAM_ERR_DISC;
 
205
 
 
206
        // process error later
 
207
        QTimer::singleShot(0, this, SLOT(delayedProcessError()));
 
208
}
 
209
 
 
210
void JabStream::sock_readyRead()
 
211
{
 
212
        //printf("jabstream: incoming data [%d]\n", (int)sock->bytesAvailable());
 
213
        int size;
 
214
        QByteArray buf;
 
215
 
 
216
        size = sock->bytesAvailable();
 
217
        buf.resize(size);
 
218
        sock->readBlock(buf.data(), size);
 
219
 
 
220
        if(use_ssl)
 
221
                ssl->putIncomingSSLData(buf);
 
222
        else
 
223
                processIncomingData(buf);
 
224
}
 
225
 
 
226
void JabStream::sock_error(int x)
 
227
{
 
228
        if(x == QSocket::ErrConnectionRefused)
 
229
                errType = JABSTREAM_ERR_CONNREFUSED;
 
230
        else if(x == QSocket::ErrHostNotFound)
 
231
                errType = JABSTREAM_ERR_DNS;
 
232
        else if(x == QSocket::ErrSocketRead)
 
233
                errType = JABSTREAM_ERR_SOCKET;
 
234
        else
 
235
                errType = JABSTREAM_ERR_CONNTIMEOUT;
 
236
 
 
237
        // process error later
 
238
        QTimer::singleShot(0, this, SLOT(delayedProcessError()));
 
239
}
 
240
 
 
241
void JabStream::ssl_outgoingReady()
 
242
{
 
243
        QByteArray a = ssl->getOutgoingSSLData();
 
244
        sock->writeBlock(a.data(), a.size());
 
245
}
 
246
 
 
247
void JabStream::ssl_readyRead()
 
248
{
 
249
        processIncomingData(ssl->recv());
 
250
}
 
251
 
 
252
void JabStream::ssl_error()
 
253
{
 
254
        errType = JABSTREAM_ERR_HANDSHAKE;
 
255
 
 
256
        // process error later
 
257
        QTimer::singleShot(0, this, SLOT(delayedProcessError()));
 
258
}
 
259
 
 
260
void JabStream::processIncomingData(const QByteArray &buf)
 
261
{
 
262
        // crunch the new data (*chomp, chomp!*)
 
263
        src->setData(buf);
 
264
        if(first_time) {
 
265
                reader->parse(src, TRUE);
 
266
                first_time = FALSE;
 
267
        }
 
268
        else
 
269
                reader->parseContinue();
 
270
 
 
271
        // process packets later
 
272
        QTimer::singleShot(0, this, SLOT(delayedProcessReceived()));
 
273
}
 
274
 
 
275
void JabStream::delayedProcessError()
 
276
{
 
277
        disc();
 
278
        error(errType);
 
279
}
 
280
 
 
281
void JabStream::delayedProcessReceived()
 
282
{
 
283
        // process chunks
 
284
        while(!in.isEmpty()) {
 
285
                QDomElement *e = in.dequeue();
 
286
                receivePacket(*e);
 
287
                delete e;
 
288
        }
 
289
}
 
290
 
 
291
void JabStream::delayedProcessHandShake()
 
292
{
 
293
        v_isHandShaken = TRUE;
 
294
 
 
295
        setNoop(noop_time);
 
296
 
 
297
        connected();
 
298
}
 
299
 
 
300
void JabStream::doNoop()
 
301
{
 
302
        if(v_isHandShaken)
 
303
                sendString("\n");
 
304
}
 
305
 
 
306
void JabStream::packetReady(const QDomElement &e)
 
307
{
 
308
        in.enqueue(new QDomElement(e));
 
309
}
 
310
 
 
311
void JabStream::handshake(bool ok, const QString &id)
 
312
{
 
313
        if(!ok) {
 
314
                errType = JABSTREAM_ERR_HANDSHAKE;
 
315
 
 
316
                // process error later
 
317
                QTimer::singleShot(0, this, SLOT(delayedProcessError()));
 
318
                return;
 
319
        }
 
320
 
 
321
        v_id = id;
 
322
 
 
323
        // process handshake later
 
324
        QTimer::singleShot(0, this, SLOT(delayedProcessHandShake()));
 
325
}
 
326
 
 
327
QCString JabStream::encodeXML(QString str)
 
328
{
 
329
        str.replace(QRegExp("&"), "&amp;");
 
330
        str.replace(QRegExp("<"), "&lt;");
 
331
        str.replace(QRegExp(">"), "&gt;");
 
332
        str.replace(QRegExp("\""), "&quot;");
 
333
        str.replace(QRegExp("'"), "&apos;");
 
334
 
 
335
        return str.utf8();
 
336
}
 
337
 
 
338
QCString JabStream::elemToString(const QDomElement &e)
 
339
{
 
340
        QString out;
 
341
        QTextStream ts(&out, IO_WriteOnly);
 
342
        e.save(ts, 0);
 
343
        return out.utf8();
 
344
}
 
345
 
 
346
 
 
347
/****************************************************************************
 
348
  JabXmlHandler
 
349
****************************************************************************/
 
350
JabXmlHandler::JabXmlHandler(QDomDocument *_doc)
 
351
{
 
352
        doc = _doc;
 
353
}
 
354
 
 
355
QString JabXmlHandler::toLower(QString s)
 
356
{
 
357
        for(unsigned int n = 0; n < s.length(); ++n)
 
358
                s.at(n) = s.at(n).lower();
 
359
 
 
360
        return s;
 
361
}
 
362
 
 
363
bool JabXmlHandler::startDocument()
 
364
{
 
365
        depth = 0;
 
366
        return TRUE;
 
367
}
 
368
 
 
369
bool JabXmlHandler::startElement(const QString &ns, const QString &, const QString &name, const QXmlAttributes &attributes)
 
370
{
 
371
        if(depth >= 1) {
 
372
                QDomElement tag = doc->createElement(toLower(name));
 
373
                for(int n = 0; n < attributes.length(); ++n)
 
374
                        tag.setAttribute(toLower(attributes.qName(n)), attributes.value(n));
 
375
 
 
376
                if(depth == 1) {
 
377
                        current = tag;
 
378
                        chunk = tag;
 
379
                }
 
380
                else {
 
381
                        current.appendChild(tag);
 
382
                        current = tag;
 
383
                }
 
384
 
 
385
                // add namespace attribute only if it's different from parents
 
386
                bool ok = TRUE;
 
387
                QDomElement par = current.parentNode().toElement();
 
388
                while(!par.isNull()) {
 
389
                        if(par.attribute("xmlns") == ns) {
 
390
                                ok = FALSE;
 
391
                                break;
 
392
                        }
 
393
                        par = par.parentNode().toElement();
 
394
                }
 
395
                // stream:stream is considered a parent also
 
396
                if(ns == "jabber:client")
 
397
                        ok = FALSE;
 
398
                if(ok)
 
399
                        tag.setAttribute("xmlns", ns);
 
400
        }
 
401
        else {
 
402
                QString id;
 
403
 
 
404
                // stream tag?
 
405
                if(toLower(name) == "stream:stream") {
 
406
                        // get the id
 
407
                        for(int n = 0; n < attributes.length(); ++n) {
 
408
                                if(toLower(attributes.qName(n)) == "id") {
 
409
                                        id = attributes.value(n);
 
410
                                        break;
 
411
                                }
 
412
                        }
 
413
 
 
414
                        handshake(TRUE, id);
 
415
                }
 
416
                else
 
417
                        handshake(FALSE, id);
 
418
        }
 
419
 
 
420
        ++depth;
 
421
 
 
422
        return TRUE;
 
423
}
 
424
 
 
425
bool JabXmlHandler::endElement(const QString &, const QString &, const QString &)
 
426
{
 
427
        --depth;
 
428
 
 
429
        if(depth >= 1) {
 
430
                // done with a section?  export the chunk
 
431
                if(depth == 1) {
 
432
                        packetReady(chunk);
 
433
 
 
434
                        // nuke
 
435
                        chunk = QDomNode().toElement();
 
436
                        current = QDomNode().toElement();
 
437
                }
 
438
                else
 
439
                        current = current.parentNode().toElement();
 
440
        }
 
441
 
 
442
        return TRUE;
 
443
}
 
444
 
 
445
bool JabXmlHandler::characters(const QString &str)
 
446
{
 
447
        if(depth >= 1) {
 
448
                QString content = str.stripWhiteSpace();
 
449
                if(content.isEmpty())
 
450
                        return TRUE;
 
451
 
 
452
                if(!current.isNull()) {
 
453
                        QDomText text = doc->createTextNode(content);
 
454
                        current.appendChild(text);
 
455
                }
 
456
        }
 
457
 
 
458
        return TRUE;
 
459
}