~ubuntu-branches/ubuntu/oneiric/psi/oneiric

« back to all changes in this revision

Viewing changes to src/jabstream.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jan Niehusmann
  • Date: 2005-01-10 17:41:43 UTC
  • mfrom: (1.2.1 upstream) (2.1.2 hoary)
  • Revision ID: james.westby@ubuntu.com-20050110174143-ltocv5zapl6blf5d
Tags: 0.9.3-1
* New upstream release
* Cleaned up debian/rules (some things are done by upstream Makefiles now)
* Fixed some lintian warnings:
  - removed executable bit from some .png files
  - moved psi.desktop to /usr/share/applications
* Updated menu files

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
 
}