summaryrefslogtreecommitdiffstats
path: root/kopete/protocols/jabber/libiris/cutestuff/network
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitbcb704366cb5e333a626c18c308c7e0448a8e69f (patch)
treef0d6ab7d78ecdd9207cf46536376b44b91a1ca71 /kopete/protocols/jabber/libiris/cutestuff/network
downloadtdenetwork-bcb704366cb5e333a626c18c308c7e0448a8e69f.tar.gz
tdenetwork-bcb704366cb5e333a626c18c308c7e0448a8e69f.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdenetwork@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kopete/protocols/jabber/libiris/cutestuff/network')
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/Makefile.am16
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/bsocket.cpp394
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/bsocket.h87
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.cpp369
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.h67
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/httppoll.cpp666
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/httppoll.h104
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/ndns.cpp378
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/ndns.h88
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/servsock.cpp112
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/servsock.h68
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/socks.cpp1223
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/socks.h160
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.cpp320
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.h65
15 files changed, 4117 insertions, 0 deletions
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/Makefile.am b/kopete/protocols/jabber/libiris/cutestuff/network/Makefile.am
new file mode 100644
index 00000000..5e370089
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/Makefile.am
@@ -0,0 +1,16 @@
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libcutestuff_network.la
+INCLUDES = -I$(srcdir)/../util -I$(srcdir)/../../qca/src $(all_includes)
+
+libcutestuff_network_la_SOURCES = \
+ bsocket.cpp \
+ httpconnect.cpp \
+ httppoll.cpp \
+ ndns.cpp \
+ servsock.cpp \
+ socks.cpp \
+ srvresolver.cpp
+
+KDE_OPTIONS = nofinal
+
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/bsocket.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/bsocket.cpp
new file mode 100644
index 00000000..57e5fe66
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/bsocket.cpp
@@ -0,0 +1,394 @@
+/*
+ * bsocket.cpp - QSocket wrapper based on Bytestream with SRV DNS support
+ * 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"bsocket.h"
+
+#include<qcstring.h>
+#include<qsocket.h>
+#include<qdns.h>
+#include<qguardedptr.h>
+#include"safedelete.h"
+#ifndef NO_NDNS
+#include"ndns.h"
+#endif
+#include"srvresolver.h"
+
+#ifdef BS_DEBUG
+#include<stdio.h>
+#endif
+
+#define READBUFSIZE 65536
+
+// CS_NAMESPACE_BEGIN
+
+class BSocket::Private
+{
+public:
+ Private()
+ {
+ qsock = 0;
+ }
+
+ QSocket *qsock;
+ int state;
+
+#ifndef NO_NDNS
+ NDns ndns;
+#endif
+ SrvResolver srv;
+ QString host;
+ int port;
+ SafeDelete sd;
+};
+
+BSocket::BSocket(QObject *parent)
+:ByteStream(parent)
+{
+ d = new Private;
+#ifndef NO_NDNS
+ connect(&d->ndns, SIGNAL(resultsReady()), SLOT(ndns_done()));
+#endif
+ connect(&d->srv, SIGNAL(resultsReady()), SLOT(srv_done()));
+
+ reset();
+}
+
+BSocket::~BSocket()
+{
+ reset(true);
+ delete d;
+}
+
+void BSocket::reset(bool clear)
+{
+ if(d->qsock) {
+ d->qsock->disconnect(this);
+
+ if(!clear && d->qsock->isOpen()) {
+ // move remaining into the local queue
+ QByteArray block(d->qsock->bytesAvailable());
+ d->qsock->readBlock(block.data(), block.size());
+ appendRead(block);
+ }
+
+ d->sd.deleteLater(d->qsock);
+ d->qsock = 0;
+ }
+ else {
+ if(clear)
+ clearReadBuffer();
+ }
+
+ if(d->srv.isBusy())
+ d->srv.stop();
+#ifndef NO_NDNS
+ if(d->ndns.isBusy())
+ d->ndns.stop();
+#endif
+ d->state = Idle;
+}
+
+void BSocket::ensureSocket()
+{
+ if(!d->qsock) {
+ d->qsock = new QSocket;
+#if QT_VERSION >= 0x030200
+ d->qsock->setReadBufferSize(READBUFSIZE);
+#endif
+ connect(d->qsock, SIGNAL(hostFound()), SLOT(qs_hostFound()));
+ connect(d->qsock, SIGNAL(connected()), SLOT(qs_connected()));
+ connect(d->qsock, SIGNAL(connectionClosed()), SLOT(qs_connectionClosed()));
+ connect(d->qsock, SIGNAL(delayedCloseFinished()), SLOT(qs_delayedCloseFinished()));
+ connect(d->qsock, SIGNAL(readyRead()), SLOT(qs_readyRead()));
+ connect(d->qsock, SIGNAL(bytesWritten(int)), SLOT(qs_bytesWritten(int)));
+ connect(d->qsock, SIGNAL(error(int)), SLOT(qs_error(int)));
+ }
+}
+
+void BSocket::connectToHost(const QString &host, Q_UINT16 port)
+{
+ reset(true);
+ d->host = host;
+ d->port = port;
+#ifdef NO_NDNS
+ d->state = Connecting;
+ do_connect();
+#else
+ d->state = HostLookup;
+ d->ndns.resolve(d->host);
+#endif
+}
+
+void BSocket::connectToServer(const QString &srv, const QString &type)
+{
+ reset(true);
+ d->state = HostLookup;
+ d->srv.resolve(srv, type, "tcp");
+}
+
+int BSocket::socket() const
+{
+ if(d->qsock)
+ return d->qsock->socket();
+ else
+ return -1;
+}
+
+void BSocket::setSocket(int s)
+{
+ reset(true);
+ ensureSocket();
+ d->state = Connected;
+ d->qsock->setSocket(s);
+}
+
+int BSocket::state() const
+{
+ return d->state;
+}
+
+bool BSocket::isOpen() const
+{
+ if(d->state == Connected)
+ return true;
+ else
+ return false;
+}
+
+void BSocket::close()
+{
+ if(d->state == Idle)
+ return;
+
+ if(d->qsock) {
+ d->qsock->close();
+ d->state = Closing;
+ if(d->qsock->bytesToWrite() == 0)
+ reset();
+ }
+ else {
+ reset();
+ }
+}
+
+void BSocket::write(const QByteArray &a)
+{
+ if(d->state != Connected)
+ return;
+#ifdef BS_DEBUG
+ QCString cs;
+ cs.resize(a.size()+1);
+ memcpy(cs.data(), a.data(), a.size());
+ QString s = QString::fromUtf8(cs);
+ fprintf(stderr, "BSocket: writing [%d]: {%s}\n", a.size(), cs.data());
+#endif
+ d->qsock->writeBlock(a.data(), a.size());
+}
+
+QByteArray BSocket::read(int bytes)
+{
+ QByteArray block;
+ if(d->qsock) {
+ int max = bytesAvailable();
+ if(bytes <= 0 || bytes > max)
+ bytes = max;
+ block.resize(bytes);
+ d->qsock->readBlock(block.data(), block.size());
+ }
+ else
+ block = ByteStream::read(bytes);
+
+#ifdef BS_DEBUG
+ QCString cs;
+ cs.resize(block.size()+1);
+ memcpy(cs.data(), block.data(), block.size());
+ QString s = QString::fromUtf8(cs);
+ fprintf(stderr, "BSocket: read [%d]: {%s}\n", block.size(), s.latin1());
+#endif
+ return block;
+}
+
+int BSocket::bytesAvailable() const
+{
+ if(d->qsock)
+ return d->qsock->bytesAvailable();
+ else
+ return ByteStream::bytesAvailable();
+}
+
+int BSocket::bytesToWrite() const
+{
+ if(!d->qsock)
+ return 0;
+ return d->qsock->bytesToWrite();
+}
+
+QHostAddress BSocket::address() const
+{
+ if(d->qsock)
+ return d->qsock->address();
+ else
+ return QHostAddress();
+}
+
+Q_UINT16 BSocket::port() const
+{
+ if(d->qsock)
+ return d->qsock->port();
+ else
+ return 0;
+}
+
+QHostAddress BSocket::peerAddress() const
+{
+ if(d->qsock)
+ return d->qsock->peerAddress();
+ else
+ return QHostAddress();
+}
+
+Q_UINT16 BSocket::peerPort() const
+{
+ if(d->qsock)
+ return d->qsock->port();
+ else
+ return 0;
+}
+
+void BSocket::srv_done()
+{
+ if(d->srv.failed()) {
+#ifdef BS_DEBUG
+ fprintf(stderr, "BSocket: Error resolving hostname.\n");
+#endif
+ error(ErrHostNotFound);
+ return;
+ }
+
+ d->host = d->srv.resultAddress().toString();
+ d->port = d->srv.resultPort();
+ do_connect();
+ //QTimer::singleShot(0, this, SLOT(do_connect()));
+ //hostFound();
+}
+
+void BSocket::ndns_done()
+{
+#ifndef NO_NDNS
+ if(d->ndns.result()) {
+ d->host = d->ndns.resultString();
+ d->state = Connecting;
+ do_connect();
+ //QTimer::singleShot(0, this, SLOT(do_connect()));
+ //hostFound();
+ }
+ else {
+#ifdef BS_DEBUG
+ fprintf(stderr, "BSocket: Error resolving hostname.\n");
+#endif
+ error(ErrHostNotFound);
+ }
+#endif
+}
+
+void BSocket::do_connect()
+{
+#ifdef BS_DEBUG
+ fprintf(stderr, "BSocket: Connecting to %s:%d\n", d->host.latin1(), d->port);
+#endif
+ ensureSocket();
+ d->qsock->connectToHost(d->host, d->port);
+}
+
+void BSocket::qs_hostFound()
+{
+ //SafeDeleteLock s(&d->sd);
+}
+
+void BSocket::qs_connected()
+{
+ d->state = Connected;
+#ifdef BS_DEBUG
+ fprintf(stderr, "BSocket: Connected.\n");
+#endif
+ SafeDeleteLock s(&d->sd);
+ connected();
+}
+
+void BSocket::qs_connectionClosed()
+{
+#ifdef BS_DEBUG
+ fprintf(stderr, "BSocket: Connection Closed.\n");
+#endif
+ SafeDeleteLock s(&d->sd);
+ reset();
+ connectionClosed();
+}
+
+void BSocket::qs_delayedCloseFinished()
+{
+#ifdef BS_DEBUG
+ fprintf(stderr, "BSocket: Delayed Close Finished.\n");
+#endif
+ SafeDeleteLock s(&d->sd);
+ reset();
+ delayedCloseFinished();
+}
+
+void BSocket::qs_readyRead()
+{
+ SafeDeleteLock s(&d->sd);
+ readyRead();
+}
+
+void BSocket::qs_bytesWritten(int x)
+{
+#ifdef BS_DEBUG
+ fprintf(stderr, "BSocket: BytesWritten [%d].\n", x);
+#endif
+ SafeDeleteLock s(&d->sd);
+ bytesWritten(x);
+}
+
+void BSocket::qs_error(int x)
+{
+#ifdef BS_DEBUG
+ fprintf(stderr, "BSocket: Error.\n");
+#endif
+ SafeDeleteLock s(&d->sd);
+
+ // connection error during SRV host connect? try next
+ if(d->state == HostLookup && (x == QSocket::ErrConnectionRefused || x == QSocket::ErrHostNotFound)) {
+ d->srv.next();
+ return;
+ }
+
+ reset();
+ if(x == QSocket::ErrConnectionRefused)
+ error(ErrConnectionRefused);
+ else if(x == QSocket::ErrHostNotFound)
+ error(ErrHostNotFound);
+ else if(x == QSocket::ErrSocketRead)
+ error(ErrRead);
+}
+
+// CS_NAMESPACE_END
+
+#include "bsocket.moc"
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/bsocket.h b/kopete/protocols/jabber/libiris/cutestuff/network/bsocket.h
new file mode 100644
index 00000000..bedaa54e
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/bsocket.h
@@ -0,0 +1,87 @@
+/*
+ * bsocket.h - QSocket wrapper based on Bytestream with SRV DNS support
+ * 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 CS_BSOCKET_H
+#define CS_BSOCKET_H
+
+#include<qobject.h>
+#include<qhostaddress.h>
+#include"bytestream.h"
+
+// CS_NAMESPACE_BEGIN
+
+class BSocket : public ByteStream
+{
+ Q_OBJECT
+public:
+ enum Error { ErrConnectionRefused = ErrCustom, ErrHostNotFound };
+ enum State { Idle, HostLookup, Connecting, Connected, Closing };
+ BSocket(QObject *parent=0);
+ ~BSocket();
+
+ void connectToHost(const QString &host, Q_UINT16 port);
+ void connectToServer(const QString &srv, const QString &type);
+ int socket() const;
+ void setSocket(int);
+ int state() const;
+
+ // from ByteStream
+ bool isOpen() const;
+ void close();
+ void write(const QByteArray &);
+ QByteArray read(int bytes=0);
+ int bytesAvailable() const;
+ int bytesToWrite() const;
+
+ // local
+ QHostAddress address() const;
+ Q_UINT16 port() const;
+
+ // remote
+ QHostAddress peerAddress() const;
+ Q_UINT16 peerPort() const;
+
+signals:
+ void hostFound();
+ void connected();
+
+private slots:
+ void qs_hostFound();
+ void qs_connected();
+ void qs_connectionClosed();
+ void qs_delayedCloseFinished();
+ void qs_readyRead();
+ void qs_bytesWritten(int);
+ void qs_error(int);
+ void srv_done();
+ void ndns_done();
+ void do_connect();
+
+private:
+ class Private;
+ Private *d;
+
+ void reset(bool clear=false);
+ void ensureSocket();
+};
+
+// CS_NAMESPACE_END
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.cpp
new file mode 100644
index 00000000..c194324a
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.cpp
@@ -0,0 +1,369 @@
+/*
+ * httpconnect.cpp - HTTP "CONNECT" proxy
+ * 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"httpconnect.h"
+
+#include<qstringlist.h>
+#include"bsocket.h"
+#include"base64.h"
+
+#ifdef PROX_DEBUG
+#include<stdio.h>
+#endif
+
+// CS_NAMESPACE_BEGIN
+
+static QString extractLine(QByteArray *buf, bool *found)
+{
+ // scan for newline
+ int n;
+ for(n = 0; n < (int)buf->size()-1; ++n) {
+ if(buf->at(n) == '\r' && buf->at(n+1) == '\n') {
+ QCString cstr;
+ cstr.resize(n+1);
+ memcpy(cstr.data(), buf->data(), n);
+ n += 2; // hack off CR/LF
+
+ memmove(buf->data(), buf->data() + n, buf->size() - n);
+ buf->resize(buf->size() - n);
+ QString s = QString::fromUtf8(cstr);
+
+ if(found)
+ *found = true;
+ return s;
+ }
+ }
+
+ if(found)
+ *found = false;
+ return "";
+}
+
+static bool extractMainHeader(const QString &line, QString *proto, int *code, QString *msg)
+{
+ int n = line.find(' ');
+ if(n == -1)
+ return false;
+ if(proto)
+ *proto = line.mid(0, n);
+ ++n;
+ int n2 = line.find(' ', n);
+ if(n2 == -1)
+ return false;
+ if(code)
+ *code = line.mid(n, n2-n).toInt();
+ n = n2+1;
+ if(msg)
+ *msg = line.mid(n);
+ return true;
+}
+
+class HttpConnect::Private
+{
+public:
+ Private() {}
+
+ BSocket sock;
+ QString host;
+ int port;
+ QString user, pass;
+ QString real_host;
+ int real_port;
+
+ QByteArray recvBuf;
+
+ bool inHeader;
+ QStringList headerLines;
+
+ int toWrite;
+ bool active;
+};
+
+HttpConnect::HttpConnect(QObject *parent)
+:ByteStream(parent)
+{
+ d = new Private;
+ connect(&d->sock, SIGNAL(connected()), SLOT(sock_connected()));
+ connect(&d->sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed()));
+ connect(&d->sock, SIGNAL(delayedCloseFinished()), SLOT(sock_delayedCloseFinished()));
+ connect(&d->sock, SIGNAL(readyRead()), SLOT(sock_readyRead()));
+ connect(&d->sock, SIGNAL(bytesWritten(int)), SLOT(sock_bytesWritten(int)));
+ connect(&d->sock, SIGNAL(error(int)), SLOT(sock_error(int)));
+
+ reset(true);
+}
+
+HttpConnect::~HttpConnect()
+{
+ reset(true);
+ delete d;
+}
+
+void HttpConnect::reset(bool clear)
+{
+ if(d->sock.state() != BSocket::Idle)
+ d->sock.close();
+ if(clear) {
+ clearReadBuffer();
+ d->recvBuf.resize(0);
+ }
+ d->active = false;
+}
+
+void HttpConnect::setAuth(const QString &user, const QString &pass)
+{
+ d->user = user;
+ d->pass = pass;
+}
+
+void HttpConnect::connectToHost(const QString &proxyHost, int proxyPort, const QString &host, int port)
+{
+ reset(true);
+
+ d->host = proxyHost;
+ d->port = proxyPort;
+ d->real_host = host;
+ d->real_port = port;
+
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpConnect: Connecting to %s:%d", proxyHost.latin1(), proxyPort);
+ if(d->user.isEmpty())
+ fprintf(stderr, "\n");
+ else
+ fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1());
+#endif
+ d->sock.connectToHost(d->host, d->port);
+}
+
+bool HttpConnect::isOpen() const
+{
+ return d->active;
+}
+
+void HttpConnect::close()
+{
+ d->sock.close();
+ if(d->sock.bytesToWrite() == 0)
+ reset();
+}
+
+void HttpConnect::write(const QByteArray &buf)
+{
+ if(d->active)
+ d->sock.write(buf);
+}
+
+QByteArray HttpConnect::read(int bytes)
+{
+ return ByteStream::read(bytes);
+}
+
+int HttpConnect::bytesAvailable() const
+{
+ return ByteStream::bytesAvailable();
+}
+
+int HttpConnect::bytesToWrite() const
+{
+ if(d->active)
+ return d->sock.bytesToWrite();
+ else
+ return 0;
+}
+
+void HttpConnect::sock_connected()
+{
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpConnect: Connected\n");
+#endif
+ d->inHeader = true;
+ d->headerLines.clear();
+
+ // connected, now send the request
+ QString s;
+ s += QString("CONNECT ") + d->real_host + ':' + QString::number(d->real_port) + " HTTP/1.0\r\n";
+ if(!d->user.isEmpty()) {
+ QString str = d->user + ':' + d->pass;
+ s += QString("Proxy-Authorization: Basic ") + Base64::encodeString(str) + "\r\n";
+ }
+ s += "Proxy-Connection: Keep-Alive\r\n";
+ s += "Pragma: no-cache\r\n";
+ s += "\r\n";
+
+ QCString cs = s.utf8();
+ QByteArray block(cs.length());
+ memcpy(block.data(), cs.data(), block.size());
+ d->toWrite = block.size();
+ d->sock.write(block);
+}
+
+void HttpConnect::sock_connectionClosed()
+{
+ if(d->active) {
+ reset();
+ connectionClosed();
+ }
+ else {
+ error(ErrProxyNeg);
+ }
+}
+
+void HttpConnect::sock_delayedCloseFinished()
+{
+ if(d->active) {
+ reset();
+ delayedCloseFinished();
+ }
+}
+
+void HttpConnect::sock_readyRead()
+{
+ QByteArray block = d->sock.read();
+
+ if(!d->active) {
+ ByteStream::appendArray(&d->recvBuf, block);
+
+ if(d->inHeader) {
+ // grab available lines
+ while(1) {
+ bool found;
+ QString line = extractLine(&d->recvBuf, &found);
+ if(!found)
+ break;
+ if(line.isEmpty()) {
+ d->inHeader = false;
+ break;
+ }
+ d->headerLines += line;
+ }
+
+ // done with grabbing the header?
+ if(!d->inHeader) {
+ QString str = d->headerLines.first();
+ d->headerLines.remove(d->headerLines.begin());
+
+ QString proto;
+ int code;
+ QString msg;
+ if(!extractMainHeader(str, &proto, &code, &msg)) {
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpConnect: invalid header!\n");
+#endif
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ else {
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpConnect: header proto=[%s] code=[%d] msg=[%s]\n", proto.latin1(), code, msg.latin1());
+ for(QStringList::ConstIterator it = d->headerLines.begin(); it != d->headerLines.end(); ++it)
+ fprintf(stderr, "HttpConnect: * [%s]\n", (*it).latin1());
+#endif
+ }
+
+ if(code == 200) { // OK
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpConnect: << Success >>\n");
+#endif
+ d->active = true;
+ connected();
+
+ if(!d->recvBuf.isEmpty()) {
+ appendRead(d->recvBuf);
+ d->recvBuf.resize(0);
+ readyRead();
+ return;
+ }
+ }
+ else {
+ int err;
+ QString errStr;
+ if(code == 407) { // Authentication failed
+ err = ErrProxyAuth;
+ errStr = tr("Authentication failed");
+ }
+ else if(code == 404) { // Host not found
+ err = ErrHostNotFound;
+ errStr = tr("Host not found");
+ }
+ else if(code == 403) { // Access denied
+ err = ErrProxyNeg;
+ errStr = tr("Access denied");
+ }
+ else if(code == 503) { // Connection refused
+ err = ErrConnectionRefused;
+ errStr = tr("Connection refused");
+ }
+ else { // invalid reply
+ err = ErrProxyNeg;
+ errStr = tr("Invalid reply");
+ }
+
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpConnect: << Error >> [%s]\n", errStr.latin1());
+#endif
+ reset(true);
+ error(err);
+ return;
+ }
+ }
+ }
+ }
+ else {
+ appendRead(block);
+ readyRead();
+ return;
+ }
+}
+
+void HttpConnect::sock_bytesWritten(int x)
+{
+ if(d->toWrite > 0) {
+ int size = x;
+ if(d->toWrite < x)
+ size = d->toWrite;
+ d->toWrite -= size;
+ x -= size;
+ }
+
+ if(d->active && x > 0)
+ bytesWritten(x);
+}
+
+void HttpConnect::sock_error(int x)
+{
+ if(d->active) {
+ reset();
+ error(ErrRead);
+ }
+ else {
+ reset(true);
+ if(x == BSocket::ErrHostNotFound)
+ error(ErrProxyConnect);
+ else if(x == BSocket::ErrConnectionRefused)
+ error(ErrProxyConnect);
+ else if(x == BSocket::ErrRead)
+ error(ErrProxyNeg);
+ }
+}
+
+// CS_NAMESPACE_END
+
+#include "httpconnect.moc"
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.h b/kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.h
new file mode 100644
index 00000000..38129c60
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.h
@@ -0,0 +1,67 @@
+/*
+ * httpconnect.h - HTTP "CONNECT" proxy
+ * 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 CS_HTTPCONNECT_H
+#define CS_HTTPCONNECT_H
+
+#include"bytestream.h"
+
+// CS_NAMESPACE_BEGIN
+
+class HttpConnect : public ByteStream
+{
+ Q_OBJECT
+public:
+ enum Error { ErrConnectionRefused = ErrCustom, ErrHostNotFound, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth };
+ HttpConnect(QObject *parent=0);
+ ~HttpConnect();
+
+ void setAuth(const QString &user, const QString &pass="");
+ void connectToHost(const QString &proxyHost, int proxyPort, const QString &host, int port);
+
+ // from ByteStream
+ bool isOpen() const;
+ void close();
+ void write(const QByteArray &);
+ QByteArray read(int bytes=0);
+ int bytesAvailable() const;
+ int bytesToWrite() const;
+
+signals:
+ void connected();
+
+private slots:
+ void sock_connected();
+ void sock_connectionClosed();
+ void sock_delayedCloseFinished();
+ void sock_readyRead();
+ void sock_bytesWritten(int);
+ void sock_error(int);
+
+private:
+ class Private;
+ Private *d;
+
+ void reset(bool clear=false);
+};
+
+// CS_NAMESPACE_END
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/httppoll.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/httppoll.cpp
new file mode 100644
index 00000000..4975d0e5
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/httppoll.cpp
@@ -0,0 +1,666 @@
+/*
+ * httppoll.cpp - HTTP polling proxy
+ * 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"httppoll.h"
+
+#include<qstringlist.h>
+#include<qurl.h>
+#include<qtimer.h>
+#include<qguardedptr.h>
+#include<qca.h>
+#include<stdlib.h>
+#include"bsocket.h"
+#include"base64.h"
+
+#ifdef PROX_DEBUG
+#include<stdio.h>
+#endif
+
+#define POLL_KEYS 64
+
+// CS_NAMESPACE_BEGIN
+
+static QByteArray randomArray(int size)
+{
+ QByteArray a(size);
+ for(int n = 0; n < size; ++n)
+ a[n] = (char)(256.0*rand()/(RAND_MAX+1.0));
+ return a;
+}
+
+//----------------------------------------------------------------------------
+// HttpPoll
+//----------------------------------------------------------------------------
+static QString hpk(int n, const QString &s)
+{
+ if(n == 0)
+ return s;
+ else
+ return Base64::arrayToString( QCA::SHA1::hash( QCString(hpk(n - 1, s).latin1()) ) );
+}
+
+class HttpPoll::Private
+{
+public:
+ Private() {}
+
+ HttpProxyPost http;
+ QString host;
+ int port;
+ QString user, pass;
+ QString url;
+ bool use_proxy;
+
+ QByteArray out;
+
+ int state;
+ bool closing;
+ QString ident;
+
+ QTimer *t;
+
+ QString key[POLL_KEYS];
+ int key_n;
+
+ int polltime;
+};
+
+HttpPoll::HttpPoll(QObject *parent)
+:ByteStream(parent)
+{
+ d = new Private;
+
+ d->polltime = 30;
+ d->t = new QTimer;
+ connect(d->t, SIGNAL(timeout()), SLOT(do_sync()));
+
+ connect(&d->http, SIGNAL(result()), SLOT(http_result()));
+ connect(&d->http, SIGNAL(error(int)), SLOT(http_error(int)));
+
+ reset(true);
+}
+
+HttpPoll::~HttpPoll()
+{
+ reset(true);
+ delete d->t;
+ delete d;
+}
+
+void HttpPoll::reset(bool clear)
+{
+ if(d->http.isActive())
+ d->http.stop();
+ if(clear)
+ clearReadBuffer();
+ clearWriteBuffer();
+ d->out.resize(0);
+ d->state = 0;
+ d->closing = false;
+ d->t->stop();
+}
+
+void HttpPoll::setAuth(const QString &user, const QString &pass)
+{
+ d->user = user;
+ d->pass = pass;
+}
+
+void HttpPoll::connectToUrl(const QString &url)
+{
+ connectToHost("", 0, url);
+}
+
+void HttpPoll::connectToHost(const QString &proxyHost, int proxyPort, const QString &url)
+{
+ reset(true);
+
+ // using proxy?
+ if(!proxyHost.isEmpty()) {
+ d->host = proxyHost;
+ d->port = proxyPort;
+ d->url = url;
+ d->use_proxy = true;
+ }
+ else {
+ QUrl u = url;
+ d->host = u.host();
+ if(u.hasPort())
+ d->port = u.port();
+ else
+ d->port = 80;
+ d->url = u.encodedPathAndQuery();
+ d->use_proxy = false;
+ }
+
+ resetKey();
+ bool last;
+ QString key = getKey(&last);
+
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpPoll: Connecting to %s:%d [%s]", d->host.latin1(), d->port, d->url.latin1());
+ if(d->user.isEmpty())
+ fprintf(stderr, "\n");
+ else
+ fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1());
+#endif
+ QGuardedPtr<QObject> self = this;
+ syncStarted();
+ if(!self)
+ return;
+
+ d->state = 1;
+ d->http.setAuth(d->user, d->pass);
+ d->http.post(d->host, d->port, d->url, makePacket("0", key, "", QByteArray()), d->use_proxy);
+}
+
+QByteArray HttpPoll::makePacket(const QString &ident, const QString &key, const QString &newkey, const QByteArray &block)
+{
+ QString str = ident;
+ if(!key.isEmpty()) {
+ str += ';';
+ str += key;
+ }
+ if(!newkey.isEmpty()) {
+ str += ';';
+ str += newkey;
+ }
+ str += ',';
+ QCString cs = str.latin1();
+ int len = cs.length();
+
+ QByteArray a(len + block.size());
+ memcpy(a.data(), cs.data(), len);
+ memcpy(a.data() + len, block.data(), block.size());
+ return a;
+}
+
+int HttpPoll::pollInterval() const
+{
+ return d->polltime;
+}
+
+void HttpPoll::setPollInterval(int seconds)
+{
+ d->polltime = seconds;
+}
+
+bool HttpPoll::isOpen() const
+{
+ return (d->state == 2 ? true: false);
+}
+
+void HttpPoll::close()
+{
+ if(d->state == 0 || d->closing)
+ return;
+
+ if(bytesToWrite() == 0)
+ reset();
+ else
+ d->closing = true;
+}
+
+void HttpPoll::http_result()
+{
+ // check for death :)
+ QGuardedPtr<QObject> self = this;
+ syncFinished();
+ if(!self)
+ return;
+
+ // get id and packet
+ QString id;
+ QString cookie = d->http.getHeader("Set-Cookie");
+ int n = cookie.find("ID=");
+ if(n == -1) {
+ reset();
+ error(ErrRead);
+ return;
+ }
+ n += 3;
+ int n2 = cookie.find(';', n);
+ if(n2 != -1)
+ id = cookie.mid(n, n2-n);
+ else
+ id = cookie.mid(n);
+ QByteArray block = d->http.body();
+
+ // session error?
+ if(id.right(2) == ":0") {
+ if(id == "0:0" && d->state == 2) {
+ reset();
+ connectionClosed();
+ return;
+ }
+ else {
+ reset();
+ error(ErrRead);
+ return;
+ }
+ }
+
+ d->ident = id;
+ bool justNowConnected = false;
+ if(d->state == 1) {
+ d->state = 2;
+ justNowConnected = true;
+ }
+
+ // sync up again soon
+ if(bytesToWrite() > 0 || !d->closing)
+ d->t->start(d->polltime * 1000, true);
+
+ // connecting
+ if(justNowConnected) {
+ connected();
+ }
+ else {
+ if(!d->out.isEmpty()) {
+ int x = d->out.size();
+ d->out.resize(0);
+ takeWrite(x);
+ bytesWritten(x);
+ }
+ }
+
+ if(!self)
+ return;
+
+ if(!block.isEmpty()) {
+ appendRead(block);
+ readyRead();
+ }
+
+ if(!self)
+ return;
+
+ if(bytesToWrite() > 0) {
+ do_sync();
+ }
+ else {
+ if(d->closing) {
+ reset();
+ delayedCloseFinished();
+ return;
+ }
+ }
+}
+
+void HttpPoll::http_error(int x)
+{
+ reset();
+ if(x == HttpProxyPost::ErrConnectionRefused)
+ error(ErrConnectionRefused);
+ else if(x == HttpProxyPost::ErrHostNotFound)
+ error(ErrHostNotFound);
+ else if(x == HttpProxyPost::ErrSocket)
+ error(ErrRead);
+ else if(x == HttpProxyPost::ErrProxyConnect)
+ error(ErrProxyConnect);
+ else if(x == HttpProxyPost::ErrProxyNeg)
+ error(ErrProxyNeg);
+ else if(x == HttpProxyPost::ErrProxyAuth)
+ error(ErrProxyAuth);
+}
+
+int HttpPoll::tryWrite()
+{
+ if(!d->http.isActive())
+ do_sync();
+ return 0;
+}
+
+void HttpPoll::do_sync()
+{
+ if(d->http.isActive())
+ return;
+
+ d->t->stop();
+ d->out = takeWrite(0, false);
+
+ bool last;
+ QString key = getKey(&last);
+ QString newkey;
+ if(last) {
+ resetKey();
+ newkey = getKey(&last);
+ }
+
+ QGuardedPtr<QObject> self = this;
+ syncStarted();
+ if(!self)
+ return;
+
+ d->http.post(d->host, d->port, d->url, makePacket(d->ident, key, newkey, d->out), d->use_proxy);
+}
+
+void HttpPoll::resetKey()
+{
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpPoll: reset key!\n");
+#endif
+ QByteArray a = randomArray(64);
+ QString str = QString::fromLatin1(a.data(), a.size());
+
+ d->key_n = POLL_KEYS;
+ for(int n = 0; n < POLL_KEYS; ++n)
+ d->key[n] = hpk(n+1, str);
+}
+
+const QString & HttpPoll::getKey(bool *last)
+{
+ *last = false;
+ --(d->key_n);
+ if(d->key_n == 0)
+ *last = true;
+ return d->key[d->key_n];
+}
+
+
+//----------------------------------------------------------------------------
+// HttpProxyPost
+//----------------------------------------------------------------------------
+static QString extractLine(QByteArray *buf, bool *found)
+{
+ // scan for newline
+ int n;
+ for(n = 0; n < (int)buf->size()-1; ++n) {
+ if(buf->at(n) == '\r' && buf->at(n+1) == '\n') {
+ QCString cstr;
+ cstr.resize(n+1);
+ memcpy(cstr.data(), buf->data(), n);
+ n += 2; // hack off CR/LF
+
+ memmove(buf->data(), buf->data() + n, buf->size() - n);
+ buf->resize(buf->size() - n);
+ QString s = QString::fromUtf8(cstr);
+
+ if(found)
+ *found = true;
+ return s;
+ }
+ }
+
+ if(found)
+ *found = false;
+ return "";
+}
+
+static bool extractMainHeader(const QString &line, QString *proto, int *code, QString *msg)
+{
+ int n = line.find(' ');
+ if(n == -1)
+ return false;
+ if(proto)
+ *proto = line.mid(0, n);
+ ++n;
+ int n2 = line.find(' ', n);
+ if(n2 == -1)
+ return false;
+ if(code)
+ *code = line.mid(n, n2-n).toInt();
+ n = n2+1;
+ if(msg)
+ *msg = line.mid(n);
+ return true;
+}
+
+class HttpProxyPost::Private
+{
+public:
+ Private() {}
+
+ BSocket sock;
+ QByteArray postdata, recvBuf, body;
+ QString url;
+ QString user, pass;
+ bool inHeader;
+ QStringList headerLines;
+ bool asProxy;
+ QString host;
+};
+
+HttpProxyPost::HttpProxyPost(QObject *parent)
+:QObject(parent)
+{
+ d = new Private;
+ connect(&d->sock, SIGNAL(connected()), SLOT(sock_connected()));
+ connect(&d->sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed()));
+ connect(&d->sock, SIGNAL(readyRead()), SLOT(sock_readyRead()));
+ connect(&d->sock, SIGNAL(error(int)), SLOT(sock_error(int)));
+ reset(true);
+}
+
+HttpProxyPost::~HttpProxyPost()
+{
+ reset(true);
+ delete d;
+}
+
+void HttpProxyPost::reset(bool clear)
+{
+ if(d->sock.state() != BSocket::Idle)
+ d->sock.close();
+ d->recvBuf.resize(0);
+ if(clear)
+ d->body.resize(0);
+}
+
+void HttpProxyPost::setAuth(const QString &user, const QString &pass)
+{
+ d->user = user;
+ d->pass = pass;
+}
+
+bool HttpProxyPost::isActive() const
+{
+ return (d->sock.state() == BSocket::Idle ? false: true);
+}
+
+void HttpProxyPost::post(const QString &proxyHost, int proxyPort, const QString &url, const QByteArray &data, bool asProxy)
+{
+ reset(true);
+
+ d->host = proxyHost;
+ d->url = url;
+ d->postdata = data;
+ d->asProxy = asProxy;
+
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpProxyPost: Connecting to %s:%d", proxyHost.latin1(), proxyPort);
+ if(d->user.isEmpty())
+ fprintf(stderr, "\n");
+ else
+ fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1());
+#endif
+ d->sock.connectToHost(proxyHost, proxyPort);
+}
+
+void HttpProxyPost::stop()
+{
+ reset();
+}
+
+QByteArray HttpProxyPost::body() const
+{
+ return d->body;
+}
+
+QString HttpProxyPost::getHeader(const QString &var) const
+{
+ for(QStringList::ConstIterator it = d->headerLines.begin(); it != d->headerLines.end(); ++it) {
+ const QString &s = *it;
+ int n = s.find(": ");
+ if(n == -1)
+ continue;
+ QString v = s.mid(0, n);
+ if(v == var)
+ return s.mid(n+2);
+ }
+ return "";
+}
+
+void HttpProxyPost::sock_connected()
+{
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpProxyPost: Connected\n");
+#endif
+ d->inHeader = true;
+ d->headerLines.clear();
+
+ QUrl u = d->url;
+
+ // connected, now send the request
+ QString s;
+ s += QString("POST ") + d->url + " HTTP/1.0\r\n";
+ if(d->asProxy) {
+ if(!d->user.isEmpty()) {
+ QString str = d->user + ':' + d->pass;
+ s += QString("Proxy-Authorization: Basic ") + Base64::encodeString(str) + "\r\n";
+ }
+ s += "Proxy-Connection: Keep-Alive\r\n";
+ s += "Pragma: no-cache\r\n";
+ s += QString("Host: ") + u.host() + "\r\n";
+ }
+ else {
+ s += QString("Host: ") + d->host + "\r\n";
+ }
+ s += "Content-Type: application/x-www-form-urlencoded\r\n";
+ s += QString("Content-Length: ") + QString::number(d->postdata.size()) + "\r\n";
+ s += "\r\n";
+
+ // write request
+ QCString cs = s.utf8();
+ QByteArray block(cs.length());
+ memcpy(block.data(), cs.data(), block.size());
+ d->sock.write(block);
+
+ // write postdata
+ d->sock.write(d->postdata);
+}
+
+void HttpProxyPost::sock_connectionClosed()
+{
+ d->body = d->recvBuf.copy();
+ reset();
+ result();
+}
+
+void HttpProxyPost::sock_readyRead()
+{
+ QByteArray block = d->sock.read();
+ ByteStream::appendArray(&d->recvBuf, block);
+
+ if(d->inHeader) {
+ // grab available lines
+ while(1) {
+ bool found;
+ QString line = extractLine(&d->recvBuf, &found);
+ if(!found)
+ break;
+ if(line.isEmpty()) {
+ d->inHeader = false;
+ break;
+ }
+ d->headerLines += line;
+ }
+
+ // done with grabbing the header?
+ if(!d->inHeader) {
+ QString str = d->headerLines.first();
+ d->headerLines.remove(d->headerLines.begin());
+
+ QString proto;
+ int code;
+ QString msg;
+ if(!extractMainHeader(str, &proto, &code, &msg)) {
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpProxyPost: invalid header!\n");
+#endif
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ else {
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpProxyPost: header proto=[%s] code=[%d] msg=[%s]\n", proto.latin1(), code, msg.latin1());
+ for(QStringList::ConstIterator it = d->headerLines.begin(); it != d->headerLines.end(); ++it)
+ fprintf(stderr, "HttpProxyPost: * [%s]\n", (*it).latin1());
+#endif
+ }
+
+ if(code == 200) { // OK
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpProxyPost: << Success >>\n");
+#endif
+ }
+ else {
+ int err;
+ QString errStr;
+ if(code == 407) { // Authentication failed
+ err = ErrProxyAuth;
+ errStr = tr("Authentication failed");
+ }
+ else if(code == 404) { // Host not found
+ err = ErrHostNotFound;
+ errStr = tr("Host not found");
+ }
+ else if(code == 403) { // Access denied
+ err = ErrProxyNeg;
+ errStr = tr("Access denied");
+ }
+ else if(code == 503) { // Connection refused
+ err = ErrConnectionRefused;
+ errStr = tr("Connection refused");
+ }
+ else { // invalid reply
+ err = ErrProxyNeg;
+ errStr = tr("Invalid reply");
+ }
+
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpProxyPost: << Error >> [%s]\n", errStr.latin1());
+#endif
+ reset(true);
+ error(err);
+ return;
+ }
+ }
+ }
+}
+
+void HttpProxyPost::sock_error(int x)
+{
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpProxyPost: socket error: %d\n", x);
+#endif
+ reset(true);
+ if(x == BSocket::ErrHostNotFound)
+ error(ErrProxyConnect);
+ else if(x == BSocket::ErrConnectionRefused)
+ error(ErrProxyConnect);
+ else if(x == BSocket::ErrRead)
+ error(ErrProxyNeg);
+}
+
+// CS_NAMESPACE_END
+
+#include "httppoll.moc"
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/httppoll.h b/kopete/protocols/jabber/libiris/cutestuff/network/httppoll.h
new file mode 100644
index 00000000..8bbebee3
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/httppoll.h
@@ -0,0 +1,104 @@
+/*
+ * httppoll.h - HTTP polling proxy
+ * 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 CS_HTTPPOLL_H
+#define CS_HTTPPOLL_H
+
+#include"bytestream.h"
+
+// CS_NAMESPACE_BEGIN
+
+class HttpPoll : public ByteStream
+{
+ Q_OBJECT
+public:
+ enum Error { ErrConnectionRefused = ErrCustom, ErrHostNotFound, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth };
+ HttpPoll(QObject *parent=0);
+ ~HttpPoll();
+
+ void setAuth(const QString &user, const QString &pass="");
+ void connectToUrl(const QString &url);
+ void connectToHost(const QString &proxyHost, int proxyPort, const QString &url);
+
+ int pollInterval() const;
+ void setPollInterval(int seconds);
+
+ // from ByteStream
+ bool isOpen() const;
+ void close();
+
+signals:
+ void connected();
+ void syncStarted();
+ void syncFinished();
+
+protected:
+ int tryWrite();
+
+private slots:
+ void http_result();
+ void http_error(int);
+ void do_sync();
+
+private:
+ class Private;
+ Private *d;
+
+ void reset(bool clear=false);
+ QByteArray makePacket(const QString &ident, const QString &key, const QString &newkey, const QByteArray &block);
+ void resetKey();
+ const QString & getKey(bool *);
+};
+
+class HttpProxyPost : public QObject
+{
+ Q_OBJECT
+public:
+ enum Error { ErrConnectionRefused, ErrHostNotFound, ErrSocket, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth };
+ HttpProxyPost(QObject *parent=0);
+ ~HttpProxyPost();
+
+ void setAuth(const QString &user, const QString &pass="");
+ bool isActive() const;
+ void post(const QString &proxyHost, int proxyPort, const QString &url, const QByteArray &data, bool asProxy=true);
+ void stop();
+ QByteArray body() const;
+ QString getHeader(const QString &) const;
+
+signals:
+ void result();
+ void error(int);
+
+private slots:
+ void sock_connected();
+ void sock_connectionClosed();
+ void sock_readyRead();
+ void sock_error(int);
+
+private:
+ class Private;
+ Private *d;
+
+ void reset(bool clear=false);
+};
+
+// CS_NAMESPACE_END
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/ndns.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/ndns.cpp
new file mode 100644
index 00000000..7fe60973
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/ndns.cpp
@@ -0,0 +1,378 @@
+/*
+ * ndns.cpp - native DNS resolution
+ * Copyright (C) 2001, 2002 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
+ *
+ */
+
+//! \class NDns ndns.h
+//! \brief Simple DNS resolution using native system calls
+//!
+//! This class is to be used when Qt's QDns is not good enough. Because QDns
+//! does not use threads, it cannot make a system call asyncronously. Thus,
+//! QDns tries to imitate the behavior of each platform's native behavior, and
+//! generally falls short.
+//!
+//! NDns uses a thread to make the system call happen in the background. This
+//! gives your program native DNS behavior, at the cost of requiring threads
+//! to build.
+//!
+//! \code
+//! #include "ndns.h"
+//!
+//! ...
+//!
+//! NDns dns;
+//! dns.resolve("psi.affinix.com");
+//!
+//! // The class will emit the resultsReady() signal when the resolution
+//! // is finished. You may then retrieve the results:
+//!
+//! uint ip_address = dns.result();
+//!
+//! // or if you want to get the IP address as a string:
+//!
+//! QString ip_address = dns.resultString();
+//! \endcode
+
+#include"ndns.h"
+
+#include<qapplication.h>
+#include<qsocketdevice.h>
+#include<qptrlist.h>
+#include<qeventloop.h>
+
+#ifdef Q_OS_UNIX
+#include<netdb.h>
+#include<sys/types.h>
+#include<netinet/in.h>
+#include<arpa/inet.h>
+#endif
+
+#ifdef Q_OS_WIN32
+#include<windows.h>
+#endif
+
+// CS_NAMESPACE_BEGIN
+
+//! \if _hide_doc_
+class NDnsWorkerEvent : public QCustomEvent
+{
+public:
+ enum Type { WorkerEvent = QEvent::User + 100 };
+ NDnsWorkerEvent(NDnsWorker *);
+
+ NDnsWorker *worker;
+};
+
+class NDnsWorker : public QThread
+{
+public:
+ NDnsWorker(QObject *, const QCString &);
+
+ bool success;
+ bool cancelled;
+ QHostAddress addr;
+
+protected:
+ void run();
+
+private:
+ QCString host;
+ QObject *par;
+};
+//! \endif
+
+//----------------------------------------------------------------------------
+// NDnsManager
+//----------------------------------------------------------------------------
+#ifndef HAVE_GETHOSTBYNAME_R
+static QMutex *workerMutex = 0;
+static QMutex *workerCancelled = 0;
+#endif
+static NDnsManager *man = 0;
+bool winsock_init = false;
+
+class NDnsManager::Item
+{
+public:
+ NDns *ndns;
+ NDnsWorker *worker;
+};
+
+class NDnsManager::Private
+{
+public:
+ Item *find(const NDns *n)
+ {
+ QPtrListIterator<Item> it(list);
+ for(Item *i; (i = it.current()); ++it) {
+ if(i->ndns == n)
+ return i;
+ }
+ return 0;
+ }
+
+ Item *find(const NDnsWorker *w)
+ {
+ QPtrListIterator<Item> it(list);
+ for(Item *i; (i = it.current()); ++it) {
+ if(i->worker == w)
+ return i;
+ }
+ return 0;
+ }
+
+ QPtrList<Item> list;
+};
+
+NDnsManager::NDnsManager()
+{
+#ifndef HAVE_GETHOSTBYNAME_R
+ workerMutex = new QMutex;
+ workerCancelled = new QMutex;
+#endif
+
+#ifdef Q_OS_WIN32
+ if(!winsock_init) {
+ winsock_init = true;
+ QSocketDevice *sd = new QSocketDevice;
+ delete sd;
+ }
+#endif
+
+ d = new Private;
+ d->list.setAutoDelete(true);
+
+ connect(qApp, SIGNAL(aboutToQuit()), SLOT(app_aboutToQuit()));
+}
+
+NDnsManager::~NDnsManager()
+{
+ delete d;
+
+#ifndef HAVE_GETHOSTBYNAME_R
+ delete workerMutex;
+ workerMutex = 0;
+ delete workerCancelled;
+ workerCancelled = 0;
+#endif
+}
+
+void NDnsManager::resolve(NDns *self, const QString &name)
+{
+ Item *i = new Item;
+ i->ndns = self;
+ i->worker = new NDnsWorker(this, name.utf8());
+ d->list.append(i);
+
+ i->worker->start();
+}
+
+void NDnsManager::stop(NDns *self)
+{
+ Item *i = d->find(self);
+ if(!i)
+ return;
+ // disassociate
+ i->ndns = 0;
+
+#ifndef HAVE_GETHOSTBYNAME_R
+ // cancel
+ workerCancelled->lock();
+ i->worker->cancelled = true;
+ workerCancelled->unlock();
+#endif
+}
+
+bool NDnsManager::isBusy(const NDns *self) const
+{
+ Item *i = d->find(self);
+ return (i ? true: false);
+}
+
+bool NDnsManager::event(QEvent *e)
+{
+ if((int)e->type() == (int)NDnsWorkerEvent::WorkerEvent) {
+ NDnsWorkerEvent *we = static_cast<NDnsWorkerEvent*>(e);
+ we->worker->wait(); // ensure that the thread is terminated
+
+ Item *i = d->find(we->worker);
+ if(!i) {
+ // should NOT happen
+ return true;
+ }
+ QHostAddress addr = i->worker->addr;
+ NDns *ndns = i->ndns;
+ delete i->worker;
+ d->list.removeRef(i);
+
+ // nuke manager if no longer needed (code that follows MUST BE SAFE!)
+ tryDestroy();
+
+ // requestor still around?
+ if(ndns)
+ ndns->finished(addr);
+ return true;
+ }
+ return false;
+}
+
+void NDnsManager::tryDestroy()
+{
+ if(d->list.isEmpty()) {
+ man = 0;
+ delete this;
+ }
+}
+
+void NDnsManager::app_aboutToQuit()
+{
+ while(man) {
+ QEventLoop *e = qApp->eventLoop();
+ e->processEvents(QEventLoop::WaitForMore);
+ }
+}
+
+
+//----------------------------------------------------------------------------
+// NDns
+//----------------------------------------------------------------------------
+
+//! \fn void NDns::resultsReady()
+//! This signal is emitted when the DNS resolution succeeds or fails.
+
+//!
+//! Constructs an NDns object with parent \a parent.
+NDns::NDns(QObject *parent)
+:QObject(parent)
+{
+}
+
+//!
+//! Destroys the object and frees allocated resources.
+NDns::~NDns()
+{
+ stop();
+}
+
+//!
+//! Resolves hostname \a host (eg. psi.affinix.com)
+void NDns::resolve(const QString &host)
+{
+ stop();
+ if(!man)
+ man = new NDnsManager;
+ man->resolve(this, host);
+}
+
+//!
+//! Cancels the lookup action.
+//! \note This will not stop the underlying system call, which must finish before the next lookup will proceed.
+void NDns::stop()
+{
+ if(man)
+ man->stop(this);
+}
+
+//!
+//! Returns the IP address as a 32-bit integer in host-byte-order. This will be 0 if the lookup failed.
+//! \sa resultsReady()
+uint NDns::result() const
+{
+ return addr.ip4Addr();
+}
+
+//!
+//! Returns the IP address as a string. This will be an empty string if the lookup failed.
+//! \sa resultsReady()
+QString NDns::resultString() const
+{
+ return addr.toString();
+}
+
+//!
+//! Returns TRUE if busy resolving a hostname.
+bool NDns::isBusy() const
+{
+ if(!man)
+ return false;
+ return man->isBusy(this);
+}
+
+void NDns::finished(const QHostAddress &a)
+{
+ addr = a;
+ resultsReady();
+}
+
+//----------------------------------------------------------------------------
+// NDnsWorkerEvent
+//----------------------------------------------------------------------------
+NDnsWorkerEvent::NDnsWorkerEvent(NDnsWorker *p)
+:QCustomEvent(WorkerEvent)
+{
+ worker = p;
+}
+
+//----------------------------------------------------------------------------
+// NDnsWorker
+//----------------------------------------------------------------------------
+NDnsWorker::NDnsWorker(QObject *_par, const QCString &_host)
+{
+ success = cancelled = false;
+ par = _par;
+ host = _host.copy(); // do we need this to avoid sharing across threads?
+}
+
+void NDnsWorker::run()
+{
+ hostent *h = 0;
+
+#ifdef HAVE_GETHOSTBYNAME_R
+ hostent buf;
+ char char_buf[1024];
+ int err;
+ gethostbyname_r(host.data(), &buf, char_buf, sizeof(char_buf), &h, &err);
+#else
+ // lock for gethostbyname
+ QMutexLocker locker(workerMutex);
+
+ // check for cancel
+ workerCancelled->lock();
+ bool cancel = cancelled;
+ workerCancelled->unlock();
+
+ if(!cancel)
+ h = gethostbyname(host.data());
+#endif
+
+ if(!h) {
+ success = false;
+ QApplication::postEvent(par, new NDnsWorkerEvent(this));
+ return;
+ }
+
+ in_addr a = *((struct in_addr *)h->h_addr_list[0]);
+ addr.setAddress(ntohl(a.s_addr));
+ success = true;
+
+ QApplication::postEvent(par, new NDnsWorkerEvent(this));
+}
+
+// CS_NAMESPACE_END
+
+#include "ndns.moc"
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/ndns.h b/kopete/protocols/jabber/libiris/cutestuff/network/ndns.h
new file mode 100644
index 00000000..c11d1a28
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/ndns.h
@@ -0,0 +1,88 @@
+/*
+ * ndns.h - native DNS resolution
+ * Copyright (C) 2001, 2002 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 CS_NDNS_H
+#define CS_NDNS_H
+
+#include<qobject.h>
+#include<qcstring.h>
+#include<qthread.h>
+#include<qmutex.h>
+#include<qhostaddress.h>
+
+// CS_NAMESPACE_BEGIN
+
+class NDnsWorker;
+class NDnsManager;
+
+class NDns : public QObject
+{
+ Q_OBJECT
+public:
+ NDns(QObject *parent=0);
+ ~NDns();
+
+ void resolve(const QString &);
+ void stop();
+ bool isBusy() const;
+
+ uint result() const;
+ QString resultString() const;
+
+signals:
+ void resultsReady();
+
+private:
+ QHostAddress addr;
+
+ friend class NDnsManager;
+ void finished(const QHostAddress &);
+};
+
+class NDnsManager : public QObject
+{
+ Q_OBJECT
+public:
+ ~NDnsManager();
+ class Item;
+
+//! \if _hide_doc_
+protected:
+ bool event(QEvent *);
+//! \endif
+
+private slots:
+ void app_aboutToQuit();
+
+private:
+ class Private;
+ Private *d;
+
+ friend class NDns;
+ NDnsManager();
+ void resolve(NDns *self, const QString &name);
+ void stop(NDns *self);
+ bool isBusy(const NDns *self) const;
+ void tryDestroy();
+};
+
+// CS_NAMESPACE_END
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/servsock.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/servsock.cpp
new file mode 100644
index 00000000..4aee36dc
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/servsock.cpp
@@ -0,0 +1,112 @@
+/*
+ * servsock.cpp - simple wrapper to QServerSocket
+ * 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"servsock.h"
+
+// CS_NAMESPACE_BEGIN
+
+//----------------------------------------------------------------------------
+// ServSock
+//----------------------------------------------------------------------------
+class ServSock::Private
+{
+public:
+ Private() {}
+
+ ServSockSignal *serv;
+};
+
+ServSock::ServSock(QObject *parent)
+:QObject(parent)
+{
+ d = new Private;
+ d->serv = 0;
+}
+
+ServSock::~ServSock()
+{
+ stop();
+ delete d;
+}
+
+bool ServSock::isActive() const
+{
+ return (d->serv ? true: false);
+}
+
+bool ServSock::listen(Q_UINT16 port)
+{
+ stop();
+
+ d->serv = new ServSockSignal(port);
+ if(!d->serv->ok()) {
+ delete d->serv;
+ d->serv = 0;
+ return false;
+ }
+ connect(d->serv, SIGNAL(connectionReady(int)), SLOT(sss_connectionReady(int)));
+
+ return true;
+}
+
+void ServSock::stop()
+{
+ delete d->serv;
+ d->serv = 0;
+}
+
+int ServSock::port() const
+{
+ if(d->serv)
+ return d->serv->port();
+ else
+ return -1;
+}
+
+QHostAddress ServSock::address() const
+{
+ if(d->serv)
+ return d->serv->address();
+ else
+ return QHostAddress();
+}
+
+void ServSock::sss_connectionReady(int s)
+{
+ connectionReady(s);
+}
+
+
+//----------------------------------------------------------------------------
+// ServSockSignal
+//----------------------------------------------------------------------------
+ServSockSignal::ServSockSignal(int port)
+:QServerSocket(port, 16)
+{
+}
+
+void ServSockSignal::newConnection(int x)
+{
+ connectionReady(x);
+}
+
+// CS_NAMESPACE_END
+
+#include "servsock.moc"
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/servsock.h b/kopete/protocols/jabber/libiris/cutestuff/network/servsock.h
new file mode 100644
index 00000000..60a0c99d
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/servsock.h
@@ -0,0 +1,68 @@
+/*
+ * servsock.h - simple wrapper to QServerSocket
+ * 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 CS_SERVSOCK_H
+#define CS_SERVSOCK_H
+
+#include<qserversocket.h>
+
+// CS_NAMESPACE_BEGIN
+
+class ServSock : public QObject
+{
+ Q_OBJECT
+public:
+ ServSock(QObject *parent=0);
+ ~ServSock();
+
+ bool isActive() const;
+ bool listen(Q_UINT16 port);
+ void stop();
+ int port() const;
+ QHostAddress address() const;
+
+signals:
+ void connectionReady(int);
+
+private slots:
+ void sss_connectionReady(int);
+
+private:
+ class Private;
+ Private *d;
+};
+
+class ServSockSignal : public QServerSocket
+{
+ Q_OBJECT
+public:
+ ServSockSignal(int port);
+
+signals:
+ void connectionReady(int);
+
+protected:
+ // reimplemented
+ void newConnection(int);
+};
+
+// CS_NAMESPACE_END
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/socks.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/socks.cpp
new file mode 100644
index 00000000..bae374f5
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/socks.cpp
@@ -0,0 +1,1223 @@
+/*
+ * socks.cpp - SOCKS5 TCP proxy client/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
+ *
+ */
+
+#include"socks.h"
+
+#include<qhostaddress.h>
+#include<qstringlist.h>
+#include<qptrlist.h>
+#include<qtimer.h>
+#include<qguardedptr.h>
+#include<qsocketdevice.h>
+#include<qsocketnotifier.h>
+
+#ifdef Q_OS_UNIX
+#include<sys/types.h>
+#include<netinet/in.h>
+#endif
+
+#ifdef Q_OS_WIN32
+#include<windows.h>
+#endif
+
+#include"servsock.h"
+#include"bsocket.h"
+
+#ifdef PROX_DEBUG
+#include<stdio.h>
+#endif
+
+// CS_NAMESPACE_BEGIN
+
+//----------------------------------------------------------------------------
+// SocksUDP
+//----------------------------------------------------------------------------
+static QByteArray sp_create_udp(const QString &host, Q_UINT16 port, const QByteArray &buf)
+{
+ // detect for IP addresses
+ //QHostAddress addr;
+ //if(addr.setAddress(host))
+ // return sp_set_request(addr, port, cmd1);
+
+ QCString h = host.utf8();
+ h.truncate(255);
+ h = QString::fromUtf8(h).utf8(); // delete any partial characters?
+ int hlen = h.length();
+
+ int at = 0;
+ QByteArray a(4);
+ a[at++] = 0x00; // reserved
+ a[at++] = 0x00; // reserved
+ a[at++] = 0x00; // frag
+ a[at++] = 0x03; // address type = domain
+
+ // host
+ a.resize(at+hlen+1);
+ a[at++] = hlen;
+ memcpy(a.data() + at, h.data(), hlen);
+ at += hlen;
+
+ // port
+ a.resize(at+2);
+ unsigned short p = htons(port);
+ memcpy(a.data() + at, &p, 2);
+ at += 2;
+
+ a.resize(at+buf.size());
+ memcpy(a.data() + at, buf.data(), buf.size());
+
+ return a;
+}
+
+struct SPS_UDP
+{
+ QString host;
+ Q_UINT16 port;
+ QByteArray data;
+};
+
+static int sp_read_udp(QByteArray *from, SPS_UDP *s)
+{
+ int full_len = 4;
+ if((int)from->size() < full_len)
+ return 0;
+
+ QString host;
+ QHostAddress addr;
+ unsigned char atype = from->at(3);
+
+ if(atype == 0x01) {
+ full_len += 4;
+ if((int)from->size() < full_len)
+ return 0;
+ Q_UINT32 ip4;
+ memcpy(&ip4, from->data() + 4, 4);
+ addr.setAddress(ntohl(ip4));
+ host = addr.toString();
+ }
+ else if(atype == 0x03) {
+ ++full_len;
+ if((int)from->size() < full_len)
+ return 0;
+ unsigned char host_len = from->at(4);
+ full_len += host_len;
+ if((int)from->size() < full_len)
+ return 0;
+ QCString cs(host_len+1);
+ memcpy(cs.data(), from->data() + 5, host_len);
+ host = QString::fromLatin1(cs);
+ }
+ else if(atype == 0x04) {
+ full_len += 16;
+ if((int)from->size() < full_len)
+ return 0;
+ Q_UINT8 a6[16];
+ memcpy(a6, from->data() + 4, 16);
+ addr.setAddress(a6);
+ host = addr.toString();
+ }
+
+ full_len += 2;
+ if((int)from->size() < full_len)
+ return 0;
+
+ Q_UINT16 p;
+ memcpy(&p, from->data() + full_len - 2, 2);
+
+ s->host = host;
+ s->port = ntohs(p);
+ s->data.resize(from->size() - full_len);
+ memcpy(s->data.data(), from->data() + full_len, s->data.size());
+
+ return 1;
+}
+
+class SocksUDP::Private
+{
+public:
+ QSocketDevice *sd;
+ QSocketNotifier *sn;
+ SocksClient *sc;
+ QHostAddress routeAddr;
+ int routePort;
+ QString host;
+ int port;
+};
+
+SocksUDP::SocksUDP(SocksClient *sc, const QString &host, int port, const QHostAddress &routeAddr, int routePort)
+:QObject(sc)
+{
+ d = new Private;
+ d->sc = sc;
+ d->sd = new QSocketDevice(QSocketDevice::Datagram);
+ d->sd->setBlocking(false);
+ d->sn = new QSocketNotifier(d->sd->socket(), QSocketNotifier::Read);
+ connect(d->sn, SIGNAL(activated(int)), SLOT(sn_activated(int)));
+ d->host = host;
+ d->port = port;
+ d->routeAddr = routeAddr;
+ d->routePort = routePort;
+}
+
+SocksUDP::~SocksUDP()
+{
+ delete d->sn;
+ delete d->sd;
+ delete d;
+}
+
+void SocksUDP::change(const QString &host, int port)
+{
+ d->host = host;
+ d->port = port;
+}
+
+void SocksUDP::write(const QByteArray &data)
+{
+ QByteArray buf = sp_create_udp(d->host, d->port, data);
+ d->sd->setBlocking(true);
+ d->sd->writeBlock(buf.data(), buf.size(), d->routeAddr, d->routePort);
+ d->sd->setBlocking(false);
+}
+
+void SocksUDP::sn_activated(int)
+{
+ QByteArray buf(8192);
+ int actual = d->sd->readBlock(buf.data(), buf.size());
+ buf.resize(actual);
+ packetReady(buf);
+}
+
+//----------------------------------------------------------------------------
+// SocksClient
+//----------------------------------------------------------------------------
+#define REQ_CONNECT 0x01
+#define REQ_BIND 0x02
+#define REQ_UDPASSOCIATE 0x03
+
+#define RET_SUCCESS 0x00
+#define RET_UNREACHABLE 0x04
+#define RET_CONNREFUSED 0x05
+
+// spc = socks packet client
+// sps = socks packet server
+// SPCS = socks packet client struct
+// SPSS = socks packet server struct
+
+// Version
+static QByteArray spc_set_version()
+{
+ QByteArray ver(4);
+ ver[0] = 0x05; // socks version 5
+ ver[1] = 0x02; // number of methods
+ ver[2] = 0x00; // no-auth
+ ver[3] = 0x02; // username
+ return ver;
+}
+
+static QByteArray sps_set_version(int method)
+{
+ QByteArray ver(2);
+ ver[0] = 0x05;
+ ver[1] = method;
+ return ver;
+}
+
+struct SPCS_VERSION
+{
+ unsigned char version;
+ QByteArray methodList;
+};
+
+static int spc_get_version(QByteArray *from, SPCS_VERSION *s)
+{
+ if(from->size() < 1)
+ return 0;
+ if(from->at(0) != 0x05) // only SOCKS5 supported
+ return -1;
+ if(from->size() < 2)
+ return 0;
+ uint num = from->at(1);
+ if(num > 16) // who the heck has over 16 auth methods??
+ return -1;
+ if(from->size() < 2 + num)
+ return 0;
+ QByteArray a = ByteStream::takeArray(from, 2+num);
+ s->version = a[0];
+ s->methodList.resize(num);
+ memcpy(s->methodList.data(), a.data() + 2, num);
+ return 1;
+}
+
+struct SPSS_VERSION
+{
+ unsigned char version;
+ unsigned char method;
+};
+
+static int sps_get_version(QByteArray *from, SPSS_VERSION *s)
+{
+ if(from->size() < 2)
+ return 0;
+ QByteArray a = ByteStream::takeArray(from, 2);
+ s->version = a[0];
+ s->method = a[1];
+ return 1;
+}
+
+// authUsername
+static QByteArray spc_set_authUsername(const QCString &user, const QCString &pass)
+{
+ int len1 = user.length();
+ int len2 = pass.length();
+ if(len1 > 255)
+ len1 = 255;
+ if(len2 > 255)
+ len2 = 255;
+ QByteArray a(1+1+len1+1+len2);
+ a[0] = 0x01; // username auth version 1
+ a[1] = len1;
+ memcpy(a.data() + 2, user.data(), len1);
+ a[2+len1] = len2;
+ memcpy(a.data() + 3 + len1, pass.data(), len2);
+ return a;
+}
+
+static QByteArray sps_set_authUsername(bool success)
+{
+ QByteArray a(2);
+ a[0] = 0x01;
+ a[1] = success ? 0x00 : 0xff;
+ return a;
+}
+
+struct SPCS_AUTHUSERNAME
+{
+ QString user, pass;
+};
+
+static int spc_get_authUsername(QByteArray *from, SPCS_AUTHUSERNAME *s)
+{
+ if(from->size() < 1)
+ return 0;
+ unsigned char ver = from->at(0);
+ if(ver != 0x01)
+ return -1;
+ if(from->size() < 2)
+ return 0;
+ unsigned char ulen = from->at(1);
+ if((int)from->size() < ulen + 3)
+ return 0;
+ unsigned char plen = from->at(ulen+2);
+ if((int)from->size() < ulen + plen + 3)
+ return 0;
+ QByteArray a = ByteStream::takeArray(from, ulen + plen + 3);
+
+ QCString user, pass;
+ user.resize(ulen+1);
+ pass.resize(plen+1);
+ memcpy(user.data(), a.data()+2, ulen);
+ memcpy(pass.data(), a.data()+ulen+3, plen);
+ s->user = QString::fromUtf8(user);
+ s->pass = QString::fromUtf8(pass);
+ return 1;
+}
+
+struct SPSS_AUTHUSERNAME
+{
+ unsigned char version;
+ bool success;
+};
+
+static int sps_get_authUsername(QByteArray *from, SPSS_AUTHUSERNAME *s)
+{
+ if(from->size() < 2)
+ return 0;
+ QByteArray a = ByteStream::takeArray(from, 2);
+ s->version = a[0];
+ s->success = a[1] == 0 ? true: false;
+ return 1;
+}
+
+// connectRequest
+static QByteArray sp_set_request(const QHostAddress &addr, unsigned short port, unsigned char cmd1)
+{
+ int at = 0;
+ QByteArray a(4);
+ a[at++] = 0x05; // socks version 5
+ a[at++] = cmd1;
+ a[at++] = 0x00; // reserved
+ if(addr.isIp4Addr()) {
+ a[at++] = 0x01; // address type = ipv4
+ Q_UINT32 ip4 = htonl(addr.ip4Addr());
+ a.resize(at+4);
+ memcpy(a.data() + at, &ip4, 4);
+ at += 4;
+ }
+ else {
+ a[at++] = 0x04;
+ Q_UINT8 a6[16];
+ QStringList s6 = QStringList::split(':', addr.toString(), true);
+ int at = 0;
+ Q_UINT16 c;
+ bool ok;
+ for(QStringList::ConstIterator it = s6.begin(); it != s6.end(); ++it) {
+ c = (*it).toInt(&ok, 16);
+ a6[at++] = (c >> 8);
+ a6[at++] = c & 0xff;
+ }
+ a.resize(at+16);
+ memcpy(a.data() + at, a6, 16);
+ at += 16;
+ }
+
+ // port
+ a.resize(at+2);
+ unsigned short p = htons(port);
+ memcpy(a.data() + at, &p, 2);
+
+ return a;
+}
+
+static QByteArray sp_set_request(const QString &host, Q_UINT16 port, unsigned char cmd1)
+{
+ // detect for IP addresses
+ QHostAddress addr;
+ if(addr.setAddress(host))
+ return sp_set_request(addr, port, cmd1);
+
+ QCString h = host.utf8();
+ h.truncate(255);
+ h = QString::fromUtf8(h).utf8(); // delete any partial characters?
+ int hlen = h.length();
+
+ int at = 0;
+ QByteArray a(4);
+ a[at++] = 0x05; // socks version 5
+ a[at++] = cmd1;
+ a[at++] = 0x00; // reserved
+ a[at++] = 0x03; // address type = domain
+
+ // host
+ a.resize(at+hlen+1);
+ a[at++] = hlen;
+ memcpy(a.data() + at, h.data(), hlen);
+ at += hlen;
+
+ // port
+ a.resize(at+2);
+ unsigned short p = htons(port);
+ memcpy(a.data() + at, &p, 2);
+
+ return a;
+}
+
+struct SPS_CONNREQ
+{
+ unsigned char version;
+ unsigned char cmd;
+ int address_type;
+ QString host;
+ QHostAddress addr;
+ Q_UINT16 port;
+};
+
+static int sp_get_request(QByteArray *from, SPS_CONNREQ *s)
+{
+ int full_len = 4;
+ if((int)from->size() < full_len)
+ return 0;
+
+ QString host;
+ QHostAddress addr;
+ unsigned char atype = from->at(3);
+
+ if(atype == 0x01) {
+ full_len += 4;
+ if((int)from->size() < full_len)
+ return 0;
+ Q_UINT32 ip4;
+ memcpy(&ip4, from->data() + 4, 4);
+ addr.setAddress(ntohl(ip4));
+ }
+ else if(atype == 0x03) {
+ ++full_len;
+ if((int)from->size() < full_len)
+ return 0;
+ unsigned char host_len = from->at(4);
+ full_len += host_len;
+ if((int)from->size() < full_len)
+ return 0;
+ QCString cs(host_len+1);
+ memcpy(cs.data(), from->data() + 5, host_len);
+ host = QString::fromLatin1(cs);
+ }
+ else if(atype == 0x04) {
+ full_len += 16;
+ if((int)from->size() < full_len)
+ return 0;
+ Q_UINT8 a6[16];
+ memcpy(a6, from->data() + 4, 16);
+ addr.setAddress(a6);
+ }
+
+ full_len += 2;
+ if((int)from->size() < full_len)
+ return 0;
+
+ QByteArray a = ByteStream::takeArray(from, full_len);
+
+ Q_UINT16 p;
+ memcpy(&p, a.data() + full_len - 2, 2);
+
+ s->version = a[0];
+ s->cmd = a[1];
+ s->address_type = atype;
+ s->host = host;
+ s->addr = addr;
+ s->port = ntohs(p);
+
+ return 1;
+}
+
+enum { StepVersion, StepAuth, StepRequest };
+
+class SocksClient::Private
+{
+public:
+ Private() {}
+
+ BSocket sock;
+ QString host;
+ int port;
+ QString user, pass;
+ QString real_host;
+ int real_port;
+
+ QByteArray recvBuf;
+ bool active;
+ int step;
+ int authMethod;
+ bool incoming, waiting;
+
+ QString rhost;
+ int rport;
+
+ int pending;
+
+ bool udp;
+ QString udpAddr;
+ int udpPort;
+};
+
+SocksClient::SocksClient(QObject *parent)
+:ByteStream(parent)
+{
+ init();
+
+ d->incoming = false;
+}
+
+SocksClient::SocksClient(int s, QObject *parent)
+:ByteStream(parent)
+{
+ init();
+
+ d->incoming = true;
+ d->waiting = true;
+ d->sock.setSocket(s);
+}
+
+void SocksClient::init()
+{
+ d = new Private;
+ connect(&d->sock, SIGNAL(connected()), SLOT(sock_connected()));
+ connect(&d->sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed()));
+ connect(&d->sock, SIGNAL(delayedCloseFinished()), SLOT(sock_delayedCloseFinished()));
+ connect(&d->sock, SIGNAL(readyRead()), SLOT(sock_readyRead()));
+ connect(&d->sock, SIGNAL(bytesWritten(int)), SLOT(sock_bytesWritten(int)));
+ connect(&d->sock, SIGNAL(error(int)), SLOT(sock_error(int)));
+
+ reset(true);
+}
+
+SocksClient::~SocksClient()
+{
+ reset(true);
+ delete d;
+}
+
+void SocksClient::reset(bool clear)
+{
+ if(d->sock.state() != BSocket::Idle)
+ d->sock.close();
+ if(clear)
+ clearReadBuffer();
+ d->recvBuf.resize(0);
+ d->active = false;
+ d->waiting = false;
+ d->udp = false;
+ d->pending = 0;
+}
+
+bool SocksClient::isIncoming() const
+{
+ return d->incoming;
+}
+
+void SocksClient::setAuth(const QString &user, const QString &pass)
+{
+ d->user = user;
+ d->pass = pass;
+}
+
+void SocksClient::connectToHost(const QString &proxyHost, int proxyPort, const QString &host, int port, bool udpMode)
+{
+ reset(true);
+
+ d->host = proxyHost;
+ d->port = proxyPort;
+ d->real_host = host;
+ d->real_port = port;
+ d->udp = udpMode;
+
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: Connecting to %s:%d", proxyHost.latin1(), proxyPort);
+ if(d->user.isEmpty())
+ fprintf(stderr, "\n");
+ else
+ fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1());
+#endif
+ d->sock.connectToHost(d->host, d->port);
+}
+
+bool SocksClient::isOpen() const
+{
+ return d->active;
+}
+
+void SocksClient::close()
+{
+ d->sock.close();
+ if(d->sock.bytesToWrite() == 0)
+ reset();
+}
+
+void SocksClient::writeData(const QByteArray &buf)
+{
+ d->pending += buf.size();
+ d->sock.write(buf);
+}
+
+void SocksClient::write(const QByteArray &buf)
+{
+ if(d->active && !d->udp)
+ d->sock.write(buf);
+}
+
+QByteArray SocksClient::read(int bytes)
+{
+ return ByteStream::read(bytes);
+}
+
+int SocksClient::bytesAvailable() const
+{
+ return ByteStream::bytesAvailable();
+}
+
+int SocksClient::bytesToWrite() const
+{
+ if(d->active)
+ return d->sock.bytesToWrite();
+ else
+ return 0;
+}
+
+void SocksClient::sock_connected()
+{
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: Connected\n");
+#endif
+
+ d->step = StepVersion;
+ writeData(spc_set_version());
+}
+
+void SocksClient::sock_connectionClosed()
+{
+ if(d->active) {
+ reset();
+ connectionClosed();
+ }
+ else {
+ error(ErrProxyNeg);
+ }
+}
+
+void SocksClient::sock_delayedCloseFinished()
+{
+ if(d->active) {
+ reset();
+ delayedCloseFinished();
+ }
+}
+
+void SocksClient::sock_readyRead()
+{
+ QByteArray block = d->sock.read();
+
+ if(!d->active) {
+ if(d->incoming)
+ processIncoming(block);
+ else
+ processOutgoing(block);
+ }
+ else {
+ if(!d->udp) {
+ appendRead(block);
+ readyRead();
+ }
+ }
+}
+
+void SocksClient::processOutgoing(const QByteArray &block)
+{
+#ifdef PROX_DEBUG
+ // show hex
+ fprintf(stderr, "SocksClient: client recv { ");
+ for(int n = 0; n < (int)block.size(); ++n)
+ fprintf(stderr, "%02X ", (unsigned char)block[n]);
+ fprintf(stderr, " } \n");
+#endif
+ ByteStream::appendArray(&d->recvBuf, block);
+
+ if(d->step == StepVersion) {
+ SPSS_VERSION s;
+ int r = sps_get_version(&d->recvBuf, &s);
+ if(r == -1) {
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ else if(r == 1) {
+ if(s.version != 0x05 || s.method == 0xff) {
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: Method selection failed\n");
+#endif
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+
+ QString str;
+ if(s.method == 0x00) {
+ str = "None";
+ d->authMethod = AuthNone;
+ }
+ else if(s.method == 0x02) {
+ str = "Username/Password";
+ d->authMethod = AuthUsername;
+ }
+ else {
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: Server wants to use unknown method '%02x'\n", s.method);
+#endif
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+
+ if(d->authMethod == AuthNone) {
+ // no auth, go straight to the request
+ do_request();
+ }
+ else if(d->authMethod == AuthUsername) {
+ d->step = StepAuth;
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: Authenticating [Username] ...\n");
+#endif
+ writeData(spc_set_authUsername(d->user.latin1(), d->pass.latin1()));
+ }
+ }
+ }
+ if(d->step == StepAuth) {
+ if(d->authMethod == AuthUsername) {
+ SPSS_AUTHUSERNAME s;
+ int r = sps_get_authUsername(&d->recvBuf, &s);
+ if(r == -1) {
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ else if(r == 1) {
+ if(s.version != 0x01) {
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ if(!s.success) {
+ reset(true);
+ error(ErrProxyAuth);
+ return;
+ }
+
+ do_request();
+ }
+ }
+ }
+ else if(d->step == StepRequest) {
+ SPS_CONNREQ s;
+ int r = sp_get_request(&d->recvBuf, &s);
+ if(r == -1) {
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ else if(r == 1) {
+ if(s.cmd != RET_SUCCESS) {
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: client << Error >> [%02x]\n", s.cmd);
+#endif
+ reset(true);
+ if(s.cmd == RET_UNREACHABLE)
+ error(ErrHostNotFound);
+ else if(s.cmd == RET_CONNREFUSED)
+ error(ErrConnectionRefused);
+ else
+ error(ErrProxyNeg);
+ return;
+ }
+
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: client << Success >>\n");
+#endif
+ if(d->udp) {
+ if(s.address_type == 0x03)
+ d->udpAddr = s.host;
+ else
+ d->udpAddr = s.addr.toString();
+ d->udpPort = s.port;
+ }
+
+ d->active = true;
+
+ QGuardedPtr<QObject> self = this;
+ connected();
+ if(!self)
+ return;
+
+ if(!d->recvBuf.isEmpty()) {
+ appendRead(d->recvBuf);
+ d->recvBuf.resize(0);
+ readyRead();
+ }
+ }
+ }
+}
+
+void SocksClient::do_request()
+{
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: Requesting ...\n");
+#endif
+ d->step = StepRequest;
+ int cmd = d->udp ? REQ_UDPASSOCIATE : REQ_CONNECT;
+ QByteArray buf;
+ if(!d->real_host.isEmpty())
+ buf = sp_set_request(d->real_host, d->real_port, cmd);
+ else
+ buf = sp_set_request(QHostAddress(), 0, cmd);
+ writeData(buf);
+}
+
+void SocksClient::sock_bytesWritten(int x)
+{
+ int bytes = x;
+ if(d->pending >= bytes) {
+ d->pending -= bytes;
+ bytes = 0;
+ }
+ else {
+ bytes -= d->pending;
+ d->pending = 0;
+ }
+ if(bytes > 0)
+ bytesWritten(bytes);
+}
+
+void SocksClient::sock_error(int x)
+{
+ if(d->active) {
+ reset();
+ error(ErrRead);
+ }
+ else {
+ reset(true);
+ if(x == BSocket::ErrHostNotFound)
+ error(ErrProxyConnect);
+ else if(x == BSocket::ErrConnectionRefused)
+ error(ErrProxyConnect);
+ else if(x == BSocket::ErrRead)
+ error(ErrProxyNeg);
+ }
+}
+
+void SocksClient::serve()
+{
+ d->waiting = false;
+ d->step = StepVersion;
+ continueIncoming();
+}
+
+void SocksClient::processIncoming(const QByteArray &block)
+{
+#ifdef PROX_DEBUG
+ // show hex
+ fprintf(stderr, "SocksClient: server recv { ");
+ for(int n = 0; n < (int)block.size(); ++n)
+ fprintf(stderr, "%02X ", (unsigned char)block[n]);
+ fprintf(stderr, " } \n");
+#endif
+ ByteStream::appendArray(&d->recvBuf, block);
+
+ if(!d->waiting)
+ continueIncoming();
+}
+
+void SocksClient::continueIncoming()
+{
+ if(d->recvBuf.isEmpty())
+ return;
+
+ if(d->step == StepVersion) {
+ SPCS_VERSION s;
+ int r = spc_get_version(&d->recvBuf, &s);
+ if(r == -1) {
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ else if(r == 1) {
+ if(s.version != 0x05) {
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+
+ int methods = 0;
+ for(int n = 0; n < (int)s.methodList.size(); ++n) {
+ unsigned char c = s.methodList[n];
+ if(c == 0x00)
+ methods |= AuthNone;
+ else if(c == 0x02)
+ methods |= AuthUsername;
+ }
+ d->waiting = true;
+ incomingMethods(methods);
+ }
+ }
+ else if(d->step == StepAuth) {
+ SPCS_AUTHUSERNAME s;
+ int r = spc_get_authUsername(&d->recvBuf, &s);
+ if(r == -1) {
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ else if(r == 1) {
+ d->waiting = true;
+ incomingAuth(s.user, s.pass);
+ }
+ }
+ else if(d->step == StepRequest) {
+ SPS_CONNREQ s;
+ int r = sp_get_request(&d->recvBuf, &s);
+ if(r == -1) {
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ else if(r == 1) {
+ d->waiting = true;
+ if(s.cmd == REQ_CONNECT) {
+ if(!s.host.isEmpty())
+ d->rhost = s.host;
+ else
+ d->rhost = s.addr.toString();
+ d->rport = s.port;
+ incomingConnectRequest(d->rhost, d->rport);
+ }
+ else if(s.cmd == REQ_UDPASSOCIATE) {
+ incomingUDPAssociateRequest();
+ }
+ else {
+ requestDeny();
+ return;
+ }
+ }
+ }
+}
+
+void SocksClient::chooseMethod(int method)
+{
+ if(d->step != StepVersion || !d->waiting)
+ return;
+
+ unsigned char c;
+ if(method == AuthNone) {
+ d->step = StepRequest;
+ c = 0x00;
+ }
+ else {
+ d->step = StepAuth;
+ c = 0x02;
+ }
+
+ // version response
+ d->waiting = false;
+ writeData(sps_set_version(c));
+ continueIncoming();
+}
+
+void SocksClient::authGrant(bool b)
+{
+ if(d->step != StepAuth || !d->waiting)
+ return;
+
+ if(b)
+ d->step = StepRequest;
+
+ // auth response
+ d->waiting = false;
+ writeData(sps_set_authUsername(b));
+ if(!b) {
+ reset(true);
+ return;
+ }
+ continueIncoming();
+}
+
+void SocksClient::requestDeny()
+{
+ if(d->step != StepRequest || !d->waiting)
+ return;
+
+ // response
+ d->waiting = false;
+ writeData(sp_set_request(d->rhost, d->rport, RET_UNREACHABLE));
+ reset(true);
+}
+
+void SocksClient::grantConnect()
+{
+ if(d->step != StepRequest || !d->waiting)
+ return;
+
+ // response
+ d->waiting = false;
+ writeData(sp_set_request(d->rhost, d->rport, RET_SUCCESS));
+ d->active = true;
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: server << Success >>\n");
+#endif
+
+ if(!d->recvBuf.isEmpty()) {
+ appendRead(d->recvBuf);
+ d->recvBuf.resize(0);
+ readyRead();
+ }
+}
+
+void SocksClient::grantUDPAssociate(const QString &relayHost, int relayPort)
+{
+ if(d->step != StepRequest || !d->waiting)
+ return;
+
+ // response
+ d->waiting = false;
+ writeData(sp_set_request(relayHost, relayPort, RET_SUCCESS));
+ d->udp = true;
+ d->active = true;
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: server << Success >>\n");
+#endif
+
+ if(!d->recvBuf.isEmpty())
+ d->recvBuf.resize(0);
+}
+
+QHostAddress SocksClient::peerAddress() const
+{
+ return d->sock.peerAddress();
+}
+
+Q_UINT16 SocksClient::peerPort() const
+{
+ return d->sock.peerPort();
+}
+
+QString SocksClient::udpAddress() const
+{
+ return d->udpAddr;
+}
+
+Q_UINT16 SocksClient::udpPort() const
+{
+ return d->udpPort;
+}
+
+SocksUDP *SocksClient::createUDP(const QString &host, int port, const QHostAddress &routeAddr, int routePort)
+{
+ return new SocksUDP(this, host, port, routeAddr, routePort);
+}
+
+//----------------------------------------------------------------------------
+// SocksServer
+//----------------------------------------------------------------------------
+class SocksServer::Private
+{
+public:
+ Private() {}
+
+ ServSock serv;
+ QPtrList<SocksClient> incomingConns;
+ QSocketDevice *sd;
+ QSocketNotifier *sn;
+};
+
+SocksServer::SocksServer(QObject *parent)
+:QObject(parent)
+{
+ d = new Private;
+ d->sd = 0;
+ d->sn = 0;
+ connect(&d->serv, SIGNAL(connectionReady(int)), SLOT(connectionReady(int)));
+}
+
+SocksServer::~SocksServer()
+{
+ stop();
+ d->incomingConns.setAutoDelete(true);
+ d->incomingConns.clear();
+ delete d;
+}
+
+bool SocksServer::isActive() const
+{
+ return d->serv.isActive();
+}
+
+bool SocksServer::listen(Q_UINT16 port, bool udp)
+{
+ stop();
+ if(!d->serv.listen(port))
+ return false;
+ if(udp) {
+ d->sd = new QSocketDevice(QSocketDevice::Datagram);
+ d->sd->setBlocking(false);
+ if(!d->sd->bind(QHostAddress(), port)) {
+ delete d->sd;
+ d->sd = 0;
+ d->serv.stop();
+ return false;
+ }
+ d->sn = new QSocketNotifier(d->sd->socket(), QSocketNotifier::Read);
+ connect(d->sn, SIGNAL(activated(int)), SLOT(sn_activated(int)));
+ }
+ return true;
+}
+
+void SocksServer::stop()
+{
+ delete d->sn;
+ d->sn = 0;
+ delete d->sd;
+ d->sd = 0;
+ d->serv.stop();
+}
+
+int SocksServer::port() const
+{
+ return d->serv.port();
+}
+
+QHostAddress SocksServer::address() const
+{
+ return d->serv.address();
+}
+
+SocksClient *SocksServer::takeIncoming()
+{
+ if(d->incomingConns.isEmpty())
+ return 0;
+
+ SocksClient *c = d->incomingConns.getFirst();
+ d->incomingConns.removeRef(c);
+
+ // we don't care about errors anymore
+ disconnect(c, SIGNAL(error(int)), this, SLOT(connectionError()));
+
+ // don't serve the connection until the event loop, to give the caller a chance to map signals
+ QTimer::singleShot(0, c, SLOT(serve()));
+
+ return c;
+}
+
+void SocksServer::writeUDP(const QHostAddress &addr, int port, const QByteArray &data)
+{
+ if(d->sd) {
+ d->sd->setBlocking(true);
+ d->sd->writeBlock(data.data(), data.size(), addr, port);
+ d->sd->setBlocking(false);
+ }
+}
+
+void SocksServer::connectionReady(int s)
+{
+ SocksClient *c = new SocksClient(s, this);
+ connect(c, SIGNAL(error(int)), this, SLOT(connectionError()));
+ d->incomingConns.append(c);
+ incomingReady();
+}
+
+void SocksServer::connectionError()
+{
+ SocksClient *c = (SocksClient *)sender();
+ d->incomingConns.removeRef(c);
+ c->deleteLater();
+}
+
+void SocksServer::sn_activated(int)
+{
+ QByteArray buf(8192);
+ int actual = d->sd->readBlock(buf.data(), buf.size());
+ buf.resize(actual);
+ QHostAddress pa = d->sd->peerAddress();
+ int pp = d->sd->peerPort();
+ SPS_UDP s;
+ int r = sp_read_udp(&buf, &s);
+ if(r != 1)
+ return;
+ incomingUDP(s.host, s.port, pa, pp, s.data);
+}
+
+// CS_NAMESPACE_END
+
+#include "socks.moc"
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/socks.h b/kopete/protocols/jabber/libiris/cutestuff/network/socks.h
new file mode 100644
index 00000000..8f1e4ddc
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/socks.h
@@ -0,0 +1,160 @@
+/*
+ * socks.h - SOCKS5 TCP proxy client/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
+ *
+ */
+
+#ifndef CS_SOCKS_H
+#define CS_SOCKS_H
+
+#include"bytestream.h"
+
+// CS_NAMESPACE_BEGIN
+
+class QHostAddress;
+class SocksClient;
+class SocksServer;
+
+class SocksUDP : public QObject
+{
+ Q_OBJECT
+public:
+ ~SocksUDP();
+
+ void change(const QString &host, int port);
+ void write(const QByteArray &data);
+
+signals:
+ void packetReady(const QByteArray &data);
+
+private slots:
+ void sn_activated(int);
+
+private:
+ class Private;
+ Private *d;
+
+ friend class SocksClient;
+ SocksUDP(SocksClient *sc, const QString &host, int port, const QHostAddress &routeAddr, int routePort);
+};
+
+class SocksClient : public ByteStream
+{
+ Q_OBJECT
+public:
+ enum Error { ErrConnectionRefused = ErrCustom, ErrHostNotFound, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth };
+ enum Method { AuthNone=0x0001, AuthUsername=0x0002 };
+ enum Request { ReqConnect, ReqUDPAssociate };
+ SocksClient(QObject *parent=0);
+ SocksClient(int, QObject *parent=0);
+ ~SocksClient();
+
+ bool isIncoming() const;
+
+ // outgoing
+ void setAuth(const QString &user, const QString &pass="");
+ void connectToHost(const QString &proxyHost, int proxyPort, const QString &host, int port, bool udpMode=false);
+
+ // incoming
+ void chooseMethod(int);
+ void authGrant(bool);
+ void requestDeny();
+ void grantConnect();
+ void grantUDPAssociate(const QString &relayHost, int relayPort);
+
+ // from ByteStream
+ bool isOpen() const;
+ void close();
+ void write(const QByteArray &);
+ QByteArray read(int bytes=0);
+ int bytesAvailable() const;
+ int bytesToWrite() const;
+
+ // remote address
+ QHostAddress peerAddress() const;
+ Q_UINT16 peerPort() const;
+
+ // udp
+ QString udpAddress() const;
+ Q_UINT16 udpPort() const;
+ SocksUDP *createUDP(const QString &host, int port, const QHostAddress &routeAddr, int routePort);
+
+signals:
+ // outgoing
+ void connected();
+
+ // incoming
+ void incomingMethods(int);
+ void incomingAuth(const QString &user, const QString &pass);
+ void incomingConnectRequest(const QString &host, int port);
+ void incomingUDPAssociateRequest();
+
+private slots:
+ void sock_connected();
+ void sock_connectionClosed();
+ void sock_delayedCloseFinished();
+ void sock_readyRead();
+ void sock_bytesWritten(int);
+ void sock_error(int);
+ void serve();
+
+private:
+ class Private;
+ Private *d;
+
+ void init();
+ void reset(bool clear=false);
+ void do_request();
+ void processOutgoing(const QByteArray &);
+ void processIncoming(const QByteArray &);
+ void continueIncoming();
+ void writeData(const QByteArray &a);
+};
+
+class SocksServer : public QObject
+{
+ Q_OBJECT
+public:
+ SocksServer(QObject *parent=0);
+ ~SocksServer();
+
+ bool isActive() const;
+ bool listen(Q_UINT16 port, bool udp=false);
+ void stop();
+ int port() const;
+ QHostAddress address() const;
+ SocksClient *takeIncoming();
+
+ void writeUDP(const QHostAddress &addr, int port, const QByteArray &data);
+
+signals:
+ void incomingReady();
+ void incomingUDP(const QString &host, int port, const QHostAddress &addr, int sourcePort, const QByteArray &data);
+
+private slots:
+ void connectionReady(int);
+ void connectionError();
+ void sn_activated(int);
+
+private:
+ class Private;
+ Private *d;
+};
+
+// CS_NAMESPACE_END
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.cpp
new file mode 100644
index 00000000..0c454c49
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.cpp
@@ -0,0 +1,320 @@
+/*
+ * srvresolver.cpp - class to simplify SRV lookups
+ * 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"srvresolver.h"
+
+#include<qcstring.h>
+#include<qtimer.h>
+#include<qdns.h>
+#include"safedelete.h"
+
+#ifndef NO_NDNS
+#include"ndns.h"
+#endif
+
+// CS_NAMESPACE_BEGIN
+
+static void sortSRVList(QValueList<QDns::Server> &list)
+{
+ QValueList<QDns::Server> tmp = list;
+ list.clear();
+
+ while(!tmp.isEmpty()) {
+ QValueList<QDns::Server>::Iterator p = tmp.end();
+ for(QValueList<QDns::Server>::Iterator it = tmp.begin(); it != tmp.end(); ++it) {
+ if(p == tmp.end())
+ p = it;
+ else {
+ int a = (*it).priority;
+ int b = (*p).priority;
+ int j = (*it).weight;
+ int k = (*p).weight;
+ if(a < b || (a == b && j < k))
+ p = it;
+ }
+ }
+ list.append(*p);
+ tmp.remove(p);
+ }
+}
+
+class SrvResolver::Private
+{
+public:
+ Private() {}
+
+ QDns *qdns;
+#ifndef NO_NDNS
+ NDns ndns;
+#endif
+
+ bool failed;
+ QHostAddress resultAddress;
+ Q_UINT16 resultPort;
+
+ bool srvonly;
+ QString srv;
+ QValueList<QDns::Server> servers;
+ bool aaaa;
+
+ QTimer t;
+ SafeDelete sd;
+};
+
+SrvResolver::SrvResolver(QObject *parent)
+:QObject(parent)
+{
+ d = new Private;
+ d->qdns = 0;
+
+#ifndef NO_NDNS
+ connect(&d->ndns, SIGNAL(resultsReady()), SLOT(ndns_done()));
+#endif
+ connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout()));
+ stop();
+}
+
+SrvResolver::~SrvResolver()
+{
+ stop();
+ delete d;
+}
+
+void SrvResolver::resolve(const QString &server, const QString &type, const QString &proto)
+{
+ stop();
+
+ d->failed = false;
+ d->srvonly = false;
+ d->srv = QString("_") + type + "._" + proto + '.' + server;
+ d->t.start(15000, true);
+ d->qdns = new QDns;
+ connect(d->qdns, SIGNAL(resultsReady()), SLOT(qdns_done()));
+ d->qdns->setRecordType(QDns::Srv);
+ d->qdns->setLabel(d->srv);
+}
+
+void SrvResolver::resolveSrvOnly(const QString &server, const QString &type, const QString &proto)
+{
+ stop();
+
+ d->failed = false;
+ d->srvonly = true;
+ d->srv = QString("_") + type + "._" + proto + '.' + server;
+ d->t.start(15000, true);
+ d->qdns = new QDns;
+ connect(d->qdns, SIGNAL(resultsReady()), SLOT(qdns_done()));
+ d->qdns->setRecordType(QDns::Srv);
+ d->qdns->setLabel(d->srv);
+}
+
+void SrvResolver::next()
+{
+ if(d->servers.isEmpty())
+ return;
+
+ tryNext();
+}
+
+void SrvResolver::stop()
+{
+ if(d->t.isActive())
+ d->t.stop();
+ if(d->qdns) {
+ d->qdns->disconnect(this);
+ d->sd.deleteLater(d->qdns);
+ d->qdns = 0;
+ }
+#ifndef NO_NDNS
+ if(d->ndns.isBusy())
+ d->ndns.stop();
+#endif
+ d->resultAddress = QHostAddress();
+ d->resultPort = 0;
+ d->servers.clear();
+ d->srv = "";
+ d->failed = true;
+}
+
+bool SrvResolver::isBusy() const
+{
+#ifndef NO_NDNS
+ if(d->qdns || d->ndns.isBusy())
+#else
+ if(d->qdns)
+#endif
+ return true;
+ else
+ return false;
+}
+
+QValueList<QDns::Server> SrvResolver::servers() const
+{
+ return d->servers;
+}
+
+bool SrvResolver::failed() const
+{
+ return d->failed;
+}
+
+QHostAddress SrvResolver::resultAddress() const
+{
+ return d->resultAddress;
+}
+
+Q_UINT16 SrvResolver::resultPort() const
+{
+ return d->resultPort;
+}
+
+void SrvResolver::tryNext()
+{
+#ifndef NO_NDNS
+ d->ndns.resolve(d->servers.first().name);
+#else
+ d->qdns = new QDns;
+ connect(d->qdns, SIGNAL(resultsReady()), SLOT(ndns_done()));
+ if(d->aaaa)
+ d->qdns->setRecordType(QDns::Aaaa); // IPv6
+ else
+ d->qdns->setRecordType(QDns::A); // IPv4
+ d->qdns->setLabel(d->servers.first().name);
+#endif
+}
+
+void SrvResolver::qdns_done()
+{
+ if(!d->qdns)
+ return;
+
+ // apparently we sometimes get this signal even though the results aren't ready
+ if(d->qdns->isWorking())
+ return;
+ d->t.stop();
+
+ SafeDeleteLock s(&d->sd);
+
+ // grab the server list and destroy the qdns object
+ QValueList<QDns::Server> list;
+ if(d->qdns->recordType() == QDns::Srv)
+ list = d->qdns->servers();
+ d->qdns->disconnect(this);
+ d->sd.deleteLater(d->qdns);
+ d->qdns = 0;
+
+ if(list.isEmpty()) {
+ stop();
+ resultsReady();
+ return;
+ }
+ sortSRVList(list);
+ d->servers = list;
+
+ if(d->srvonly)
+ resultsReady();
+ else {
+ // kick it off
+ d->aaaa = true;
+ tryNext();
+ }
+}
+
+void SrvResolver::ndns_done()
+{
+#ifndef NO_NDNS
+ SafeDeleteLock s(&d->sd);
+
+ uint r = d->ndns.result();
+ int port = d->servers.first().port;
+ d->servers.remove(d->servers.begin());
+
+ if(r) {
+ d->resultAddress = QHostAddress(d->ndns.result());
+ d->resultPort = port;
+ resultsReady();
+ }
+ else {
+ // failed? bail if last one
+ if(d->servers.isEmpty()) {
+ stop();
+ resultsReady();
+ return;
+ }
+
+ // otherwise try the next
+ tryNext();
+ }
+#else
+ 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
+ QValueList<QHostAddress> list;
+ if(d->qdns->recordType() == QDns::A || d->qdns->recordType() == QDns::Aaaa)
+ list = d->qdns->addresses();
+ d->qdns->disconnect(this);
+ d->sd.deleteLater(d->qdns);
+ d->qdns = 0;
+
+ if(!list.isEmpty()) {
+ int port = d->servers.first().port;
+ d->servers.remove(d->servers.begin());
+ d->aaaa = true;
+
+ d->resultAddress = list.first();
+ d->resultPort = port;
+ resultsReady();
+ }
+ else {
+ if(!d->aaaa)
+ d->servers.remove(d->servers.begin());
+ d->aaaa = !d->aaaa;
+
+ // failed? bail if last one
+ if(d->servers.isEmpty()) {
+ stop();
+ resultsReady();
+ return;
+ }
+
+ // otherwise try the next
+ tryNext();
+ }
+#endif
+}
+
+void SrvResolver::t_timeout()
+{
+ SafeDeleteLock s(&d->sd);
+
+ stop();
+ resultsReady();
+}
+
+// CS_NAMESPACE_END
+
+#include "srvresolver.moc"
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.h b/kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.h
new file mode 100644
index 00000000..6c9ac4f3
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.h
@@ -0,0 +1,65 @@
+/*
+ * srvresolver.h - class to simplify SRV lookups
+ * 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 CS_SRVRESOLVER_H
+#define CS_SRVRESOLVER_H
+
+#include<qvaluelist.h>
+#include<qdns.h>
+
+// CS_NAMESPACE_BEGIN
+
+class SrvResolver : public QObject
+{
+ Q_OBJECT
+public:
+ SrvResolver(QObject *parent=0);
+ ~SrvResolver();
+
+ void resolve(const QString &server, const QString &type, const QString &proto);
+ void resolveSrvOnly(const QString &server, const QString &type, const QString &proto);
+ void next();
+ void stop();
+ bool isBusy() const;
+
+ QValueList<QDns::Server> servers() const;
+
+ bool failed() const;
+ QHostAddress resultAddress() const;
+ Q_UINT16 resultPort() const;
+
+signals:
+ void resultsReady();
+
+private slots:
+ void qdns_done();
+ void ndns_done();
+ void t_timeout();
+
+private:
+ class Private;
+ Private *d;
+
+ void tryNext();
+};
+
+// CS_NAMESPACE_END
+
+#endif