1
/****************************************************************************
2
** jabstream.cpp - handles a Jabber XML stream
3
** Copyright (C) 2001, 2002 Justin Karneges
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.
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.
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.
19
****************************************************************************/
22
#include<qtextstream.h>
26
/****************************************************************************
28
****************************************************************************/
29
JabStream::JabStream()
40
v_isConnected = v_isHandShaken = first_time = FALSE;
44
in.setAutoDelete(TRUE);
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");
55
//printf("jabstream: SSL not available.\n");
59
JabStream::~JabStream()
67
void JabStream::connectToHost(const QString &_host, int _port)
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)));
80
sock->connectToHost(host, port);
83
void JabStream::disc()
92
sendString("</stream:stream>\n");
113
v_isConnected = v_isHandShaken = first_time = FALSE;
116
void JabStream::setNoop(int mills)
132
t = new QTimer(this);
133
connect(t, SIGNAL(timeout()), SLOT(doNoop()));
139
bool JabStream::isSSLSupported()
141
return ssl ? TRUE: FALSE;
144
void JabStream::setSSLEnabled(bool use)
155
void JabStream::sendPacket(const QDomElement &e)
157
sendString(elemToString(e));
160
void JabStream::sendString(const QCString &str)
166
a.resize(a.size()-1); // kick off the trailing zero
170
sock->writeBlock(str, str.length());
175
void JabStream::sock_connected()
180
v_isConnected = TRUE;
182
// start an XML document
183
doc = new QDomDocument;
185
// setup the input source
186
src = new QXmlInputSource;
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);
196
// Start the handshake
198
str.sprintf("<stream:stream to=\"%s\" xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\">\n", encodeXML(host).data());
202
void JabStream::sock_disconnected()
204
errType = JABSTREAM_ERR_DISC;
206
// process error later
207
QTimer::singleShot(0, this, SLOT(delayedProcessError()));
210
void JabStream::sock_readyRead()
212
//printf("jabstream: incoming data [%d]\n", (int)sock->bytesAvailable());
216
size = sock->bytesAvailable();
218
sock->readBlock(buf.data(), size);
221
ssl->putIncomingSSLData(buf);
223
processIncomingData(buf);
226
void JabStream::sock_error(int x)
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;
235
errType = JABSTREAM_ERR_CONNTIMEOUT;
237
// process error later
238
QTimer::singleShot(0, this, SLOT(delayedProcessError()));
241
void JabStream::ssl_outgoingReady()
243
QByteArray a = ssl->getOutgoingSSLData();
244
sock->writeBlock(a.data(), a.size());
247
void JabStream::ssl_readyRead()
249
processIncomingData(ssl->recv());
252
void JabStream::ssl_error()
254
errType = JABSTREAM_ERR_HANDSHAKE;
256
// process error later
257
QTimer::singleShot(0, this, SLOT(delayedProcessError()));
260
void JabStream::processIncomingData(const QByteArray &buf)
262
// crunch the new data (*chomp, chomp!*)
265
reader->parse(src, TRUE);
269
reader->parseContinue();
271
// process packets later
272
QTimer::singleShot(0, this, SLOT(delayedProcessReceived()));
275
void JabStream::delayedProcessError()
281
void JabStream::delayedProcessReceived()
284
while(!in.isEmpty()) {
285
QDomElement *e = in.dequeue();
291
void JabStream::delayedProcessHandShake()
293
v_isHandShaken = TRUE;
300
void JabStream::doNoop()
306
void JabStream::packetReady(const QDomElement &e)
308
in.enqueue(new QDomElement(e));
311
void JabStream::handshake(bool ok, const QString &id)
314
errType = JABSTREAM_ERR_HANDSHAKE;
316
// process error later
317
QTimer::singleShot(0, this, SLOT(delayedProcessError()));
323
// process handshake later
324
QTimer::singleShot(0, this, SLOT(delayedProcessHandShake()));
327
QCString JabStream::encodeXML(QString str)
329
str.replace(QRegExp("&"), "&");
330
str.replace(QRegExp("<"), "<");
331
str.replace(QRegExp(">"), ">");
332
str.replace(QRegExp("\""), """);
333
str.replace(QRegExp("'"), "'");
338
QCString JabStream::elemToString(const QDomElement &e)
341
QTextStream ts(&out, IO_WriteOnly);
347
/****************************************************************************
349
****************************************************************************/
350
JabXmlHandler::JabXmlHandler(QDomDocument *_doc)
355
QString JabXmlHandler::toLower(QString s)
357
for(unsigned int n = 0; n < s.length(); ++n)
358
s.at(n) = s.at(n).lower();
363
bool JabXmlHandler::startDocument()
369
bool JabXmlHandler::startElement(const QString &ns, const QString &, const QString &name, const QXmlAttributes &attributes)
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));
381
current.appendChild(tag);
385
// add namespace attribute only if it's different from parents
387
QDomElement par = current.parentNode().toElement();
388
while(!par.isNull()) {
389
if(par.attribute("xmlns") == ns) {
393
par = par.parentNode().toElement();
395
// stream:stream is considered a parent also
396
if(ns == "jabber:client")
399
tag.setAttribute("xmlns", ns);
405
if(toLower(name) == "stream:stream") {
407
for(int n = 0; n < attributes.length(); ++n) {
408
if(toLower(attributes.qName(n)) == "id") {
409
id = attributes.value(n);
417
handshake(FALSE, id);
425
bool JabXmlHandler::endElement(const QString &, const QString &, const QString &)
430
// done with a section? export the chunk
435
chunk = QDomNode().toElement();
436
current = QDomNode().toElement();
439
current = current.parentNode().toElement();
445
bool JabXmlHandler::characters(const QString &str)
448
QString content = str.stripWhiteSpace();
449
if(content.isEmpty())
452
if(!current.isNull()) {
453
QDomText text = doc->createTextNode(content);
454
current.appendChild(text);