initial commit
This commit is contained in:
commit
9d20827c46
2469 changed files with 470994 additions and 0 deletions
200
iris-legacy/iris/xmpp-core/compress.cpp
Normal file
200
iris-legacy/iris/xmpp-core/compress.cpp
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
#include <QtCore> // for qWarning()
|
||||
#include <QObject>
|
||||
#include <QIODevice>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "compress.h"
|
||||
|
||||
#define CHUNK_SIZE 1024
|
||||
|
||||
static void initZStream(z_stream* z)
|
||||
{
|
||||
z->next_in = NULL;
|
||||
z->avail_in = 0;
|
||||
z->total_in = 0;
|
||||
z->next_out = NULL;
|
||||
z->avail_out = 0;
|
||||
z->total_out = 0;
|
||||
z->msg = NULL;
|
||||
z->state = NULL;
|
||||
z->zalloc = Z_NULL;
|
||||
z->zfree = Z_NULL;
|
||||
z->opaque = Z_NULL;
|
||||
z->data_type = Z_BINARY;
|
||||
z->adler = 0;
|
||||
z->reserved = 0;
|
||||
}
|
||||
|
||||
Compressor::Compressor(QIODevice* device, int compression) : device_(device)
|
||||
{
|
||||
zlib_stream_ = (z_stream*) malloc(sizeof(z_stream));
|
||||
initZStream(zlib_stream_);
|
||||
int result = deflateInit(zlib_stream_, compression);
|
||||
Q_ASSERT(result == Z_OK);
|
||||
Q_UNUSED(result);
|
||||
connect(device, SIGNAL(aboutToClose()), this, SLOT(flush()));
|
||||
flushed_ = false;
|
||||
}
|
||||
|
||||
Compressor::~Compressor()
|
||||
{
|
||||
flush();
|
||||
free(zlib_stream_);
|
||||
}
|
||||
|
||||
void Compressor::flush()
|
||||
{
|
||||
if (flushed_)
|
||||
return;
|
||||
|
||||
// Flush
|
||||
write(QByteArray(),true);
|
||||
int result = deflateEnd(zlib_stream_);
|
||||
if (result != Z_OK)
|
||||
qWarning("compressor.c: deflateEnd failed (%d)", result);
|
||||
|
||||
flushed_ = true;
|
||||
}
|
||||
|
||||
int Compressor::write(const QByteArray& input)
|
||||
{
|
||||
return write(input,false);
|
||||
}
|
||||
|
||||
int Compressor::write(const QByteArray& input, bool flush)
|
||||
{
|
||||
int result;
|
||||
zlib_stream_->avail_in = input.size();
|
||||
zlib_stream_->next_in = (Bytef*) input.data();
|
||||
QByteArray output;
|
||||
|
||||
// Write the data
|
||||
int output_position = 0;
|
||||
do {
|
||||
output.resize(output_position + CHUNK_SIZE);
|
||||
zlib_stream_->avail_out = CHUNK_SIZE;
|
||||
zlib_stream_->next_out = (Bytef*) (output.data() + output_position);
|
||||
result = deflate(zlib_stream_,(flush ? Z_FINISH : Z_NO_FLUSH));
|
||||
if (result == Z_STREAM_ERROR) {
|
||||
qWarning("compressor.cpp: Error ('%s')", zlib_stream_->msg);
|
||||
return result;
|
||||
}
|
||||
output_position += CHUNK_SIZE;
|
||||
}
|
||||
while (zlib_stream_->avail_out == 0);
|
||||
if (zlib_stream_->avail_in != 0) {
|
||||
qWarning("Compressor: avail_in != 0");
|
||||
}
|
||||
output_position -= zlib_stream_->avail_out;
|
||||
|
||||
// Flush the data
|
||||
if (!flush) {
|
||||
do {
|
||||
output.resize(output_position + CHUNK_SIZE);
|
||||
zlib_stream_->avail_out = CHUNK_SIZE;
|
||||
zlib_stream_->next_out = (Bytef*) (output.data() + output_position);
|
||||
result = deflate(zlib_stream_,Z_SYNC_FLUSH);
|
||||
if (result == Z_STREAM_ERROR) {
|
||||
qWarning("compressor.cpp: Error ('%s')", zlib_stream_->msg);
|
||||
return result;
|
||||
}
|
||||
output_position += CHUNK_SIZE;
|
||||
}
|
||||
while (zlib_stream_->avail_out == 0);
|
||||
output_position -= zlib_stream_->avail_out;
|
||||
}
|
||||
output.resize(output_position);
|
||||
|
||||
// Write the compressed data
|
||||
device_->write(output);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
Decompressor::Decompressor(QIODevice* device) : device_(device)
|
||||
{
|
||||
zlib_stream_ = (z_stream*) malloc(sizeof(z_stream));
|
||||
initZStream(zlib_stream_);
|
||||
int result = inflateInit(zlib_stream_);
|
||||
Q_ASSERT(result == Z_OK);
|
||||
Q_UNUSED(result);
|
||||
connect(device, SIGNAL(aboutToClose()), this, SLOT(flush()));
|
||||
flushed_ = false;
|
||||
}
|
||||
|
||||
Decompressor::~Decompressor()
|
||||
{
|
||||
flush();
|
||||
free(zlib_stream_);
|
||||
}
|
||||
|
||||
void Decompressor::flush()
|
||||
{
|
||||
if (flushed_)
|
||||
return;
|
||||
|
||||
// Flush
|
||||
write(QByteArray(),true);
|
||||
int result = inflateEnd(zlib_stream_);
|
||||
if (result != Z_OK)
|
||||
qWarning("compressor.c: inflateEnd failed (%d)", result);
|
||||
|
||||
flushed_ = true;
|
||||
}
|
||||
|
||||
int Decompressor::write(const QByteArray& input)
|
||||
{
|
||||
return write(input,false);
|
||||
}
|
||||
|
||||
int Decompressor::write(const QByteArray& input, bool flush)
|
||||
{
|
||||
int result;
|
||||
zlib_stream_->avail_in = input.size();
|
||||
zlib_stream_->next_in = (Bytef*) input.data();
|
||||
QByteArray output;
|
||||
|
||||
// Write the data
|
||||
int output_position = 0;
|
||||
do {
|
||||
output.resize(output_position + CHUNK_SIZE);
|
||||
zlib_stream_->avail_out = CHUNK_SIZE;
|
||||
zlib_stream_->next_out = (Bytef*) (output.data() + output_position);
|
||||
result = inflate(zlib_stream_,(flush ? Z_FINISH : Z_NO_FLUSH));
|
||||
if (result == Z_STREAM_ERROR) {
|
||||
qWarning("compressor.cpp: Error ('%s')", zlib_stream_->msg);
|
||||
return result;
|
||||
}
|
||||
output_position += CHUNK_SIZE;
|
||||
}
|
||||
while (zlib_stream_->avail_out == 0);
|
||||
//Q_ASSERT(zlib_stream_->avail_in == 0);
|
||||
if (zlib_stream_->avail_in != 0) {
|
||||
qWarning() << "Decompressor: Unexpected state: avail_in=" << zlib_stream_->avail_in << ",avail_out=" << zlib_stream_->avail_out << ",result=" << result;
|
||||
return Z_STREAM_ERROR; // FIXME: Should probably return 'result'
|
||||
}
|
||||
output_position -= zlib_stream_->avail_out;
|
||||
|
||||
// Flush the data
|
||||
if (!flush) {
|
||||
do {
|
||||
output.resize(output_position + CHUNK_SIZE);
|
||||
zlib_stream_->avail_out = CHUNK_SIZE;
|
||||
zlib_stream_->next_out = (Bytef*) (output.data() + output_position);
|
||||
result = inflate(zlib_stream_,Z_SYNC_FLUSH);
|
||||
if (result == Z_STREAM_ERROR) {
|
||||
qWarning("compressor.cpp: Error ('%s')", zlib_stream_->msg);
|
||||
return result;
|
||||
}
|
||||
output_position += CHUNK_SIZE;
|
||||
}
|
||||
while (zlib_stream_->avail_out == 0);
|
||||
output_position -= zlib_stream_->avail_out;
|
||||
}
|
||||
output.resize(output_position);
|
||||
|
||||
// Write the compressed data
|
||||
device_->write(output);
|
||||
return 0;
|
||||
}
|
||||
54
iris-legacy/iris/xmpp-core/compress.h
Normal file
54
iris-legacy/iris/xmpp-core/compress.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#ifndef COMPRESS_H
|
||||
#define COMPRESS_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "zlib.h"
|
||||
|
||||
class QIODevice;
|
||||
|
||||
class Compressor : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Compressor(QIODevice* device, int compression = Z_DEFAULT_COMPRESSION);
|
||||
~Compressor();
|
||||
|
||||
int write(const QByteArray&);
|
||||
|
||||
protected slots:
|
||||
void flush();
|
||||
|
||||
protected:
|
||||
int write(const QByteArray&, bool flush);
|
||||
|
||||
private:
|
||||
QIODevice* device_;
|
||||
z_stream* zlib_stream_;
|
||||
bool flushed_;
|
||||
};
|
||||
|
||||
class Decompressor : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Decompressor(QIODevice* device);
|
||||
~Decompressor();
|
||||
|
||||
int write(const QByteArray&);
|
||||
|
||||
protected slots:
|
||||
void flush();
|
||||
|
||||
protected:
|
||||
int write(const QByteArray&, bool flush);
|
||||
|
||||
private:
|
||||
QIODevice* device_;
|
||||
z_stream* zlib_stream_;
|
||||
bool flushed_;
|
||||
};
|
||||
|
||||
#endif
|
||||
67
iris-legacy/iris/xmpp-core/compressionhandler.cpp
Normal file
67
iris-legacy/iris/xmpp-core/compressionhandler.cpp
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
#include <QTimer>
|
||||
#include <QDebug>
|
||||
|
||||
#include "compressionhandler.h"
|
||||
#include "compress.h"
|
||||
|
||||
CompressionHandler::CompressionHandler()
|
||||
: errorCode_(0)
|
||||
{
|
||||
outgoing_buffer_.open(QIODevice::ReadWrite);
|
||||
compressor_ = new Compressor(&outgoing_buffer_);
|
||||
|
||||
incoming_buffer_.open(QIODevice::ReadWrite);
|
||||
decompressor_ = new Decompressor(&incoming_buffer_);
|
||||
}
|
||||
|
||||
CompressionHandler::~CompressionHandler()
|
||||
{
|
||||
delete compressor_;
|
||||
delete decompressor_;
|
||||
}
|
||||
|
||||
void CompressionHandler::writeIncoming(const QByteArray& a)
|
||||
{
|
||||
//qDebug("CompressionHandler::writeIncoming");
|
||||
//qDebug() << (QString("Incoming %1 bytes").arg(a.size()).toAscii());
|
||||
errorCode_ = decompressor_->write(a);
|
||||
if (!errorCode_)
|
||||
QTimer::singleShot(0, this, SIGNAL(readyRead()));
|
||||
else
|
||||
QTimer::singleShot(0, this, SIGNAL(error()));
|
||||
}
|
||||
|
||||
void CompressionHandler::write(const QByteArray& a)
|
||||
{
|
||||
//qDebug() << (QString("CompressionHandler::write(%1)").arg(a.size()).toAscii());
|
||||
errorCode_ = compressor_->write(a);
|
||||
if (!errorCode_)
|
||||
QTimer::singleShot(0, this, SIGNAL(readyReadOutgoing()));
|
||||
else
|
||||
QTimer::singleShot(0, this, SIGNAL(error()));
|
||||
}
|
||||
|
||||
QByteArray CompressionHandler::read()
|
||||
{
|
||||
//qDebug("CompressionHandler::read");
|
||||
QByteArray b = incoming_buffer_.buffer();
|
||||
incoming_buffer_.buffer().clear();
|
||||
incoming_buffer_.reset();
|
||||
return b;
|
||||
}
|
||||
|
||||
QByteArray CompressionHandler::readOutgoing(int* i)
|
||||
{
|
||||
//qDebug("CompressionHandler::readOutgoing");
|
||||
//qDebug() << (QString("Outgoing %1 bytes").arg(outgoing_buffer_.size()).toAscii());
|
||||
QByteArray b = outgoing_buffer_.buffer();
|
||||
outgoing_buffer_.buffer().clear();
|
||||
outgoing_buffer_.reset();
|
||||
*i = b.size();
|
||||
return b;
|
||||
}
|
||||
|
||||
int CompressionHandler::errorCode()
|
||||
{
|
||||
return errorCode_;
|
||||
}
|
||||
35
iris-legacy/iris/xmpp-core/compressionhandler.h
Normal file
35
iris-legacy/iris/xmpp-core/compressionhandler.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#ifndef COMPRESSIONHANDLER_H
|
||||
#define COMPRESSIONHANDLER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QBuffer>
|
||||
|
||||
class Compressor;
|
||||
class Decompressor;
|
||||
|
||||
class CompressionHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CompressionHandler();
|
||||
~CompressionHandler();
|
||||
void writeIncoming(const QByteArray& a);
|
||||
void write(const QByteArray& a);
|
||||
QByteArray read();
|
||||
QByteArray readOutgoing(int*);
|
||||
int errorCode();
|
||||
|
||||
signals:
|
||||
void readyRead();
|
||||
void readyReadOutgoing();
|
||||
void error();
|
||||
|
||||
private:
|
||||
Compressor* compressor_;
|
||||
Decompressor* decompressor_;
|
||||
QBuffer outgoing_buffer_, incoming_buffer_;
|
||||
int errorCode_;
|
||||
};
|
||||
|
||||
#endif
|
||||
742
iris-legacy/iris/xmpp-core/connector.cpp
Normal file
742
iris-legacy/iris/xmpp-core/connector.cpp
Normal file
|
|
@ -0,0 +1,742 @@
|
|||
/*
|
||||
* connector.cpp - establish a connection to an XMPP server
|
||||
* Copyright (C) 2003 Justin Karneges
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
TODO:
|
||||
|
||||
- Test and analyze all possible branches
|
||||
|
||||
XMPP::AdvancedConnector is "good for now." The only real issue is that
|
||||
most of what it provides is just to work around the old Jabber/XMPP 0.9
|
||||
connection behavior. When XMPP 1.0 has taken over the world, we can
|
||||
greatly simplify this class. - Sep 3rd, 2003.
|
||||
*/
|
||||
|
||||
#include "xmpp.h"
|
||||
|
||||
#include <qpointer.h>
|
||||
#include <qca.h>
|
||||
#include <QList>
|
||||
#include <QUrl>
|
||||
#include "safedelete.h"
|
||||
#include <libidn/idna.h>
|
||||
|
||||
#ifdef NO_NDNS
|
||||
#include <q3dns.h>
|
||||
#else
|
||||
#include "ndns.h"
|
||||
#endif
|
||||
|
||||
#include "bsocket.h"
|
||||
#include "httpconnect.h"
|
||||
#include "httppoll.h"
|
||||
#include "socks.h"
|
||||
#include "srvresolver.h"
|
||||
|
||||
//#define XMPP_DEBUG
|
||||
|
||||
using namespace XMPP;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Connector
|
||||
//----------------------------------------------------------------------------
|
||||
Connector::Connector(QObject *parent)
|
||||
:QObject(parent)
|
||||
{
|
||||
setUseSSL(false);
|
||||
setPeerAddressNone();
|
||||
}
|
||||
|
||||
Connector::~Connector()
|
||||
{
|
||||
}
|
||||
|
||||
bool Connector::useSSL() const
|
||||
{
|
||||
return ssl;
|
||||
}
|
||||
|
||||
bool Connector::havePeerAddress() const
|
||||
{
|
||||
return haveaddr;
|
||||
}
|
||||
|
||||
QHostAddress Connector::peerAddress() const
|
||||
{
|
||||
return addr;
|
||||
}
|
||||
|
||||
Q_UINT16 Connector::peerPort() const
|
||||
{
|
||||
return port;
|
||||
}
|
||||
|
||||
void Connector::setUseSSL(bool b)
|
||||
{
|
||||
ssl = b;
|
||||
}
|
||||
|
||||
void Connector::setPeerAddressNone()
|
||||
{
|
||||
haveaddr = false;
|
||||
addr = QHostAddress();
|
||||
port = 0;
|
||||
}
|
||||
|
||||
void Connector::setPeerAddress(const QHostAddress &_addr, Q_UINT16 _port)
|
||||
{
|
||||
haveaddr = true;
|
||||
addr = _addr;
|
||||
port = _port;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// AdvancedConnector::Proxy
|
||||
//----------------------------------------------------------------------------
|
||||
AdvancedConnector::Proxy::Proxy()
|
||||
{
|
||||
t = None;
|
||||
v_poll = 30;
|
||||
}
|
||||
|
||||
AdvancedConnector::Proxy::~Proxy()
|
||||
{
|
||||
}
|
||||
|
||||
int AdvancedConnector::Proxy::type() const
|
||||
{
|
||||
return t;
|
||||
}
|
||||
|
||||
QString AdvancedConnector::Proxy::host() const
|
||||
{
|
||||
return v_host;
|
||||
}
|
||||
|
||||
Q_UINT16 AdvancedConnector::Proxy::port() const
|
||||
{
|
||||
return v_port;
|
||||
}
|
||||
|
||||
QString AdvancedConnector::Proxy::url() const
|
||||
{
|
||||
return v_url;
|
||||
}
|
||||
|
||||
QString AdvancedConnector::Proxy::user() const
|
||||
{
|
||||
return v_user;
|
||||
}
|
||||
|
||||
QString AdvancedConnector::Proxy::pass() const
|
||||
{
|
||||
return v_pass;
|
||||
}
|
||||
|
||||
int AdvancedConnector::Proxy::pollInterval() const
|
||||
{
|
||||
return v_poll;
|
||||
}
|
||||
|
||||
void AdvancedConnector::Proxy::setHttpConnect(const QString &host, Q_UINT16 port)
|
||||
{
|
||||
t = HttpConnect;
|
||||
v_host = host;
|
||||
v_port = port;
|
||||
}
|
||||
|
||||
void AdvancedConnector::Proxy::setHttpPoll(const QString &host, Q_UINT16 port, const QString &url)
|
||||
{
|
||||
t = HttpPoll;
|
||||
v_host = host;
|
||||
v_port = port;
|
||||
v_url = url;
|
||||
}
|
||||
|
||||
void AdvancedConnector::Proxy::setSocks(const QString &host, Q_UINT16 port)
|
||||
{
|
||||
t = Socks;
|
||||
v_host = host;
|
||||
v_port = port;
|
||||
}
|
||||
|
||||
void AdvancedConnector::Proxy::setUserPass(const QString &user, const QString &pass)
|
||||
{
|
||||
v_user = user;
|
||||
v_pass = pass;
|
||||
}
|
||||
|
||||
void AdvancedConnector::Proxy::setPollInterval(int secs)
|
||||
{
|
||||
v_poll = secs;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// AdvancedConnector
|
||||
//----------------------------------------------------------------------------
|
||||
enum { Idle, Connecting, Connected };
|
||||
class AdvancedConnector::Private
|
||||
{
|
||||
public:
|
||||
int mode;
|
||||
ByteStream *bs;
|
||||
#ifdef NO_NDNS
|
||||
Q3Dns *qdns;
|
||||
#else
|
||||
NDns dns;
|
||||
#endif
|
||||
SrvResolver srv;
|
||||
|
||||
QString server;
|
||||
QString opt_host;
|
||||
int opt_port;
|
||||
bool opt_probe, opt_ssl;
|
||||
Proxy proxy;
|
||||
|
||||
QString host;
|
||||
int port;
|
||||
QList<Q3Dns::Server> servers;
|
||||
int errorCode;
|
||||
|
||||
bool multi, using_srv;
|
||||
bool will_be_ssl;
|
||||
int probe_mode;
|
||||
|
||||
bool aaaa;
|
||||
SafeDelete sd;
|
||||
};
|
||||
|
||||
AdvancedConnector::AdvancedConnector(QObject *parent)
|
||||
:Connector(parent)
|
||||
{
|
||||
d = new Private;
|
||||
d->bs = 0;
|
||||
#ifdef NO_NDNS
|
||||
d->qdns = 0;
|
||||
#else
|
||||
connect(&d->dns, SIGNAL(resultsReady()), SLOT(dns_done()));
|
||||
#endif
|
||||
connect(&d->srv, SIGNAL(resultsReady()), SLOT(srv_done()));
|
||||
d->opt_probe = false;
|
||||
d->opt_ssl = false;
|
||||
cleanup();
|
||||
d->errorCode = 0;
|
||||
}
|
||||
|
||||
AdvancedConnector::~AdvancedConnector()
|
||||
{
|
||||
cleanup();
|
||||
delete d;
|
||||
}
|
||||
|
||||
void AdvancedConnector::cleanup()
|
||||
{
|
||||
d->mode = Idle;
|
||||
|
||||
// stop any dns
|
||||
#ifdef NO_NDNS
|
||||
if(d->qdns) {
|
||||
d->qdns->disconnect(this);
|
||||
d->qdns->deleteLater();
|
||||
//d->sd.deleteLater(d->qdns);
|
||||
d->qdns = 0;
|
||||
}
|
||||
#else
|
||||
if(d->dns.isBusy())
|
||||
d->dns.stop();
|
||||
#endif
|
||||
if(d->srv.isBusy())
|
||||
d->srv.stop();
|
||||
|
||||
// destroy the bytestream, if there is one
|
||||
delete d->bs;
|
||||
d->bs = 0;
|
||||
|
||||
d->multi = false;
|
||||
d->using_srv = false;
|
||||
d->will_be_ssl = false;
|
||||
d->probe_mode = -1;
|
||||
|
||||
setUseSSL(false);
|
||||
setPeerAddressNone();
|
||||
}
|
||||
|
||||
void AdvancedConnector::setProxy(const Proxy &proxy)
|
||||
{
|
||||
if(d->mode != Idle)
|
||||
return;
|
||||
d->proxy = proxy;
|
||||
}
|
||||
|
||||
void AdvancedConnector::setOptHostPort(const QString &host, Q_UINT16 _port)
|
||||
{
|
||||
if(d->mode != Idle)
|
||||
return;
|
||||
d->opt_host = host;
|
||||
d->opt_port = _port;
|
||||
}
|
||||
|
||||
void AdvancedConnector::setOptProbe(bool b)
|
||||
{
|
||||
if(d->mode != Idle)
|
||||
return;
|
||||
d->opt_probe = b;
|
||||
}
|
||||
|
||||
void AdvancedConnector::setOptSSL(bool b)
|
||||
{
|
||||
if(d->mode != Idle)
|
||||
return;
|
||||
d->opt_ssl = b;
|
||||
}
|
||||
|
||||
void AdvancedConnector::connectToServer(const QString &server)
|
||||
{
|
||||
if(d->mode != Idle)
|
||||
return;
|
||||
if(server.isEmpty())
|
||||
return;
|
||||
|
||||
d->errorCode = 0;
|
||||
d->mode = Connecting;
|
||||
d->aaaa = true;
|
||||
|
||||
// Encode the servername
|
||||
d->server = QUrl::toAce(server);
|
||||
//char* server_encoded;
|
||||
//if (!idna_to_ascii_8z(server.utf8().data(), &server_encoded, 0)) {
|
||||
// d->server = QString(server_encoded);
|
||||
// free(server_encoded);
|
||||
//}
|
||||
//else {
|
||||
// d->server = server;
|
||||
//}
|
||||
|
||||
if(d->proxy.type() == Proxy::HttpPoll) {
|
||||
// need SHA1 here
|
||||
//if(!QCA::isSupported(QCA::CAP_SHA1))
|
||||
// QCA::insertProvider(createProviderHash());
|
||||
|
||||
HttpPoll *s = new HttpPoll;
|
||||
d->bs = s;
|
||||
connect(s, SIGNAL(connected()), SLOT(bs_connected()));
|
||||
connect(s, SIGNAL(syncStarted()), SLOT(http_syncStarted()));
|
||||
connect(s, SIGNAL(syncFinished()), SLOT(http_syncFinished()));
|
||||
connect(s, SIGNAL(error(int)), SLOT(bs_error(int)));
|
||||
if(!d->proxy.user().isEmpty())
|
||||
s->setAuth(d->proxy.user(), d->proxy.pass());
|
||||
s->setPollInterval(d->proxy.pollInterval());
|
||||
|
||||
if(d->proxy.host().isEmpty())
|
||||
s->connectToUrl(d->proxy.url());
|
||||
else
|
||||
s->connectToHost(d->proxy.host(), d->proxy.port(), d->proxy.url());
|
||||
}
|
||||
else if (d->proxy.type() == Proxy::HttpConnect) {
|
||||
if(!d->opt_host.isEmpty()) {
|
||||
d->host = d->opt_host;
|
||||
d->port = d->opt_port;
|
||||
}
|
||||
else {
|
||||
d->host = server;
|
||||
d->port = 5222;
|
||||
}
|
||||
do_connect();
|
||||
}
|
||||
else {
|
||||
if(!d->opt_host.isEmpty()) {
|
||||
d->host = d->opt_host;
|
||||
d->port = d->opt_port;
|
||||
do_resolve();
|
||||
}
|
||||
else {
|
||||
d->multi = true;
|
||||
|
||||
QPointer<QObject> self = this;
|
||||
emit srvLookup(d->server);
|
||||
if(!self)
|
||||
return;
|
||||
|
||||
d->srv.resolveSrvOnly(d->server, "xmpp-client", "tcp");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AdvancedConnector::changePollInterval(int secs)
|
||||
{
|
||||
if(d->bs && (d->bs->inherits("XMPP::HttpPoll") || d->bs->inherits("HttpPoll"))) {
|
||||
HttpPoll *s = static_cast<HttpPoll*>(d->bs);
|
||||
s->setPollInterval(secs);
|
||||
}
|
||||
}
|
||||
|
||||
ByteStream *AdvancedConnector::stream() const
|
||||
{
|
||||
if(d->mode == Connected)
|
||||
return d->bs;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AdvancedConnector::done()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
int AdvancedConnector::errorCode() const
|
||||
{
|
||||
return d->errorCode;
|
||||
}
|
||||
|
||||
void AdvancedConnector::do_resolve()
|
||||
{
|
||||
#ifdef NO_NDNS
|
||||
printf("resolving (aaaa=%d)\n", d->aaaa);
|
||||
d->qdns = new Q3Dns;
|
||||
connect(d->qdns, SIGNAL(resultsReady()), SLOT(dns_done()));
|
||||
if(d->aaaa)
|
||||
d->qdns->setRecordType(Q3Dns::Aaaa); // IPv6
|
||||
else
|
||||
d->qdns->setRecordType(Q3Dns::A); // IPv4
|
||||
d->qdns->setLabel(d->host);
|
||||
#else
|
||||
d->dns.resolve(d->host);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AdvancedConnector::dns_done()
|
||||
{
|
||||
bool failed = false;
|
||||
QHostAddress addr;
|
||||
|
||||
#ifdef NO_NDNS
|
||||
//if(!d->qdns)
|
||||
// return;
|
||||
|
||||
// apparently we sometimes get this signal even though the results aren' t ready
|
||||
//if(d->qdns->isWorking())
|
||||
// return;
|
||||
|
||||
//SafeDeleteLock s(&d->sd);
|
||||
|
||||
// grab the address list and destroy the qdns object
|
||||
QList<QHostAddress> list = d->qdns->addresses();
|
||||
d->qdns->disconnect(this);
|
||||
d->qdns->deleteLater();
|
||||
//d->sd.deleteLater(d->qdns);
|
||||
d->qdns = 0;
|
||||
|
||||
if(list.isEmpty()) {
|
||||
if(d->aaaa) {
|
||||
d->aaaa = false;
|
||||
do_resolve();
|
||||
return;
|
||||
}
|
||||
//do_resolve();
|
||||
//return;
|
||||
failed = true;
|
||||
}
|
||||
else
|
||||
addr = list.first();
|
||||
#else
|
||||
if(d->dns.result().isNull ())
|
||||
failed = true;
|
||||
else
|
||||
addr = QHostAddress(d->dns.result());
|
||||
#endif
|
||||
|
||||
if(failed) {
|
||||
#ifdef XMPP_DEBUG
|
||||
printf("dns1\n");
|
||||
#endif
|
||||
// using proxy? then try the unresolved host through the proxy
|
||||
if(d->proxy.type() != Proxy::None) {
|
||||
#ifdef XMPP_DEBUG
|
||||
printf("dns1.1\n");
|
||||
#endif
|
||||
do_connect();
|
||||
}
|
||||
else if(d->using_srv) {
|
||||
#ifdef XMPP_DEBUG
|
||||
printf("dns1.2\n");
|
||||
#endif
|
||||
if(d->servers.isEmpty()) {
|
||||
#ifdef XMPP_DEBUG
|
||||
printf("dns1.2.1\n");
|
||||
#endif
|
||||
cleanup();
|
||||
d->errorCode = ErrConnectionRefused;
|
||||
error();
|
||||
}
|
||||
else {
|
||||
#ifdef XMPP_DEBUG
|
||||
printf("dns1.2.2\n");
|
||||
#endif
|
||||
tryNextSrv();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
#ifdef XMPP_DEBUG
|
||||
printf("dns1.3\n");
|
||||
#endif
|
||||
cleanup();
|
||||
d->errorCode = ErrHostNotFound;
|
||||
error();
|
||||
}
|
||||
}
|
||||
else {
|
||||
#ifdef XMPP_DEBUG
|
||||
printf("dns2\n");
|
||||
#endif
|
||||
d->host = addr.toString();
|
||||
do_connect();
|
||||
}
|
||||
}
|
||||
|
||||
void AdvancedConnector::do_connect()
|
||||
{
|
||||
#ifdef XMPP_DEBUG
|
||||
printf("trying %s:%d\n", d->host.latin1(), d->port);
|
||||
#endif
|
||||
int t = d->proxy.type();
|
||||
if(t == Proxy::None) {
|
||||
#ifdef XMPP_DEBUG
|
||||
printf("do_connect1\n");
|
||||
#endif
|
||||
BSocket *s = new BSocket;
|
||||
d->bs = s;
|
||||
connect(s, SIGNAL(connected()), SLOT(bs_connected()));
|
||||
connect(s, SIGNAL(error(int)), SLOT(bs_error(int)));
|
||||
s->connectToHost(d->host, d->port);
|
||||
}
|
||||
else if(t == Proxy::HttpConnect) {
|
||||
#ifdef XMPP_DEBUG
|
||||
printf("do_connect2\n");
|
||||
#endif
|
||||
HttpConnect *s = new HttpConnect;
|
||||
d->bs = s;
|
||||
connect(s, SIGNAL(connected()), SLOT(bs_connected()));
|
||||
connect(s, SIGNAL(error(int)), SLOT(bs_error(int)));
|
||||
if(!d->proxy.user().isEmpty())
|
||||
s->setAuth(d->proxy.user(), d->proxy.pass());
|
||||
s->connectToHost(d->proxy.host(), d->proxy.port(), d->host, d->port);
|
||||
}
|
||||
else if(t == Proxy::Socks) {
|
||||
#ifdef XMPP_DEBUG
|
||||
printf("do_connect3\n");
|
||||
#endif
|
||||
SocksClient *s = new SocksClient;
|
||||
d->bs = s;
|
||||
connect(s, SIGNAL(connected()), SLOT(bs_connected()));
|
||||
connect(s, SIGNAL(error(int)), SLOT(bs_error(int)));
|
||||
if(!d->proxy.user().isEmpty())
|
||||
s->setAuth(d->proxy.user(), d->proxy.pass());
|
||||
s->connectToHost(d->proxy.host(), d->proxy.port(), d->host, d->port);
|
||||
}
|
||||
}
|
||||
|
||||
void AdvancedConnector::tryNextSrv()
|
||||
{
|
||||
#ifdef XMPP_DEBUG
|
||||
printf("trying next srv\n");
|
||||
#endif
|
||||
d->host = d->servers.first().name;
|
||||
d->port = d->servers.first().port;
|
||||
d->servers.remove(d->servers.begin());
|
||||
do_resolve();
|
||||
}
|
||||
|
||||
void AdvancedConnector::srv_done()
|
||||
{
|
||||
QPointer<QObject> self = this;
|
||||
#ifdef XMPP_DEBUG
|
||||
printf("srv_done1\n");
|
||||
#endif
|
||||
d->servers = d->srv.servers();
|
||||
if(d->servers.isEmpty()) {
|
||||
srvResult(false);
|
||||
if(!self)
|
||||
return;
|
||||
|
||||
#ifdef XMPP_DEBUG
|
||||
printf("srv_done1.1\n");
|
||||
#endif
|
||||
// fall back to A record
|
||||
d->using_srv = false;
|
||||
d->host = d->server;
|
||||
if(d->opt_probe) {
|
||||
#ifdef XMPP_DEBUG
|
||||
printf("srv_done1.1.1\n");
|
||||
#endif
|
||||
d->probe_mode = 0;
|
||||
d->port = 5223;
|
||||
d->will_be_ssl = true;
|
||||
}
|
||||
else {
|
||||
#ifdef XMPP_DEBUG
|
||||
printf("srv_done1.1.2\n");
|
||||
#endif
|
||||
d->probe_mode = 1;
|
||||
d->port = 5222;
|
||||
}
|
||||
do_resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
srvResult(true);
|
||||
if(!self)
|
||||
return;
|
||||
|
||||
d->using_srv = true;
|
||||
tryNextSrv();
|
||||
}
|
||||
|
||||
void AdvancedConnector::bs_connected()
|
||||
{
|
||||
if(d->proxy.type() == Proxy::None) {
|
||||
QHostAddress h = (static_cast<BSocket*>(d->bs))->peerAddress();
|
||||
int p = (static_cast<BSocket*>(d->bs))->peerPort();
|
||||
setPeerAddress(h, p);
|
||||
}
|
||||
|
||||
// only allow ssl override if proxy==poll or host:port
|
||||
if((d->proxy.type() == Proxy::HttpPoll || !d->opt_host.isEmpty()) && d->opt_ssl)
|
||||
setUseSSL(true);
|
||||
else if(d->will_be_ssl)
|
||||
setUseSSL(true);
|
||||
|
||||
d->mode = Connected;
|
||||
connected();
|
||||
}
|
||||
|
||||
void AdvancedConnector::bs_error(int x)
|
||||
{
|
||||
if(d->mode == Connected) {
|
||||
d->errorCode = ErrStream;
|
||||
error();
|
||||
return;
|
||||
}
|
||||
|
||||
bool proxyError = false;
|
||||
int err = ErrConnectionRefused;
|
||||
int t = d->proxy.type();
|
||||
|
||||
#ifdef XMPP_DEBUG
|
||||
printf("bse1\n");
|
||||
#endif
|
||||
|
||||
// figure out the error
|
||||
if(t == Proxy::None) {
|
||||
if(x == BSocket::ErrHostNotFound)
|
||||
err = ErrHostNotFound;
|
||||
else
|
||||
err = ErrConnectionRefused;
|
||||
}
|
||||
else if(t == Proxy::HttpConnect) {
|
||||
if(x == HttpConnect::ErrConnectionRefused)
|
||||
err = ErrConnectionRefused;
|
||||
else if(x == HttpConnect::ErrHostNotFound)
|
||||
err = ErrHostNotFound;
|
||||
else {
|
||||
proxyError = true;
|
||||
if(x == HttpConnect::ErrProxyAuth)
|
||||
err = ErrProxyAuth;
|
||||
else if(x == HttpConnect::ErrProxyNeg)
|
||||
err = ErrProxyNeg;
|
||||
else
|
||||
err = ErrProxyConnect;
|
||||
}
|
||||
}
|
||||
else if(t == Proxy::HttpPoll) {
|
||||
if(x == HttpPoll::ErrConnectionRefused)
|
||||
err = ErrConnectionRefused;
|
||||
else if(x == HttpPoll::ErrHostNotFound)
|
||||
err = ErrHostNotFound;
|
||||
else {
|
||||
proxyError = true;
|
||||
if(x == HttpPoll::ErrProxyAuth)
|
||||
err = ErrProxyAuth;
|
||||
else if(x == HttpPoll::ErrProxyNeg)
|
||||
err = ErrProxyNeg;
|
||||
else
|
||||
err = ErrProxyConnect;
|
||||
}
|
||||
}
|
||||
else if(t == Proxy::Socks) {
|
||||
if(x == SocksClient::ErrConnectionRefused)
|
||||
err = ErrConnectionRefused;
|
||||
else if(x == SocksClient::ErrHostNotFound)
|
||||
err = ErrHostNotFound;
|
||||
else {
|
||||
proxyError = true;
|
||||
if(x == SocksClient::ErrProxyAuth)
|
||||
err = ErrProxyAuth;
|
||||
else if(x == SocksClient::ErrProxyNeg)
|
||||
err = ErrProxyNeg;
|
||||
else
|
||||
err = ErrProxyConnect;
|
||||
}
|
||||
}
|
||||
|
||||
// no-multi or proxy error means we quit
|
||||
if(!d->multi || proxyError) {
|
||||
cleanup();
|
||||
d->errorCode = err;
|
||||
error();
|
||||
return;
|
||||
}
|
||||
|
||||
if(d->using_srv && !d->servers.isEmpty()) {
|
||||
#ifdef XMPP_DEBUG
|
||||
printf("bse1.1\n");
|
||||
#endif
|
||||
tryNextSrv();
|
||||
}
|
||||
else if(!d->using_srv && d->opt_probe && d->probe_mode == 0) {
|
||||
#ifdef XMPP_DEBUG
|
||||
printf("bse1.2\n");
|
||||
#endif
|
||||
d->probe_mode = 1;
|
||||
d->port = 5222;
|
||||
d->will_be_ssl = false;
|
||||
do_connect();
|
||||
}
|
||||
else {
|
||||
#ifdef XMPP_DEBUG
|
||||
printf("bse1.3\n");
|
||||
#endif
|
||||
cleanup();
|
||||
d->errorCode = ErrConnectionRefused;
|
||||
error();
|
||||
}
|
||||
}
|
||||
|
||||
void AdvancedConnector::http_syncStarted()
|
||||
{
|
||||
httpSyncStarted();
|
||||
}
|
||||
|
||||
void AdvancedConnector::http_syncFinished()
|
||||
{
|
||||
httpSyncFinished();
|
||||
}
|
||||
441
iris-legacy/iris/xmpp-core/jid.cpp
Normal file
441
iris-legacy/iris/xmpp-core/jid.cpp
Normal file
|
|
@ -0,0 +1,441 @@
|
|||
/*
|
||||
* jid.cpp - class for verifying and manipulating Jabber IDs
|
||||
* Copyright (C) 2003 Justin Karneges
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "xmpp_jid.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QByteArray>
|
||||
#include <QHash>
|
||||
#include <libidn/stringprep.h>
|
||||
|
||||
|
||||
using namespace XMPP;
|
||||
|
||||
uint qHash(const XMPP::Jid& jid)
|
||||
{
|
||||
return qHash(jid.full());
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// StringPrepCache
|
||||
//----------------------------------------------------------------------------
|
||||
class StringPrepCache : public QObject
|
||||
{
|
||||
public:
|
||||
static bool nameprep(const QString &in, int maxbytes, QString *out)
|
||||
{
|
||||
if(in.isEmpty())
|
||||
{
|
||||
if(out)
|
||||
*out = QString();
|
||||
return true;
|
||||
}
|
||||
|
||||
StringPrepCache *that = get_instance();
|
||||
|
||||
Result *r = that->nameprep_table[in];
|
||||
if(r)
|
||||
{
|
||||
if(!r->norm)
|
||||
return false;
|
||||
if(out)
|
||||
*out = *(r->norm);
|
||||
return true;
|
||||
}
|
||||
|
||||
QByteArray cs = in.toUtf8();
|
||||
cs.resize(maxbytes);
|
||||
if(stringprep(cs.data(), maxbytes, (Stringprep_profile_flags)0, stringprep_nameprep) != 0)
|
||||
{
|
||||
that->nameprep_table.insert(in, new Result);
|
||||
return false;
|
||||
}
|
||||
|
||||
QString norm = QString::fromUtf8(cs);
|
||||
that->nameprep_table.insert(in, new Result(norm));
|
||||
if(out)
|
||||
*out = norm;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool nodeprep(const QString &in, int maxbytes, QString *out)
|
||||
{
|
||||
if(in.isEmpty())
|
||||
{
|
||||
if(out)
|
||||
*out = QString();
|
||||
return true;
|
||||
}
|
||||
|
||||
StringPrepCache *that = get_instance();
|
||||
|
||||
Result *r = that->nodeprep_table[in];
|
||||
if(r)
|
||||
{
|
||||
if(!r->norm)
|
||||
return false;
|
||||
if(out)
|
||||
*out = *(r->norm);
|
||||
return true;
|
||||
}
|
||||
|
||||
QByteArray cs = in.toUtf8();
|
||||
cs.resize(maxbytes);
|
||||
if(stringprep(cs.data(), maxbytes, (Stringprep_profile_flags)0, stringprep_xmpp_nodeprep) != 0)
|
||||
{
|
||||
that->nodeprep_table.insert(in, new Result);
|
||||
return false;
|
||||
}
|
||||
|
||||
QString norm = QString::fromUtf8(cs);
|
||||
that->nodeprep_table.insert(in, new Result(norm));
|
||||
if(out)
|
||||
*out = norm;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool resourceprep(const QString &in, int maxbytes, QString *out)
|
||||
{
|
||||
if(in.isEmpty())
|
||||
{
|
||||
if(out)
|
||||
*out = QString();
|
||||
return true;
|
||||
}
|
||||
|
||||
StringPrepCache *that = get_instance();
|
||||
|
||||
Result *r = that->resourceprep_table[in];
|
||||
if(r)
|
||||
{
|
||||
if(!r->norm)
|
||||
return false;
|
||||
if(out)
|
||||
*out = *(r->norm);
|
||||
return true;
|
||||
}
|
||||
|
||||
QByteArray cs = in.toUtf8();
|
||||
cs.resize(maxbytes);
|
||||
if(stringprep(cs.data(), maxbytes, (Stringprep_profile_flags)0, stringprep_xmpp_resourceprep) != 0)
|
||||
{
|
||||
that->resourceprep_table.insert(in, new Result);
|
||||
return false;
|
||||
}
|
||||
|
||||
QString norm = QString::fromUtf8(cs);
|
||||
that->resourceprep_table.insert(in, new Result(norm));
|
||||
if(out)
|
||||
*out = norm;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
class Result
|
||||
{
|
||||
public:
|
||||
QString *norm;
|
||||
|
||||
Result() : norm(0)
|
||||
{
|
||||
}
|
||||
|
||||
Result(const QString &s) : norm(new QString(s))
|
||||
{
|
||||
}
|
||||
|
||||
~Result()
|
||||
{
|
||||
delete norm;
|
||||
}
|
||||
};
|
||||
|
||||
QHash<QString,Result*> nameprep_table;
|
||||
QHash<QString,Result*> nodeprep_table;
|
||||
QHash<QString,Result*> resourceprep_table;
|
||||
|
||||
static StringPrepCache *instance;
|
||||
|
||||
static StringPrepCache *get_instance()
|
||||
{
|
||||
if(!instance)
|
||||
instance = new StringPrepCache;
|
||||
return instance;
|
||||
}
|
||||
|
||||
StringPrepCache()
|
||||
: QObject(qApp)
|
||||
{
|
||||
}
|
||||
|
||||
~StringPrepCache()
|
||||
{
|
||||
foreach(Result* r, nameprep_table) {
|
||||
delete r;
|
||||
}
|
||||
nameprep_table.clear();
|
||||
foreach(Result* r, nodeprep_table) {
|
||||
delete r;
|
||||
}
|
||||
nodeprep_table.clear();
|
||||
foreach(Result* r, resourceprep_table) {
|
||||
delete r;
|
||||
}
|
||||
resourceprep_table.clear();
|
||||
}
|
||||
};
|
||||
|
||||
StringPrepCache *StringPrepCache::instance = 0;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Jid
|
||||
//----------------------------------------------------------------------------
|
||||
Jid::Jid()
|
||||
{
|
||||
valid = false;
|
||||
null = true;
|
||||
}
|
||||
|
||||
Jid::~Jid()
|
||||
{
|
||||
}
|
||||
|
||||
Jid::Jid(const QString &s)
|
||||
{
|
||||
set(s);
|
||||
}
|
||||
|
||||
Jid::Jid(const char *s)
|
||||
{
|
||||
set(QString(s));
|
||||
}
|
||||
|
||||
Jid & Jid::operator=(const QString &s)
|
||||
{
|
||||
set(s);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Jid & Jid::operator=(const char *s)
|
||||
{
|
||||
set(QString(s));
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Jid::reset()
|
||||
{
|
||||
f = QString();
|
||||
b = QString();
|
||||
d = QString();
|
||||
n = QString();
|
||||
r = QString();
|
||||
valid = false;
|
||||
null = true;
|
||||
}
|
||||
|
||||
void Jid::update()
|
||||
{
|
||||
// build 'bare' and 'full' jids
|
||||
if(n.isEmpty())
|
||||
b = d;
|
||||
else
|
||||
b = n + '@' + d;
|
||||
if(r.isEmpty())
|
||||
f = b;
|
||||
else
|
||||
f = b + '/' + r;
|
||||
if(f.isEmpty())
|
||||
valid = false;
|
||||
null = f.isEmpty() && r.isEmpty();
|
||||
}
|
||||
|
||||
void Jid::set(const QString &s)
|
||||
{
|
||||
QString rest, domain, node, resource;
|
||||
QString norm_domain, norm_node, norm_resource;
|
||||
int x = s.indexOf('/');
|
||||
if(x != -1) {
|
||||
rest = s.mid(0, x);
|
||||
resource = s.mid(x+1);
|
||||
}
|
||||
else {
|
||||
rest = s;
|
||||
resource = QString();
|
||||
}
|
||||
if(!validResource(resource, &norm_resource)) {
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
|
||||
x = rest.indexOf('@');
|
||||
if(x != -1) {
|
||||
node = rest.mid(0, x);
|
||||
domain = rest.mid(x+1);
|
||||
}
|
||||
else {
|
||||
node = QString();
|
||||
domain = rest;
|
||||
}
|
||||
if(!validDomain(domain, &norm_domain) || !validNode(node, &norm_node)) {
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
|
||||
valid = true;
|
||||
null = false;
|
||||
d = norm_domain;
|
||||
n = norm_node;
|
||||
r = norm_resource;
|
||||
update();
|
||||
}
|
||||
|
||||
void Jid::set(const QString &domain, const QString &node, const QString &resource)
|
||||
{
|
||||
QString norm_domain, norm_node, norm_resource;
|
||||
if(!validDomain(domain, &norm_domain) || !validNode(node, &norm_node) || !validResource(resource, &norm_resource)) {
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
valid = true;
|
||||
null = false;
|
||||
d = norm_domain;
|
||||
n = norm_node;
|
||||
r = norm_resource;
|
||||
update();
|
||||
}
|
||||
|
||||
void Jid::setDomain(const QString &s)
|
||||
{
|
||||
if(!valid)
|
||||
return;
|
||||
QString norm;
|
||||
if(!validDomain(s, &norm)) {
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
d = norm;
|
||||
update();
|
||||
}
|
||||
|
||||
void Jid::setNode(const QString &s)
|
||||
{
|
||||
if(!valid)
|
||||
return;
|
||||
QString norm;
|
||||
if(!validNode(s, &norm)) {
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
n = norm;
|
||||
update();
|
||||
}
|
||||
|
||||
void Jid::setResource(const QString &s)
|
||||
{
|
||||
if(!valid)
|
||||
return;
|
||||
QString norm;
|
||||
if(!validResource(s, &norm)) {
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
r = norm;
|
||||
update();
|
||||
}
|
||||
|
||||
Jid Jid::withNode(const QString &s) const
|
||||
{
|
||||
Jid j = *this;
|
||||
j.setNode(s);
|
||||
return j;
|
||||
}
|
||||
|
||||
Jid Jid::withResource(const QString &s) const
|
||||
{
|
||||
Jid j = *this;
|
||||
j.setResource(s);
|
||||
return j;
|
||||
}
|
||||
|
||||
bool Jid::isValid() const
|
||||
{
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool Jid::isEmpty() const
|
||||
{
|
||||
return f.isEmpty();
|
||||
}
|
||||
|
||||
bool Jid::compare(const Jid &a, bool compareRes) const
|
||||
{
|
||||
if(null && a.null)
|
||||
return true;
|
||||
|
||||
// only compare valid jids
|
||||
if(!valid || !a.valid)
|
||||
return false;
|
||||
|
||||
if(compareRes ? (f != a.f) : (b != a.b))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Jid::validDomain(const QString &s, QString *norm)
|
||||
{
|
||||
/*QCString cs = s.utf8();
|
||||
cs.resize(1024);
|
||||
if(stringprep(cs.data(), 1024, (Stringprep_profile_flags)0, stringprep_nameprep) != 0)
|
||||
return false;
|
||||
if(norm)
|
||||
*norm = QString::fromUtf8(cs);
|
||||
return true;*/
|
||||
return StringPrepCache::nameprep(s, 1024, norm);
|
||||
}
|
||||
|
||||
bool Jid::validNode(const QString &s, QString *norm)
|
||||
{
|
||||
/*QCString cs = s.utf8();
|
||||
cs.resize(1024);
|
||||
if(stringprep(cs.data(), 1024, (Stringprep_profile_flags)0, stringprep_xmpp_nodeprep) != 0)
|
||||
return false;
|
||||
if(norm)
|
||||
*norm = QString::fromUtf8(cs);
|
||||
return true;*/
|
||||
return StringPrepCache::nodeprep(s, 1024, norm);
|
||||
}
|
||||
|
||||
bool Jid::validResource(const QString &s, QString *norm)
|
||||
{
|
||||
/*QCString cs = s.utf8();
|
||||
cs.resize(1024);
|
||||
if(stringprep(cs.data(), 1024, (Stringprep_profile_flags)0, stringprep_xmpp_resourceprep) != 0)
|
||||
return false;
|
||||
if(norm)
|
||||
*norm = QString::fromUtf8(cs);
|
||||
return true;*/
|
||||
return StringPrepCache::resourceprep(s, 1024, norm);
|
||||
}
|
||||
|
||||
bool Jid::operator==(const Jid& other) const
|
||||
{
|
||||
return compare(other);
|
||||
}
|
||||
798
iris-legacy/iris/xmpp-core/parser.cpp
Normal file
798
iris-legacy/iris/xmpp-core/parser.cpp
Normal file
|
|
@ -0,0 +1,798 @@
|
|||
/*
|
||||
* parser.cpp - parse an XMPP "document"
|
||||
* Copyright (C) 2003 Justin Karneges
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
TODO:
|
||||
|
||||
For XMPP::Parser to be "perfect", some things must be solved/changed in the
|
||||
Qt library:
|
||||
|
||||
- Fix weird QDomElement::haveAttributeNS() bug (patch submitted to
|
||||
Trolltech on Aug 31st, 2003).
|
||||
- Fix weird behavior in QXmlSimpleReader of reporting endElement() when
|
||||
the '/' character of a self-closing tag is reached, instead of when
|
||||
the final '>' is reached.
|
||||
- Fix incremental parsing bugs in QXmlSimpleReader. At the moment, the
|
||||
only bug I've found is related to attribute parsing, but there might
|
||||
be more (search for '###' in $QTDIR/src/xml/qxml.cpp).
|
||||
|
||||
We have workarounds for all of the above problems in the code below.
|
||||
|
||||
- Deal with the <?xml?> processing instruction as an event type, so that we
|
||||
can feed it back to the application properly. Right now it is completely
|
||||
untrackable and is simply tacked into the first event's actualString. We
|
||||
can't easily do this because QXmlSimpleReader eats an extra byte beyond
|
||||
the processing instruction before reporting it.
|
||||
|
||||
- Make QXmlInputSource capable of accepting data incrementally, to ensure
|
||||
proper text encoding detection and processing over a network. This is
|
||||
technically not a bug, as we have our own subclass below to do it, but
|
||||
it would be nice if Qt had this already.
|
||||
*/
|
||||
|
||||
#include "parser.h"
|
||||
|
||||
#include <qtextcodec.h>
|
||||
#include <q3ptrlist.h>
|
||||
#include <string.h>
|
||||
|
||||
using namespace XMPP;
|
||||
|
||||
static bool qt_bug_check = false;
|
||||
static bool qt_bug_have;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// StreamInput
|
||||
//----------------------------------------------------------------------------
|
||||
class StreamInput : public QXmlInputSource
|
||||
{
|
||||
public:
|
||||
StreamInput()
|
||||
{
|
||||
dec = 0;
|
||||
reset();
|
||||
}
|
||||
|
||||
~StreamInput()
|
||||
{
|
||||
delete dec;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
delete dec;
|
||||
dec = 0;
|
||||
in.resize(0);
|
||||
out = "";
|
||||
at = 0;
|
||||
paused = false;
|
||||
mightChangeEncoding = true;
|
||||
checkBad = true;
|
||||
last = QChar();
|
||||
v_encoding = "";
|
||||
resetLastData();
|
||||
}
|
||||
|
||||
void resetLastData()
|
||||
{
|
||||
last_string = "";
|
||||
}
|
||||
|
||||
QString lastString() const
|
||||
{
|
||||
return last_string;
|
||||
}
|
||||
|
||||
void appendData(const QByteArray &a)
|
||||
{
|
||||
int oldsize = in.size();
|
||||
in.resize(oldsize + a.size());
|
||||
memcpy(in.data() + oldsize, a.data(), a.size());
|
||||
processBuf();
|
||||
}
|
||||
|
||||
QChar lastRead()
|
||||
{
|
||||
return last;
|
||||
}
|
||||
|
||||
QChar next()
|
||||
{
|
||||
if(paused)
|
||||
return EndOfData;
|
||||
else
|
||||
return readNext();
|
||||
}
|
||||
|
||||
// NOTE: setting 'peek' to true allows the same char to be read again,
|
||||
// however this still advances the internal byte processing.
|
||||
QChar readNext(bool peek=false)
|
||||
{
|
||||
QChar c;
|
||||
if(mightChangeEncoding)
|
||||
c = EndOfData;
|
||||
else {
|
||||
if(out.isEmpty()) {
|
||||
QString s;
|
||||
if(!tryExtractPart(&s))
|
||||
c = EndOfData;
|
||||
else {
|
||||
out = s;
|
||||
c = out[0];
|
||||
}
|
||||
}
|
||||
else
|
||||
c = out[0];
|
||||
if(!peek)
|
||||
out.remove(0, 1);
|
||||
}
|
||||
if(c == EndOfData) {
|
||||
#ifdef XMPP_PARSER_DEBUG
|
||||
printf("next() = EOD\n");
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
#ifdef XMPP_PARSER_DEBUG
|
||||
printf("next() = [%c]\n", c.latin1());
|
||||
#endif
|
||||
last = c;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
QByteArray unprocessed() const
|
||||
{
|
||||
QByteArray a(in.size() - at);
|
||||
memcpy(a.data(), in.data() + at, a.size());
|
||||
return a;
|
||||
}
|
||||
|
||||
void pause(bool b)
|
||||
{
|
||||
paused = b;
|
||||
}
|
||||
|
||||
bool isPaused()
|
||||
{
|
||||
return paused;
|
||||
}
|
||||
|
||||
QString encoding() const
|
||||
{
|
||||
return v_encoding;
|
||||
}
|
||||
|
||||
private:
|
||||
QTextDecoder *dec;
|
||||
QByteArray in;
|
||||
QString out;
|
||||
int at;
|
||||
bool paused;
|
||||
bool mightChangeEncoding;
|
||||
QChar last;
|
||||
QString v_encoding;
|
||||
QString last_string;
|
||||
bool checkBad;
|
||||
|
||||
void processBuf()
|
||||
{
|
||||
#ifdef XMPP_PARSER_DEBUG
|
||||
printf("processing. size=%d, at=%d\n", in.size(), at);
|
||||
#endif
|
||||
if(!dec) {
|
||||
QTextCodec *codec = 0;
|
||||
uchar *p = (uchar *)in.data() + at;
|
||||
int size = in.size() - at;
|
||||
|
||||
// do we have enough information to determine the encoding?
|
||||
if(size == 0)
|
||||
return;
|
||||
bool utf16 = false;
|
||||
if(p[0] == 0xfe || p[0] == 0xff) {
|
||||
// probably going to be a UTF-16 byte order mark
|
||||
if(size < 2)
|
||||
return;
|
||||
if((p[0] == 0xfe && p[1] == 0xff) || (p[0] == 0xff && p[1] == 0xfe)) {
|
||||
// ok it is UTF-16
|
||||
utf16 = true;
|
||||
}
|
||||
}
|
||||
if(utf16)
|
||||
codec = QTextCodec::codecForMib(1000); // UTF-16
|
||||
else
|
||||
codec = QTextCodec::codecForMib(106); // UTF-8
|
||||
|
||||
v_encoding = codec->name();
|
||||
dec = codec->makeDecoder();
|
||||
|
||||
// for utf16, put in the byte order mark
|
||||
if(utf16) {
|
||||
out += dec->toUnicode((const char *)p, 2);
|
||||
at += 2;
|
||||
}
|
||||
}
|
||||
|
||||
if(mightChangeEncoding) {
|
||||
while(1) {
|
||||
int n = out.find('<');
|
||||
if(n != -1) {
|
||||
// we need a closing bracket
|
||||
int n2 = out.find('>', n);
|
||||
if(n2 != -1) {
|
||||
++n2;
|
||||
QString h = out.mid(n, n2-n);
|
||||
QString enc = processXmlHeader(h);
|
||||
QTextCodec *codec = 0;
|
||||
if(!enc.isEmpty())
|
||||
codec = QTextCodec::codecForName(enc.latin1());
|
||||
|
||||
// changing codecs
|
||||
if(codec) {
|
||||
v_encoding = codec->name();
|
||||
delete dec;
|
||||
dec = codec->makeDecoder();
|
||||
}
|
||||
mightChangeEncoding = false;
|
||||
out.truncate(0);
|
||||
at = 0;
|
||||
resetLastData();
|
||||
break;
|
||||
}
|
||||
}
|
||||
QString s;
|
||||
if(!tryExtractPart(&s))
|
||||
break;
|
||||
if(checkBad && checkForBadChars(s)) {
|
||||
// go to the parser
|
||||
mightChangeEncoding = false;
|
||||
out.truncate(0);
|
||||
at = 0;
|
||||
resetLastData();
|
||||
break;
|
||||
}
|
||||
out += s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString processXmlHeader(const QString &h)
|
||||
{
|
||||
if(h.left(5) != "<?xml")
|
||||
return "";
|
||||
|
||||
int endPos = h.find(">");
|
||||
int startPos = h.find("encoding");
|
||||
if(startPos < endPos && startPos != -1) {
|
||||
QString encoding;
|
||||
do {
|
||||
startPos++;
|
||||
if(startPos > endPos) {
|
||||
return "";
|
||||
}
|
||||
} while(h[startPos] != '"' && h[startPos] != '\'');
|
||||
startPos++;
|
||||
while(h[startPos] != '"' && h[startPos] != '\'') {
|
||||
encoding += h[startPos];
|
||||
startPos++;
|
||||
if(startPos > endPos) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
return encoding;
|
||||
}
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
bool tryExtractPart(QString *s)
|
||||
{
|
||||
int size = in.size() - at;
|
||||
if(size == 0)
|
||||
return false;
|
||||
uchar *p = (uchar *)in.data() + at;
|
||||
QString nextChars;
|
||||
while(1) {
|
||||
nextChars = dec->toUnicode((const char *)p, 1);
|
||||
++p;
|
||||
++at;
|
||||
if(!nextChars.isEmpty())
|
||||
break;
|
||||
if(at == (int)in.size())
|
||||
return false;
|
||||
}
|
||||
last_string += nextChars;
|
||||
*s = nextChars;
|
||||
|
||||
// free processed data?
|
||||
if(at >= 1024) {
|
||||
char *p = in.data();
|
||||
int size = in.size() - at;
|
||||
memmove(p, p + at, size);
|
||||
in.resize(size);
|
||||
at = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool checkForBadChars(const QString &s)
|
||||
{
|
||||
int len = s.find('<');
|
||||
if(len == -1)
|
||||
len = s.length();
|
||||
else
|
||||
checkBad = false;
|
||||
for(int n = 0; n < len; ++n) {
|
||||
if(!s.at(n).isSpace())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// ParserHandler
|
||||
//----------------------------------------------------------------------------
|
||||
namespace XMPP
|
||||
{
|
||||
class ParserHandler : public QXmlDefaultHandler
|
||||
{
|
||||
public:
|
||||
ParserHandler(StreamInput *_in, QDomDocument *_doc)
|
||||
{
|
||||
in = _in;
|
||||
doc = _doc;
|
||||
needMore = false;
|
||||
}
|
||||
|
||||
~ParserHandler()
|
||||
{
|
||||
eventList.setAutoDelete(true);
|
||||
eventList.clear();
|
||||
}
|
||||
|
||||
bool startDocument()
|
||||
{
|
||||
depth = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool endDocument()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool startPrefixMapping(const QString &prefix, const QString &uri)
|
||||
{
|
||||
if(depth == 0) {
|
||||
nsnames += prefix;
|
||||
nsvalues += uri;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool startElement(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &atts)
|
||||
{
|
||||
if(depth == 0) {
|
||||
Parser::Event *e = new Parser::Event;
|
||||
QXmlAttributes a;
|
||||
for(int n = 0; n < atts.length(); ++n) {
|
||||
QString uri = atts.uri(n);
|
||||
QString ln = atts.localName(n);
|
||||
if(a.index(uri, ln) == -1)
|
||||
a.append(atts.qName(n), uri, ln, atts.value(n));
|
||||
}
|
||||
e->setDocumentOpen(namespaceURI, localName, qName, a, nsnames, nsvalues);
|
||||
nsnames.clear();
|
||||
nsvalues.clear();
|
||||
e->setActualString(in->lastString());
|
||||
|
||||
in->resetLastData();
|
||||
eventList.append(e);
|
||||
in->pause(true);
|
||||
}
|
||||
else {
|
||||
QDomElement e = doc->createElementNS(namespaceURI, qName);
|
||||
for(int n = 0; n < atts.length(); ++n) {
|
||||
QString uri = atts.uri(n);
|
||||
QString ln = atts.localName(n);
|
||||
bool have;
|
||||
if(!uri.isEmpty()) {
|
||||
have = e.hasAttributeNS(uri, ln);
|
||||
if(qt_bug_have)
|
||||
have = !have;
|
||||
}
|
||||
else
|
||||
have = e.hasAttribute(ln);
|
||||
if(!have)
|
||||
e.setAttributeNS(uri, atts.qName(n), atts.value(n));
|
||||
}
|
||||
|
||||
if(depth == 1) {
|
||||
elem = e;
|
||||
current = e;
|
||||
}
|
||||
else {
|
||||
current.appendChild(e);
|
||||
current = e;
|
||||
}
|
||||
}
|
||||
++depth;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool endElement(const QString &namespaceURI, const QString &localName, const QString &qName)
|
||||
{
|
||||
--depth;
|
||||
if(depth == 0) {
|
||||
Parser::Event *e = new Parser::Event;
|
||||
e->setDocumentClose(namespaceURI, localName, qName);
|
||||
e->setActualString(in->lastString());
|
||||
in->resetLastData();
|
||||
eventList.append(e);
|
||||
in->pause(true);
|
||||
}
|
||||
else {
|
||||
// done with a depth 1 element?
|
||||
if(depth == 1) {
|
||||
Parser::Event *e = new Parser::Event;
|
||||
e->setElement(elem);
|
||||
e->setActualString(in->lastString());
|
||||
in->resetLastData();
|
||||
eventList.append(e);
|
||||
in->pause(true);
|
||||
|
||||
elem = QDomElement();
|
||||
current = QDomElement();
|
||||
}
|
||||
else
|
||||
current = current.parentNode().toElement();
|
||||
}
|
||||
|
||||
if(in->lastRead() == '/')
|
||||
checkNeedMore();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool characters(const QString &str)
|
||||
{
|
||||
if(depth >= 1) {
|
||||
QString content = str;
|
||||
if(content.isEmpty())
|
||||
return true;
|
||||
|
||||
if(!current.isNull()) {
|
||||
QDomText text = doc->createTextNode(content);
|
||||
current.appendChild(text);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*bool processingInstruction(const QString &target, const QString &data)
|
||||
{
|
||||
printf("Processing: [%s], [%s]\n", target.latin1(), data.latin1());
|
||||
in->resetLastData();
|
||||
return true;
|
||||
}*/
|
||||
|
||||
void checkNeedMore()
|
||||
{
|
||||
// Here we will work around QXmlSimpleReader strangeness and self-closing tags.
|
||||
// The problem is that endElement() is called when the '/' is read, not when
|
||||
// the final '>' is read. This is a potential problem when obtaining unprocessed
|
||||
// bytes from StreamInput after this event, as the '>' character will end up
|
||||
// in the unprocessed chunk. To work around this, we need to advance StreamInput's
|
||||
// internal byte processing, but not the xml character data. This way, the '>'
|
||||
// will get processed and will no longer be in the unprocessed return, but
|
||||
// QXmlSimpleReader can still read it. To do this, we call StreamInput::readNext
|
||||
// with 'peek' mode.
|
||||
QChar c = in->readNext(true); // peek
|
||||
if(c == QXmlInputSource::EndOfData) {
|
||||
needMore = true;
|
||||
}
|
||||
else {
|
||||
// We'll assume the next char is a '>'. If it isn't, then
|
||||
// QXmlSimpleReader will deal with that problem on the next
|
||||
// parse. We don't need to take any action here.
|
||||
needMore = false;
|
||||
|
||||
// there should have been a pending event
|
||||
Parser::Event *e = eventList.getFirst();
|
||||
if(e) {
|
||||
e->setActualString(e->actualString() + '>');
|
||||
in->resetLastData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Parser::Event *takeEvent()
|
||||
{
|
||||
if(needMore)
|
||||
return 0;
|
||||
if(eventList.isEmpty())
|
||||
return 0;
|
||||
|
||||
Parser::Event *e = eventList.getFirst();
|
||||
eventList.removeRef(e);
|
||||
in->pause(false);
|
||||
return e;
|
||||
}
|
||||
|
||||
StreamInput *in;
|
||||
QDomDocument *doc;
|
||||
int depth;
|
||||
QStringList nsnames, nsvalues;
|
||||
QDomElement elem, current;
|
||||
Q3PtrList<Parser::Event> eventList;
|
||||
bool needMore;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Event
|
||||
//----------------------------------------------------------------------------
|
||||
class Parser::Event::Private
|
||||
{
|
||||
public:
|
||||
int type;
|
||||
QString ns, ln, qn;
|
||||
QXmlAttributes a;
|
||||
QDomElement e;
|
||||
QString str;
|
||||
QStringList nsnames, nsvalues;
|
||||
};
|
||||
|
||||
Parser::Event::Event()
|
||||
{
|
||||
d = 0;
|
||||
}
|
||||
|
||||
Parser::Event::Event(const Event &from)
|
||||
{
|
||||
d = 0;
|
||||
*this = from;
|
||||
}
|
||||
|
||||
Parser::Event & Parser::Event::operator=(const Event &from)
|
||||
{
|
||||
delete d;
|
||||
d = 0;
|
||||
if(from.d)
|
||||
d = new Private(*from.d);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Parser::Event::~Event()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool Parser::Event::isNull() const
|
||||
{
|
||||
return (d ? false: true);
|
||||
}
|
||||
|
||||
int Parser::Event::type() const
|
||||
{
|
||||
if(isNull())
|
||||
return -1;
|
||||
return d->type;
|
||||
}
|
||||
|
||||
QString Parser::Event::nsprefix(const QString &s) const
|
||||
{
|
||||
QStringList::ConstIterator it = d->nsnames.begin();
|
||||
QStringList::ConstIterator it2 = d->nsvalues.begin();
|
||||
for(; it != d->nsnames.end(); ++it) {
|
||||
if((*it) == s)
|
||||
return (*it2);
|
||||
++it2;
|
||||
}
|
||||
return QString::null;
|
||||
}
|
||||
|
||||
QString Parser::Event::namespaceURI() const
|
||||
{
|
||||
return d->ns;
|
||||
}
|
||||
|
||||
QString Parser::Event::localName() const
|
||||
{
|
||||
return d->ln;
|
||||
}
|
||||
|
||||
QString Parser::Event::qName() const
|
||||
{
|
||||
return d->qn;
|
||||
}
|
||||
|
||||
QXmlAttributes Parser::Event::atts() const
|
||||
{
|
||||
return d->a;
|
||||
}
|
||||
|
||||
QString Parser::Event::actualString() const
|
||||
{
|
||||
return d->str;
|
||||
}
|
||||
|
||||
QDomElement Parser::Event::element() const
|
||||
{
|
||||
return d->e;
|
||||
}
|
||||
|
||||
void Parser::Event::setDocumentOpen(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &atts, const QStringList &nsnames, const QStringList &nsvalues)
|
||||
{
|
||||
if(!d)
|
||||
d = new Private;
|
||||
d->type = DocumentOpen;
|
||||
d->ns = namespaceURI;
|
||||
d->ln = localName;
|
||||
d->qn = qName;
|
||||
d->a = atts;
|
||||
d->nsnames = nsnames;
|
||||
d->nsvalues = nsvalues;
|
||||
}
|
||||
|
||||
void Parser::Event::setDocumentClose(const QString &namespaceURI, const QString &localName, const QString &qName)
|
||||
{
|
||||
if(!d)
|
||||
d = new Private;
|
||||
d->type = DocumentClose;
|
||||
d->ns = namespaceURI;
|
||||
d->ln = localName;
|
||||
d->qn = qName;
|
||||
}
|
||||
|
||||
void Parser::Event::setElement(const QDomElement &elem)
|
||||
{
|
||||
if(!d)
|
||||
d = new Private;
|
||||
d->type = Element;
|
||||
d->e = elem;
|
||||
}
|
||||
|
||||
void Parser::Event::setError()
|
||||
{
|
||||
if(!d)
|
||||
d = new Private;
|
||||
d->type = Error;
|
||||
}
|
||||
|
||||
void Parser::Event::setActualString(const QString &str)
|
||||
{
|
||||
d->str = str;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Parser
|
||||
//----------------------------------------------------------------------------
|
||||
class Parser::Private
|
||||
{
|
||||
public:
|
||||
Private()
|
||||
{
|
||||
doc = 0;
|
||||
in = 0;
|
||||
handler = 0;
|
||||
reader = 0;
|
||||
reset();
|
||||
}
|
||||
|
||||
~Private()
|
||||
{
|
||||
reset(false);
|
||||
}
|
||||
|
||||
void reset(bool create=true)
|
||||
{
|
||||
delete reader;
|
||||
delete handler;
|
||||
delete in;
|
||||
delete doc;
|
||||
|
||||
if(create) {
|
||||
doc = new QDomDocument;
|
||||
in = new StreamInput;
|
||||
handler = new ParserHandler(in, doc);
|
||||
reader = new QXmlSimpleReader;
|
||||
reader->setContentHandler(handler);
|
||||
|
||||
// initialize the reader
|
||||
in->pause(true);
|
||||
reader->parse(in, true);
|
||||
in->pause(false);
|
||||
}
|
||||
}
|
||||
|
||||
QDomDocument *doc;
|
||||
StreamInput *in;
|
||||
ParserHandler *handler;
|
||||
QXmlSimpleReader *reader;
|
||||
};
|
||||
|
||||
Parser::Parser()
|
||||
{
|
||||
d = new Private;
|
||||
|
||||
// check for evil bug in Qt <= 3.2.1
|
||||
if(!qt_bug_check) {
|
||||
qt_bug_check = true;
|
||||
QDomElement e = d->doc->createElementNS("someuri", "somename");
|
||||
if(e.hasAttributeNS("someuri", "somename"))
|
||||
qt_bug_have = true;
|
||||
else
|
||||
qt_bug_have = false;
|
||||
}
|
||||
}
|
||||
|
||||
Parser::~Parser()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void Parser::reset()
|
||||
{
|
||||
d->reset();
|
||||
}
|
||||
|
||||
void Parser::appendData(const QByteArray &a)
|
||||
{
|
||||
d->in->appendData(a);
|
||||
|
||||
// if handler was waiting for more, give it a kick
|
||||
if(d->handler->needMore)
|
||||
d->handler->checkNeedMore();
|
||||
}
|
||||
|
||||
Parser::Event Parser::readNext()
|
||||
{
|
||||
Event e;
|
||||
if(d->handler->needMore)
|
||||
return e;
|
||||
Event *ep = d->handler->takeEvent();
|
||||
if(!ep) {
|
||||
if(!d->reader->parseContinue()) {
|
||||
e.setError();
|
||||
return e;
|
||||
}
|
||||
ep = d->handler->takeEvent();
|
||||
if(!ep)
|
||||
return e;
|
||||
}
|
||||
e = *ep;
|
||||
delete ep;
|
||||
return e;
|
||||
}
|
||||
|
||||
QByteArray Parser::unprocessed() const
|
||||
{
|
||||
return d->in->unprocessed();
|
||||
}
|
||||
|
||||
QString Parser::encoding() const
|
||||
{
|
||||
return d->in->encoding();
|
||||
}
|
||||
86
iris-legacy/iris/xmpp-core/parser.h
Normal file
86
iris-legacy/iris/xmpp-core/parser.h
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* parser.h - parse an XMPP "document"
|
||||
* Copyright (C) 2003 Justin Karneges
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PARSER_H
|
||||
#define PARSER_H
|
||||
|
||||
#include <qdom.h>
|
||||
#include <qxml.h>
|
||||
|
||||
namespace XMPP
|
||||
{
|
||||
class Parser
|
||||
{
|
||||
public:
|
||||
Parser();
|
||||
~Parser();
|
||||
|
||||
class Event
|
||||
{
|
||||
public:
|
||||
enum Type { DocumentOpen, DocumentClose, Element, Error };
|
||||
Event();
|
||||
Event(const Event &);
|
||||
Event & operator=(const Event &);
|
||||
~Event();
|
||||
|
||||
bool isNull() const;
|
||||
int type() const;
|
||||
|
||||
// for document open
|
||||
QString nsprefix(const QString &s=QString::null) const;
|
||||
|
||||
// for document open / close
|
||||
QString namespaceURI() const;
|
||||
QString localName() const;
|
||||
QString qName() const;
|
||||
QXmlAttributes atts() const;
|
||||
|
||||
// for element
|
||||
QDomElement element() const;
|
||||
|
||||
// for any
|
||||
QString actualString() const;
|
||||
|
||||
// setup
|
||||
void setDocumentOpen(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &atts, const QStringList &nsnames, const QStringList &nsvalues);
|
||||
void setDocumentClose(const QString &namespaceURI, const QString &localName, const QString &qName);
|
||||
void setElement(const QDomElement &elem);
|
||||
void setError();
|
||||
void setActualString(const QString &);
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *d;
|
||||
};
|
||||
|
||||
void reset();
|
||||
void appendData(const QByteArray &a);
|
||||
Event readNext();
|
||||
QByteArray unprocessed() const;
|
||||
QString encoding() const;
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *d;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
1695
iris-legacy/iris/xmpp-core/protocol.cpp
Normal file
1695
iris-legacy/iris/xmpp-core/protocol.cpp
Normal file
File diff suppressed because it is too large
Load diff
361
iris-legacy/iris/xmpp-core/protocol.h
Normal file
361
iris-legacy/iris/xmpp-core/protocol.h
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
/*
|
||||
* protocol.h - XMPP-Core protocol state machine
|
||||
* Copyright (C) 2004 Justin Karneges
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PROTOCOL_H
|
||||
#define PROTOCOL_H
|
||||
|
||||
#include <qpair.h>
|
||||
//Added by qt3to4:
|
||||
#include <QList>
|
||||
#include "xmlprotocol.h"
|
||||
#include "xmpp.h"
|
||||
|
||||
#define NS_ETHERX "http://etherx.jabber.org/streams"
|
||||
#define NS_CLIENT "jabber:client"
|
||||
#define NS_SERVER "jabber:server"
|
||||
#define NS_DIALBACK "jabber:server:dialback"
|
||||
#define NS_STREAMS "urn:ietf:params:xml:ns:xmpp-streams"
|
||||
#define NS_TLS "urn:ietf:params:xml:ns:xmpp-tls"
|
||||
#define NS_SASL "urn:ietf:params:xml:ns:xmpp-sasl"
|
||||
#define NS_SESSION "urn:ietf:params:xml:ns:xmpp-session"
|
||||
#define NS_STANZAS "urn:ietf:params:xml:ns:xmpp-stanzas"
|
||||
#define NS_BIND "urn:ietf:params:xml:ns:xmpp-bind"
|
||||
#define NS_COMPRESS_FEATURE "http://jabber.org/features/compress"
|
||||
#define NS_COMPRESS_PROTOCOL "http://jabber.org/protocol/compress"
|
||||
|
||||
namespace XMPP
|
||||
{
|
||||
class Version
|
||||
{
|
||||
public:
|
||||
Version(int maj=0, int min=0);
|
||||
|
||||
int major, minor;
|
||||
};
|
||||
|
||||
class StreamFeatures
|
||||
{
|
||||
public:
|
||||
StreamFeatures();
|
||||
|
||||
bool tls_supported, sasl_supported, bind_supported, compress_supported;
|
||||
bool tls_required;
|
||||
QStringList sasl_mechs;
|
||||
QStringList compression_mechs;
|
||||
};
|
||||
|
||||
class BasicProtocol : public XmlProtocol
|
||||
{
|
||||
public:
|
||||
// xmpp 1.0 error conditions
|
||||
enum SASLCond {
|
||||
Aborted,
|
||||
IncorrectEncoding,
|
||||
InvalidAuthzid,
|
||||
InvalidMech,
|
||||
MechTooWeak,
|
||||
NotAuthorized,
|
||||
TemporaryAuthFailure
|
||||
};
|
||||
enum StreamCond {
|
||||
BadFormat,
|
||||
BadNamespacePrefix,
|
||||
Conflict,
|
||||
ConnectionTimeout,
|
||||
HostGone,
|
||||
HostUnknown,
|
||||
ImproperAddressing,
|
||||
InternalServerError,
|
||||
InvalidFrom,
|
||||
InvalidId,
|
||||
InvalidNamespace,
|
||||
InvalidXml,
|
||||
StreamNotAuthorized,
|
||||
PolicyViolation,
|
||||
RemoteConnectionFailed,
|
||||
ResourceConstraint,
|
||||
RestrictedXml,
|
||||
SeeOtherHost,
|
||||
SystemShutdown,
|
||||
UndefinedCondition,
|
||||
UnsupportedEncoding,
|
||||
UnsupportedStanzaType,
|
||||
UnsupportedVersion,
|
||||
XmlNotWellFormed
|
||||
};
|
||||
enum BindCond {
|
||||
BindBadRequest,
|
||||
BindNotAllowed,
|
||||
BindConflict
|
||||
};
|
||||
|
||||
// extend the XmlProtocol enums
|
||||
enum Need {
|
||||
NSASLMechs = XmlProtocol::NCustom, // need SASL mechlist
|
||||
NStartTLS, // need to switch on TLS layer
|
||||
NCompress, // need to switch on compression layer
|
||||
NSASLFirst, // need SASL first step
|
||||
NSASLNext, // need SASL next step
|
||||
NSASLLayer, // need to switch on SASL layer
|
||||
NCustom = XmlProtocol::NCustom+10
|
||||
};
|
||||
enum Event {
|
||||
EFeatures = XmlProtocol::ECustom, // breakpoint after features packet is received
|
||||
ESASLSuccess, // breakpoint after successful sasl auth
|
||||
EStanzaReady, // a stanza was received
|
||||
EStanzaSent, // a stanza was sent
|
||||
EReady, // stream is ready for stanza use
|
||||
ECustom = XmlProtocol::ECustom+10
|
||||
};
|
||||
enum Error {
|
||||
ErrProtocol = XmlProtocol::ErrCustom, // there was an error in the xmpp-core protocol exchange
|
||||
ErrStream, // <stream:error>, see errCond, errText, and errAppSpec for details
|
||||
ErrStartTLS, // server refused starttls
|
||||
ErrCompress, // server refused compression
|
||||
ErrAuth, // authorization error. errCond holds sasl condition (or numeric code for old-protocol)
|
||||
ErrBind, // server refused resource bind
|
||||
ErrCustom = XmlProtocol::ErrCustom+10
|
||||
};
|
||||
|
||||
BasicProtocol();
|
||||
~BasicProtocol();
|
||||
|
||||
void reset();
|
||||
|
||||
// for outgoing xml
|
||||
QDomDocument doc;
|
||||
|
||||
// sasl-related
|
||||
QString saslMech() const;
|
||||
QByteArray saslStep() const;
|
||||
void setSASLMechList(const QStringList &list);
|
||||
void setSASLFirst(const QString &mech, const QByteArray &step);
|
||||
void setSASLNext(const QByteArray &step);
|
||||
void setSASLAuthed();
|
||||
|
||||
// send / recv
|
||||
void sendStanza(const QDomElement &e);
|
||||
void sendDirect(const QString &s);
|
||||
void sendWhitespace();
|
||||
QDomElement recvStanza();
|
||||
|
||||
// shutdown
|
||||
void shutdown();
|
||||
void shutdownWithError(int cond, const QString &otherHost="");
|
||||
|
||||
// <stream> information
|
||||
QString to, from, id, lang;
|
||||
Version version;
|
||||
|
||||
// error output
|
||||
int errCond;
|
||||
QString errText;
|
||||
QDomElement errAppSpec;
|
||||
QString otherHost;
|
||||
|
||||
QByteArray spare; // filled with unprocessed data on NStartTLS and NSASLLayer
|
||||
|
||||
bool isReady() const;
|
||||
|
||||
enum { TypeElement, TypeStanza, TypeDirect, TypePing };
|
||||
|
||||
protected:
|
||||
static int stringToSASLCond(const QString &s);
|
||||
static int stringToStreamCond(const QString &s);
|
||||
static QString saslCondToString(int);
|
||||
static QString streamCondToString(int);
|
||||
|
||||
void send(const QDomElement &e, bool clip=false);
|
||||
void sendStreamError(int cond, const QString &text="", const QDomElement &appSpec=QDomElement());
|
||||
void sendStreamError(const QString &text); // old-style
|
||||
|
||||
bool errorAndClose(int cond, const QString &text="", const QDomElement &appSpec=QDomElement());
|
||||
bool error(int code);
|
||||
void delayErrorAndClose(int cond, const QString &text="", const QDomElement &appSpec=QDomElement());
|
||||
void delayError(int code);
|
||||
|
||||
// reimplemented
|
||||
QDomElement docElement();
|
||||
void handleDocOpen(const Parser::Event &pe);
|
||||
bool handleError();
|
||||
bool handleCloseFinished();
|
||||
bool doStep(const QDomElement &e);
|
||||
void itemWritten(int id, int size);
|
||||
|
||||
virtual QString defaultNamespace();
|
||||
virtual QStringList extraNamespaces(); // stringlist: prefix,uri,prefix,uri, [...]
|
||||
virtual void handleStreamOpen(const Parser::Event &pe);
|
||||
virtual bool doStep2(const QDomElement &e)=0;
|
||||
|
||||
void setReady(bool b);
|
||||
|
||||
QString sasl_mech;
|
||||
QStringList sasl_mechlist;
|
||||
QByteArray sasl_step;
|
||||
bool sasl_authed;
|
||||
|
||||
QDomElement stanzaToRecv;
|
||||
|
||||
private:
|
||||
struct SASLCondEntry
|
||||
{
|
||||
const char *str;
|
||||
int cond;
|
||||
};
|
||||
static SASLCondEntry saslCondTable[];
|
||||
|
||||
struct StreamCondEntry
|
||||
{
|
||||
const char *str;
|
||||
int cond;
|
||||
};
|
||||
static StreamCondEntry streamCondTable[];
|
||||
|
||||
struct SendItem
|
||||
{
|
||||
QDomElement stanzaToSend;
|
||||
QString stringToSend;
|
||||
bool doWhitespace;
|
||||
};
|
||||
QList<SendItem> sendList;
|
||||
|
||||
bool doShutdown, delayedError, closeError, ready;
|
||||
int stanzasPending, stanzasWritten;
|
||||
|
||||
void init();
|
||||
void extractStreamError(const QDomElement &e);
|
||||
};
|
||||
|
||||
class CoreProtocol : public BasicProtocol
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
NPassword = NCustom, // need password for old-mode
|
||||
EDBVerify = ECustom, // breakpoint after db:verify request
|
||||
ErrPlain = ErrCustom // server only supports plain, but allowPlain is false locally
|
||||
};
|
||||
|
||||
CoreProtocol();
|
||||
~CoreProtocol();
|
||||
|
||||
void reset();
|
||||
|
||||
void startClientOut(const Jid &jid, bool oldOnly, bool tlsActive, bool doAuth, bool doCompression);
|
||||
void startServerOut(const QString &to);
|
||||
void startDialbackOut(const QString &to, const QString &from);
|
||||
void startDialbackVerifyOut(const QString &to, const QString &from, const QString &id, const QString &key);
|
||||
void startClientIn(const QString &id);
|
||||
void startServerIn(const QString &id);
|
||||
|
||||
void setLang(const QString &s);
|
||||
void setAllowTLS(bool b);
|
||||
void setAllowBind(bool b);
|
||||
void setAllowPlain(bool b); // old-mode
|
||||
const Jid& jid() const;
|
||||
|
||||
void setPassword(const QString &s);
|
||||
void setFrom(const QString &s);
|
||||
void setDialbackKey(const QString &s);
|
||||
|
||||
// input
|
||||
QString user, host;
|
||||
|
||||
// status
|
||||
bool old;
|
||||
|
||||
StreamFeatures features;
|
||||
|
||||
//static QString xmlToString(const QDomElement &e, bool clip=false);
|
||||
|
||||
class DBItem
|
||||
{
|
||||
public:
|
||||
enum { ResultRequest, ResultGrant, VerifyRequest, VerifyGrant, Validated };
|
||||
int type;
|
||||
Jid to, from;
|
||||
QString key, id;
|
||||
bool ok;
|
||||
};
|
||||
|
||||
private:
|
||||
enum Step {
|
||||
Start,
|
||||
Done,
|
||||
SendFeatures,
|
||||
GetRequest,
|
||||
HandleTLS,
|
||||
GetSASLResponse,
|
||||
IncHandleSASLSuccess,
|
||||
GetFeatures, // read features packet
|
||||
HandleFeatures, // act on features, by initiating tls, sasl, or bind
|
||||
GetTLSProceed, // read <proceed/> tls response
|
||||
GetCompressProceed, // read <compressed/> compression response
|
||||
GetSASLFirst, // perform sasl first step using provided data
|
||||
GetSASLChallenge, // read server sasl challenge
|
||||
GetSASLNext, // perform sasl next step using provided data
|
||||
HandleSASLSuccess, // handle what must be done after reporting sasl success
|
||||
GetBindResponse, // read bind response
|
||||
HandleAuthGet, // send old-protocol auth-get
|
||||
GetAuthGetResponse, // read auth-get response
|
||||
HandleAuthSet, // send old-protocol auth-set
|
||||
GetAuthSetResponse // read auth-set response
|
||||
};
|
||||
|
||||
QList<DBItem> dbrequests, dbpending, dbvalidated;
|
||||
|
||||
bool server, dialback, dialback_verify;
|
||||
int step;
|
||||
|
||||
bool digest;
|
||||
bool tls_started, sasl_started, compress_started;
|
||||
|
||||
Jid jid_;
|
||||
bool oldOnly;
|
||||
bool allowPlain;
|
||||
bool doTLS, doAuth, doBinding, doCompress;
|
||||
QString password;
|
||||
|
||||
QString dialback_id, dialback_key;
|
||||
QString self_from;
|
||||
|
||||
void init();
|
||||
static int getOldErrorCode(const QDomElement &e);
|
||||
bool loginComplete();
|
||||
|
||||
bool isValidStanza(const QDomElement &e) const;
|
||||
bool grabPendingItem(const Jid &to, const Jid &from, int type, DBItem *item);
|
||||
bool normalStep(const QDomElement &e);
|
||||
bool dialbackStep(const QDomElement &e);
|
||||
|
||||
// reimplemented
|
||||
bool stepAdvancesParser() const;
|
||||
bool stepRequiresElement() const;
|
||||
void stringSend(const QString &s);
|
||||
void stringRecv(const QString &s);
|
||||
QString defaultNamespace();
|
||||
QStringList extraNamespaces();
|
||||
void handleStreamOpen(const Parser::Event &pe);
|
||||
bool doStep2(const QDomElement &e);
|
||||
void elementSend(const QDomElement &e);
|
||||
void elementRecv(const QDomElement &e);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
651
iris-legacy/iris/xmpp-core/securestream.cpp
Normal file
651
iris-legacy/iris/xmpp-core/securestream.cpp
Normal file
|
|
@ -0,0 +1,651 @@
|
|||
/*
|
||||
* securestream.cpp - combines a ByteStream with TLS and SASL
|
||||
* Copyright (C) 2004 Justin Karneges
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
Note: SecureStream depends on the underlying security layers to signal
|
||||
plain-to-encrypted results immediately (as opposed to waiting for the
|
||||
event loop) so that the user cannot add/remove security layers during
|
||||
this conversion moment. QCA::TLS and QCA::SASL behave as expected,
|
||||
but future layers might not.
|
||||
*/
|
||||
|
||||
#include "securestream.h"
|
||||
|
||||
#include <qpointer.h>
|
||||
#include <QList>
|
||||
#include <qtimer.h>
|
||||
//Added by qt3to4:
|
||||
#include <Q3PtrList>
|
||||
|
||||
#ifdef USE_TLSHANDLER
|
||||
#include "xmpp.h"
|
||||
#endif
|
||||
#include "compressionhandler.h"
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// LayerTracker
|
||||
//----------------------------------------------------------------------------
|
||||
class LayerTracker
|
||||
{
|
||||
public:
|
||||
struct Item
|
||||
{
|
||||
int plain;
|
||||
int encoded;
|
||||
};
|
||||
|
||||
LayerTracker();
|
||||
|
||||
void reset();
|
||||
void addPlain(int plain);
|
||||
void specifyEncoded(int encoded, int plain);
|
||||
int finished(int encoded);
|
||||
|
||||
int p;
|
||||
QList<Item> list;
|
||||
};
|
||||
|
||||
LayerTracker::LayerTracker()
|
||||
{
|
||||
p = 0;
|
||||
}
|
||||
|
||||
void LayerTracker::reset()
|
||||
{
|
||||
p = 0;
|
||||
list.clear();
|
||||
}
|
||||
|
||||
void LayerTracker::addPlain(int plain)
|
||||
{
|
||||
p += plain;
|
||||
}
|
||||
|
||||
void LayerTracker::specifyEncoded(int encoded, int plain)
|
||||
{
|
||||
// can't specify more bytes than we have
|
||||
if(plain > p)
|
||||
plain = p;
|
||||
p -= plain;
|
||||
Item i;
|
||||
i.plain = plain;
|
||||
i.encoded = encoded;
|
||||
list += i;
|
||||
}
|
||||
|
||||
int LayerTracker::finished(int encoded)
|
||||
{
|
||||
int plain = 0;
|
||||
for(QList<Item>::Iterator it = list.begin(); it != list.end();) {
|
||||
Item &i = *it;
|
||||
|
||||
// not enough?
|
||||
if(encoded < i.encoded) {
|
||||
i.encoded -= encoded;
|
||||
break;
|
||||
}
|
||||
|
||||
encoded -= i.encoded;
|
||||
plain += i.plain;
|
||||
it = list.remove(it);
|
||||
}
|
||||
return plain;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// SecureStream
|
||||
//----------------------------------------------------------------------------
|
||||
class SecureLayer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum { TLS, SASL, TLSH, Compression };
|
||||
int type;
|
||||
union {
|
||||
QCA::TLS *tls;
|
||||
QCA::SASL *sasl;
|
||||
#ifdef USE_TLSHANDLER
|
||||
XMPP::TLSHandler *tlsHandler;
|
||||
#endif
|
||||
CompressionHandler *compressionHandler;
|
||||
} p;
|
||||
LayerTracker layer;
|
||||
bool tls_done;
|
||||
int prebytes;
|
||||
|
||||
SecureLayer(QCA::TLS *t)
|
||||
{
|
||||
type = TLS;
|
||||
p.tls = t;
|
||||
init();
|
||||
connect(p.tls, SIGNAL(handshaken()), SLOT(tls_handshaken()));
|
||||
connect(p.tls, SIGNAL(readyRead()), SLOT(tls_readyRead()));
|
||||
connect(p.tls, SIGNAL(readyReadOutgoing(int)), SLOT(tls_readyReadOutgoing(int)));
|
||||
connect(p.tls, SIGNAL(closed()), SLOT(tls_closed()));
|
||||
connect(p.tls, SIGNAL(error(int)), SLOT(tls_error(int)));
|
||||
}
|
||||
|
||||
SecureLayer(QCA::SASL *s)
|
||||
{
|
||||
type = SASL;
|
||||
p.sasl = s;
|
||||
init();
|
||||
connect(p.sasl, SIGNAL(readyRead()), SLOT(sasl_readyRead()));
|
||||
connect(p.sasl, SIGNAL(readyReadOutgoing()), SLOT(sasl_readyReadOutgoing()));
|
||||
connect(p.sasl, SIGNAL(error()), SLOT(sasl_error()));
|
||||
}
|
||||
|
||||
SecureLayer(CompressionHandler *t)
|
||||
{
|
||||
t->setParent(this); // automatically clean up CompressionHandler when SecureLayer is destroyed
|
||||
type = Compression;
|
||||
p.compressionHandler = t;
|
||||
init();
|
||||
connect(p.compressionHandler, SIGNAL(readyRead()), SLOT(compressionHandler_readyRead()));
|
||||
connect(p.compressionHandler, SIGNAL(readyReadOutgoing()), SLOT(compressionHandler_readyReadOutgoing()));
|
||||
connect(p.compressionHandler, SIGNAL(error()), SLOT(compressionHandler_error()));
|
||||
}
|
||||
|
||||
#ifdef USE_TLSHANDLER
|
||||
SecureLayer(XMPP::TLSHandler *t)
|
||||
{
|
||||
type = TLSH;
|
||||
p.tlsHandler = t;
|
||||
init();
|
||||
connect(p.tlsHandler, SIGNAL(success()), SLOT(tlsHandler_success()));
|
||||
connect(p.tlsHandler, SIGNAL(fail()), SLOT(tlsHandler_fail()));
|
||||
connect(p.tlsHandler, SIGNAL(closed()), SLOT(tlsHandler_closed()));
|
||||
connect(p.tlsHandler, SIGNAL(readyRead(const QByteArray &)), SLOT(tlsHandler_readyRead(const QByteArray &)));
|
||||
connect(p.tlsHandler, SIGNAL(readyReadOutgoing(const QByteArray &, int)), SLOT(tlsHandler_readyReadOutgoing(const QByteArray &, int)));
|
||||
}
|
||||
#endif
|
||||
|
||||
void init()
|
||||
{
|
||||
tls_done = false;
|
||||
prebytes = 0;
|
||||
}
|
||||
|
||||
void write(const QByteArray &a)
|
||||
{
|
||||
layer.addPlain(a.size());
|
||||
switch(type) {
|
||||
case TLS: { p.tls->write(a); break; }
|
||||
case SASL: { p.sasl->write(a); break; }
|
||||
#ifdef USE_TLSHANDLER
|
||||
case TLSH: { p.tlsHandler->write(a); break; }
|
||||
#endif
|
||||
case Compression: { p.compressionHandler->write(a); break; }
|
||||
}
|
||||
}
|
||||
|
||||
void writeIncoming(const QByteArray &a)
|
||||
{
|
||||
switch(type) {
|
||||
case TLS: { p.tls->writeIncoming(a); break; }
|
||||
case SASL: { p.sasl->writeIncoming(a); break; }
|
||||
#ifdef USE_TLSHANDLER
|
||||
case TLSH: { p.tlsHandler->writeIncoming(a); break; }
|
||||
#endif
|
||||
case Compression: { p.compressionHandler->writeIncoming(a); break; }
|
||||
}
|
||||
}
|
||||
|
||||
int finished(int plain)
|
||||
{
|
||||
int written = 0;
|
||||
|
||||
// deal with prebytes (bytes sent prior to this security layer)
|
||||
if(prebytes > 0) {
|
||||
if(prebytes >= plain) {
|
||||
written += plain;
|
||||
prebytes -= plain;
|
||||
plain = 0;
|
||||
}
|
||||
else {
|
||||
written += prebytes;
|
||||
plain -= prebytes;
|
||||
prebytes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// put remainder into the layer tracker
|
||||
if(type == SASL || tls_done)
|
||||
written += layer.finished(plain);
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
signals:
|
||||
void tlsHandshaken();
|
||||
void tlsClosed(const QByteArray &);
|
||||
void readyRead(const QByteArray &);
|
||||
void needWrite(const QByteArray &);
|
||||
void error(int);
|
||||
|
||||
private slots:
|
||||
void tls_handshaken()
|
||||
{
|
||||
tls_done = true;
|
||||
tlsHandshaken();
|
||||
}
|
||||
|
||||
void tls_readyRead()
|
||||
{
|
||||
QByteArray a = p.tls->read();
|
||||
readyRead(a);
|
||||
}
|
||||
|
||||
void tls_readyReadOutgoing(int plainBytes)
|
||||
{
|
||||
QByteArray a = p.tls->readOutgoing();
|
||||
if(tls_done)
|
||||
layer.specifyEncoded(a.size(), plainBytes);
|
||||
needWrite(a);
|
||||
}
|
||||
|
||||
void tls_closed()
|
||||
{
|
||||
QByteArray a = p.tls->readUnprocessed();
|
||||
tlsClosed(a);
|
||||
}
|
||||
|
||||
void tls_error(int x)
|
||||
{
|
||||
error(x);
|
||||
}
|
||||
|
||||
void sasl_readyRead()
|
||||
{
|
||||
QByteArray a = p.sasl->read();
|
||||
readyRead(a);
|
||||
}
|
||||
|
||||
void sasl_readyReadOutgoing()
|
||||
{
|
||||
int plainBytes;
|
||||
QByteArray a = p.sasl->readOutgoing(&plainBytes);
|
||||
layer.specifyEncoded(a.size(), plainBytes);
|
||||
needWrite(a);
|
||||
}
|
||||
|
||||
void sasl_error()
|
||||
{
|
||||
error(p.sasl->errorCode());
|
||||
}
|
||||
|
||||
void compressionHandler_readyRead()
|
||||
{
|
||||
QByteArray a = p.compressionHandler->read();
|
||||
readyRead(a);
|
||||
}
|
||||
|
||||
void compressionHandler_readyReadOutgoing()
|
||||
{
|
||||
int plainBytes;
|
||||
QByteArray a = p.compressionHandler->readOutgoing(&plainBytes);
|
||||
layer.specifyEncoded(a.size(), plainBytes);
|
||||
needWrite(a);
|
||||
}
|
||||
|
||||
void compressionHandler_error()
|
||||
{
|
||||
error(p.compressionHandler->errorCode());
|
||||
}
|
||||
|
||||
#ifdef USE_TLSHANDLER
|
||||
void tlsHandler_success()
|
||||
{
|
||||
tls_done = true;
|
||||
tlsHandshaken();
|
||||
}
|
||||
|
||||
void tlsHandler_fail()
|
||||
{
|
||||
error(0);
|
||||
}
|
||||
|
||||
void tlsHandler_closed()
|
||||
{
|
||||
tlsClosed(QByteArray());
|
||||
}
|
||||
|
||||
void tlsHandler_readyRead(const QByteArray &a)
|
||||
{
|
||||
readyRead(a);
|
||||
}
|
||||
|
||||
void tlsHandler_readyReadOutgoing(const QByteArray &a, int plainBytes)
|
||||
{
|
||||
if(tls_done)
|
||||
layer.specifyEncoded(a.size(), plainBytes);
|
||||
needWrite(a);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
#include "securestream.moc"
|
||||
|
||||
class SecureStream::Private
|
||||
{
|
||||
public:
|
||||
ByteStream *bs;
|
||||
Q3PtrList<SecureLayer> layers;
|
||||
int pending;
|
||||
int errorCode;
|
||||
bool active;
|
||||
bool topInProgress;
|
||||
|
||||
bool haveTLS() const
|
||||
{
|
||||
Q3PtrListIterator<SecureLayer> it(layers);
|
||||
for(SecureLayer *s; (s = it.current()); ++it) {
|
||||
if(s->type == SecureLayer::TLS
|
||||
#ifdef USE_TLSHANDLER
|
||||
|| s->type == SecureLayer::TLSH
|
||||
#endif
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool haveSASL() const
|
||||
{
|
||||
Q3PtrListIterator<SecureLayer> it(layers);
|
||||
for(SecureLayer *s; (s = it.current()); ++it) {
|
||||
if(s->type == SecureLayer::SASL)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool haveCompress() const
|
||||
{
|
||||
Q3PtrListIterator<SecureLayer> it(layers);
|
||||
for(SecureLayer *s; (s = it.current()); ++it) {
|
||||
if(s->type == SecureLayer::Compression)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
SecureStream::SecureStream(ByteStream *s)
|
||||
:ByteStream(0)
|
||||
{
|
||||
d = new Private;
|
||||
|
||||
d->bs = s;
|
||||
connect(d->bs, SIGNAL(readyRead()), SLOT(bs_readyRead()));
|
||||
connect(d->bs, SIGNAL(bytesWritten(int)), SLOT(bs_bytesWritten(int)));
|
||||
|
||||
d->layers.setAutoDelete(true);
|
||||
d->pending = 0;
|
||||
d->active = true;
|
||||
d->topInProgress = false;
|
||||
}
|
||||
|
||||
SecureStream::~SecureStream()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void SecureStream::linkLayer(QObject *s)
|
||||
{
|
||||
connect(s, SIGNAL(tlsHandshaken()), SLOT(layer_tlsHandshaken()));
|
||||
connect(s, SIGNAL(tlsClosed(const QByteArray &)), SLOT(layer_tlsClosed(const QByteArray &)));
|
||||
connect(s, SIGNAL(readyRead(const QByteArray &)), SLOT(layer_readyRead(const QByteArray &)));
|
||||
connect(s, SIGNAL(needWrite(const QByteArray &)), SLOT(layer_needWrite(const QByteArray &)));
|
||||
connect(s, SIGNAL(error(int)), SLOT(layer_error(int)));
|
||||
}
|
||||
|
||||
int SecureStream::calcPrebytes() const
|
||||
{
|
||||
int x = 0;
|
||||
Q3PtrListIterator<SecureLayer> it(d->layers);
|
||||
for(SecureLayer *s; (s = it.current()); ++it)
|
||||
x += s->prebytes;
|
||||
return (d->pending - x);
|
||||
}
|
||||
|
||||
void SecureStream::startTLSClient(QCA::TLS *t, const QByteArray &spare)
|
||||
{
|
||||
if(!d->active || d->topInProgress || d->haveTLS())
|
||||
return;
|
||||
|
||||
SecureLayer *s = new SecureLayer(t);
|
||||
s->prebytes = calcPrebytes();
|
||||
linkLayer(s);
|
||||
d->layers.append(s);
|
||||
d->topInProgress = true;
|
||||
|
||||
insertData(spare);
|
||||
}
|
||||
|
||||
void SecureStream::startTLSServer(QCA::TLS *t, const QByteArray &spare)
|
||||
{
|
||||
if(!d->active || d->topInProgress || d->haveTLS())
|
||||
return;
|
||||
|
||||
SecureLayer *s = new SecureLayer(t);
|
||||
s->prebytes = calcPrebytes();
|
||||
linkLayer(s);
|
||||
d->layers.append(s);
|
||||
d->topInProgress = true;
|
||||
|
||||
insertData(spare);
|
||||
}
|
||||
|
||||
void SecureStream::setLayerCompress(const QByteArray& spare)
|
||||
{
|
||||
if(!d->active || d->topInProgress || d->haveCompress())
|
||||
return;
|
||||
|
||||
SecureLayer *s = new SecureLayer(new CompressionHandler());
|
||||
s->prebytes = calcPrebytes();
|
||||
linkLayer(s);
|
||||
d->layers.append(s);
|
||||
|
||||
insertData(spare);
|
||||
}
|
||||
|
||||
void SecureStream::setLayerSASL(QCA::SASL *sasl, const QByteArray &spare)
|
||||
{
|
||||
if(!d->active || d->topInProgress || d->haveSASL())
|
||||
return;
|
||||
|
||||
SecureLayer *s = new SecureLayer(sasl);
|
||||
s->prebytes = calcPrebytes();
|
||||
linkLayer(s);
|
||||
d->layers.append(s);
|
||||
|
||||
insertData(spare);
|
||||
}
|
||||
|
||||
#ifdef USE_TLSHANDLER
|
||||
void SecureStream::startTLSClient(XMPP::TLSHandler *t, const QString &server, const QByteArray &spare)
|
||||
{
|
||||
if(!d->active || d->topInProgress || d->haveTLS())
|
||||
return;
|
||||
|
||||
SecureLayer *s = new SecureLayer(t);
|
||||
s->prebytes = calcPrebytes();
|
||||
linkLayer(s);
|
||||
d->layers.append(s);
|
||||
d->topInProgress = true;
|
||||
|
||||
// unlike QCA::TLS, XMPP::TLSHandler has no return value
|
||||
s->p.tlsHandler->startClient(server);
|
||||
|
||||
insertData(spare);
|
||||
}
|
||||
#endif
|
||||
|
||||
void SecureStream::closeTLS()
|
||||
{
|
||||
SecureLayer *s = d->layers.getLast();
|
||||
if(s) {
|
||||
if(s->type == SecureLayer::TLS)
|
||||
s->p.tls->close();
|
||||
}
|
||||
}
|
||||
|
||||
int SecureStream::errorCode() const
|
||||
{
|
||||
return d->errorCode;
|
||||
}
|
||||
|
||||
bool SecureStream::isOpen() const
|
||||
{
|
||||
return d->active;
|
||||
}
|
||||
|
||||
void SecureStream::write(const QByteArray &a)
|
||||
{
|
||||
if(!isOpen())
|
||||
return;
|
||||
|
||||
d->pending += a.size();
|
||||
|
||||
// send to the last layer
|
||||
SecureLayer *s = d->layers.getLast();
|
||||
if(s)
|
||||
s->write(a);
|
||||
else
|
||||
writeRawData(a);
|
||||
}
|
||||
|
||||
int SecureStream::bytesToWrite() const
|
||||
{
|
||||
return d->pending;
|
||||
}
|
||||
|
||||
void SecureStream::bs_readyRead()
|
||||
{
|
||||
QByteArray a = d->bs->read();
|
||||
|
||||
// send to the first layer
|
||||
SecureLayer *s = d->layers.getFirst();
|
||||
if(s) {
|
||||
s->writeIncoming(a);
|
||||
}
|
||||
else {
|
||||
incomingData(a);
|
||||
}
|
||||
}
|
||||
|
||||
void SecureStream::bs_bytesWritten(int bytes)
|
||||
{
|
||||
Q3PtrListIterator<SecureLayer> it(d->layers);
|
||||
for(SecureLayer *s; (s = it.current()); ++it)
|
||||
bytes = s->finished(bytes);
|
||||
|
||||
if(bytes > 0) {
|
||||
d->pending -= bytes;
|
||||
bytesWritten(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
void SecureStream::layer_tlsHandshaken()
|
||||
{
|
||||
d->topInProgress = false;
|
||||
tlsHandshaken();
|
||||
}
|
||||
|
||||
void SecureStream::layer_tlsClosed(const QByteArray &)
|
||||
{
|
||||
d->active = false;
|
||||
d->layers.clear();
|
||||
tlsClosed();
|
||||
}
|
||||
|
||||
void SecureStream::layer_readyRead(const QByteArray &a)
|
||||
{
|
||||
SecureLayer *s = (SecureLayer *)sender();
|
||||
Q3PtrListIterator<SecureLayer> it(d->layers);
|
||||
while(it.current() != s)
|
||||
++it;
|
||||
|
||||
// pass upwards
|
||||
++it;
|
||||
s = it.current();
|
||||
if(s)
|
||||
s->writeIncoming(a);
|
||||
else
|
||||
incomingData(a);
|
||||
}
|
||||
|
||||
void SecureStream::layer_needWrite(const QByteArray &a)
|
||||
{
|
||||
SecureLayer *s = (SecureLayer *)sender();
|
||||
Q3PtrListIterator<SecureLayer> it(d->layers);
|
||||
while(it.current() != s)
|
||||
++it;
|
||||
|
||||
// pass downwards
|
||||
--it;
|
||||
s = it.current();
|
||||
if(s)
|
||||
s->write(a);
|
||||
else
|
||||
writeRawData(a);
|
||||
}
|
||||
|
||||
void SecureStream::layer_error(int x)
|
||||
{
|
||||
SecureLayer *s = (SecureLayer *)sender();
|
||||
int type = s->type;
|
||||
d->errorCode = x;
|
||||
d->active = false;
|
||||
d->layers.clear();
|
||||
if(type == SecureLayer::TLS)
|
||||
error(ErrTLS);
|
||||
else if(type == SecureLayer::SASL)
|
||||
error(ErrSASL);
|
||||
#ifdef USE_TLSHANDLER
|
||||
else if(type == SecureLayer::TLSH)
|
||||
error(ErrTLS);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SecureStream::insertData(const QByteArray &a)
|
||||
{
|
||||
if(!a.isEmpty()) {
|
||||
SecureLayer *s = d->layers.getLast();
|
||||
if(s)
|
||||
s->writeIncoming(a);
|
||||
else
|
||||
incomingData(a);
|
||||
}
|
||||
}
|
||||
|
||||
void SecureStream::writeRawData(const QByteArray &a)
|
||||
{
|
||||
d->bs->write(a);
|
||||
}
|
||||
|
||||
void SecureStream::incomingData(const QByteArray &a)
|
||||
{
|
||||
appendRead(a);
|
||||
if(bytesAvailable())
|
||||
readyRead();
|
||||
}
|
||||
87
iris-legacy/iris/xmpp-core/securestream.h
Normal file
87
iris-legacy/iris/xmpp-core/securestream.h
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* securestream.h - combines a ByteStream with TLS and SASL
|
||||
* Copyright (C) 2004 Justin Karneges
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SECURESTREAM_H
|
||||
#define SECURESTREAM_H
|
||||
|
||||
#include <qca.h>
|
||||
#include "bytestream.h"
|
||||
|
||||
#define USE_TLSHANDLER
|
||||
|
||||
#ifdef USE_TLSHANDLER
|
||||
namespace XMPP
|
||||
{
|
||||
class TLSHandler;
|
||||
}
|
||||
#endif
|
||||
|
||||
class CompressionHandler;
|
||||
|
||||
class SecureStream : public ByteStream
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Error { ErrTLS = ErrCustom, ErrSASL };
|
||||
SecureStream(ByteStream *s);
|
||||
~SecureStream();
|
||||
|
||||
void startTLSClient(QCA::TLS *t, const QByteArray &spare=QByteArray());
|
||||
void startTLSServer(QCA::TLS *t, const QByteArray &spare=QByteArray());
|
||||
void setLayerCompress(const QByteArray &spare=QByteArray());
|
||||
void setLayerSASL(QCA::SASL *s, const QByteArray &spare=QByteArray());
|
||||
#ifdef USE_TLSHANDLER
|
||||
void startTLSClient(XMPP::TLSHandler *t, const QString &server, const QByteArray &spare=QByteArray());
|
||||
#endif
|
||||
|
||||
void closeTLS();
|
||||
int errorCode() const;
|
||||
|
||||
// reimplemented
|
||||
bool isOpen() const;
|
||||
void write(const QByteArray &);
|
||||
int bytesToWrite() const;
|
||||
|
||||
signals:
|
||||
void tlsHandshaken();
|
||||
void tlsClosed();
|
||||
|
||||
private slots:
|
||||
void bs_readyRead();
|
||||
void bs_bytesWritten(int);
|
||||
|
||||
void layer_tlsHandshaken();
|
||||
void layer_tlsClosed(const QByteArray &);
|
||||
void layer_readyRead(const QByteArray &);
|
||||
void layer_needWrite(const QByteArray &);
|
||||
void layer_error(int);
|
||||
|
||||
private:
|
||||
void linkLayer(QObject *);
|
||||
int calcPrebytes() const;
|
||||
void insertData(const QByteArray &a);
|
||||
void writeRawData(const QByteArray &a);
|
||||
void incomingData(const QByteArray &a);
|
||||
|
||||
class Private;
|
||||
Private *d;
|
||||
};
|
||||
|
||||
#endif
|
||||
676
iris-legacy/iris/xmpp-core/simplesasl.cpp
Normal file
676
iris-legacy/iris/xmpp-core/simplesasl.cpp
Normal file
|
|
@ -0,0 +1,676 @@
|
|||
/*
|
||||
* simplesasl.cpp - Simple SASL implementation
|
||||
* Copyright (C) 2003 Justin Karneges
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "simplesasl.h"
|
||||
|
||||
#include <qhostaddress.h>
|
||||
#include <qstringlist.h>
|
||||
#include <q3ptrlist.h>
|
||||
#include <QList>
|
||||
#include <qca.h>
|
||||
#include <Q3CString>
|
||||
#include <stdlib.h>
|
||||
#include <QtCrypto>
|
||||
#include <QDebug>
|
||||
|
||||
#ifdef YAPSI
|
||||
#include <QUrl>
|
||||
#include <QVariantMap>
|
||||
#include <QDateTime>
|
||||
#include "yaonline.h"
|
||||
#endif
|
||||
|
||||
#define SIMPLESASL_PLAIN
|
||||
|
||||
namespace XMPP {
|
||||
struct Prop
|
||||
{
|
||||
Q3CString var, val;
|
||||
};
|
||||
|
||||
class PropList : public QList<Prop>
|
||||
{
|
||||
public:
|
||||
PropList() : QList<Prop>()
|
||||
{
|
||||
}
|
||||
|
||||
void set(const Q3CString &var, const Q3CString &val)
|
||||
{
|
||||
Prop p;
|
||||
p.var = var;
|
||||
p.val = val;
|
||||
append(p);
|
||||
}
|
||||
|
||||
Q3CString get(const Q3CString &var)
|
||||
{
|
||||
for(ConstIterator it = begin(); it != end(); ++it) {
|
||||
if((*it).var == var)
|
||||
return (*it).val;
|
||||
}
|
||||
return Q3CString();
|
||||
}
|
||||
|
||||
Q3CString toString() const
|
||||
{
|
||||
Q3CString str;
|
||||
bool first = true;
|
||||
for(ConstIterator it = begin(); it != end(); ++it) {
|
||||
if(!first)
|
||||
str += ',';
|
||||
if ((*it).var == "realm" || (*it).var == "nonce" || (*it).var == "username" || (*it).var == "cnonce" || (*it).var == "digest-uri" || (*it).var == "authzid")
|
||||
str += (*it).var + "=\"" + (*it).val + '\"';
|
||||
else
|
||||
str += (*it).var + "=" + (*it).val;
|
||||
first = false;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
bool fromString(const QByteArray &str)
|
||||
{
|
||||
PropList list;
|
||||
int at = 0;
|
||||
while(1) {
|
||||
while (at < str.length() && (str[at] == ',' || str[at] == ' ' || str[at] == '\t'))
|
||||
++at;
|
||||
int n = str.find('=', at);
|
||||
if(n == -1)
|
||||
break;
|
||||
Q3CString var, val;
|
||||
var = str.mid(at, n-at);
|
||||
at = n + 1;
|
||||
if(str[at] == '\"') {
|
||||
++at;
|
||||
n = str.find('\"', at);
|
||||
if(n == -1)
|
||||
break;
|
||||
val = str.mid(at, n-at);
|
||||
at = n + 1;
|
||||
}
|
||||
else {
|
||||
n = at;
|
||||
while (n < str.length() && str[n] != ',' && str[n] != ' ' && str[n] != '\t')
|
||||
++n;
|
||||
val = str.mid(at, n-at);
|
||||
at = n;
|
||||
}
|
||||
Prop prop;
|
||||
prop.var = var;
|
||||
if (var == "qop" || var == "cipher") {
|
||||
int a = 0;
|
||||
while (a < val.length()) {
|
||||
while (a < val.length() && (val[a] == ',' || val[a] == ' ' || val[a] == '\t'))
|
||||
++a;
|
||||
if (a == val.length())
|
||||
break;
|
||||
n = a+1;
|
||||
while (n < val.length() && val[n] != ',' && val[n] != ' ' && val[n] != '\t')
|
||||
++n;
|
||||
prop.val = val.mid(a, n-a);
|
||||
list.append(prop);
|
||||
a = n+1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
prop.val = val;
|
||||
list.append(prop);
|
||||
}
|
||||
|
||||
if(at >= str.size() - 1 || (str[at] != ',' && str[at] != ' ' && str[at] != '\t'))
|
||||
break;
|
||||
}
|
||||
|
||||
// integrity check
|
||||
if(list.varCount("nonce") != 1)
|
||||
return false;
|
||||
if(list.varCount("algorithm") != 1)
|
||||
return false;
|
||||
*this = list;
|
||||
return true;
|
||||
}
|
||||
|
||||
int varCount(const Q3CString &var)
|
||||
{
|
||||
int n = 0;
|
||||
for(ConstIterator it = begin(); it != end(); ++it) {
|
||||
if((*it).var == var)
|
||||
++n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
QStringList getValues(const Q3CString &var)
|
||||
{
|
||||
QStringList list;
|
||||
for(ConstIterator it = begin(); it != end(); ++it) {
|
||||
if((*it).var == var)
|
||||
list += (*it).val;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
};
|
||||
|
||||
class SimpleSASLContext : public QCA::SASLContext
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
class ParamsMutable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
User is held
|
||||
*/
|
||||
bool user;
|
||||
|
||||
/**
|
||||
Authorization ID is held
|
||||
*/
|
||||
bool authzid;
|
||||
|
||||
/**
|
||||
Password is held
|
||||
*/
|
||||
bool pass;
|
||||
|
||||
/**
|
||||
Realm is held
|
||||
*/
|
||||
bool realm;
|
||||
};
|
||||
// core props
|
||||
QString service, host;
|
||||
|
||||
// state
|
||||
int step;
|
||||
bool capable;
|
||||
bool allow_plain;
|
||||
#ifdef YAPSI
|
||||
bool allow_xFacebookPlatform;
|
||||
#endif
|
||||
QByteArray out_buf, in_buf;
|
||||
QString mechanism_;
|
||||
QString out_mech;
|
||||
|
||||
ParamsMutable need;
|
||||
ParamsMutable have;
|
||||
QString user, authz, realm;
|
||||
QCA::SecureArray pass;
|
||||
Result result_;
|
||||
QCA::SASL::AuthCondition authCondition_;
|
||||
QByteArray result_to_net_, result_to_app_;
|
||||
int encoded_;
|
||||
|
||||
SimpleSASLContext(QCA::Provider* p) : QCA::SASLContext(p)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
~SimpleSASLContext()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
resetState();
|
||||
|
||||
capable = true;
|
||||
allow_plain = false;
|
||||
#ifdef YAPSI
|
||||
allow_xFacebookPlatform = false;
|
||||
#endif
|
||||
need.user = false;
|
||||
need.authzid = false;
|
||||
need.pass = false;
|
||||
need.realm = false;
|
||||
have.user = false;
|
||||
have.authzid = false;
|
||||
have.pass = false;
|
||||
have.realm = false;
|
||||
user = QString();
|
||||
authz = QString();
|
||||
pass = QCA::SecureArray();
|
||||
realm = QString();
|
||||
}
|
||||
|
||||
void resetState()
|
||||
{
|
||||
out_mech = QString();
|
||||
out_buf.resize(0);
|
||||
authCondition_ = QCA::SASL::AuthFail;
|
||||
}
|
||||
|
||||
virtual void setConstraints(QCA::SASL::AuthFlags flags, int ssfMin, int) {
|
||||
if(flags & (QCA::SASL::RequireForwardSecrecy | QCA::SASL::RequirePassCredentials | QCA::SASL::RequireMutualAuth) || ssfMin > 0)
|
||||
capable = false;
|
||||
else
|
||||
capable = true;
|
||||
allow_plain = flags & QCA::SASL::AllowPlain;
|
||||
#ifdef YAPSI
|
||||
allow_xFacebookPlatform = flags & QCA::SASL::AllowXFacebookPlatform;
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual void setup(const QString& _service, const QString& _host, const QCA::SASLContext::HostPort*, const QCA::SASLContext::HostPort*, const QString&, int) {
|
||||
service = _service;
|
||||
host = _host;
|
||||
}
|
||||
|
||||
virtual void startClient(const QStringList &mechlist, bool allowClientSendFirst) {
|
||||
Q_UNUSED(allowClientSendFirst);
|
||||
|
||||
mechanism_ = QString();
|
||||
foreach(QString mech, mechlist) {
|
||||
#ifdef YAPSI
|
||||
if (mech == "X-FACEBOOK-PLATFORM" && allow_xFacebookPlatform) {
|
||||
mechanism_ = "X-FACEBOOK-PLATFORM";
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mech == "DIGEST-MD5") {
|
||||
mechanism_ = "DIGEST-MD5";
|
||||
break;
|
||||
}
|
||||
#ifdef SIMPLESASL_PLAIN
|
||||
if (mech == "PLAIN" && allow_plain)
|
||||
mechanism_ = "PLAIN";
|
||||
#endif
|
||||
}
|
||||
|
||||
if(!capable || mechanism_.isEmpty()) {
|
||||
result_ = Error;
|
||||
authCondition_ = QCA::SASL::NoMechanism;
|
||||
if (!capable)
|
||||
qWarning("simplesasl.cpp: Not enough capabilities");
|
||||
if (mechanism_.isEmpty())
|
||||
qWarning("simplesasl.cpp: No mechanism available");
|
||||
QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
|
||||
resetState();
|
||||
result_ = Continue;
|
||||
step = 0;
|
||||
tryAgain();
|
||||
}
|
||||
|
||||
virtual void nextStep(const QByteArray &from_net) {
|
||||
in_buf = from_net;
|
||||
tryAgain();
|
||||
}
|
||||
|
||||
virtual void tryAgain() {
|
||||
// All exits of the method must emit the ready signal
|
||||
// so all exits go through a goto ready;
|
||||
if(step == 0) {
|
||||
out_mech = mechanism_;
|
||||
|
||||
#ifdef SIMPLESASL_PLAIN
|
||||
// PLAIN
|
||||
if (out_mech == "PLAIN") {
|
||||
// First, check if we have everything
|
||||
if(need.user || need.pass) {
|
||||
qWarning("simplesasl.cpp: Did not receive necessary auth parameters");
|
||||
result_ = Error;
|
||||
goto ready;
|
||||
}
|
||||
if(!have.user)
|
||||
need.user = true;
|
||||
if(!have.pass)
|
||||
need.pass = true;
|
||||
if(need.user || need.pass) {
|
||||
result_ = Params;
|
||||
goto ready;
|
||||
}
|
||||
|
||||
// Continue with authentication
|
||||
QByteArray plain;
|
||||
if (!authz.isEmpty())
|
||||
plain += authz.utf8();
|
||||
plain += '\0' + user.toUtf8() + '\0' + pass.toByteArray();
|
||||
out_buf.resize(plain.length());
|
||||
memcpy(out_buf.data(), plain.data(), out_buf.size());
|
||||
}
|
||||
#endif
|
||||
++step;
|
||||
if (out_mech == "PLAIN")
|
||||
result_ = Success;
|
||||
else
|
||||
result_ = Continue;
|
||||
}
|
||||
else if(step == 1) {
|
||||
#ifdef YAPSI
|
||||
if (out_mech == "X-FACEBOOK-PLATFORM") {
|
||||
QString fakeUrl = "http://facebook.com/?" + QString(in_buf);
|
||||
QUrl url = QUrl(fakeUrl, QUrl::TolerantMode);
|
||||
|
||||
QVariantMap map;
|
||||
map["method"] = url.queryItemValue("method");
|
||||
map["nonce"] = url.queryItemValue("nonce");
|
||||
map["call_id"] = QDateTime::currentDateTime().toTime_t();
|
||||
xFacebookPlatformLogin(map);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// if we still need params, then the app has failed us!
|
||||
if(need.user || need.authzid || need.pass || need.realm) {
|
||||
qWarning("simplesasl.cpp: Did not receive necessary auth parameters");
|
||||
result_ = Error;
|
||||
goto ready;
|
||||
}
|
||||
|
||||
// see if some params are needed
|
||||
if(!have.user)
|
||||
need.user = true;
|
||||
//if(!have.authzid)
|
||||
// need.authzid = true;
|
||||
if(!have.pass)
|
||||
need.pass = true;
|
||||
if(need.user || need.authzid || need.pass) {
|
||||
result_ = Params;
|
||||
goto ready;
|
||||
}
|
||||
|
||||
// get props
|
||||
QByteArray cs(in_buf);
|
||||
PropList in;
|
||||
if(!in.fromString(cs)) {
|
||||
authCondition_ = QCA::SASL::BadProtocol;
|
||||
result_ = Error;
|
||||
goto ready;
|
||||
}
|
||||
//qDebug() << (QString("simplesasl.cpp: IN: %1").arg(QString(in.toString())));
|
||||
|
||||
// make a cnonce
|
||||
QByteArray a(32);
|
||||
for(int n = 0; n < (int)a.size(); ++n)
|
||||
a[n] = (char)(256.0*rand()/(RAND_MAX+1.0));
|
||||
Q3CString cnonce = QCA::Base64().arrayToString(a).toLatin1();
|
||||
|
||||
// make other variables
|
||||
if (realm.isEmpty())
|
||||
realm = QString::fromUtf8(in.get("realm"));
|
||||
Q3CString nonce = in.get("nonce");
|
||||
Q3CString nc = "00000001";
|
||||
Q3CString uri = service.utf8() + '/' + host.utf8();
|
||||
Q3CString qop = "auth";
|
||||
|
||||
// build 'response'
|
||||
Q3CString X = user.utf8() + ':' + realm.utf8() + ':' + Q3CString(pass.toByteArray());
|
||||
QByteArray Y = QCA::Hash("md5").hash(X).toByteArray();
|
||||
QByteArray tmp = ':' + nonce + ':' + cnonce;
|
||||
if (!authz.isEmpty())
|
||||
tmp += ':' + authz.utf8();
|
||||
//qDebug() << (QString(tmp));
|
||||
|
||||
QByteArray A1(Y + tmp);
|
||||
QByteArray A2 = QByteArray("AUTHENTICATE:") + uri;
|
||||
Q3CString HA1 = QCA::Hash("md5").hashToString(A1).toLatin1();
|
||||
Q3CString HA2 = QCA::Hash("md5").hashToString(A2).toLatin1();
|
||||
Q3CString KD = HA1 + ':' + nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + HA2;
|
||||
Q3CString Z = QCA::Hash("md5").hashToString(KD).toLatin1();
|
||||
|
||||
//qDebug() << (QString("simplesasl.cpp: A1 = %1").arg(QString(A1)).toAscii());
|
||||
//qDebug() << (QString("simplesasl.cpp: A2 = %1").arg(QString(A2)).toAscii());
|
||||
//qDebug() << (QString("simplesasl.cpp: KD = %1").arg(QString(KD)).toAscii());
|
||||
|
||||
// build output
|
||||
PropList out;
|
||||
out.set("username", user.utf8());
|
||||
if (!realm.isEmpty())
|
||||
out.set("realm", realm.utf8());
|
||||
out.set("nonce", nonce);
|
||||
out.set("cnonce", cnonce);
|
||||
out.set("nc", nc);
|
||||
//out.set("serv-type", service.utf8());
|
||||
//out.set("host", host.utf8());
|
||||
out.set("digest-uri", uri);
|
||||
out.set("qop", qop);
|
||||
out.set("response", Z);
|
||||
out.set("charset", "utf-8");
|
||||
if (!authz.isEmpty())
|
||||
out.set("authzid", authz.utf8());
|
||||
QByteArray s(out.toString());
|
||||
//qDebug() << (QString("OUT: %1").arg(QString(out.toString())));
|
||||
|
||||
// done
|
||||
out_buf.resize(s.length());
|
||||
memcpy(out_buf.data(), s.data(), out_buf.size());
|
||||
++step;
|
||||
result_ = Continue;
|
||||
}
|
||||
/*else if (step == 2) {
|
||||
//Commenting this out is Justin's fix for updated QCA.
|
||||
out_buf.resize(0);
|
||||
result_ = Continue;
|
||||
++step;
|
||||
}*/
|
||||
else {
|
||||
out_buf.resize(0);
|
||||
result_ = Success;
|
||||
}
|
||||
ready:
|
||||
QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
virtual void update(const QByteArray &from_net, const QByteArray &from_app) {
|
||||
result_to_app_ = from_net;
|
||||
result_to_net_ = from_app;
|
||||
encoded_ = from_app.size();
|
||||
result_ = Success;
|
||||
QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
virtual bool waitForResultsReady(int msecs) {
|
||||
|
||||
// TODO: for now, all operations block anyway
|
||||
Q_UNUSED(msecs);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual Result result() const {
|
||||
return result_;
|
||||
}
|
||||
|
||||
virtual QStringList mechlist() const {
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
virtual QString mech() const {
|
||||
return out_mech;
|
||||
}
|
||||
|
||||
virtual bool haveClientInit() const {
|
||||
return out_mech == "PLAIN";
|
||||
}
|
||||
|
||||
virtual QByteArray stepData() const {
|
||||
return out_buf;
|
||||
}
|
||||
|
||||
virtual QByteArray to_net() {
|
||||
return result_to_net_;
|
||||
}
|
||||
|
||||
virtual int encoded() const {
|
||||
return encoded_;
|
||||
}
|
||||
|
||||
virtual QByteArray to_app() {
|
||||
return result_to_app_;
|
||||
}
|
||||
|
||||
virtual int ssf() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual QCA::SASL::AuthCondition authCondition() const {
|
||||
return authCondition_;
|
||||
}
|
||||
|
||||
virtual QCA::SASL::Params clientParams() const {
|
||||
return QCA::SASL::Params(need.user, need.authzid, need.pass, need.realm);
|
||||
}
|
||||
|
||||
virtual void setClientParams(const QString *_user, const QString *_authzid, const QCA::SecureArray *_pass, const QString *_realm) {
|
||||
if(_user) {
|
||||
user = *_user;
|
||||
need.user = false;
|
||||
have.user = true;
|
||||
}
|
||||
if(_authzid) {
|
||||
authz = *_authzid;
|
||||
need.authzid = false;
|
||||
have.authzid = true;
|
||||
}
|
||||
if(_pass) {
|
||||
pass = *_pass;
|
||||
need.pass = false;
|
||||
have.pass = true;
|
||||
}
|
||||
if(_realm) {
|
||||
realm = *_realm;
|
||||
need.realm = false;
|
||||
have.realm = true;
|
||||
}
|
||||
}
|
||||
|
||||
virtual QStringList realmlist() const
|
||||
{
|
||||
// TODO
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
virtual QString username() const {
|
||||
return QString();
|
||||
}
|
||||
|
||||
virtual QString authzid() const {
|
||||
return QString();
|
||||
}
|
||||
|
||||
virtual QCA::Provider::Context* clone() const {
|
||||
SimpleSASLContext* s = new SimpleSASLContext(provider());
|
||||
// TODO: Copy all the members
|
||||
return s;
|
||||
}
|
||||
|
||||
virtual void startServer(const QString &, bool) {
|
||||
result_ = QCA::SASLContext::Error;
|
||||
QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection);
|
||||
}
|
||||
virtual void serverFirstStep(const QString &, const QByteArray *) {
|
||||
result_ = QCA::SASLContext::Error;
|
||||
QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
public slots:
|
||||
#ifdef YAPSI
|
||||
void xFacebookPlatformLogin(QVariantMap map) {
|
||||
#ifdef YAPSI_ACTIVEX_SERVER
|
||||
YaOnlineHelper::instance()->xFacebookPlatformLogin(map, this, "xFacebookPlatformDataReady");
|
||||
#else
|
||||
QString apiKey = "API_KEY";
|
||||
QString sessionKey = "SESSION_KEY";
|
||||
QString secret = "SECRET";
|
||||
|
||||
map["api_key"] = apiKey;
|
||||
map["session_key"] = sessionKey;
|
||||
map["v"] = "1.0";
|
||||
|
||||
QStringList signingList;
|
||||
QStringList resultList;
|
||||
QMapIterator<QString, QVariant> it(map);
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
|
||||
signingList += QString("%1=%2")
|
||||
.arg(it.key())
|
||||
.arg(it.value().toString());
|
||||
resultList += QString("%1=%2")
|
||||
.arg(it.key())
|
||||
.arg(QUrl::toPercentEncoding(it.value().toString()).constData());
|
||||
}
|
||||
|
||||
QString signingData = signingList.join("") + secret;
|
||||
QString resultData = resultList.join("&");
|
||||
|
||||
QString signature = QCA::Hash("md5").hashToString(signingData.toUtf8());
|
||||
resultData += "&sig=" + signature;
|
||||
|
||||
// qWarning("signingData = '%s'", qPrintable(signingData));
|
||||
// qWarning("resultData = '%s'", qPrintable(resultData));
|
||||
xFacebookPlatformDataReady(resultData);
|
||||
#endif
|
||||
}
|
||||
|
||||
void xFacebookPlatformDataReady(const QString& data) {
|
||||
Q_ASSERT(out_mech == "X-FACEBOOK-PLATFORM");
|
||||
out_buf = data.toUtf8();
|
||||
++step;
|
||||
result_ = Success;
|
||||
|
||||
QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
class QCASimpleSASL : public QCA::Provider
|
||||
{
|
||||
public:
|
||||
QCASimpleSASL() {}
|
||||
~QCASimpleSASL() {}
|
||||
|
||||
void init()
|
||||
{
|
||||
}
|
||||
|
||||
QString name() const {
|
||||
return "simplesasl";
|
||||
}
|
||||
|
||||
QStringList features() const {
|
||||
return QStringList("sasl");
|
||||
}
|
||||
|
||||
QCA::Provider::Context* createContext(const QString& cap)
|
||||
{
|
||||
if(cap == "sasl")
|
||||
return new SimpleSASLContext(this);
|
||||
return 0;
|
||||
}
|
||||
int qcaVersion() const
|
||||
{
|
||||
return QCA_VERSION;
|
||||
}
|
||||
};
|
||||
|
||||
QCA::Provider *createProviderSimpleSASL()
|
||||
{
|
||||
return (new QCASimpleSASL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "simplesasl.moc"
|
||||
33
iris-legacy/iris/xmpp-core/simplesasl.h
Normal file
33
iris-legacy/iris/xmpp-core/simplesasl.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* simplesasl.h - Simple SASL implementation
|
||||
* Copyright (C) 2003 Justin Karneges
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SIMPLESASL_H
|
||||
#define SIMPLESASL_H
|
||||
|
||||
namespace QCA {
|
||||
class Provider;
|
||||
}
|
||||
|
||||
namespace XMPP
|
||||
{
|
||||
QCA::Provider* createProviderSimpleSASL();
|
||||
}
|
||||
|
||||
#endif
|
||||
1389
iris-legacy/iris/xmpp-core/stream.cpp
Normal file
1389
iris-legacy/iris/xmpp-core/stream.cpp
Normal file
File diff suppressed because it is too large
Load diff
20
iris-legacy/iris/xmpp-core/td.h
Normal file
20
iris-legacy/iris/xmpp-core/td.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef TESTDEBUG_H
|
||||
#define TESTDEBUG_H
|
||||
|
||||
#include <qdom.h>
|
||||
|
||||
class TD
|
||||
{
|
||||
public:
|
||||
TD();
|
||||
~TD();
|
||||
|
||||
static void msg(const QString &);
|
||||
static void outgoingTag(const QString &);
|
||||
static void incomingTag(const QString &);
|
||||
static void outgoingXml(const QDomElement &);
|
||||
static void incomingXml(const QDomElement &);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
171
iris-legacy/iris/xmpp-core/tlshandler.cpp
Normal file
171
iris-legacy/iris/xmpp-core/tlshandler.cpp
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* tlshandler.cpp - abstract wrapper for TLS
|
||||
* Copyright (C) 2003 Justin Karneges
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "xmpp.h"
|
||||
|
||||
#include <qtimer.h>
|
||||
#include "qca.h"
|
||||
|
||||
using namespace XMPP;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// TLSHandler
|
||||
//----------------------------------------------------------------------------
|
||||
TLSHandler::TLSHandler(QObject *parent)
|
||||
:QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
TLSHandler::~TLSHandler()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// QCATLSHandler
|
||||
//----------------------------------------------------------------------------
|
||||
class QCATLSHandler::Private
|
||||
{
|
||||
public:
|
||||
QCA::TLS *tls;
|
||||
int state, err;
|
||||
QString host;
|
||||
bool internalHostMatch;
|
||||
};
|
||||
|
||||
QCATLSHandler::QCATLSHandler(QCA::TLS *parent)
|
||||
:TLSHandler(parent)
|
||||
{
|
||||
d = new Private;
|
||||
d->tls = parent;
|
||||
connect(d->tls, SIGNAL(handshaken()), SLOT(tls_handshaken()));
|
||||
connect(d->tls, SIGNAL(readyRead()), SLOT(tls_readyRead()));
|
||||
connect(d->tls, SIGNAL(readyReadOutgoing()), SLOT(tls_readyReadOutgoing()));
|
||||
connect(d->tls, SIGNAL(closed()), SLOT(tls_closed()));
|
||||
connect(d->tls, SIGNAL(error()), SLOT(tls_error()));
|
||||
d->state = 0;
|
||||
d->err = -1;
|
||||
d->internalHostMatch = false;
|
||||
}
|
||||
|
||||
QCATLSHandler::~QCATLSHandler()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void QCATLSHandler::setXMPPCertCheck(bool enable)
|
||||
{
|
||||
d->internalHostMatch = enable;
|
||||
}
|
||||
bool QCATLSHandler::XMPPCertCheck()
|
||||
{
|
||||
return d->internalHostMatch;
|
||||
}
|
||||
bool QCATLSHandler::certMatchesHostname()
|
||||
{
|
||||
if (!d->internalHostMatch) return false;
|
||||
QCA::CertificateChain peerCert = d->tls->peerCertificateChain();
|
||||
|
||||
if (peerCert.primary().matchesHostName(d->host))
|
||||
return true;
|
||||
|
||||
Jid host(d->host);
|
||||
|
||||
foreach( const QString &idOnXmppAddr, peerCert.primary().subjectInfo().values(QCA::XMPP) ) {
|
||||
if (host.compare(Jid(idOnXmppAddr)))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
QCA::TLS *QCATLSHandler::tls() const
|
||||
{
|
||||
return d->tls;
|
||||
}
|
||||
|
||||
int QCATLSHandler::tlsError() const
|
||||
{
|
||||
return d->err;
|
||||
}
|
||||
|
||||
void QCATLSHandler::reset()
|
||||
{
|
||||
d->tls->reset();
|
||||
d->state = 0;
|
||||
}
|
||||
|
||||
void QCATLSHandler::startClient(const QString &host)
|
||||
{
|
||||
d->state = 0;
|
||||
d->err = -1;
|
||||
if (d->internalHostMatch) d->host = host;
|
||||
d->tls->startClient(d->internalHostMatch ? QString() : host);
|
||||
}
|
||||
|
||||
void QCATLSHandler::write(const QByteArray &a)
|
||||
{
|
||||
d->tls->write(a);
|
||||
}
|
||||
|
||||
void QCATLSHandler::writeIncoming(const QByteArray &a)
|
||||
{
|
||||
d->tls->writeIncoming(a);
|
||||
}
|
||||
|
||||
void QCATLSHandler::continueAfterHandshake()
|
||||
{
|
||||
if(d->state == 2) {
|
||||
d->tls->continueAfterStep();
|
||||
success();
|
||||
d->state = 3;
|
||||
}
|
||||
}
|
||||
|
||||
void QCATLSHandler::tls_handshaken()
|
||||
{
|
||||
d->state = 2;
|
||||
tlsHandshaken();
|
||||
}
|
||||
|
||||
void QCATLSHandler::tls_readyRead()
|
||||
{
|
||||
readyRead(d->tls->read());
|
||||
}
|
||||
|
||||
void QCATLSHandler::tls_readyReadOutgoing()
|
||||
{
|
||||
int plainBytes;
|
||||
QByteArray buf = d->tls->readOutgoing(&plainBytes);
|
||||
readyReadOutgoing(buf, plainBytes);
|
||||
}
|
||||
|
||||
void QCATLSHandler::tls_closed()
|
||||
{
|
||||
closed();
|
||||
}
|
||||
|
||||
void QCATLSHandler::tls_error()
|
||||
{
|
||||
d->err = d->tls->errorCode();
|
||||
d->state = 0;
|
||||
fail();
|
||||
}
|
||||
646
iris-legacy/iris/xmpp-core/xmlprotocol.cpp
Normal file
646
iris-legacy/iris/xmpp-core/xmlprotocol.cpp
Normal file
|
|
@ -0,0 +1,646 @@
|
|||
/*
|
||||
* xmlprotocol.cpp - state machine for 'jabber-like' protocols
|
||||
* Copyright (C) 2004 Justin Karneges
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "xmlprotocol.h"
|
||||
|
||||
#include "bytestream.h"
|
||||
//Added by qt3to4:
|
||||
#include <QList>
|
||||
#include <QTextStream>
|
||||
#include <Q3CString>
|
||||
#include <QApplication>
|
||||
|
||||
using namespace XMPP;
|
||||
|
||||
// stripExtraNS
|
||||
//
|
||||
// This function removes namespace information from various nodes for
|
||||
// display purposes only (the element is pretty much useless for processing
|
||||
// after this). We do this because QXml is a bit overzealous about outputting
|
||||
// redundant namespaces.
|
||||
static QDomElement stripExtraNS(const QDomElement &e)
|
||||
{
|
||||
// find closest parent with a namespace
|
||||
QDomNode par = e.parentNode();
|
||||
while(!par.isNull() && par.namespaceURI().isNull())
|
||||
par = par.parentNode();
|
||||
bool noShowNS = false;
|
||||
if(!par.isNull() && par.namespaceURI() == e.namespaceURI())
|
||||
noShowNS = true;
|
||||
|
||||
// build qName (prefix:localName)
|
||||
QString qName;
|
||||
if(!e.prefix().isEmpty())
|
||||
qName = e.prefix() + ':' + e.localName();
|
||||
else
|
||||
qName = e.tagName();
|
||||
|
||||
QDomElement i;
|
||||
int x;
|
||||
if(noShowNS)
|
||||
i = e.ownerDocument().createElement(qName);
|
||||
else
|
||||
i = e.ownerDocument().createElementNS(e.namespaceURI(), qName);
|
||||
|
||||
// copy attributes
|
||||
QDomNamedNodeMap al = e.attributes();
|
||||
for(x = 0; x < al.count(); ++x) {
|
||||
QDomAttr a = al.item(x).cloneNode().toAttr();
|
||||
|
||||
// don't show xml namespace
|
||||
if(a.namespaceURI() == NS_XML)
|
||||
i.setAttribute(QString("xml:") + a.name(), a.value());
|
||||
else
|
||||
i.setAttributeNodeNS(a);
|
||||
}
|
||||
|
||||
// copy children
|
||||
QDomNodeList nl = e.childNodes();
|
||||
for(x = 0; x < nl.count(); ++x) {
|
||||
QDomNode n = nl.item(x);
|
||||
if(n.isElement())
|
||||
i.appendChild(stripExtraNS(n.toElement()));
|
||||
else
|
||||
i.appendChild(n.cloneNode());
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
// xmlToString
|
||||
//
|
||||
// This function converts a QDomElement into a QString, using stripExtraNS
|
||||
// to make it pretty.
|
||||
static QString xmlToString(const QDomElement &e, const QString &fakeNS, const QString &fakeQName, bool clip)
|
||||
{
|
||||
QDomElement i = e.cloneNode().toElement();
|
||||
|
||||
// It seems QDom can only have one namespace attribute at a time (see docElement 'HACK').
|
||||
// Fortunately we only need one kind depending on the input, so it is specified here.
|
||||
QDomElement fake = e.ownerDocument().createElementNS(fakeNS, fakeQName);
|
||||
fake.appendChild(i);
|
||||
fake = stripExtraNS(fake);
|
||||
QString out;
|
||||
{
|
||||
QTextStream ts(&out, QIODevice::WriteOnly);
|
||||
fake.firstChild().save(ts, 0);
|
||||
}
|
||||
// 'clip' means to remove any unwanted (and unneeded) characters, such as a trailing newline
|
||||
if(clip) {
|
||||
int n = out.findRev('>');
|
||||
out.truncate(n+1);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// createRootXmlTags
|
||||
//
|
||||
// This function creates three QStrings, one being an <?xml .. ?> processing
|
||||
// instruction, and the others being the opening and closing tags of an
|
||||
// element, <foo> and </foo>. This basically allows us to get the raw XML
|
||||
// text needed to open/close an XML stream, without resorting to generating
|
||||
// the XML ourselves. This function uses QDom to do the generation, which
|
||||
// ensures proper encoding and entity output.
|
||||
static void createRootXmlTags(const QDomElement &root, QString *xmlHeader, QString *tagOpen, QString *tagClose)
|
||||
{
|
||||
QDomElement e = root.cloneNode(false).toElement();
|
||||
|
||||
// insert a dummy element to ensure open and closing tags are generated
|
||||
QDomElement dummy = e.ownerDocument().createElement("dummy");
|
||||
e.appendChild(dummy);
|
||||
|
||||
// convert to xml->text
|
||||
QString str;
|
||||
{
|
||||
QTextStream ts(&str, QIODevice::WriteOnly);
|
||||
e.save(ts, 0);
|
||||
}
|
||||
|
||||
// parse the tags out
|
||||
int n = str.find('<');
|
||||
int n2 = str.find('>', n);
|
||||
++n2;
|
||||
*tagOpen = str.mid(n, n2-n);
|
||||
n2 = str.findRev('>');
|
||||
n = str.findRev('<');
|
||||
++n2;
|
||||
*tagClose = str.mid(n, n2-n);
|
||||
|
||||
// generate a nice xml processing header
|
||||
*xmlHeader = "<?xml version=\"1.0\"?>";
|
||||
}
|
||||
|
||||
// w3c xml spec:
|
||||
// [2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
|
||||
static inline bool validChar(const quint32 ch)
|
||||
{
|
||||
return ch == 0x9 || ch == 0xA || ch == 0xD
|
||||
|| (ch >= 0x20 && ch <= 0xD7FF)
|
||||
|| (ch >= 0xE000 && ch <= 0xFFFD)
|
||||
|| (ch >= 0x10000 && ch <= 0x10FFFF);
|
||||
}
|
||||
|
||||
static inline bool lowSurrogate(const quint32 ch)
|
||||
{
|
||||
return ch >= 0xDC00 && ch <= 0xDFFF;
|
||||
}
|
||||
|
||||
static inline bool highSurrogate(const quint32 ch)
|
||||
{
|
||||
return ch >= 0xD800 && ch <= 0xDBFF;
|
||||
}
|
||||
|
||||
|
||||
// force encoding of '>'. this function is needed for XMPP-Core, which
|
||||
// requires the '>' character to be encoded as ">" even though this is
|
||||
// not required by the XML spec.
|
||||
// Also remove chars that are ouside the allowed range for XML (see validChar)
|
||||
// and invalid surrogate pairs
|
||||
static QString sanitizeForStream(const QString &in)
|
||||
{
|
||||
QString out;
|
||||
bool intag = false;
|
||||
bool inquote = false;
|
||||
QChar quotechar;
|
||||
int inlength = in.length();
|
||||
for(int n = 0; n < inlength; ++n)
|
||||
{
|
||||
QChar c = in[n];
|
||||
bool escape = false;
|
||||
if(c == '<')
|
||||
{
|
||||
intag = true;
|
||||
}
|
||||
else if(c == '>')
|
||||
{
|
||||
if(inquote) {
|
||||
escape = true;
|
||||
} else if(!intag) {
|
||||
escape = true;
|
||||
} else {
|
||||
intag = false;
|
||||
}
|
||||
}
|
||||
else if(c == '\'' || c == '\"')
|
||||
{
|
||||
if(intag)
|
||||
{
|
||||
if(!inquote)
|
||||
{
|
||||
inquote = true;
|
||||
quotechar = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(quotechar == c) {
|
||||
inquote = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(escape) {
|
||||
out += ">";
|
||||
} else {
|
||||
// don't silently drop invalid chars in element or attribute names,
|
||||
// because that's something that should not happen.
|
||||
if (intag && (!inquote)) {
|
||||
out += c;
|
||||
} else if (validChar(c.unicode())) {
|
||||
out += c;
|
||||
} else if (highSurrogate(c.unicode()) && (n+1 < inlength) && lowSurrogate(in[n+1].unicode())) {
|
||||
//uint unicode = (c.unicode() & 0x3FF) << 10 | in[n+1].unicode() & 0x3FF + 0x10000;
|
||||
// we don't need to recheck this, because 0x10000 <= unicode <= 0x100000 is always true
|
||||
out += c;
|
||||
out += in[n+1];
|
||||
++n;
|
||||
} else {
|
||||
qDebug("Dropping invalid XML char U+%04x",c.unicode());
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Protocol
|
||||
//----------------------------------------------------------------------------
|
||||
XmlProtocol::TransferItem::TransferItem()
|
||||
{
|
||||
}
|
||||
|
||||
XmlProtocol::TransferItem::TransferItem(const QString &_str, bool sent, bool external)
|
||||
{
|
||||
isString = true;
|
||||
isSent = sent;
|
||||
isExternal = external;
|
||||
str = _str;
|
||||
}
|
||||
|
||||
XmlProtocol::TransferItem::TransferItem(const QDomElement &_elem, bool sent, bool external)
|
||||
{
|
||||
isString = false;
|
||||
isSent = sent;
|
||||
isExternal = external;
|
||||
elem = _elem;
|
||||
}
|
||||
|
||||
XmlProtocol::XmlProtocol()
|
||||
// : QObject(qApp) // ONLINE-2555
|
||||
: QObject()
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
XmlProtocol::~XmlProtocol()
|
||||
{
|
||||
}
|
||||
|
||||
void XmlProtocol::init()
|
||||
{
|
||||
incoming = false;
|
||||
peerClosed = false;
|
||||
closeWritten = false;
|
||||
}
|
||||
|
||||
void XmlProtocol::reset()
|
||||
{
|
||||
init();
|
||||
|
||||
elem = QDomElement();
|
||||
elemDoc = QDomDocument();
|
||||
tagOpen = QString();
|
||||
tagClose = QString();
|
||||
xml.reset();
|
||||
outData.resize(0);
|
||||
trackQueue.clear();
|
||||
transferItemList.clear();
|
||||
}
|
||||
|
||||
void XmlProtocol::addIncomingData(const QByteArray &a)
|
||||
{
|
||||
xml.appendData(a);
|
||||
}
|
||||
|
||||
QByteArray XmlProtocol::takeOutgoingData()
|
||||
{
|
||||
QByteArray a = outData;
|
||||
outData.resize(0);
|
||||
return a;
|
||||
}
|
||||
|
||||
void XmlProtocol::outgoingDataWritten(int bytes)
|
||||
{
|
||||
for(QList<TrackItem>::Iterator it = trackQueue.begin(); it != trackQueue.end();) {
|
||||
TrackItem &i = *it;
|
||||
|
||||
// enough bytes?
|
||||
if(bytes < i.size) {
|
||||
i.size -= bytes;
|
||||
break;
|
||||
}
|
||||
int type = i.type;
|
||||
int id = i.id;
|
||||
int size = i.size;
|
||||
bytes -= i.size;
|
||||
it = trackQueue.remove(it);
|
||||
|
||||
if(type == TrackItem::Raw) {
|
||||
// do nothing
|
||||
}
|
||||
else if(type == TrackItem::Close) {
|
||||
closeWritten = true;
|
||||
}
|
||||
else if(type == TrackItem::Custom) {
|
||||
itemWritten(id, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool XmlProtocol::processStep()
|
||||
{
|
||||
Parser::Event pe;
|
||||
notify = 0;
|
||||
transferItemList.clear();
|
||||
|
||||
if(state != Closing && (state == RecvOpen || stepAdvancesParser())) {
|
||||
// if we get here, then it's because we're in some step that advances the parser
|
||||
pe = xml.readNext();
|
||||
if(!pe.isNull()) {
|
||||
// note: error/close events should be handled for ALL steps, so do them here
|
||||
switch(pe.type()) {
|
||||
case Parser::Event::DocumentOpen: {
|
||||
transferItemList += TransferItem(pe.actualString(), false);
|
||||
|
||||
//stringRecv(pe.actualString());
|
||||
break;
|
||||
}
|
||||
case Parser::Event::DocumentClose: {
|
||||
transferItemList += TransferItem(pe.actualString(), false);
|
||||
|
||||
//stringRecv(pe.actualString());
|
||||
if(incoming) {
|
||||
sendTagClose();
|
||||
event = ESend;
|
||||
peerClosed = true;
|
||||
state = Closing;
|
||||
}
|
||||
else {
|
||||
event = EPeerClosed;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Parser::Event::Element: {
|
||||
QDomElement e = elemDoc.importNode(pe.element(),true).toElement();
|
||||
transferItemList += TransferItem(e, false);
|
||||
|
||||
//elementRecv(pe.element());
|
||||
break;
|
||||
}
|
||||
case Parser::Event::Error: {
|
||||
if(incoming) {
|
||||
// If we get a parse error during the initial element exchange,
|
||||
// flip immediately into 'open' mode so that we can report an error.
|
||||
if(state == RecvOpen) {
|
||||
sendTagOpen();
|
||||
state = Open;
|
||||
}
|
||||
return handleError();
|
||||
}
|
||||
else {
|
||||
event = EError;
|
||||
errorCode = ErrParse;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(state == RecvOpen || stepRequiresElement()) {
|
||||
need = NNotify;
|
||||
notify |= NRecv;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return baseStep(pe);
|
||||
}
|
||||
|
||||
QString XmlProtocol::xmlEncoding() const
|
||||
{
|
||||
return xml.encoding();
|
||||
}
|
||||
|
||||
QString XmlProtocol::elementToString(const QDomElement &e, bool clip)
|
||||
{
|
||||
if(elem.isNull())
|
||||
elem = elemDoc.importNode(docElement(), true).toElement();
|
||||
|
||||
// Determine the appropriate 'fakeNS' to use
|
||||
QString ns;
|
||||
|
||||
// first, check root namespace
|
||||
QString pre = e.prefix();
|
||||
if(pre.isNull())
|
||||
pre = "";
|
||||
if(pre == elem.prefix()) {
|
||||
ns = elem.namespaceURI();
|
||||
}
|
||||
else {
|
||||
// scan the root attributes for 'xmlns' (oh joyous hacks)
|
||||
QDomNamedNodeMap al = elem.attributes();
|
||||
int n;
|
||||
for(n = 0; n < al.count(); ++n) {
|
||||
QDomAttr a = al.item(n).toAttr();
|
||||
QString s = a.name();
|
||||
int x = s.find(':');
|
||||
if(x != -1)
|
||||
s = s.mid(x+1);
|
||||
else
|
||||
s = "";
|
||||
if(pre == s) {
|
||||
ns = a.value();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(n >= al.count()) {
|
||||
// if we get here, then no appropriate ns was found. use root then..
|
||||
ns = elem.namespaceURI();
|
||||
}
|
||||
}
|
||||
|
||||
// build qName
|
||||
QString qn;
|
||||
if(!elem.prefix().isEmpty())
|
||||
qn = elem.prefix() + ':';
|
||||
qn += elem.localName();
|
||||
|
||||
// make the string
|
||||
return sanitizeForStream(xmlToString(e, ns, qn, clip));
|
||||
}
|
||||
|
||||
bool XmlProtocol::stepRequiresElement() const
|
||||
{
|
||||
// default returns false
|
||||
return false;
|
||||
}
|
||||
|
||||
void XmlProtocol::itemWritten(int, int)
|
||||
{
|
||||
// default does nothing
|
||||
}
|
||||
|
||||
void XmlProtocol::stringSend(const QString &)
|
||||
{
|
||||
// default does nothing
|
||||
}
|
||||
|
||||
void XmlProtocol::stringRecv(const QString &)
|
||||
{
|
||||
// default does nothing
|
||||
}
|
||||
|
||||
void XmlProtocol::elementSend(const QDomElement &)
|
||||
{
|
||||
// default does nothing
|
||||
}
|
||||
|
||||
void XmlProtocol::elementRecv(const QDomElement &)
|
||||
{
|
||||
// default does nothing
|
||||
}
|
||||
|
||||
void XmlProtocol::startConnect()
|
||||
{
|
||||
incoming = false;
|
||||
state = SendOpen;
|
||||
}
|
||||
|
||||
void XmlProtocol::startAccept()
|
||||
{
|
||||
incoming = true;
|
||||
state = RecvOpen;
|
||||
}
|
||||
|
||||
bool XmlProtocol::close()
|
||||
{
|
||||
sendTagClose();
|
||||
event = ESend;
|
||||
state = Closing;
|
||||
return true;
|
||||
}
|
||||
|
||||
int XmlProtocol::writeString(const QString &s, int id, bool external)
|
||||
{
|
||||
transferItemList += TransferItem(s, true, external);
|
||||
return internalWriteString(s, TrackItem::Custom, id);
|
||||
}
|
||||
|
||||
int XmlProtocol::writeElement(const QDomElement &e, int id, bool external, bool clip)
|
||||
{
|
||||
if(e.isNull())
|
||||
return 0;
|
||||
transferItemList += TransferItem(e, true, external);
|
||||
|
||||
//elementSend(e);
|
||||
QString out = sanitizeForStream(elementToString(e, clip));
|
||||
return internalWriteString(out, TrackItem::Custom, id);
|
||||
}
|
||||
|
||||
QByteArray XmlProtocol::resetStream()
|
||||
{
|
||||
// reset the state
|
||||
if(incoming)
|
||||
state = RecvOpen;
|
||||
else
|
||||
state = SendOpen;
|
||||
|
||||
// grab unprocessed data before resetting
|
||||
QByteArray spare = xml.unprocessed();
|
||||
xml.reset();
|
||||
return spare;
|
||||
}
|
||||
|
||||
int XmlProtocol::internalWriteData(const QByteArray &a, TrackItem::Type t, int id)
|
||||
{
|
||||
TrackItem i;
|
||||
i.type = t;
|
||||
i.id = id;
|
||||
i.size = a.size();
|
||||
trackQueue += i;
|
||||
|
||||
ByteStream::appendArray(&outData, a);
|
||||
return a.size();
|
||||
}
|
||||
|
||||
int XmlProtocol::internalWriteString(const QString &s, TrackItem::Type t, int id)
|
||||
{
|
||||
QString out=sanitizeForStream(s);
|
||||
Q3CString cs = s.utf8();
|
||||
QByteArray a(cs.length());
|
||||
memcpy(a.data(), cs.data(), a.size());
|
||||
return internalWriteData(a, t, id);
|
||||
}
|
||||
|
||||
void XmlProtocol::sendTagOpen()
|
||||
{
|
||||
if(elem.isNull())
|
||||
elem = elemDoc.importNode(docElement(), true).toElement();
|
||||
|
||||
QString xmlHeader;
|
||||
createRootXmlTags(elem, &xmlHeader, &tagOpen, &tagClose);
|
||||
|
||||
QString s;
|
||||
s += xmlHeader + '\n';
|
||||
s += sanitizeForStream(tagOpen) + '\n';
|
||||
|
||||
transferItemList += TransferItem(xmlHeader, true);
|
||||
transferItemList += TransferItem(tagOpen, true);
|
||||
|
||||
//stringSend(xmlHeader);
|
||||
//stringSend(tagOpen);
|
||||
internalWriteString(s, TrackItem::Raw);
|
||||
}
|
||||
|
||||
void XmlProtocol::sendTagClose()
|
||||
{
|
||||
transferItemList += TransferItem(tagClose, true);
|
||||
|
||||
//stringSend(tagClose);
|
||||
internalWriteString(tagClose, TrackItem::Close);
|
||||
}
|
||||
|
||||
bool XmlProtocol::baseStep(const Parser::Event &pe)
|
||||
{
|
||||
// Basic
|
||||
if(state == SendOpen) {
|
||||
sendTagOpen();
|
||||
event = ESend;
|
||||
if(incoming)
|
||||
state = Open;
|
||||
else
|
||||
state = RecvOpen;
|
||||
return true;
|
||||
}
|
||||
else if(state == RecvOpen) {
|
||||
if(incoming)
|
||||
state = SendOpen;
|
||||
else
|
||||
state = Open;
|
||||
|
||||
// note: event will always be DocumentOpen here
|
||||
handleDocOpen(pe);
|
||||
event = ERecvOpen;
|
||||
return true;
|
||||
}
|
||||
else if(state == Open) {
|
||||
QDomElement e;
|
||||
if(pe.type() == Parser::Event::Element)
|
||||
e = pe.element();
|
||||
return doStep(e);
|
||||
}
|
||||
// Closing
|
||||
else {
|
||||
if(closeWritten) {
|
||||
if(peerClosed) {
|
||||
event = EPeerClosed;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return handleCloseFinished();
|
||||
}
|
||||
|
||||
need = NNotify;
|
||||
notify = NSend;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void XmlProtocol::setIncomingAsExternal()
|
||||
{
|
||||
for(QList<TransferItem>::Iterator it = transferItemList.begin(); it != transferItemList.end(); ++it) {
|
||||
TransferItem &i = *it;
|
||||
// look for elements received
|
||||
if(!i.isString && !i.isSent)
|
||||
i.isExternal = true;
|
||||
}
|
||||
}
|
||||
|
||||
146
iris-legacy/iris/xmpp-core/xmlprotocol.h
Normal file
146
iris-legacy/iris/xmpp-core/xmlprotocol.h
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* xmlprotocol.h - state machine for 'jabber-like' protocols
|
||||
* Copyright (C) 2004 Justin Karneges
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef XMLPROTOCOL_H
|
||||
#define XMLPROTOCOL_H
|
||||
|
||||
#include <qdom.h>
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include "parser.h"
|
||||
|
||||
#define NS_XML "http://www.w3.org/XML/1998/namespace"
|
||||
|
||||
namespace XMPP
|
||||
{
|
||||
class XmlProtocol : public QObject
|
||||
{
|
||||
public:
|
||||
enum Need {
|
||||
NNotify, // need a data send and/or recv update
|
||||
NCustom = 10
|
||||
};
|
||||
enum Event {
|
||||
EError, // unrecoverable error, see errorCode for details
|
||||
ESend, // data needs to be sent, use takeOutgoingData()
|
||||
ERecvOpen, // breakpoint after root element open tag is received
|
||||
EPeerClosed, // root element close tag received
|
||||
EClosed, // finished closing
|
||||
ECustom = 10
|
||||
};
|
||||
enum Error {
|
||||
ErrParse, // there was an error parsing the xml
|
||||
ErrCustom = 10
|
||||
};
|
||||
enum Notify {
|
||||
NSend = 0x01, // need to know if data has been written
|
||||
NRecv = 0x02 // need incoming data
|
||||
};
|
||||
|
||||
XmlProtocol();
|
||||
virtual ~XmlProtocol();
|
||||
|
||||
virtual void reset();
|
||||
|
||||
// byte I/O for the stream
|
||||
void addIncomingData(const QByteArray &);
|
||||
QByteArray takeOutgoingData();
|
||||
void outgoingDataWritten(int);
|
||||
|
||||
// advance the state machine
|
||||
bool processStep();
|
||||
|
||||
// set these before returning from a step
|
||||
int need, event, errorCode, notify;
|
||||
|
||||
inline bool isIncoming() const { return incoming; }
|
||||
QString xmlEncoding() const;
|
||||
QString elementToString(const QDomElement &e, bool clip=false);
|
||||
|
||||
class TransferItem
|
||||
{
|
||||
public:
|
||||
TransferItem();
|
||||
TransferItem(const QString &str, bool sent, bool external=false);
|
||||
TransferItem(const QDomElement &elem, bool sent, bool external=false);
|
||||
|
||||
bool isSent; // else, received
|
||||
bool isString; // else, is element
|
||||
bool isExternal; // not owned by protocol
|
||||
QString str;
|
||||
QDomElement elem;
|
||||
};
|
||||
QList<TransferItem> transferItemList;
|
||||
void setIncomingAsExternal();
|
||||
|
||||
protected:
|
||||
virtual QDomElement docElement()=0;
|
||||
virtual void handleDocOpen(const Parser::Event &pe)=0;
|
||||
virtual bool handleError()=0;
|
||||
virtual bool handleCloseFinished()=0;
|
||||
virtual bool stepAdvancesParser() const=0;
|
||||
virtual bool stepRequiresElement() const;
|
||||
virtual bool doStep(const QDomElement &e)=0;
|
||||
virtual void itemWritten(int id, int size);
|
||||
|
||||
// 'debug'
|
||||
virtual void stringSend(const QString &s);
|
||||
virtual void stringRecv(const QString &s);
|
||||
virtual void elementSend(const QDomElement &e);
|
||||
virtual void elementRecv(const QDomElement &e);
|
||||
|
||||
void startConnect();
|
||||
void startAccept();
|
||||
bool close();
|
||||
int writeString(const QString &s, int id, bool external);
|
||||
int writeElement(const QDomElement &e, int id, bool external, bool clip=false);
|
||||
QByteArray resetStream();
|
||||
|
||||
private:
|
||||
enum { SendOpen, RecvOpen, Open, Closing };
|
||||
class TrackItem
|
||||
{
|
||||
public:
|
||||
enum Type { Raw, Close, Custom };
|
||||
int type, id, size;
|
||||
};
|
||||
|
||||
bool incoming;
|
||||
QDomDocument elemDoc;
|
||||
QDomElement elem;
|
||||
QString tagOpen, tagClose;
|
||||
int state;
|
||||
bool peerClosed;
|
||||
bool closeWritten;
|
||||
|
||||
Parser xml;
|
||||
QByteArray outData;
|
||||
QList<TrackItem> trackQueue;
|
||||
|
||||
void init();
|
||||
int internalWriteData(const QByteArray &a, TrackItem::Type t, int id=-1);
|
||||
int internalWriteString(const QString &s, TrackItem::Type t, int id=-1);
|
||||
void sendTagOpen();
|
||||
void sendTagClose();
|
||||
bool baseStep(const Parser::Event &pe);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
660
iris-legacy/iris/xmpp-core/xmpp_stanza.cpp
Normal file
660
iris-legacy/iris/xmpp-core/xmpp_stanza.cpp
Normal file
|
|
@ -0,0 +1,660 @@
|
|||
/*
|
||||
* Copyright (C) 2003 Justin Karneges
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "xmpp_stanza.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include "xmpp_jid.h"
|
||||
#include "xmpp_stream.h"
|
||||
|
||||
using namespace XMPP;
|
||||
|
||||
#define NS_STANZAS "urn:ietf:params:xml:ns:xmpp-stanzas"
|
||||
#define NS_XML "http://www.w3.org/XML/1998/namespace"
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Stanza::Error
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
\class Stanza::Error
|
||||
\brief Represents stanza error
|
||||
|
||||
Stanza error consists of error type and condition.
|
||||
In addition, it may contain a human readable description,
|
||||
and application specific element.
|
||||
|
||||
One of the usages of this class is to easily generate error XML:
|
||||
|
||||
\code
|
||||
QDomElement e = createIQ(client()->doc(), "error", jid, id);
|
||||
Error error(Stanza::Error::Auth, Stanza::Error::NotAuthorized);
|
||||
e.appendChild(error.toXml(*client()->doc(), client()->stream().baseNS()));
|
||||
\endcode
|
||||
|
||||
This class implements JEP-0086, which means that it can read both
|
||||
old and new style error elements. Also, generated XML will contain
|
||||
both type/condition and code.
|
||||
Error text in output XML is always presented in XMPP-style only.
|
||||
|
||||
All functions will always try to guess missing information based on mappings defined in the JEP.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
\enum Stanza::Error::ErrorType
|
||||
\brief Represents error type
|
||||
*/
|
||||
|
||||
/**
|
||||
\enum Stanza::Error::ErrorCond
|
||||
\brief Represents error condition
|
||||
*/
|
||||
|
||||
/**
|
||||
\brief Constructs new error
|
||||
*/
|
||||
Stanza::Error::Error(int _type, int _condition, const QString &_text, const QDomElement &_appSpec)
|
||||
{
|
||||
type = _type;
|
||||
condition = _condition;
|
||||
text = _text;
|
||||
appSpec = _appSpec;
|
||||
}
|
||||
|
||||
|
||||
class Stanza::Error::Private
|
||||
{
|
||||
public:
|
||||
struct ErrorTypeEntry
|
||||
{
|
||||
const char *str;
|
||||
int type;
|
||||
};
|
||||
static ErrorTypeEntry errorTypeTable[];
|
||||
|
||||
struct ErrorCondEntry
|
||||
{
|
||||
const char *str;
|
||||
int cond;
|
||||
};
|
||||
static ErrorCondEntry errorCondTable[];
|
||||
|
||||
struct ErrorCodeEntry
|
||||
{
|
||||
int cond;
|
||||
int type;
|
||||
int code;
|
||||
};
|
||||
static ErrorCodeEntry errorCodeTable[];
|
||||
|
||||
struct ErrorDescEntry
|
||||
{
|
||||
int cond;
|
||||
const char *name;
|
||||
const char *str;
|
||||
};
|
||||
static ErrorDescEntry errorDescriptions[];
|
||||
|
||||
|
||||
static int stringToErrorType(const QString &s)
|
||||
{
|
||||
for(int n = 0; errorTypeTable[n].str; ++n) {
|
||||
if(s == errorTypeTable[n].str)
|
||||
return errorTypeTable[n].type;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static QString errorTypeToString(int x)
|
||||
{
|
||||
for(int n = 0; errorTypeTable[n].str; ++n) {
|
||||
if(x == errorTypeTable[n].type)
|
||||
return errorTypeTable[n].str;
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
static int stringToErrorCond(const QString &s)
|
||||
{
|
||||
for(int n = 0; errorCondTable[n].str; ++n) {
|
||||
if(s == errorCondTable[n].str)
|
||||
return errorCondTable[n].cond;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static QString errorCondToString(int x)
|
||||
{
|
||||
for(int n = 0; errorCondTable[n].str; ++n) {
|
||||
if(x == errorCondTable[n].cond)
|
||||
return errorCondTable[n].str;
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
static int errorTypeCondToCode(int t, int c)
|
||||
{
|
||||
Q_UNUSED(t);
|
||||
for(int n = 0; errorCodeTable[n].cond; ++n) {
|
||||
if(c == errorCodeTable[n].cond)
|
||||
return errorCodeTable[n].code;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static QPair<int, int> errorCodeToTypeCond(int x)
|
||||
{
|
||||
for(int n = 0; errorCodeTable[n].cond; ++n) {
|
||||
if(x == errorCodeTable[n].code)
|
||||
return QPair<int, int>(errorCodeTable[n].type, errorCodeTable[n].cond);
|
||||
}
|
||||
return QPair<int, int>(-1, -1);
|
||||
}
|
||||
|
||||
static QPair<QString,QString> errorCondToDesc(int x)
|
||||
{
|
||||
for(int n = 0; errorDescriptions[n].str; ++n) {
|
||||
if(x == errorDescriptions[n].cond)
|
||||
return QPair<QString, QString>(QCoreApplication::translate("Stanza::Error::Private", errorDescriptions[n].name),
|
||||
QCoreApplication::translate("Stanza::Error::Private", errorDescriptions[n].str));
|
||||
}
|
||||
return QPair<QString,QString>();
|
||||
}
|
||||
};
|
||||
|
||||
Stanza::Error::Private::ErrorTypeEntry Stanza::Error::Private::errorTypeTable[] =
|
||||
{
|
||||
{ "cancel", Cancel },
|
||||
{ "continue", Continue },
|
||||
{ "modify", Modify },
|
||||
{ "auth", Auth },
|
||||
{ "wait", Wait },
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
Stanza::Error::Private::ErrorCondEntry Stanza::Error::Private::errorCondTable[] =
|
||||
{
|
||||
{ "bad-request", BadRequest },
|
||||
{ "conflict", Conflict },
|
||||
{ "feature-not-implemented", FeatureNotImplemented },
|
||||
{ "forbidden", Forbidden },
|
||||
{ "gone", Gone },
|
||||
{ "internal-server-error", InternalServerError },
|
||||
{ "item-not-found", ItemNotFound },
|
||||
{ "jid-malformed", JidMalformed },
|
||||
{ "not-acceptable", NotAcceptable },
|
||||
{ "not-allowed", NotAllowed },
|
||||
{ "not-authorized", NotAuthorized },
|
||||
{ "payment-required", PaymentRequired },
|
||||
{ "recipient-unavailable", RecipientUnavailable },
|
||||
{ "redirect", Redirect },
|
||||
{ "registration-required", RegistrationRequired },
|
||||
{ "remote-server-not-found", RemoteServerNotFound },
|
||||
{ "remote-server-timeout", RemoteServerTimeout },
|
||||
{ "resource-constraint", ResourceConstraint },
|
||||
{ "service-unavailable", ServiceUnavailable },
|
||||
{ "subscription-required", SubscriptionRequired },
|
||||
{ "undefined-condition", UndefinedCondition },
|
||||
{ "unexpected-request", UnexpectedRequest },
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
Stanza::Error::Private::ErrorCodeEntry Stanza::Error::Private::errorCodeTable[] =
|
||||
{
|
||||
{ BadRequest, Modify, 400 },
|
||||
{ Conflict, Cancel, 409 },
|
||||
{ FeatureNotImplemented, Cancel, 501 },
|
||||
{ Forbidden, Auth, 403 },
|
||||
{ Gone, Modify, 302 }, // permanent
|
||||
{ InternalServerError, Wait, 500 },
|
||||
{ ItemNotFound, Cancel, 404 },
|
||||
{ JidMalformed, Modify, 400 },
|
||||
{ NotAcceptable, Modify, 406 },
|
||||
{ NotAllowed, Cancel, 405 },
|
||||
{ NotAuthorized, Auth, 401 },
|
||||
{ PaymentRequired, Auth, 402 },
|
||||
{ RecipientUnavailable, Wait, 404 },
|
||||
{ Redirect, Modify, 302 }, // temporary
|
||||
{ RegistrationRequired, Auth, 407 },
|
||||
{ RemoteServerNotFound, Cancel, 404 },
|
||||
{ RemoteServerTimeout, Wait, 504 },
|
||||
{ ResourceConstraint, Wait, 500 },
|
||||
{ ServiceUnavailable, Cancel, 503 },
|
||||
{ SubscriptionRequired, Auth, 407 },
|
||||
{ UndefinedCondition, Wait, 500 }, // Note: any type matches really
|
||||
{ UnexpectedRequest, Wait, 400 },
|
||||
{ 0, 0, 0 },
|
||||
};
|
||||
|
||||
Stanza::Error::Private::ErrorDescEntry Stanza::Error::Private::errorDescriptions[] =
|
||||
{
|
||||
{ BadRequest, QT_TR_NOOP("Bad request"), QT_TR_NOOP("The sender has sent XML that is malformed or that cannot be processed.") },
|
||||
{ Conflict, QT_TR_NOOP("Conflict"), QT_TR_NOOP("Access cannot be granted because an existing resource or session exists with the same name or address.") },
|
||||
{ FeatureNotImplemented, QT_TR_NOOP("Feature not implemented"), QT_TR_NOOP("The feature requested is not implemented by the recipient or server and therefore cannot be processed.") },
|
||||
{ Forbidden, QT_TR_NOOP("Forbidden"), QT_TR_NOOP("The requesting entity does not possess the required permissions to perform the action.") },
|
||||
{ Gone, QT_TR_NOOP("Gone"), QT_TR_NOOP("The recipient or server can no longer be contacted at this address.") },
|
||||
{ InternalServerError, QT_TR_NOOP("Internal server error"), QT_TR_NOOP("The server could not process the stanza because of a misconfiguration or an otherwise-undefined internal server error.") },
|
||||
{ ItemNotFound, QT_TR_NOOP("Item not found"), QT_TR_NOOP("The addressed JID or item requested cannot be found.") },
|
||||
{ JidMalformed, QT_TR_NOOP("JID malformed"), QT_TR_NOOP("The sending entity has provided or communicated an XMPP address (e.g., a value of the 'to' attribute) or aspect thereof (e.g., a resource identifier) that does not adhere to the syntax defined in Addressing Scheme.") },
|
||||
{ NotAcceptable, QT_TR_NOOP("Not acceptable"), QT_TR_NOOP("The recipient or server understands the request but is refusing to process it because it does not meet criteria defined by the recipient or server (e.g., a local policy regarding acceptable words in messages).") },
|
||||
{ NotAllowed, QT_TR_NOOP("Not allowed"), QT_TR_NOOP("The recipient or server does not allow any entity to perform the action.") },
|
||||
{ NotAuthorized, QT_TR_NOOP("Not authorized"), QT_TR_NOOP("The sender must provide proper credentials before being allowed to perform the action, or has provided improper credentials.") },
|
||||
{ PaymentRequired, QT_TR_NOOP("Payment required"), QT_TR_NOOP("The requesting entity is not authorized to access the requested service because payment is required.") },
|
||||
{ RecipientUnavailable, QT_TR_NOOP("Recipient unavailable"), QT_TR_NOOP("The intended recipient is temporarily unavailable.") },
|
||||
{ Redirect, QT_TR_NOOP("Redirect"), QT_TR_NOOP("The recipient or server is redirecting requests for this information to another entity, usually temporarily.") },
|
||||
{ RegistrationRequired, QT_TR_NOOP("Registration required"), QT_TR_NOOP("The requesting entity is not authorized to access the requested service because registration is required.") },
|
||||
{ RemoteServerNotFound, QT_TR_NOOP("Remote server not found"), QT_TR_NOOP("A remote server or service specified as part or all of the JID of the intended recipient does not exist.") },
|
||||
{ RemoteServerTimeout, QT_TR_NOOP("Remote server timeout"), QT_TR_NOOP("A remote server or service specified as part or all of the JID of the intended recipient (or required to fulfill a request) could not be contacted within a reasonable amount of time.") },
|
||||
{ ResourceConstraint, QT_TR_NOOP("Resource constraint"), QT_TR_NOOP("The server or recipient lacks the system resources necessary to service the request.") },
|
||||
{ ServiceUnavailable, QT_TR_NOOP("Service unavailable"), QT_TR_NOOP("The server or recipient does not currently provide the requested service.") },
|
||||
{ SubscriptionRequired, QT_TR_NOOP("Subscription required"), QT_TR_NOOP("The requesting entity is not authorized to access the requested service because a subscription is required.") },
|
||||
{ UndefinedCondition, QT_TR_NOOP("Undefined condition"), QT_TR_NOOP("The error condition is not one of those defined by the other conditions in this list.") },
|
||||
{ UnexpectedRequest, QT_TR_NOOP("Unexpected request"), QT_TR_NOOP("The recipient or server understood the request but was not expecting it at this time (e.g., the request was out of order).") },
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Returns the error code
|
||||
|
||||
If the error object was constructed with a code, this code will be returned.
|
||||
Otherwise, the code will be guessed.
|
||||
|
||||
0 means unknown code.
|
||||
*/
|
||||
int Stanza::Error::code() const
|
||||
{
|
||||
return originalCode ? originalCode : Private::errorTypeCondToCode(type, condition);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Creates a StanzaError from \a code.
|
||||
|
||||
The error's type and condition are guessed from the give \a code.
|
||||
The application-specific error element is preserved.
|
||||
*/
|
||||
bool Stanza::Error::fromCode(int code)
|
||||
{
|
||||
QPair<int, int> guess = Private::errorCodeToTypeCond(code);
|
||||
if(guess.first == -1 || guess.second == -1)
|
||||
return false;
|
||||
|
||||
type = guess.first;
|
||||
condition = guess.second;
|
||||
originalCode = code;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Reads the error from XML
|
||||
|
||||
This function finds and reads the error element \a e.
|
||||
|
||||
You need to provide the base namespace of the stream which this stanza belongs to
|
||||
(probably by using stream.baseNS() function).
|
||||
*/
|
||||
bool Stanza::Error::fromXml(const QDomElement &e, const QString &baseNS)
|
||||
{
|
||||
if(e.tagName() != "error" && e.namespaceURI() != baseNS)
|
||||
return false;
|
||||
|
||||
// type
|
||||
type = Private::stringToErrorType(e.attribute("type"));
|
||||
|
||||
// condition
|
||||
QDomNodeList nl = e.childNodes();
|
||||
QDomElement t;
|
||||
condition = -1;
|
||||
int n;
|
||||
for(n = 0; n < nl.count(); ++n) {
|
||||
QDomNode i = nl.item(n);
|
||||
t = i.toElement();
|
||||
if(!t.isNull()) {
|
||||
// FIX-ME: this shouldn't be needed
|
||||
if(t.namespaceURI() == NS_STANZAS || t.attribute("xmlns") == NS_STANZAS) {
|
||||
condition = Private::stringToErrorCond(t.tagName());
|
||||
if (condition != -1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// code
|
||||
originalCode = e.attribute("code").toInt();
|
||||
|
||||
// try to guess type/condition
|
||||
if(type == -1 || condition == -1) {
|
||||
QPair<int, int> guess(-1, -1);
|
||||
if (originalCode)
|
||||
guess = Private::errorCodeToTypeCond(originalCode);
|
||||
|
||||
if (type == -1)
|
||||
type = guess.first != -1 ? guess.first : Cancel;
|
||||
if (condition == -1)
|
||||
condition = guess.second != -1 ? guess.second : UndefinedCondition;
|
||||
}
|
||||
|
||||
// text
|
||||
t = e.elementsByTagNameNS(NS_STANZAS, "text").item(0).toElement();
|
||||
if(!t.isNull())
|
||||
text = t.text().trimmed();
|
||||
else
|
||||
text = e.text().trimmed();
|
||||
|
||||
// appspec: find first non-standard namespaced element
|
||||
appSpec = QDomElement();
|
||||
nl = e.childNodes();
|
||||
for(n = 0; n < nl.count(); ++n) {
|
||||
QDomNode i = nl.item(n);
|
||||
if(i.isElement() && i.namespaceURI() != NS_STANZAS) {
|
||||
appSpec = i.toElement();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Writes the error to XML
|
||||
|
||||
This function creates an error element representing the error object.
|
||||
|
||||
You need to provide the base namespace of the stream to which this stanza belongs to
|
||||
(probably by using stream.baseNS() function).
|
||||
*/
|
||||
QDomElement Stanza::Error::toXml(QDomDocument &doc, const QString &baseNS) const
|
||||
{
|
||||
QDomElement errElem = doc.createElementNS(baseNS, "error");
|
||||
QDomElement t;
|
||||
|
||||
// XMPP error
|
||||
QString stype = Private::errorTypeToString(type);
|
||||
if(stype.isEmpty())
|
||||
return errElem;
|
||||
QString scond = Private::errorCondToString(condition);
|
||||
if(scond.isEmpty())
|
||||
return errElem;
|
||||
|
||||
errElem.setAttribute("type", stype);
|
||||
errElem.appendChild(t = doc.createElementNS(NS_STANZAS, scond));
|
||||
t.setAttribute("xmlns", NS_STANZAS); // FIX-ME: this shouldn't be needed
|
||||
|
||||
// old code
|
||||
int scode = code();
|
||||
if(scode)
|
||||
errElem.setAttribute("code", scode);
|
||||
|
||||
// text
|
||||
if(!text.isEmpty()) {
|
||||
t = doc.createElementNS(NS_STANZAS, "text");
|
||||
t.setAttribute("xmlns", NS_STANZAS); // FIX-ME: this shouldn't be needed
|
||||
t.appendChild(doc.createTextNode(text));
|
||||
errElem.appendChild(t);
|
||||
}
|
||||
|
||||
// application specific
|
||||
errElem.appendChild(appSpec);
|
||||
|
||||
return errElem;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Returns the error name and description
|
||||
|
||||
Returns the error name (e.g. "Not Allowed") and generic description.
|
||||
*/
|
||||
QPair<QString,QString> Stanza::Error::description() const
|
||||
{
|
||||
return Private::errorCondToDesc(condition);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Stanza
|
||||
//----------------------------------------------------------------------------
|
||||
class Stanza::Private
|
||||
{
|
||||
public:
|
||||
static int stringToKind(const QString &s)
|
||||
{
|
||||
if(s == "message")
|
||||
return Message;
|
||||
else if(s == "presence")
|
||||
return Presence;
|
||||
else if(s == "iq")
|
||||
return IQ;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
static QString kindToString(Kind k)
|
||||
{
|
||||
if(k == Message)
|
||||
return "message";
|
||||
else if(k == Presence)
|
||||
return "presence";
|
||||
else
|
||||
return "iq";
|
||||
}
|
||||
|
||||
Stream *s;
|
||||
QDomElement e;
|
||||
};
|
||||
|
||||
Stanza::Stanza()
|
||||
{
|
||||
d = 0;
|
||||
}
|
||||
|
||||
Stanza::Stanza(Stream *s, Kind k, const Jid &to, const QString &type, const QString &id)
|
||||
{
|
||||
Q_ASSERT(s);
|
||||
d = new Private;
|
||||
|
||||
Kind kind;
|
||||
if(k == Message || k == Presence || k == IQ)
|
||||
kind = k;
|
||||
else
|
||||
kind = Message;
|
||||
|
||||
d->s = s;
|
||||
if(d->s)
|
||||
d->e = d->s->doc().createElementNS(s->baseNS(), Private::kindToString(kind));
|
||||
if(to.isValid())
|
||||
setTo(to);
|
||||
if(!type.isEmpty())
|
||||
setType(type);
|
||||
if(!id.isEmpty())
|
||||
setId(id);
|
||||
}
|
||||
|
||||
Stanza::Stanza(Stream *s, const QDomElement &e)
|
||||
{
|
||||
Q_ASSERT(s);
|
||||
d = 0;
|
||||
if(e.namespaceURI() != s->baseNS())
|
||||
return;
|
||||
int x = Private::stringToKind(e.tagName());
|
||||
if(x == -1)
|
||||
return;
|
||||
d = new Private;
|
||||
d->s = s;
|
||||
d->e = e;
|
||||
}
|
||||
|
||||
Stanza::Stanza(const Stanza &from)
|
||||
{
|
||||
d = 0;
|
||||
*this = from;
|
||||
}
|
||||
|
||||
Stanza & Stanza::operator=(const Stanza &from)
|
||||
{
|
||||
delete d;
|
||||
d = 0;
|
||||
if(from.d)
|
||||
d = new Private(*from.d);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Stanza::~Stanza()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool Stanza::isNull() const
|
||||
{
|
||||
return (d ? false: true);
|
||||
}
|
||||
|
||||
QDomElement Stanza::element() const
|
||||
{
|
||||
return d->e;
|
||||
}
|
||||
|
||||
QString Stanza::toString() const
|
||||
{
|
||||
return Stream::xmlToString(d->e);
|
||||
}
|
||||
|
||||
QDomDocument & Stanza::doc() const
|
||||
{
|
||||
return d->s->doc();
|
||||
}
|
||||
|
||||
QString Stanza::baseNS() const
|
||||
{
|
||||
return d->s->baseNS();
|
||||
}
|
||||
|
||||
QDomElement Stanza::createElement(const QString &ns, const QString &tagName)
|
||||
{
|
||||
return d->s->doc().createElementNS(ns, tagName);
|
||||
}
|
||||
|
||||
QDomElement Stanza::createTextElement(const QString &ns, const QString &tagName, const QString &text)
|
||||
{
|
||||
QDomElement e = d->s->doc().createElementNS(ns, tagName);
|
||||
e.appendChild(d->s->doc().createTextNode(text));
|
||||
return e;
|
||||
}
|
||||
|
||||
void Stanza::appendChild(const QDomElement &e)
|
||||
{
|
||||
d->e.appendChild(e);
|
||||
}
|
||||
|
||||
Stanza::Kind Stanza::kind() const
|
||||
{
|
||||
return (Kind)Private::stringToKind(d->e.tagName());
|
||||
}
|
||||
|
||||
void Stanza::setKind(Kind k)
|
||||
{
|
||||
d->e.setTagName(Private::kindToString(k));
|
||||
}
|
||||
|
||||
Jid Stanza::to() const
|
||||
{
|
||||
return Jid(d->e.attribute("to"));
|
||||
}
|
||||
|
||||
Jid Stanza::from() const
|
||||
{
|
||||
return Jid(d->e.attribute("from"));
|
||||
}
|
||||
|
||||
QString Stanza::id() const
|
||||
{
|
||||
return d->e.attribute("id");
|
||||
}
|
||||
|
||||
QString Stanza::type() const
|
||||
{
|
||||
return d->e.attribute("type");
|
||||
}
|
||||
|
||||
QString Stanza::lang() const
|
||||
{
|
||||
return d->e.attributeNS(NS_XML, "lang", QString());
|
||||
}
|
||||
|
||||
void Stanza::setTo(const Jid &j)
|
||||
{
|
||||
d->e.setAttribute("to", j.full());
|
||||
}
|
||||
|
||||
void Stanza::setFrom(const Jid &j)
|
||||
{
|
||||
d->e.setAttribute("from", j.full());
|
||||
}
|
||||
|
||||
void Stanza::setId(const QString &id)
|
||||
{
|
||||
d->e.setAttribute("id", id);
|
||||
}
|
||||
|
||||
void Stanza::setType(const QString &type)
|
||||
{
|
||||
d->e.setAttribute("type", type);
|
||||
}
|
||||
|
||||
void Stanza::setLang(const QString &lang)
|
||||
{
|
||||
d->e.setAttribute("xml:lang", lang);
|
||||
}
|
||||
|
||||
Stanza::Error Stanza::error() const
|
||||
{
|
||||
Error err;
|
||||
QDomElement e = d->e.elementsByTagNameNS(d->s->baseNS(), "error").item(0).toElement();
|
||||
if(!e.isNull())
|
||||
err.fromXml(e, d->s->baseNS());
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void Stanza::setError(const Error &err)
|
||||
{
|
||||
QDomDocument doc = d->e.ownerDocument();
|
||||
QDomElement errElem = err.toXml(doc, d->s->baseNS());
|
||||
|
||||
QDomElement oldElem = d->e.elementsByTagNameNS(d->s->baseNS(), "error").item(0).toElement();
|
||||
if(oldElem.isNull()) {
|
||||
d->e.appendChild(errElem);
|
||||
}
|
||||
else {
|
||||
d->e.replaceChild(errElem, oldElem);
|
||||
}
|
||||
}
|
||||
|
||||
void Stanza::clearError()
|
||||
{
|
||||
QDomElement errElem = d->e.elementsByTagNameNS(d->s->baseNS(), "error").item(0).toElement();
|
||||
if(!errElem.isNull())
|
||||
d->e.removeChild(errElem);
|
||||
}
|
||||
|
||||
#ifdef YAPSI
|
||||
void Stanza::setAttribute(const QString& name, const QString& value)
|
||||
{
|
||||
d->e.setAttribute(name, value);
|
||||
}
|
||||
#endif
|
||||
183
iris-legacy/iris/xmpp-core/xmpp_yadatetime.cpp
Normal file
183
iris-legacy/iris/xmpp-core/xmpp_yadatetime.cpp
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* xmpp_yadatetime.cpp
|
||||
* Copyright (C) 2009 Yandex LLC (Michail Pishchagin)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "xmpp_yadatetime.h"
|
||||
|
||||
#include <QHash>
|
||||
|
||||
uint qHash(const XMPP::YaDateTime& yaDateTime)
|
||||
{
|
||||
return qHash(yaDateTime.toYaTime_t());
|
||||
}
|
||||
|
||||
XMPP::YaDateTime::YaDateTime()
|
||||
: QDateTime()
|
||||
, microsec_(0)
|
||||
{
|
||||
}
|
||||
|
||||
XMPP::YaDateTime::YaDateTime(const QDateTime& dateTime)
|
||||
: QDateTime(dateTime)
|
||||
, microsec_(0)
|
||||
{
|
||||
}
|
||||
|
||||
XMPP::YaDateTime::YaDateTime(const YaDateTime& dateTime)
|
||||
: QDateTime(dateTime)
|
||||
, microsec_(dateTime.microsec_)
|
||||
{
|
||||
}
|
||||
|
||||
bool XMPP::YaDateTime::isNull() const
|
||||
{
|
||||
return !QDateTime::isValid();
|
||||
}
|
||||
|
||||
int XMPP::YaDateTime::microsec() const
|
||||
{
|
||||
return microsec_;
|
||||
}
|
||||
|
||||
void XMPP::YaDateTime::setMiscosec(int microsec)
|
||||
{
|
||||
microsec_ = microsec;
|
||||
}
|
||||
|
||||
XMPP::YaDateTime& XMPP::YaDateTime::operator=(const YaDateTime &other)
|
||||
{
|
||||
setDate(other.date());
|
||||
setTime(other.time());
|
||||
microsec_ = other.microsec_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool XMPP::YaDateTime::operator==(const YaDateTime& other) const
|
||||
{
|
||||
return (date() == other.date()) &&
|
||||
(time() == other.time()) &&
|
||||
(microsec() == other.microsec());
|
||||
}
|
||||
|
||||
bool XMPP::YaDateTime::operator!=(const YaDateTime& other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
QString XMPP::YaDateTime::toYaTime_t() const
|
||||
{
|
||||
QString msec;
|
||||
msec.sprintf("%06d", microsec_);
|
||||
QString ts = QString::number(toUTC().toTime_t()) + msec;
|
||||
return ts;
|
||||
}
|
||||
|
||||
XMPP::YaDateTime XMPP::YaDateTime::fromYaTime_t(const QString& str)
|
||||
{
|
||||
QString timeStamp = str;
|
||||
YaDateTime result;
|
||||
if (str.isEmpty())
|
||||
return result;
|
||||
result.microsec_ = getMicrosec(timeStamp.right(6));
|
||||
timeStamp.chop(6);
|
||||
QDateTime ts = QDateTime::fromTime_t(timeStamp.toInt());
|
||||
// ts.setTimeSpec(Qt::UTC);
|
||||
// ts = ts.toLocalTime();
|
||||
result.setDate(ts.date());
|
||||
result.setTime(ts.time());
|
||||
return result;
|
||||
}
|
||||
|
||||
QString XMPP::YaDateTime::toYaIsoTime() const
|
||||
{
|
||||
QString result = toUTC().toString("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
QString msec;
|
||||
msec.sprintf("%06d", microsec_);
|
||||
|
||||
result += "." + msec;
|
||||
return result;
|
||||
}
|
||||
|
||||
XMPP::YaDateTime XMPP::YaDateTime::fromYaIsoTime(const QString& str)
|
||||
{
|
||||
YaDateTime result;
|
||||
if (str.isEmpty())
|
||||
return result;
|
||||
QString timeStamp = str;
|
||||
if (timeStamp.length() == 26) {
|
||||
result.microsec_ = getMicrosec(timeStamp.right(6));
|
||||
timeStamp.chop(7);
|
||||
}
|
||||
else {
|
||||
result.microsec_ = 0;
|
||||
}
|
||||
|
||||
QDateTime ts = QDateTime::fromString(timeStamp, "yyyy-MM-dd HH:mm:ss");
|
||||
ts.setTimeSpec(Qt::UTC);
|
||||
ts = ts.toLocalTime();
|
||||
result.setDate(ts.date());
|
||||
result.setTime(ts.time());
|
||||
return result;
|
||||
}
|
||||
|
||||
bool XMPP::YaDateTime::operator<(const YaDateTime& other) const
|
||||
{
|
||||
if (date() != other.date())
|
||||
return date() < other.date();
|
||||
if (time() != other.time())
|
||||
return time() < other.time();
|
||||
return microsec_ < other.microsec_;
|
||||
}
|
||||
|
||||
bool XMPP::YaDateTime::operator<=(const YaDateTime& other) const
|
||||
{
|
||||
if (date() != other.date())
|
||||
return date() <= other.date();
|
||||
if (time() != other.time())
|
||||
return time() <= other.time();
|
||||
return microsec_ <= other.microsec_;
|
||||
}
|
||||
|
||||
bool XMPP::YaDateTime::operator>(const YaDateTime& other) const
|
||||
{
|
||||
if (date() != other.date())
|
||||
return date() > other.date();
|
||||
if (time() != other.time())
|
||||
return time() > other.time();
|
||||
return microsec_ > other.microsec_;
|
||||
}
|
||||
|
||||
bool XMPP::YaDateTime::operator>=(const YaDateTime& other) const
|
||||
{
|
||||
if (date() != other.date())
|
||||
return date() >= other.date();
|
||||
if (time() != other.time())
|
||||
return time() >= other.time();
|
||||
return microsec_ >= other.microsec_;
|
||||
}
|
||||
|
||||
// WEBCHAT-2480, ONLINE-2275
|
||||
int XMPP::YaDateTime::getMicrosec(const QString& str)
|
||||
{
|
||||
QString s = str;
|
||||
s.chop(2);
|
||||
s += "00";
|
||||
return s.toInt();
|
||||
}
|
||||
Reference in a new issue