summaryrefslogtreecommitdiffstats
path: root/kopete/protocols/jabber/libiris/iris/xmpp-core
diff options
context:
space:
mode:
Diffstat (limited to 'kopete/protocols/jabber/libiris/iris/xmpp-core')
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/Makefile.am30
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/connector.cpp719
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/hash.cpp670
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/hash.h31
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/jid.cpp409
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/parser.cpp798
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/parser.h86
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/protocol.cpp1595
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/protocol.h355
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/qcaprovider.h191
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/securestream.cpp589
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/securestream.h84
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/simplesasl.cpp459
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/simplesasl.h31
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/stream.cpp1762
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/td.h20
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/tlshandler.cpp138
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/xmlprotocol.cpp543
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/xmlprotocol.h145
19 files changed, 8655 insertions, 0 deletions
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/Makefile.am b/kopete/protocols/jabber/libiris/iris/xmpp-core/Makefile.am
new file mode 100644
index 00000000..f35b1c68
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/Makefile.am
@@ -0,0 +1,30 @@
+# The only Q_OBJECT lines are in securestream.{h,cpp} and we deal with them below.
+# Give metasources a file with no Q_OBJECT line to stop unsermake assuming we want METASOURCES = AUTO
+METASOURCES = ignore_this_warning.moc
+
+noinst_LTLIBRARIES = libiris_xmpp_core.la
+AM_CPPFLAGS = $(IDN_CFLAGS)
+INCLUDES = -I$(srcdir)/../include -I$(srcdir)/../xmpp-core -I$(srcdir)/../xmpp-im -I$(srcdir)/../../cutestuff/util -I$(srcdir)/../../cutestuff/network -I$(srcdir)/../../qca/src $(all_includes)
+
+libiris_xmpp_core_la_CPPFLAGS = $(IDN_CFLAGS)
+libiris_xmpp_core_la_LDFLAGS = $(IDN_LIBS)
+libiris_xmpp_core_la_SOURCES = \
+ connector.cpp \
+ jid.cpp \
+ securestream.cpp \
+ tlshandler.cpp \
+ hash.cpp \
+ protocol.cpp \
+ stream.cpp \
+ xmlprotocol.cpp \
+ parser.cpp \
+ simplesasl.cpp
+
+libiris_xmpp_core_la_COMPILE_FIRST = securestream.moc
+
+CLEANFILES = securestream.moc
+securestream.moc: $(srcdir)/securestream.cpp $(srcdir)/securestream.h
+ ${MOC} $(srcdir)/securestream.h > $@
+ ${MOC} $(srcdir)/securestream.cpp >> $@
+
+KDE_OPTIONS = nofinal
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/connector.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-core/connector.cpp
new file mode 100644
index 00000000..8ebc3ee8
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/connector.cpp
@@ -0,0 +1,719 @@
+/*
+ * connector.cpp - establish a connection to an XMPP server
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ TODO:
+
+ - Test and analyze all possible branches
+
+ XMPP::AdvancedConnector is "good for now." The only real issue is that
+ most of what it provides is just to work around the old Jabber/XMPP 0.9
+ connection behavior. When XMPP 1.0 has taken over the world, we can
+ greatly simplify this class. - Sep 3rd, 2003.
+*/
+
+#include"xmpp.h"
+
+#include<qguardedptr.h>
+#include<qca.h>
+#include"safedelete.h"
+
+#ifdef NO_NDNS
+#include<qdns.h>
+#else
+#include"ndns.h"
+#endif
+
+#include"srvresolver.h"
+#include"bsocket.h"
+#include"httpconnect.h"
+#include"httppoll.h"
+#include"socks.h"
+#include"hash.h"
+
+//#define XMPP_DEBUG
+
+using namespace XMPP;
+
+//----------------------------------------------------------------------------
+// Connector
+//----------------------------------------------------------------------------
+Connector::Connector(QObject *parent)
+:QObject(parent)
+{
+ setUseSSL(false);
+ setPeerAddressNone();
+}
+
+Connector::~Connector()
+{
+}
+
+bool Connector::useSSL() const
+{
+ return ssl;
+}
+
+bool Connector::havePeerAddress() const
+{
+ return haveaddr;
+}
+
+QHostAddress Connector::peerAddress() const
+{
+ return addr;
+}
+
+Q_UINT16 Connector::peerPort() const
+{
+ return port;
+}
+
+void Connector::setUseSSL(bool b)
+{
+ ssl = b;
+}
+
+void Connector::setPeerAddressNone()
+{
+ haveaddr = false;
+ addr = QHostAddress();
+ port = 0;
+}
+
+void Connector::setPeerAddress(const QHostAddress &_addr, Q_UINT16 _port)
+{
+ haveaddr = true;
+ addr = _addr;
+ port = _port;
+}
+
+
+//----------------------------------------------------------------------------
+// AdvancedConnector::Proxy
+//----------------------------------------------------------------------------
+AdvancedConnector::Proxy::Proxy()
+{
+ t = None;
+ v_poll = 30;
+}
+
+AdvancedConnector::Proxy::~Proxy()
+{
+}
+
+int AdvancedConnector::Proxy::type() const
+{
+ return t;
+}
+
+QString AdvancedConnector::Proxy::host() const
+{
+ return v_host;
+}
+
+Q_UINT16 AdvancedConnector::Proxy::port() const
+{
+ return v_port;
+}
+
+QString AdvancedConnector::Proxy::url() const
+{
+ return v_url;
+}
+
+QString AdvancedConnector::Proxy::user() const
+{
+ return v_user;
+}
+
+QString AdvancedConnector::Proxy::pass() const
+{
+ return v_pass;
+}
+
+int AdvancedConnector::Proxy::pollInterval() const
+{
+ return v_poll;
+}
+
+void AdvancedConnector::Proxy::setHttpConnect(const QString &host, Q_UINT16 port)
+{
+ t = HttpConnect;
+ v_host = host;
+ v_port = port;
+}
+
+void AdvancedConnector::Proxy::setHttpPoll(const QString &host, Q_UINT16 port, const QString &url)
+{
+ t = HttpPoll;
+ v_host = host;
+ v_port = port;
+ v_url = url;
+}
+
+void AdvancedConnector::Proxy::setSocks(const QString &host, Q_UINT16 port)
+{
+ t = Socks;
+ v_host = host;
+ v_port = port;
+}
+
+void AdvancedConnector::Proxy::setUserPass(const QString &user, const QString &pass)
+{
+ v_user = user;
+ v_pass = pass;
+}
+
+void AdvancedConnector::Proxy::setPollInterval(int secs)
+{
+ v_poll = secs;
+}
+
+
+//----------------------------------------------------------------------------
+// AdvancedConnector
+//----------------------------------------------------------------------------
+enum { Idle, Connecting, Connected };
+class AdvancedConnector::Private
+{
+public:
+ int mode;
+ ByteStream *bs;
+#ifdef NO_NDNS
+ QDns *qdns;
+#else
+ NDns dns;
+#endif
+ SrvResolver srv;
+
+ QString server;
+ QString opt_host;
+ int opt_port;
+ bool opt_probe, opt_ssl;
+ Proxy proxy;
+
+ QString host;
+ int port;
+ QValueList<QDns::Server> servers;
+ int errorCode;
+
+ bool multi, using_srv;
+ bool will_be_ssl;
+ int probe_mode;
+
+ bool aaaa;
+ SafeDelete sd;
+};
+
+AdvancedConnector::AdvancedConnector(QObject *parent)
+:Connector(parent)
+{
+ d = new Private;
+ d->bs = 0;
+#ifdef NO_NDNS
+ d->qdns = 0;
+#else
+ connect(&d->dns, SIGNAL(resultsReady()), SLOT(dns_done()));
+#endif
+ connect(&d->srv, SIGNAL(resultsReady()), SLOT(srv_done()));
+ d->opt_probe = false;
+ d->opt_ssl = false;
+ cleanup();
+ d->errorCode = 0;
+}
+
+AdvancedConnector::~AdvancedConnector()
+{
+ cleanup();
+ delete d;
+}
+
+void AdvancedConnector::cleanup()
+{
+ d->mode = Idle;
+
+ // stop any dns
+#ifdef NO_NDNS
+ if(d->qdns) {
+ d->qdns->disconnect(this);
+ d->qdns->deleteLater();
+ //d->sd.deleteLater(d->qdns);
+ d->qdns = 0;
+ }
+#else
+ if(d->dns.isBusy())
+ d->dns.stop();
+#endif
+ if(d->srv.isBusy())
+ d->srv.stop();
+
+ // destroy the bytestream, if there is one
+ delete d->bs;
+ d->bs = 0;
+
+ d->multi = false;
+ d->using_srv = false;
+ d->will_be_ssl = false;
+ d->probe_mode = -1;
+
+ setUseSSL(false);
+ setPeerAddressNone();
+}
+
+void AdvancedConnector::setProxy(const Proxy &proxy)
+{
+ if(d->mode != Idle)
+ return;
+ d->proxy = proxy;
+}
+
+void AdvancedConnector::setOptHostPort(const QString &host, Q_UINT16 _port)
+{
+ if(d->mode != Idle)
+ return;
+ d->opt_host = host;
+ d->opt_port = _port;
+}
+
+void AdvancedConnector::setOptProbe(bool b)
+{
+ if(d->mode != Idle)
+ return;
+ d->opt_probe = b;
+}
+
+void AdvancedConnector::setOptSSL(bool b)
+{
+ if(d->mode != Idle)
+ return;
+ d->opt_ssl = b;
+}
+
+void AdvancedConnector::connectToServer(const QString &server)
+{
+ if(d->mode != Idle)
+ return;
+ if(server.isEmpty())
+ return;
+
+ d->errorCode = 0;
+ d->server = server;
+ d->mode = Connecting;
+ d->aaaa = true;
+
+ if(d->proxy.type() == Proxy::HttpPoll) {
+ // need SHA1 here
+ if(!QCA::isSupported(QCA::CAP_SHA1))
+ QCA::insertProvider(createProviderHash());
+
+ HttpPoll *s = new HttpPoll;
+ d->bs = s;
+ connect(s, SIGNAL(connected()), SLOT(bs_connected()));
+ connect(s, SIGNAL(syncStarted()), SLOT(http_syncStarted()));
+ connect(s, SIGNAL(syncFinished()), SLOT(http_syncFinished()));
+ connect(s, SIGNAL(error(int)), SLOT(bs_error(int)));
+ if(!d->proxy.user().isEmpty())
+ s->setAuth(d->proxy.user(), d->proxy.pass());
+ s->setPollInterval(d->proxy.pollInterval());
+
+ if(d->proxy.host().isEmpty())
+ s->connectToUrl(d->proxy.url());
+ else
+ s->connectToHost(d->proxy.host(), d->proxy.port(), d->proxy.url());
+ }
+ else {
+ if(!d->opt_host.isEmpty()) {
+ d->host = d->opt_host;
+ d->port = d->opt_port;
+ do_resolve();
+ }
+ else {
+ d->multi = true;
+
+ QGuardedPtr<QObject> self = this;
+ srvLookup(d->server);
+ if(!self)
+ return;
+
+ d->srv.resolveSrvOnly(d->server, "xmpp-client", "tcp");
+ }
+ }
+}
+
+void AdvancedConnector::changePollInterval(int secs)
+{
+ if(d->bs && (d->bs->inherits("XMPP::HttpPoll") || d->bs->inherits("HttpPoll"))) {
+ HttpPoll *s = static_cast<HttpPoll*>(d->bs);
+ s->setPollInterval(secs);
+ }
+}
+
+ByteStream *AdvancedConnector::stream() const
+{
+ if(d->mode == Connected)
+ return d->bs;
+ else
+ return 0;
+}
+
+void AdvancedConnector::done()
+{
+ cleanup();
+}
+
+int AdvancedConnector::errorCode() const
+{
+ return d->errorCode;
+}
+
+void AdvancedConnector::do_resolve()
+{
+#ifdef NO_NDNS
+ printf("resolving (aaaa=%d)\n", d->aaaa);
+ d->qdns = new QDns;
+ connect(d->qdns, SIGNAL(resultsReady()), SLOT(dns_done()));
+ if(d->aaaa)
+ d->qdns->setRecordType(QDns::Aaaa); // IPv6
+ else
+ d->qdns->setRecordType(QDns::A); // IPv4
+ d->qdns->setLabel(d->host);
+#else
+ d->dns.resolve(d->host);
+#endif
+}
+
+void AdvancedConnector::dns_done()
+{
+ bool failed = false;
+ QHostAddress addr;
+
+#ifdef NO_NDNS
+ //if(!d->qdns)
+ // return;
+
+ // apparently we sometimes get this signal even though the results aren' t ready
+ //if(d->qdns->isWorking())
+ // return;
+
+ //SafeDeleteLock s(&d->sd);
+
+ // grab the address list and destroy the qdns object
+ QValueList<QHostAddress> list = d->qdns->addresses();
+ d->qdns->disconnect(this);
+ d->qdns->deleteLater();
+ //d->sd.deleteLater(d->qdns);
+ d->qdns = 0;
+
+ if(list.isEmpty()) {
+ if(d->aaaa) {
+ d->aaaa = false;
+ do_resolve();
+ return;
+ }
+ //do_resolve();
+ //return;
+ failed = true;
+ }
+ else
+ addr = list.first();
+#else
+ if(d->dns.result() == 0)
+ failed = true;
+ else
+ addr = QHostAddress(d->dns.result());
+#endif
+
+ if(failed) {
+#ifdef XMPP_DEBUG
+ printf("dns1\n");
+#endif
+ // using proxy? then try the unresolved host through the proxy
+ if(d->proxy.type() != Proxy::None) {
+#ifdef XMPP_DEBUG
+ printf("dns1.1\n");
+#endif
+ do_connect();
+ }
+ else if(d->using_srv) {
+#ifdef XMPP_DEBUG
+ printf("dns1.2\n");
+#endif
+ if(d->servers.isEmpty()) {
+#ifdef XMPP_DEBUG
+ printf("dns1.2.1\n");
+#endif
+ cleanup();
+ d->errorCode = ErrConnectionRefused;
+ error();
+ }
+ else {
+#ifdef XMPP_DEBUG
+ printf("dns1.2.2\n");
+#endif
+ tryNextSrv();
+ return;
+ }
+ }
+ else {
+#ifdef XMPP_DEBUG
+ printf("dns1.3\n");
+#endif
+ cleanup();
+ d->errorCode = ErrHostNotFound;
+ error();
+ }
+ }
+ else {
+#ifdef XMPP_DEBUG
+ printf("dns2\n");
+#endif
+ d->host = addr.toString();
+ do_connect();
+ }
+}
+
+void AdvancedConnector::do_connect()
+{
+#ifdef XMPP_DEBUG
+ printf("trying %s:%d\n", d->host.latin1(), d->port);
+#endif
+ int t = d->proxy.type();
+ if(t == Proxy::None) {
+#ifdef XMPP_DEBUG
+ printf("do_connect1\n");
+#endif
+ BSocket *s = new BSocket;
+ d->bs = s;
+ connect(s, SIGNAL(connected()), SLOT(bs_connected()));
+ connect(s, SIGNAL(error(int)), SLOT(bs_error(int)));
+ s->connectToHost(d->host, d->port);
+ }
+ else if(t == Proxy::HttpConnect) {
+#ifdef XMPP_DEBUG
+ printf("do_connect2\n");
+#endif
+ HttpConnect *s = new HttpConnect;
+ d->bs = s;
+ connect(s, SIGNAL(connected()), SLOT(bs_connected()));
+ connect(s, SIGNAL(error(int)), SLOT(bs_error(int)));
+ if(!d->proxy.user().isEmpty())
+ s->setAuth(d->proxy.user(), d->proxy.pass());
+ s->connectToHost(d->proxy.host(), d->proxy.port(), d->host, d->port);
+ }
+ else if(t == Proxy::Socks) {
+#ifdef XMPP_DEBUG
+ printf("do_connect3\n");
+#endif
+ SocksClient *s = new SocksClient;
+ d->bs = s;
+ connect(s, SIGNAL(connected()), SLOT(bs_connected()));
+ connect(s, SIGNAL(error(int)), SLOT(bs_error(int)));
+ if(!d->proxy.user().isEmpty())
+ s->setAuth(d->proxy.user(), d->proxy.pass());
+ s->connectToHost(d->proxy.host(), d->proxy.port(), d->host, d->port);
+ }
+}
+
+void AdvancedConnector::tryNextSrv()
+{
+#ifdef XMPP_DEBUG
+ printf("trying next srv\n");
+#endif
+ d->host = d->servers.first().name;
+ d->port = d->servers.first().port;
+ d->servers.remove(d->servers.begin());
+ do_resolve();
+}
+
+void AdvancedConnector::srv_done()
+{
+ QGuardedPtr<QObject> self = this;
+#ifdef XMPP_DEBUG
+ printf("srv_done1\n");
+#endif
+ d->servers = d->srv.servers();
+ if(d->servers.isEmpty()) {
+ srvResult(false);
+ if(!self)
+ return;
+
+#ifdef XMPP_DEBUG
+ printf("srv_done1.1\n");
+#endif
+ // fall back to A record
+ d->using_srv = false;
+ d->host = d->server;
+ if(d->opt_probe) {
+#ifdef XMPP_DEBUG
+ printf("srv_done1.1.1\n");
+#endif
+ d->probe_mode = 0;
+ d->port = 5223;
+ d->will_be_ssl = true;
+ }
+ else {
+#ifdef XMPP_DEBUG
+ printf("srv_done1.1.2\n");
+#endif
+ d->probe_mode = 1;
+ d->port = 5222;
+ }
+ do_resolve();
+ return;
+ }
+
+ srvResult(true);
+ if(!self)
+ return;
+
+ d->using_srv = true;
+ tryNextSrv();
+}
+
+void AdvancedConnector::bs_connected()
+{
+ if(d->proxy.type() == Proxy::None) {
+ QHostAddress h = (static_cast<BSocket*>(d->bs))->peerAddress();
+ int p = (static_cast<BSocket*>(d->bs))->peerPort();
+ setPeerAddress(h, p);
+ }
+
+ // only allow ssl override if proxy==poll or host:port
+ if((d->proxy.type() == Proxy::HttpPoll || !d->opt_host.isEmpty()) && d->opt_ssl)
+ setUseSSL(true);
+ else if(d->will_be_ssl)
+ setUseSSL(true);
+
+ d->mode = Connected;
+ connected();
+}
+
+void AdvancedConnector::bs_error(int x)
+{
+ if(d->mode == Connected) {
+ d->errorCode = ErrStream;
+ error();
+ return;
+ }
+
+ bool proxyError = false;
+ int err = ErrConnectionRefused;
+ int t = d->proxy.type();
+
+#ifdef XMPP_DEBUG
+ printf("bse1\n");
+#endif
+
+ // figure out the error
+ if(t == Proxy::None) {
+ if(x == BSocket::ErrHostNotFound)
+ err = ErrHostNotFound;
+ else
+ err = ErrConnectionRefused;
+ }
+ else if(t == Proxy::HttpConnect) {
+ if(x == HttpConnect::ErrConnectionRefused)
+ err = ErrConnectionRefused;
+ else if(x == HttpConnect::ErrHostNotFound)
+ err = ErrHostNotFound;
+ else {
+ proxyError = true;
+ if(x == HttpConnect::ErrProxyAuth)
+ err = ErrProxyAuth;
+ else if(x == HttpConnect::ErrProxyNeg)
+ err = ErrProxyNeg;
+ else
+ err = ErrProxyConnect;
+ }
+ }
+ else if(t == Proxy::HttpPoll) {
+ if(x == HttpPoll::ErrConnectionRefused)
+ err = ErrConnectionRefused;
+ else if(x == HttpPoll::ErrHostNotFound)
+ err = ErrHostNotFound;
+ else {
+ proxyError = true;
+ if(x == HttpPoll::ErrProxyAuth)
+ err = ErrProxyAuth;
+ else if(x == HttpPoll::ErrProxyNeg)
+ err = ErrProxyNeg;
+ else
+ err = ErrProxyConnect;
+ }
+ }
+ else if(t == Proxy::Socks) {
+ if(x == SocksClient::ErrConnectionRefused)
+ err = ErrConnectionRefused;
+ else if(x == SocksClient::ErrHostNotFound)
+ err = ErrHostNotFound;
+ else {
+ proxyError = true;
+ if(x == SocksClient::ErrProxyAuth)
+ err = ErrProxyAuth;
+ else if(x == SocksClient::ErrProxyNeg)
+ err = ErrProxyNeg;
+ else
+ err = ErrProxyConnect;
+ }
+ }
+
+ // no-multi or proxy error means we quit
+ if(!d->multi || proxyError) {
+ cleanup();
+ d->errorCode = err;
+ error();
+ return;
+ }
+
+ if(d->using_srv && !d->servers.isEmpty()) {
+#ifdef XMPP_DEBUG
+ printf("bse1.1\n");
+#endif
+ tryNextSrv();
+ }
+ else if(!d->using_srv && d->opt_probe && d->probe_mode == 0) {
+#ifdef XMPP_DEBUG
+ printf("bse1.2\n");
+#endif
+ d->probe_mode = 1;
+ d->port = 5222;
+ d->will_be_ssl = false;
+ do_connect();
+ }
+ else {
+#ifdef XMPP_DEBUG
+ printf("bse1.3\n");
+#endif
+ cleanup();
+ d->errorCode = ErrConnectionRefused;
+ error();
+ }
+}
+
+void AdvancedConnector::http_syncStarted()
+{
+ httpSyncStarted();
+}
+
+void AdvancedConnector::http_syncFinished()
+{
+ httpSyncFinished();
+}
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/hash.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-core/hash.cpp
new file mode 100644
index 00000000..4d7f9e41
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/hash.cpp
@@ -0,0 +1,670 @@
+/*
+ * hash.cpp - hashing functions for SHA1 and MD5
+ * 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"hash.h"
+
+namespace XMPP
+{
+
+static bool bigEndian;
+static bool haveEndian = false;
+
+static void ensureEndian()
+{
+ if(!haveEndian) {
+ haveEndian = true;
+ int wordSize;
+ qSysInfo(&wordSize, &bigEndian);
+ }
+}
+
+//----------------------------------------------------------------------------
+// MD5
+//----------------------------------------------------------------------------
+
+/* NOTE: the following code was modified to not need BYTE_ORDER -- Justin */
+
+/*
+ Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/* $Id$ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.c is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+ either statically or dynamically; added missing #include <string.h>
+ in library.
+ 2002-03-11 lpd Corrected argument list for main(), and added int return
+ type, in test program and T value program.
+ 2002-02-21 lpd Added missing #include <stdio.h> in test program.
+ 2000-07-03 lpd Patched to eliminate warnings about "constant is
+ unsigned in ANSI C, signed in traditional"; made test program
+ self-checking.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+ 1999-05-03 lpd Original version.
+ */
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+typedef Q_UINT8 md5_byte_t; /* 8-bit byte */
+typedef Q_UINT32 md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+ md5_word_t count[2]; /* message length in bits, lsw first */
+ md5_word_t abcd[4]; /* digest buffer */
+ md5_byte_t buf[64]; /* accumulate block */
+} md5_state_t;
+
+/* Initialize the algorithm. */
+void md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+
+/* Finish the message and return the digest. */
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3 0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6 0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9 0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13 0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16 0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19 0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22 0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25 0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28 0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31 0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35 0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38 0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41 0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44 0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47 0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50 0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53 0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57 0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60 0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63 0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+ md5_word_t
+ a = pms->abcd[0], b = pms->abcd[1],
+ c = pms->abcd[2], d = pms->abcd[3];
+ md5_word_t t;
+
+ /* Define storage for little-endian or both types of CPUs. */
+ md5_word_t xbuf[16];
+ const md5_word_t *X;
+
+ {
+ if(bigEndian)
+ {
+ /*
+ * On big-endian machines, we must arrange the bytes in the
+ * right order.
+ */
+ const md5_byte_t *xp = data;
+ int i;
+
+ X = xbuf; /* (dynamic only) */
+
+ for (i = 0; i < 16; ++i, xp += 4)
+ xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+ }
+ else /* dynamic big-endian */
+ {
+ /*
+ * On little-endian machines, we can process properly aligned
+ * data without copying it.
+ */
+ if (!((data - (const md5_byte_t *)0) & 3)) {
+ /* data are properly aligned */
+ X = (const md5_word_t *)data;
+ } else {
+ /* not aligned */
+ memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+ }
+ }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+ /* Round 1. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + F(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 7, T1);
+ SET(d, a, b, c, 1, 12, T2);
+ SET(c, d, a, b, 2, 17, T3);
+ SET(b, c, d, a, 3, 22, T4);
+ SET(a, b, c, d, 4, 7, T5);
+ SET(d, a, b, c, 5, 12, T6);
+ SET(c, d, a, b, 6, 17, T7);
+ SET(b, c, d, a, 7, 22, T8);
+ SET(a, b, c, d, 8, 7, T9);
+ SET(d, a, b, c, 9, 12, T10);
+ SET(c, d, a, b, 10, 17, T11);
+ SET(b, c, d, a, 11, 22, T12);
+ SET(a, b, c, d, 12, 7, T13);
+ SET(d, a, b, c, 13, 12, T14);
+ SET(c, d, a, b, 14, 17, T15);
+ SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+ /* Round 2. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + G(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 1, 5, T17);
+ SET(d, a, b, c, 6, 9, T18);
+ SET(c, d, a, b, 11, 14, T19);
+ SET(b, c, d, a, 0, 20, T20);
+ SET(a, b, c, d, 5, 5, T21);
+ SET(d, a, b, c, 10, 9, T22);
+ SET(c, d, a, b, 15, 14, T23);
+ SET(b, c, d, a, 4, 20, T24);
+ SET(a, b, c, d, 9, 5, T25);
+ SET(d, a, b, c, 14, 9, T26);
+ SET(c, d, a, b, 3, 14, T27);
+ SET(b, c, d, a, 8, 20, T28);
+ SET(a, b, c, d, 13, 5, T29);
+ SET(d, a, b, c, 2, 9, T30);
+ SET(c, d, a, b, 7, 14, T31);
+ SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+ /* Round 3. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + H(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 5, 4, T33);
+ SET(d, a, b, c, 8, 11, T34);
+ SET(c, d, a, b, 11, 16, T35);
+ SET(b, c, d, a, 14, 23, T36);
+ SET(a, b, c, d, 1, 4, T37);
+ SET(d, a, b, c, 4, 11, T38);
+ SET(c, d, a, b, 7, 16, T39);
+ SET(b, c, d, a, 10, 23, T40);
+ SET(a, b, c, d, 13, 4, T41);
+ SET(d, a, b, c, 0, 11, T42);
+ SET(c, d, a, b, 3, 16, T43);
+ SET(b, c, d, a, 6, 23, T44);
+ SET(a, b, c, d, 9, 4, T45);
+ SET(d, a, b, c, 12, 11, T46);
+ SET(c, d, a, b, 15, 16, T47);
+ SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+ /* Round 4. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + I(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 6, T49);
+ SET(d, a, b, c, 7, 10, T50);
+ SET(c, d, a, b, 14, 15, T51);
+ SET(b, c, d, a, 5, 21, T52);
+ SET(a, b, c, d, 12, 6, T53);
+ SET(d, a, b, c, 3, 10, T54);
+ SET(c, d, a, b, 10, 15, T55);
+ SET(b, c, d, a, 1, 21, T56);
+ SET(a, b, c, d, 8, 6, T57);
+ SET(d, a, b, c, 15, 10, T58);
+ SET(c, d, a, b, 6, 15, T59);
+ SET(b, c, d, a, 13, 21, T60);
+ SET(a, b, c, d, 4, 6, T61);
+ SET(d, a, b, c, 11, 10, T62);
+ SET(c, d, a, b, 2, 15, T63);
+ SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+ pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+ pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+ const md5_byte_t *p = data;
+ int left = nbytes;
+ int offset = (pms->count[0] >> 3) & 63;
+ md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += nbytes >> 29;
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+ memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+ static const md5_byte_t pad[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ md5_byte_t data[8];
+ int i;
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ md5_append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
+
+
+//----------------------------------------------------------------------------
+// SHA1 - from a public domain implementation by Steve Reid (steve@edmweb.com)
+//----------------------------------------------------------------------------
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15]^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+struct SHA1_CONTEXT
+{
+ Q_UINT32 state[5];
+ Q_UINT32 count[2];
+ unsigned char buffer[64];
+};
+
+typedef union {
+ unsigned char c[64];
+ Q_UINT32 l[16];
+} CHAR64LONG16;
+
+class SHA1Context : public QCA_HashContext
+{
+public:
+ SHA1_CONTEXT _context;
+ CHAR64LONG16* block;
+
+ SHA1Context()
+ {
+ reset();
+ }
+
+ QCA_HashContext *clone()
+ {
+ return new SHA1Context(*this);
+ }
+
+ void reset()
+ {
+ sha1_init(&_context);
+ }
+
+ void update(const char *in, unsigned int len)
+ {
+ sha1_update(&_context, (unsigned char *)in, (unsigned int)len);
+ }
+
+ void final(QByteArray *out)
+ {
+ QByteArray b(20);
+ sha1_final((unsigned char *)b.data(), &_context);
+ *out = b;
+ }
+
+ unsigned long blk0(Q_UINT32 i)
+ {
+ if(bigEndian)
+ return block->l[i];
+ else
+ return (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) | (rol(block->l[i],8)&0x00FF00FF));
+ }
+
+ // Hash a single 512-bit block. This is the core of the algorithm.
+ void transform(Q_UINT32 state[5], unsigned char buffer[64])
+ {
+ Q_UINT32 a, b, c, d, e;
+
+ block = (CHAR64LONG16*)buffer;
+
+ // Copy context->state[] to working vars
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+
+ // 4 rounds of 20 operations each. Loop unrolled.
+ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+ R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+ R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+ R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+ R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+ R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+ R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+ R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+ R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+ R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+ R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+ R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+ R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+
+ // Add the working vars back into context.state[]
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+
+ // Wipe variables
+ a = b = c = d = e = 0;
+ }
+
+ // SHA1Init - Initialize new context
+ void sha1_init(SHA1_CONTEXT* context)
+ {
+ // SHA1 initialization constants
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+ }
+
+ // Run your data through this
+ void sha1_update(SHA1_CONTEXT* context, unsigned char* data, Q_UINT32 len)
+ {
+ Q_UINT32 i, j;
+
+ j = (context->count[0] >> 3) & 63;
+ if((context->count[0] += len << 3) < (len << 3))
+ context->count[1]++;
+
+ context->count[1] += (len >> 29);
+
+ if((j + len) > 63) {
+ memcpy(&context->buffer[j], data, (i = 64-j));
+ transform(context->state, context->buffer);
+ for ( ; i + 63 < len; i += 64) {
+ transform(context->state, &data[i]);
+ }
+ j = 0;
+ }
+ else i = 0;
+ memcpy(&context->buffer[j], &data[i], len - i);
+ }
+
+ // Add padding and return the message digest
+ void sha1_final(unsigned char digest[20], SHA1_CONTEXT* context)
+ {
+ Q_UINT32 i, j;
+ unsigned char finalcount[8];
+
+ for (i = 0; i < 8; i++) {
+ finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+ >> ((3-(i & 3)) * 8) ) & 255); // Endian independent
+ }
+ sha1_update(context, (unsigned char *)"\200", 1);
+ while ((context->count[0] & 504) != 448) {
+ sha1_update(context, (unsigned char *)"\0", 1);
+ }
+ sha1_update(context, finalcount, 8); // Should cause a transform()
+ for (i = 0; i < 20; i++) {
+ digest[i] = (unsigned char) ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+ }
+
+ // Wipe variables
+ i = j = 0;
+ memset(context->buffer, 0, 64);
+ memset(context->state, 0, 20);
+ memset(context->count, 0, 8);
+ memset(&finalcount, 0, 8);
+ }
+};
+
+class MD5Context : public QCA_HashContext
+{
+public:
+ MD5Context()
+ {
+ reset();
+ }
+
+ QCA_HashContext *clone()
+ {
+ return new MD5Context(*this);
+ }
+
+ void reset()
+ {
+ md5_init(&md5);
+ }
+
+ void update(const char *in, unsigned int len)
+ {
+ md5_append(&md5, (const md5_byte_t *)in, len);
+ }
+
+ void final(QByteArray *out)
+ {
+ QByteArray b(16);
+ md5_finish(&md5, (md5_byte_t *)b.data());
+ *out = b;
+ }
+
+ md5_state_t md5;
+};
+
+class HashProvider : public QCAProvider
+{
+public:
+ HashProvider() {}
+ ~HashProvider() {}
+
+ void init()
+ {
+ ensureEndian();
+ }
+
+ int qcaVersion() const
+ {
+ return QCA_PLUGIN_VERSION;
+ }
+
+ int capabilities() const
+ {
+ return (QCA::CAP_SHA1 | QCA::CAP_MD5);
+ }
+
+ void *context(int cap)
+ {
+ if(cap == QCA::CAP_SHA1)
+ return new SHA1Context;
+ if(cap == QCA::CAP_MD5)
+ return new MD5Context;
+ return 0;
+ }
+};
+
+QCAProvider *createProviderHash()
+{
+ return (new HashProvider);
+}
+
+}
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/hash.h b/kopete/protocols/jabber/libiris/iris/xmpp-core/hash.h
new file mode 100644
index 00000000..a4d2eea8
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/hash.h
@@ -0,0 +1,31 @@
+/*
+ * hash.h - hashing functions for SHA1 and MD5
+ * 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 HASH_H
+#define HASH_H
+
+#include"qcaprovider.h"
+
+namespace XMPP
+{
+ QCAProvider *createProviderHash();
+}
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/jid.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-core/jid.cpp
new file mode 100644
index 00000000..29932513
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/jid.cpp
@@ -0,0 +1,409 @@
+/*
+ * jid.cpp - class for verifying and manipulating Jabber IDs
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include"xmpp.h"
+
+#include<qdict.h>
+#include<stringprep.h>
+
+using namespace XMPP;
+
+//----------------------------------------------------------------------------
+// StringPrepCache
+//----------------------------------------------------------------------------
+class StringPrepCache
+{
+public:
+ static bool nameprep(const QString &in, int maxbytes, QString *out)
+ {
+ if(in.isEmpty())
+ {
+ if(out)
+ *out = QString();
+ return true;
+ }
+
+ StringPrepCache *that = get_instance();
+
+ Result *r = that->nameprep_table.find(in);
+ if(r)
+ {
+ if(!r->norm)
+ return false;
+ if(out)
+ *out = *(r->norm);
+ return true;
+ }
+
+ QCString cs = in.utf8();
+ cs.resize(maxbytes);
+ if(stringprep(cs.data(), maxbytes, (Stringprep_profile_flags)0, stringprep_nameprep) != 0)
+ {
+ that->nameprep_table.insert(in, new Result);
+ return false;
+ }
+
+ QString norm = QString::fromUtf8(cs);
+ that->nameprep_table.insert(in, new Result(norm));
+ if(out)
+ *out = norm;
+ return true;
+ }
+
+ static bool nodeprep(const QString &in, int maxbytes, QString *out)
+ {
+ if(in.isEmpty())
+ {
+ if(out)
+ *out = QString();
+ return true;
+ }
+
+ StringPrepCache *that = get_instance();
+
+ Result *r = that->nodeprep_table.find(in);
+ if(r)
+ {
+ if(!r->norm)
+ return false;
+ if(out)
+ *out = *(r->norm);
+ return true;
+ }
+
+ QCString cs = in.utf8();
+ cs.resize(maxbytes);
+ if(stringprep(cs.data(), maxbytes, (Stringprep_profile_flags)0, stringprep_xmpp_nodeprep) != 0)
+ {
+ that->nodeprep_table.insert(in, new Result);
+ return false;
+ }
+
+ QString norm = QString::fromUtf8(cs);
+ that->nodeprep_table.insert(in, new Result(norm));
+ if(out)
+ *out = norm;
+ return true;
+ }
+
+ static bool resourceprep(const QString &in, int maxbytes, QString *out)
+ {
+ if(in.isEmpty())
+ {
+ if(out)
+ *out = QString();
+ return true;
+ }
+
+ StringPrepCache *that = get_instance();
+
+ Result *r = that->resourceprep_table.find(in);
+ if(r)
+ {
+ if(!r->norm)
+ return false;
+ if(out)
+ *out = *(r->norm);
+ return true;
+ }
+
+ QCString cs = in.utf8();
+ cs.resize(maxbytes);
+ if(stringprep(cs.data(), maxbytes, (Stringprep_profile_flags)0, stringprep_xmpp_resourceprep) != 0)
+ {
+ that->resourceprep_table.insert(in, new Result);
+ return false;
+ }
+
+ QString norm = QString::fromUtf8(cs);
+ that->resourceprep_table.insert(in, new Result(norm));
+ if(out)
+ *out = norm;
+ return true;
+ }
+
+private:
+ class Result
+ {
+ public:
+ QString *norm;
+
+ Result() : norm(0)
+ {
+ }
+
+ Result(const QString &s) : norm(new QString(s))
+ {
+ }
+
+ ~Result()
+ {
+ delete norm;
+ }
+ };
+
+ QDict<Result> nameprep_table;
+ QDict<Result> nodeprep_table;
+ QDict<Result> resourceprep_table;
+
+ static StringPrepCache *instance;
+
+ static StringPrepCache *get_instance()
+ {
+ if(!instance)
+ instance = new StringPrepCache;
+ return instance;
+ }
+
+ StringPrepCache()
+ {
+ nameprep_table.setAutoDelete(true);
+ nodeprep_table.setAutoDelete(true);
+ resourceprep_table.setAutoDelete(true);
+ }
+};
+
+StringPrepCache *StringPrepCache::instance = 0;
+
+//----------------------------------------------------------------------------
+// Jid
+//----------------------------------------------------------------------------
+Jid::Jid()
+{
+ valid = false;
+}
+
+Jid::~Jid()
+{
+}
+
+Jid::Jid(const QString &s)
+{
+ set(s);
+}
+
+Jid::Jid(const char *s)
+{
+ set(QString(s));
+}
+
+Jid & Jid::operator=(const QString &s)
+{
+ set(s);
+ return *this;
+}
+
+Jid & Jid::operator=(const char *s)
+{
+ set(QString(s));
+ return *this;
+}
+
+void Jid::reset()
+{
+ f = QString();
+ b = QString();
+ d = QString();
+ n = QString();
+ r = QString();
+ valid = false;
+}
+
+void Jid::update()
+{
+ // build 'bare' and 'full' jids
+ if(n.isEmpty())
+ b = d;
+ else
+ b = n + '@' + d;
+
+ b=b.lower(); // JID are not case sensitive
+
+ if(r.isEmpty())
+ f = b;
+ else
+ f = b + '/' + r;
+ if(f.isEmpty())
+ valid = false;
+}
+
+void Jid::set(const QString &s)
+{
+ QString rest, domain, node, resource;
+ QString norm_domain, norm_node, norm_resource;
+ int x = s.find('/');
+ if(x != -1) {
+ rest = s.mid(0, x);
+ resource = s.mid(x+1);
+ }
+ else {
+ rest = s;
+ resource = QString();
+ }
+ if(!validResource(resource, &norm_resource)) {
+ reset();
+ return;
+ }
+
+ x = rest.find('@');
+ if(x != -1) {
+ node = rest.mid(0, x);
+ domain = rest.mid(x+1);
+ }
+ else {
+ node = QString();
+ domain = rest;
+ }
+ if(!validDomain(domain, &norm_domain) || !validNode(node, &norm_node)) {
+ reset();
+ return;
+ }
+
+ valid = true;
+ d = norm_domain;
+ n = norm_node;
+ r = norm_resource;
+ update();
+}
+
+void Jid::set(const QString &domain, const QString &node, const QString &resource)
+{
+ QString norm_domain, norm_node, norm_resource;
+ if(!validDomain(domain, &norm_domain) || !validNode(node, &norm_node) || !validResource(resource, &norm_resource)) {
+ reset();
+ return;
+ }
+ valid = true;
+ d = norm_domain;
+ n = norm_node;
+ r = norm_resource;
+ update();
+}
+
+void Jid::setDomain(const QString &s)
+{
+ if(!valid)
+ return;
+ QString norm;
+ if(!validDomain(s, &norm)) {
+ reset();
+ return;
+ }
+ d = norm;
+ update();
+}
+
+void Jid::setNode(const QString &s)
+{
+ if(!valid)
+ return;
+ QString norm;
+ if(!validNode(s, &norm)) {
+ reset();
+ return;
+ }
+ n = norm;
+ update();
+}
+
+void Jid::setResource(const QString &s)
+{
+ if(!valid)
+ return;
+ QString norm;
+ if(!validResource(s, &norm)) {
+ reset();
+ return;
+ }
+ r = norm;
+ update();
+}
+
+Jid Jid::withNode(const QString &s) const
+{
+ Jid j = *this;
+ j.setNode(s);
+ return j;
+}
+
+Jid Jid::withResource(const QString &s) const
+{
+ Jid j = *this;
+ j.setResource(s);
+ return j;
+}
+
+bool Jid::isValid() const
+{
+ return valid;
+}
+
+bool Jid::isEmpty() const
+{
+ return f.isEmpty();
+}
+
+bool Jid::compare(const Jid &a, bool compareRes) const
+{
+ // only compare valid jids
+ if(!valid || !a.valid)
+ return false;
+
+ if(compareRes ? (f != a.f) : (b != a.b))
+ return false;
+
+ return true;
+}
+
+bool Jid::validDomain(const QString &s, QString *norm)
+{
+ /*QCString cs = s.utf8();
+ cs.resize(1024);
+ if(stringprep(cs.data(), 1024, (Stringprep_profile_flags)0, stringprep_nameprep) != 0)
+ return false;
+ if(norm)
+ *norm = QString::fromUtf8(cs);
+ return true;*/
+ return StringPrepCache::nameprep(s, 1024, norm);
+}
+
+bool Jid::validNode(const QString &s, QString *norm)
+{
+ /*QCString cs = s.utf8();
+ cs.resize(1024);
+ if(stringprep(cs.data(), 1024, (Stringprep_profile_flags)0, stringprep_xmpp_nodeprep) != 0)
+ return false;
+ if(norm)
+ *norm = QString::fromUtf8(cs);
+ return true;*/
+ return StringPrepCache::nodeprep(s, 1024, norm);
+}
+
+bool Jid::validResource(const QString &s, QString *norm)
+{
+ /*QCString cs = s.utf8();
+ cs.resize(1024);
+ if(stringprep(cs.data(), 1024, (Stringprep_profile_flags)0, stringprep_xmpp_resourceprep) != 0)
+ return false;
+ if(norm)
+ *norm = QString::fromUtf8(cs);
+ return true;*/
+ return StringPrepCache::resourceprep(s, 1024, norm);
+}
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/parser.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-core/parser.cpp
new file mode 100644
index 00000000..e1a64532
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/parser.cpp
@@ -0,0 +1,798 @@
+/*
+ * parser.cpp - parse an XMPP "document"
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ TODO:
+
+ For XMPP::Parser to be "perfect", some things must be solved/changed in the
+ Qt library:
+
+ - Fix weird QDomElement::haveAttributeNS() bug (patch submitted to
+ Trolltech on Aug 31st, 2003).
+ - Fix weird behavior in QXmlSimpleReader of reporting endElement() when
+ the '/' character of a self-closing tag is reached, instead of when
+ the final '>' is reached.
+ - Fix incremental parsing bugs in QXmlSimpleReader. At the moment, the
+ only bug I've found is related to attribute parsing, but there might
+ be more (search for '###' in $QTDIR/src/xml/qxml.cpp).
+
+ We have workarounds for all of the above problems in the code below.
+
+ - Deal with the <?xml?> processing instruction as an event type, so that we
+ can feed it back to the application properly. Right now it is completely
+ untrackable and is simply tacked into the first event's actualString. We
+ can't easily do this because QXmlSimpleReader eats an extra byte beyond
+ the processing instruction before reporting it.
+
+ - Make QXmlInputSource capable of accepting data incrementally, to ensure
+ proper text encoding detection and processing over a network. This is
+ technically not a bug, as we have our own subclass below to do it, but
+ it would be nice if Qt had this already.
+*/
+
+#include"parser.h"
+
+#include<qtextcodec.h>
+#include<qptrlist.h>
+#include<string.h>
+
+using namespace XMPP;
+
+static bool qt_bug_check = false;
+static bool qt_bug_have;
+
+//----------------------------------------------------------------------------
+// StreamInput
+//----------------------------------------------------------------------------
+class StreamInput : public QXmlInputSource
+{
+public:
+ StreamInput()
+ {
+ dec = 0;
+ reset();
+ }
+
+ ~StreamInput()
+ {
+ delete dec;
+ }
+
+ void reset()
+ {
+ delete dec;
+ dec = 0;
+ in.resize(0);
+ out = "";
+ at = 0;
+ paused = false;
+ mightChangeEncoding = true;
+ checkBad = true;
+ last = QChar();
+ v_encoding = "";
+ resetLastData();
+ }
+
+ void resetLastData()
+ {
+ last_string = "";
+ }
+
+ QString lastString() const
+ {
+ return last_string;
+ }
+
+ void appendData(const QByteArray &a)
+ {
+ int oldsize = in.size();
+ in.resize(oldsize + a.size());
+ memcpy(in.data() + oldsize, a.data(), a.size());
+ processBuf();
+ }
+
+ QChar lastRead()
+ {
+ return last;
+ }
+
+ QChar next()
+ {
+ if(paused)
+ return EndOfData;
+ else
+ return readNext();
+ }
+
+ // NOTE: setting 'peek' to true allows the same char to be read again,
+ // however this still advances the internal byte processing.
+ QChar readNext(bool peek=false)
+ {
+ QChar c;
+ if(mightChangeEncoding)
+ c = EndOfData;
+ else {
+ if(out.isEmpty()) {
+ QString s;
+ if(!tryExtractPart(&s))
+ c = EndOfData;
+ else {
+ out = s;
+ c = out[0];
+ }
+ }
+ else
+ c = out[0];
+ if(!peek)
+ out.remove(0, 1);
+ }
+ if(c == EndOfData) {
+#ifdef XMPP_PARSER_DEBUG
+ printf("next() = EOD\n");
+#endif
+ }
+ else {
+#ifdef XMPP_PARSER_DEBUG
+ printf("next() = [%c]\n", c.latin1());
+#endif
+ last = c;
+ }
+
+ return c;
+ }
+
+ QByteArray unprocessed() const
+ {
+ QByteArray a(in.size() - at);
+ memcpy(a.data(), in.data() + at, a.size());
+ return a;
+ }
+
+ void pause(bool b)
+ {
+ paused = b;
+ }
+
+ bool isPaused()
+ {
+ return paused;
+ }
+
+ QString encoding() const
+ {
+ return v_encoding;
+ }
+
+private:
+ QTextDecoder *dec;
+ QByteArray in;
+ QString out;
+ int at;
+ bool paused;
+ bool mightChangeEncoding;
+ QChar last;
+ QString v_encoding;
+ QString last_string;
+ bool checkBad;
+
+ void processBuf()
+ {
+#ifdef XMPP_PARSER_DEBUG
+ printf("processing. size=%d, at=%d\n", in.size(), at);
+#endif
+ if(!dec) {
+ QTextCodec *codec = 0;
+ uchar *p = (uchar *)in.data() + at;
+ int size = in.size() - at;
+
+ // do we have enough information to determine the encoding?
+ if(size == 0)
+ return;
+ bool utf16 = false;
+ if(p[0] == 0xfe || p[0] == 0xff) {
+ // probably going to be a UTF-16 byte order mark
+ if(size < 2)
+ return;
+ if((p[0] == 0xfe && p[1] == 0xff) || (p[0] == 0xff && p[1] == 0xfe)) {
+ // ok it is UTF-16
+ utf16 = true;
+ }
+ }
+ if(utf16)
+ codec = QTextCodec::codecForMib(1000); // UTF-16
+ else
+ codec = QTextCodec::codecForMib(106); // UTF-8
+
+ v_encoding = codec->name();
+ dec = codec->makeDecoder();
+
+ // for utf16, put in the byte order mark
+ if(utf16) {
+ out += dec->toUnicode((const char *)p, 2);
+ at += 2;
+ }
+ }
+
+ if(mightChangeEncoding) {
+ while(1) {
+ int n = out.find('<');
+ if(n != -1) {
+ // we need a closing bracket
+ int n2 = out.find('>', n);
+ if(n2 != -1) {
+ ++n2;
+ QString h = out.mid(n, n2-n);
+ QString enc = processXmlHeader(h);
+ QTextCodec *codec = 0;
+ if(!enc.isEmpty())
+ codec = QTextCodec::codecForName(enc.latin1());
+
+ // changing codecs
+ if(codec) {
+ v_encoding = codec->name();
+ delete dec;
+ dec = codec->makeDecoder();
+ }
+ mightChangeEncoding = false;
+ out.truncate(0);
+ at = 0;
+ resetLastData();
+ break;
+ }
+ }
+ QString s;
+ if(!tryExtractPart(&s))
+ break;
+ if(checkBad && checkForBadChars(s)) {
+ // go to the parser
+ mightChangeEncoding = false;
+ out.truncate(0);
+ at = 0;
+ resetLastData();
+ break;
+ }
+ out += s;
+ }
+ }
+ }
+
+ QString processXmlHeader(const QString &h)
+ {
+ if(h.left(5) != "<?xml")
+ return "";
+
+ int endPos = h.find(">");
+ int startPos = h.find("encoding");
+ if(startPos < endPos && startPos != -1) {
+ QString encoding;
+ do {
+ startPos++;
+ if(startPos > endPos) {
+ return "";
+ }
+ } while(h[startPos] != '"' && h[startPos] != '\'');
+ startPos++;
+ while(h[startPos] != '"' && h[startPos] != '\'') {
+ encoding += h[startPos];
+ startPos++;
+ if(startPos > endPos) {
+ return "";
+ }
+ }
+ return encoding;
+ }
+ else
+ return "";
+ }
+
+ bool tryExtractPart(QString *s)
+ {
+ int size = in.size() - at;
+ if(size == 0)
+ return false;
+ uchar *p = (uchar *)in.data() + at;
+ QString nextChars;
+ while(1) {
+ nextChars = dec->toUnicode((const char *)p, 1);
+ ++p;
+ ++at;
+ if(!nextChars.isEmpty())
+ break;
+ if(at == (int)in.size())
+ return false;
+ }
+ last_string += nextChars;
+ *s = nextChars;
+
+ // free processed data?
+ if(at >= 1024) {
+ char *p = in.data();
+ int size = in.size() - at;
+ memmove(p, p + at, size);
+ in.resize(size);
+ at = 0;
+ }
+
+ return true;
+ }
+
+ bool checkForBadChars(const QString &s)
+ {
+ int len = s.find('<');
+ if(len == -1)
+ len = s.length();
+ else
+ checkBad = false;
+ for(int n = 0; n < len; ++n) {
+ if(!s.at(n).isSpace())
+ return true;
+ }
+ return false;
+ }
+};
+
+
+//----------------------------------------------------------------------------
+// ParserHandler
+//----------------------------------------------------------------------------
+namespace XMPP
+{
+ class ParserHandler : public QXmlDefaultHandler
+ {
+ public:
+ ParserHandler(StreamInput *_in, QDomDocument *_doc)
+ {
+ in = _in;
+ doc = _doc;
+ needMore = false;
+ }
+
+ ~ParserHandler()
+ {
+ eventList.setAutoDelete(true);
+ eventList.clear();
+ }
+
+ bool startDocument()
+ {
+ depth = 0;
+ return true;
+ }
+
+ bool endDocument()
+ {
+ return true;
+ }
+
+ bool startPrefixMapping(const QString &prefix, const QString &uri)
+ {
+ if(depth == 0) {
+ nsnames += prefix;
+ nsvalues += uri;
+ }
+ return true;
+ }
+
+ bool startElement(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &atts)
+ {
+ if(depth == 0) {
+ Parser::Event *e = new Parser::Event;
+ QXmlAttributes a;
+ for(int n = 0; n < atts.length(); ++n) {
+ QString uri = atts.uri(n);
+ QString ln = atts.localName(n);
+ if(a.index(uri, ln) == -1)
+ a.append(atts.qName(n), uri, ln, atts.value(n));
+ }
+ e->setDocumentOpen(namespaceURI, localName, qName, a, nsnames, nsvalues);
+ nsnames.clear();
+ nsvalues.clear();
+ e->setActualString(in->lastString());
+
+ in->resetLastData();
+ eventList.append(e);
+ in->pause(true);
+ }
+ else {
+ QDomElement e = doc->createElementNS(namespaceURI, qName);
+ for(int n = 0; n < atts.length(); ++n) {
+ QString uri = atts.uri(n);
+ QString ln = atts.localName(n);
+ bool have;
+ if(!uri.isEmpty()) {
+ have = e.hasAttributeNS(uri, ln);
+ if(qt_bug_have)
+ have = !have;
+ }
+ else
+ have = e.hasAttribute(ln);
+ if(!have)
+ e.setAttributeNS(uri, atts.qName(n), atts.value(n));
+ }
+
+ if(depth == 1) {
+ elem = e;
+ current = e;
+ }
+ else {
+ current.appendChild(e);
+ current = e;
+ }
+ }
+ ++depth;
+ return true;
+ }
+
+ bool endElement(const QString &namespaceURI, const QString &localName, const QString &qName)
+ {
+ --depth;
+ if(depth == 0) {
+ Parser::Event *e = new Parser::Event;
+ e->setDocumentClose(namespaceURI, localName, qName);
+ e->setActualString(in->lastString());
+ in->resetLastData();
+ eventList.append(e);
+ in->pause(true);
+ }
+ else {
+ // done with a depth 1 element?
+ if(depth == 1) {
+ Parser::Event *e = new Parser::Event;
+ e->setElement(elem);
+ e->setActualString(in->lastString());
+ in->resetLastData();
+ eventList.append(e);
+ in->pause(true);
+
+ elem = QDomElement();
+ current = QDomElement();
+ }
+ else
+ current = current.parentNode().toElement();
+ }
+
+ if(in->lastRead() == '/')
+ checkNeedMore();
+
+ return true;
+ }
+
+ bool characters(const QString &str)
+ {
+ if(depth >= 1) {
+ QString content = str;
+ if(content.isEmpty())
+ return true;
+
+ if(!current.isNull()) {
+ QDomText text = doc->createTextNode(content);
+ current.appendChild(text);
+ }
+ }
+ return true;
+ }
+
+ /*bool processingInstruction(const QString &target, const QString &data)
+ {
+ printf("Processing: [%s], [%s]\n", target.latin1(), data.latin1());
+ in->resetLastData();
+ return true;
+ }*/
+
+ void checkNeedMore()
+ {
+ // Here we will work around QXmlSimpleReader strangeness and self-closing tags.
+ // The problem is that endElement() is called when the '/' is read, not when
+ // the final '>' is read. This is a potential problem when obtaining unprocessed
+ // bytes from StreamInput after this event, as the '>' character will end up
+ // in the unprocessed chunk. To work around this, we need to advance StreamInput's
+ // internal byte processing, but not the xml character data. This way, the '>'
+ // will get processed and will no longer be in the unprocessed return, but
+ // QXmlSimpleReader can still read it. To do this, we call StreamInput::readNext
+ // with 'peek' mode.
+ QChar c = in->readNext(true); // peek
+ if(c == QXmlInputSource::EndOfData) {
+ needMore = true;
+ }
+ else {
+ // We'll assume the next char is a '>'. If it isn't, then
+ // QXmlSimpleReader will deal with that problem on the next
+ // parse. We don't need to take any action here.
+ needMore = false;
+
+ // there should have been a pending event
+ Parser::Event *e = eventList.getFirst();
+ if(e) {
+ e->setActualString(e->actualString() + '>');
+ in->resetLastData();
+ }
+ }
+ }
+
+ Parser::Event *takeEvent()
+ {
+ if(needMore)
+ return 0;
+ if(eventList.isEmpty())
+ return 0;
+
+ Parser::Event *e = eventList.getFirst();
+ eventList.removeRef(e);
+ in->pause(false);
+ return e;
+ }
+
+ StreamInput *in;
+ QDomDocument *doc;
+ int depth;
+ QStringList nsnames, nsvalues;
+ QDomElement elem, current;
+ QPtrList<Parser::Event> eventList;
+ bool needMore;
+ };
+}
+
+
+//----------------------------------------------------------------------------
+// Event
+//----------------------------------------------------------------------------
+class Parser::Event::Private
+{
+public:
+ int type;
+ QString ns, ln, qn;
+ QXmlAttributes a;
+ QDomElement e;
+ QString str;
+ QStringList nsnames, nsvalues;
+};
+
+Parser::Event::Event()
+{
+ d = 0;
+}
+
+Parser::Event::Event(const Event &from)
+{
+ d = 0;
+ *this = from;
+}
+
+Parser::Event & Parser::Event::operator=(const Event &from)
+{
+ delete d;
+ d = 0;
+ if(from.d)
+ d = new Private(*from.d);
+ return *this;
+}
+
+Parser::Event::~Event()
+{
+ delete d;
+}
+
+bool Parser::Event::isNull() const
+{
+ return (d ? false: true);
+}
+
+int Parser::Event::type() const
+{
+ if(isNull())
+ return -1;
+ return d->type;
+}
+
+QString Parser::Event::nsprefix(const QString &s) const
+{
+ QStringList::ConstIterator it = d->nsnames.begin();
+ QStringList::ConstIterator it2 = d->nsvalues.begin();
+ for(; it != d->nsnames.end(); ++it) {
+ if((*it) == s)
+ return (*it2);
+ ++it2;
+ }
+ return QString::null;
+}
+
+QString Parser::Event::namespaceURI() const
+{
+ return d->ns;
+}
+
+QString Parser::Event::localName() const
+{
+ return d->ln;
+}
+
+QString Parser::Event::qName() const
+{
+ return d->qn;
+}
+
+QXmlAttributes Parser::Event::atts() const
+{
+ return d->a;
+}
+
+QString Parser::Event::actualString() const
+{
+ return d->str;
+}
+
+QDomElement Parser::Event::element() const
+{
+ return d->e;
+}
+
+void Parser::Event::setDocumentOpen(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &atts, const QStringList &nsnames, const QStringList &nsvalues)
+{
+ if(!d)
+ d = new Private;
+ d->type = DocumentOpen;
+ d->ns = namespaceURI;
+ d->ln = localName;
+ d->qn = qName;
+ d->a = atts;
+ d->nsnames = nsnames;
+ d->nsvalues = nsvalues;
+}
+
+void Parser::Event::setDocumentClose(const QString &namespaceURI, const QString &localName, const QString &qName)
+{
+ if(!d)
+ d = new Private;
+ d->type = DocumentClose;
+ d->ns = namespaceURI;
+ d->ln = localName;
+ d->qn = qName;
+}
+
+void Parser::Event::setElement(const QDomElement &elem)
+{
+ if(!d)
+ d = new Private;
+ d->type = Element;
+ d->e = elem;
+}
+
+void Parser::Event::setError()
+{
+ if(!d)
+ d = new Private;
+ d->type = Error;
+}
+
+void Parser::Event::setActualString(const QString &str)
+{
+ d->str = str;
+}
+
+//----------------------------------------------------------------------------
+// Parser
+//----------------------------------------------------------------------------
+class Parser::Private
+{
+public:
+ Private()
+ {
+ doc = 0;
+ in = 0;
+ handler = 0;
+ reader = 0;
+ reset();
+ }
+
+ ~Private()
+ {
+ reset(false);
+ }
+
+ void reset(bool create=true)
+ {
+ delete reader;
+ delete handler;
+ delete in;
+ delete doc;
+
+ if(create) {
+ doc = new QDomDocument;
+ in = new StreamInput;
+ handler = new ParserHandler(in, doc);
+ reader = new QXmlSimpleReader;
+ reader->setContentHandler(handler);
+
+ // initialize the reader
+ in->pause(true);
+ reader->parse(in, true);
+ in->pause(false);
+ }
+ }
+
+ QDomDocument *doc;
+ StreamInput *in;
+ ParserHandler *handler;
+ QXmlSimpleReader *reader;
+};
+
+Parser::Parser()
+{
+ d = new Private;
+
+ // check for evil bug in Qt <= 3.2.1
+ if(!qt_bug_check) {
+ qt_bug_check = true;
+ QDomElement e = d->doc->createElementNS("someuri", "somename");
+ if(e.hasAttributeNS("someuri", "somename"))
+ qt_bug_have = true;
+ else
+ qt_bug_have = false;
+ }
+}
+
+Parser::~Parser()
+{
+ delete d;
+}
+
+void Parser::reset()
+{
+ d->reset();
+}
+
+void Parser::appendData(const QByteArray &a)
+{
+ d->in->appendData(a);
+
+ // if handler was waiting for more, give it a kick
+ if(d->handler->needMore)
+ d->handler->checkNeedMore();
+}
+
+Parser::Event Parser::readNext()
+{
+ Event e;
+ if(d->handler->needMore)
+ return e;
+ Event *ep = d->handler->takeEvent();
+ if(!ep) {
+ if(!d->reader->parseContinue()) {
+ e.setError();
+ return e;
+ }
+ ep = d->handler->takeEvent();
+ if(!ep)
+ return e;
+ }
+ e = *ep;
+ delete ep;
+ return e;
+}
+
+QByteArray Parser::unprocessed() const
+{
+ return d->in->unprocessed();
+}
+
+QString Parser::encoding() const
+{
+ return d->in->encoding();
+}
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/parser.h b/kopete/protocols/jabber/libiris/iris/xmpp-core/parser.h
new file mode 100644
index 00000000..808b6c3d
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/parser.h
@@ -0,0 +1,86 @@
+/*
+ * parser.h - parse an XMPP "document"
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef PARSER_H
+#define PARSER_H
+
+#include<qdom.h>
+#include<qxml.h>
+
+namespace XMPP
+{
+ class Parser
+ {
+ public:
+ Parser();
+ ~Parser();
+
+ class Event
+ {
+ public:
+ enum Type { DocumentOpen, DocumentClose, Element, Error };
+ Event();
+ Event(const Event &);
+ Event & operator=(const Event &);
+ ~Event();
+
+ bool isNull() const;
+ int type() const;
+
+ // for document open
+ QString nsprefix(const QString &s=QString::null) const;
+
+ // for document open / close
+ QString namespaceURI() const;
+ QString localName() const;
+ QString qName() const;
+ QXmlAttributes atts() const;
+
+ // for element
+ QDomElement element() const;
+
+ // for any
+ QString actualString() const;
+
+ // setup
+ void setDocumentOpen(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &atts, const QStringList &nsnames, const QStringList &nsvalues);
+ void setDocumentClose(const QString &namespaceURI, const QString &localName, const QString &qName);
+ void setElement(const QDomElement &elem);
+ void setError();
+ void setActualString(const QString &);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ void reset();
+ void appendData(const QByteArray &a);
+ Event readNext();
+ QByteArray unprocessed() const;
+ QString encoding() const;
+
+ private:
+ class Private;
+ Private *d;
+ };
+}
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/protocol.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-core/protocol.cpp
new file mode 100644
index 00000000..dfd3253c
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/protocol.cpp
@@ -0,0 +1,1595 @@
+/*
+ * protocol.cpp - XMPP-Core protocol state machine
+ * Copyright (C) 2004 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+// TODO: let the app know if tls is required
+// require mutual auth for server out/in
+// report ErrProtocol if server uses wrong NS
+// use send() instead of writeElement() in CoreProtocol
+
+#include"protocol.h"
+
+#include<qca.h>
+#include"base64.h"
+#include"hash.h"
+
+#ifdef XMPP_TEST
+#include"td.h"
+#endif
+
+using namespace XMPP;
+
+// printArray
+//
+// This function prints out an array of bytes as latin characters, converting
+// non-printable bytes into hex values as necessary. Useful for displaying
+// QByteArrays for debugging purposes.
+static QString printArray(const QByteArray &a)
+{
+ QString s;
+ for(uint n = 0; n < a.size(); ++n) {
+ unsigned char c = (unsigned char)a[(int)n];
+ if(c < 32 || c >= 127) {
+ QString str;
+ str.sprintf("[%02x]", c);
+ s += str;
+ }
+ else
+ s += c;
+ }
+ return s;
+}
+
+// firstChildElement
+//
+// Get an element's first child element
+static QDomElement firstChildElement(const QDomElement &e)
+{
+ for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ if(n.isElement())
+ return n.toElement();
+ }
+ return QDomElement();
+}
+
+//----------------------------------------------------------------------------
+// Version
+//----------------------------------------------------------------------------
+Version::Version(int maj, int min)
+{
+ major = maj;
+ minor = min;
+}
+
+//----------------------------------------------------------------------------
+// StreamFeatures
+//----------------------------------------------------------------------------
+StreamFeatures::StreamFeatures()
+{
+ tls_supported = false;
+ sasl_supported = false;
+ bind_supported = false;
+ tls_required = false;
+}
+
+//----------------------------------------------------------------------------
+// BasicProtocol
+//----------------------------------------------------------------------------
+BasicProtocol::SASLCondEntry BasicProtocol::saslCondTable[] =
+{
+ { "aborted", Aborted },
+ { "incorrect-encoding", IncorrectEncoding },
+ { "invalid-authzid", InvalidAuthzid },
+ { "invalid-mechanism", InvalidMech },
+ { "mechanism-too-weak", MechTooWeak },
+ { "not-authorized", NotAuthorized },
+ { "temporary-auth-failure", TemporaryAuthFailure },
+ { 0, 0 },
+};
+
+BasicProtocol::StreamCondEntry BasicProtocol::streamCondTable[] =
+{
+ { "bad-format", BadFormat },
+ { "bad-namespace-prefix", BadNamespacePrefix },
+ { "conflict", Conflict },
+ { "connection-timeout", ConnectionTimeout },
+ { "host-gone", HostGone },
+ { "host-unknown", HostUnknown },
+ { "improper-addressing", ImproperAddressing },
+ { "internal-server-error", InternalServerError },
+ { "invalid-from", InvalidFrom },
+ { "invalid-id", InvalidId },
+ { "invalid-namespace", InvalidNamespace },
+ { "invalid-xml", InvalidXml },
+ { "not-authorized", StreamNotAuthorized },
+ { "policy-violation", PolicyViolation },
+ { "remote-connection-failed", RemoteConnectionFailed },
+ { "resource-constraint", ResourceConstraint },
+ { "restricted-xml", RestrictedXml },
+ { "see-other-host", SeeOtherHost },
+ { "system-shutdown", SystemShutdown },
+ { "undefined-condition", UndefinedCondition },
+ { "unsupported-encoding", UnsupportedEncoding },
+ { "unsupported-stanza-type", UnsupportedStanzaType },
+ { "unsupported-version", UnsupportedVersion },
+ { "xml-not-well-formed", XmlNotWellFormed },
+ { 0, 0 },
+};
+
+BasicProtocol::BasicProtocol()
+:XmlProtocol()
+{
+ init();
+}
+
+BasicProtocol::~BasicProtocol()
+{
+}
+
+void BasicProtocol::init()
+{
+ errCond = -1;
+ sasl_authed = false;
+ doShutdown = false;
+ delayedError = false;
+ closeError = false;
+ ready = false;
+ stanzasPending = 0;
+ stanzasWritten = 0;
+}
+
+void BasicProtocol::reset()
+{
+ XmlProtocol::reset();
+ init();
+
+ to = QString();
+ from = QString();
+ id = QString();
+ lang = QString();
+ version = Version(1,0);
+ errText = QString();
+ errAppSpec = QDomElement();
+ otherHost = QString();
+ spare.resize(0);
+ sasl_mech = QString();
+ sasl_mechlist.clear();
+ sasl_step.resize(0);
+ stanzaToRecv = QDomElement();
+ sendList.clear();
+}
+
+void BasicProtocol::sendStanza(const QDomElement &e)
+{
+ SendItem i;
+ i.stanzaToSend = e;
+ sendList += i;
+}
+
+void BasicProtocol::sendDirect(const QString &s)
+{
+ SendItem i;
+ i.stringToSend = s;
+ sendList += i;
+}
+
+void BasicProtocol::sendWhitespace()
+{
+ SendItem i;
+ i.doWhitespace = true;
+ sendList += i;
+}
+
+QDomElement BasicProtocol::recvStanza()
+{
+ QDomElement e = stanzaToRecv;
+ stanzaToRecv = QDomElement();
+ return e;
+}
+
+void BasicProtocol::shutdown()
+{
+ doShutdown = true;
+}
+
+void BasicProtocol::shutdownWithError(int cond, const QString &str)
+{
+ otherHost = str;
+ delayErrorAndClose(cond);
+}
+
+bool BasicProtocol::isReady() const
+{
+ return ready;
+}
+
+void BasicProtocol::setReady(bool b)
+{
+ ready = b;
+}
+
+QString BasicProtocol::saslMech() const
+{
+ return sasl_mech;
+}
+
+QByteArray BasicProtocol::saslStep() const
+{
+ return sasl_step;
+}
+
+void BasicProtocol::setSASLMechList(const QStringList &list)
+{
+ sasl_mechlist = list;
+}
+
+void BasicProtocol::setSASLFirst(const QString &mech, const QByteArray &step)
+{
+ sasl_mech = mech;
+ sasl_step = step;
+}
+
+void BasicProtocol::setSASLNext(const QByteArray &step)
+{
+ sasl_step = step;
+}
+
+void BasicProtocol::setSASLAuthed()
+{
+ sasl_authed = true;
+}
+
+int BasicProtocol::stringToSASLCond(const QString &s)
+{
+ for(int n = 0; saslCondTable[n].str; ++n) {
+ if(s == saslCondTable[n].str)
+ return saslCondTable[n].cond;
+ }
+ return -1;
+}
+
+int BasicProtocol::stringToStreamCond(const QString &s)
+{
+ for(int n = 0; streamCondTable[n].str; ++n) {
+ if(s == streamCondTable[n].str)
+ return streamCondTable[n].cond;
+ }
+ return -1;
+}
+
+QString BasicProtocol::saslCondToString(int x)
+{
+ for(int n = 0; saslCondTable[n].str; ++n) {
+ if(x == saslCondTable[n].cond)
+ return saslCondTable[n].str;
+ }
+ return QString();
+}
+
+QString BasicProtocol::streamCondToString(int x)
+{
+ for(int n = 0; streamCondTable[n].str; ++n) {
+ if(x == streamCondTable[n].cond)
+ return streamCondTable[n].str;
+ }
+ return QString();
+}
+
+void BasicProtocol::extractStreamError(const QDomElement &e)
+{
+ QString text;
+ QDomElement appSpec;
+
+ QDomElement t = firstChildElement(e);
+ if(t.isNull() || t.namespaceURI() != NS_STREAMS) {
+ // probably old-style error
+ errCond = -1;
+ errText = e.text();
+ }
+ else
+ errCond = stringToStreamCond(t.tagName());
+
+ if(errCond != -1) {
+ if(errCond == SeeOtherHost)
+ otherHost = t.text();
+
+ t = e.elementsByTagNameNS(NS_STREAMS, "text").item(0).toElement();
+ if(!t.isNull())
+ text = t.text();
+
+ // find first non-standard namespaced element
+ QDomNodeList nl = e.childNodes();
+ for(uint n = 0; n < nl.count(); ++n) {
+ QDomNode i = nl.item(n);
+ if(i.isElement() && i.namespaceURI() != NS_STREAMS) {
+ appSpec = i.toElement();
+ break;
+ }
+ }
+
+ errText = text;
+ errAppSpec = appSpec;
+ }
+}
+
+void BasicProtocol::send(const QDomElement &e, bool clip)
+{
+ writeElement(e, TypeElement, false, clip);
+}
+
+void BasicProtocol::sendStreamError(int cond, const QString &text, const QDomElement &appSpec)
+{
+ QDomElement se = doc.createElementNS(NS_ETHERX, "stream:error");
+ QDomElement err = doc.createElementNS(NS_STREAMS, streamCondToString(cond));
+ if(!otherHost.isEmpty())
+ err.appendChild(doc.createTextNode(otherHost));
+ se.appendChild(err);
+ if(!text.isEmpty()) {
+ QDomElement te = doc.createElementNS(NS_STREAMS, "text");
+ te.setAttributeNS(NS_XML, "xml:lang", "en");
+ te.appendChild(doc.createTextNode(text));
+ se.appendChild(te);
+ }
+ se.appendChild(appSpec);
+
+ writeElement(se, 100, false);
+}
+
+void BasicProtocol::sendStreamError(const QString &text)
+{
+ QDomElement se = doc.createElementNS(NS_ETHERX, "stream:error");
+ se.appendChild(doc.createTextNode(text));
+
+ writeElement(se, 100, false);
+}
+
+bool BasicProtocol::errorAndClose(int cond, const QString &text, const QDomElement &appSpec)
+{
+ closeError = true;
+ errCond = cond;
+ errText = text;
+ errAppSpec = appSpec;
+ sendStreamError(cond, text, appSpec);
+ return close();
+}
+
+bool BasicProtocol::error(int code)
+{
+ event = EError;
+ errorCode = code;
+ return true;
+}
+
+void BasicProtocol::delayErrorAndClose(int cond, const QString &text, const QDomElement &appSpec)
+{
+ errorCode = ErrStream;
+ errCond = cond;
+ errText = text;
+ errAppSpec = appSpec;
+ delayedError = true;
+}
+
+void BasicProtocol::delayError(int code)
+{
+ errorCode = code;
+ delayedError = true;
+}
+
+QDomElement BasicProtocol::docElement()
+{
+ // create the root element
+ QDomElement e = doc.createElementNS(NS_ETHERX, "stream:stream");
+
+ QString defns = defaultNamespace();
+ QStringList list = extraNamespaces();
+
+ // HACK: using attributes seems to be the only way to get additional namespaces in here
+ if(!defns.isEmpty())
+ e.setAttribute("xmlns", defns);
+ for(QStringList::ConstIterator it = list.begin(); it != list.end();) {
+ QString prefix = *(it++);
+ QString uri = *(it++);
+ e.setAttribute(QString("xmlns:") + prefix, uri);
+ }
+
+ // additional attributes
+ if(!isIncoming() && !to.isEmpty())
+ e.setAttribute("to", to);
+ if(isIncoming() && !from.isEmpty())
+ e.setAttribute("from", from);
+ if(!id.isEmpty())
+ e.setAttribute("id", id);
+ if(!lang.isEmpty())
+ e.setAttributeNS(NS_XML, "xml:lang", lang);
+ if(version.major > 0 || version.minor > 0)
+ e.setAttribute("version", QString::number(version.major) + '.' + QString::number(version.minor));
+
+ return e;
+}
+
+void BasicProtocol::handleDocOpen(const Parser::Event &pe)
+{
+ if(isIncoming()) {
+ if(xmlEncoding() != "UTF-8") {
+ delayErrorAndClose(UnsupportedEncoding);
+ return;
+ }
+ }
+
+ if(pe.namespaceURI() == NS_ETHERX && pe.localName() == "stream") {
+ QXmlAttributes atts = pe.atts();
+
+ // grab the version
+ int major = 0;
+ int minor = 0;
+ QString verstr = atts.value("version");
+ if(!verstr.isEmpty()) {
+ int n = verstr.find('.');
+ if(n != -1) {
+ major = verstr.mid(0, n).toInt();
+ minor = verstr.mid(n+1).toInt();
+ }
+ else {
+ major = verstr.toInt();
+ minor = 0;
+ }
+ }
+ version = Version(major, minor);
+
+ if(isIncoming()) {
+ to = atts.value("to");
+ QString peerLang = atts.value(NS_XML, "lang");
+ if(!peerLang.isEmpty())
+ lang = peerLang;
+ }
+ // outgoing
+ else {
+ from = atts.value("from");
+ lang = atts.value(NS_XML, "lang");
+ id = atts.value("id");
+ }
+
+ handleStreamOpen(pe);
+ }
+ else {
+ if(isIncoming())
+ delayErrorAndClose(BadFormat);
+ else
+ delayError(ErrProtocol);
+ }
+}
+
+bool BasicProtocol::handleError()
+{
+ if(isIncoming())
+ return errorAndClose(XmlNotWellFormed);
+ else
+ return error(ErrParse);
+}
+
+bool BasicProtocol::handleCloseFinished()
+{
+ if(closeError) {
+ event = EError;
+ errorCode = ErrStream;
+ // note: errCond and friends are already set at this point
+ }
+ else
+ event = EClosed;
+ return true;
+}
+
+bool BasicProtocol::doStep(const QDomElement &e)
+{
+ // handle pending error
+ if(delayedError) {
+ if(isIncoming())
+ return errorAndClose(errCond, errText, errAppSpec);
+ else
+ return error(errorCode);
+ }
+
+ // shutdown?
+ if(doShutdown) {
+ doShutdown = false;
+ return close();
+ }
+
+ if(!e.isNull()) {
+ // check for error
+ if(e.namespaceURI() == NS_ETHERX && e.tagName() == "error") {
+ extractStreamError(e);
+ return error(ErrStream);
+ }
+ }
+
+ if(ready) {
+ // stanzas written?
+ if(stanzasWritten > 0) {
+ --stanzasWritten;
+ event = EStanzaSent;
+ return true;
+ }
+ // send items?
+ if(!sendList.isEmpty()) {
+ SendItem i;
+ {
+ QValueList<SendItem>::Iterator it = sendList.begin();
+ i = (*it);
+ sendList.remove(it);
+ }
+
+ // outgoing stanza?
+ if(!i.stanzaToSend.isNull()) {
+ ++stanzasPending;
+ writeElement(i.stanzaToSend, TypeStanza, true);
+ event = ESend;
+ }
+ // direct send?
+ else if(!i.stringToSend.isEmpty()) {
+ writeString(i.stringToSend, TypeDirect, true);
+ event = ESend;
+ }
+ // whitespace keepalive?
+ else if(i.doWhitespace) {
+ writeString("\n", TypePing, false);
+ event = ESend;
+ }
+ return true;
+ }
+ else {
+ // if we have pending outgoing stanzas, ask for write notification
+ if(stanzasPending)
+ notify |= NSend;
+ }
+ }
+
+ return doStep2(e);
+}
+
+void BasicProtocol::itemWritten(int id, int)
+{
+ if(id == TypeStanza) {
+ --stanzasPending;
+ ++stanzasWritten;
+ }
+}
+
+QString BasicProtocol::defaultNamespace()
+{
+ // default none
+ return QString();
+}
+
+QStringList BasicProtocol::extraNamespaces()
+{
+ // default none
+ return QStringList();
+}
+
+void BasicProtocol::handleStreamOpen(const Parser::Event &)
+{
+ // default does nothing
+}
+
+//----------------------------------------------------------------------------
+// CoreProtocol
+//----------------------------------------------------------------------------
+CoreProtocol::CoreProtocol()
+:BasicProtocol()
+{
+ init();
+}
+
+CoreProtocol::~CoreProtocol()
+{
+}
+
+void CoreProtocol::init()
+{
+ step = Start;
+
+ // ??
+ server = false;
+ dialback = false;
+ dialback_verify = false;
+
+ // settings
+ jid = Jid();
+ password = QString();
+ oldOnly = false;
+ allowPlain = false;
+ doTLS = true;
+ doAuth = true;
+ doBinding = true;
+
+ // input
+ user = QString();
+ host = QString();
+
+ // status
+ old = false;
+ digest = false;
+ tls_started = false;
+ sasl_started = false;
+}
+
+void CoreProtocol::reset()
+{
+ BasicProtocol::reset();
+ init();
+}
+
+void CoreProtocol::startClientOut(const Jid &_jid, bool _oldOnly, bool tlsActive, bool _doAuth)
+{
+ jid = _jid;
+ to = _jid.domain();
+ oldOnly = _oldOnly;
+ doAuth = _doAuth;
+ tls_started = tlsActive;
+
+ if(oldOnly)
+ version = Version(0,0);
+ startConnect();
+}
+
+void CoreProtocol::startServerOut(const QString &_to)
+{
+ server = true;
+ to = _to;
+ startConnect();
+}
+
+void CoreProtocol::startDialbackOut(const QString &_to, const QString &_from)
+{
+ server = true;
+ dialback = true;
+ to = _to;
+ self_from = _from;
+ startConnect();
+}
+
+void CoreProtocol::startDialbackVerifyOut(const QString &_to, const QString &_from, const QString &id, const QString &key)
+{
+ server = true;
+ dialback = true;
+ dialback_verify = true;
+ to = _to;
+ self_from = _from;
+ dialback_id = id;
+ dialback_key = key;
+ startConnect();
+}
+
+void CoreProtocol::startClientIn(const QString &_id)
+{
+ id = _id;
+ startAccept();
+}
+
+void CoreProtocol::startServerIn(const QString &_id)
+{
+ server = true;
+ id = _id;
+ startAccept();
+}
+
+void CoreProtocol::setLang(const QString &s)
+{
+ lang = s;
+}
+
+void CoreProtocol::setAllowTLS(bool b)
+{
+ doTLS = b;
+}
+
+void CoreProtocol::setAllowBind(bool b)
+{
+ doBinding = b;
+}
+
+void CoreProtocol::setAllowPlain(bool b)
+{
+ allowPlain = b;
+}
+
+void CoreProtocol::setPassword(const QString &s)
+{
+ password = s;
+}
+
+void CoreProtocol::setFrom(const QString &s)
+{
+ from = s;
+}
+
+void CoreProtocol::setDialbackKey(const QString &s)
+{
+ dialback_key = s;
+}
+
+bool CoreProtocol::loginComplete()
+{
+ setReady(true);
+
+ event = EReady;
+ step = Done;
+ return true;
+}
+
+int CoreProtocol::getOldErrorCode(const QDomElement &e)
+{
+ QDomElement err = e.elementsByTagNameNS(NS_CLIENT, "error").item(0).toElement();
+ if(err.isNull() || !err.hasAttribute("code"))
+ return -1;
+ return err.attribute("code").toInt();
+}
+
+/*QString CoreProtocol::xmlToString(const QDomElement &e, bool clip)
+{
+ // determine an appropriate 'fakeNS' to use
+ QString ns;
+ if(e.prefix() == "stream")
+ ns = NS_ETHERX;
+ else if(e.prefix() == "db")
+ ns = NS_DIALBACK;
+ else
+ ns = NS_CLIENT;
+ return ::xmlToString(e, ns, "stream:stream", clip);
+}*/
+
+bool CoreProtocol::stepAdvancesParser() const
+{
+ if(stepRequiresElement())
+ return true;
+ else if(isReady())
+ return true;
+ return false;
+}
+
+// all element-needing steps need to be registered here
+bool CoreProtocol::stepRequiresElement() const
+{
+ switch(step) {
+ case GetFeatures:
+ case GetTLSProceed:
+ case GetSASLChallenge:
+ case GetBindResponse:
+ case GetAuthGetResponse:
+ case GetAuthSetResponse:
+ case GetRequest:
+ case GetSASLResponse:
+ return true;
+ }
+ return false;
+}
+
+void CoreProtocol::stringSend(const QString &s)
+{
+#ifdef XMPP_TEST
+ TD::outgoingTag(s);
+#endif
+}
+
+void CoreProtocol::stringRecv(const QString &s)
+{
+#ifdef XMPP_TEST
+ TD::incomingTag(s);
+#endif
+}
+
+QString CoreProtocol::defaultNamespace()
+{
+ if(server)
+ return NS_SERVER;
+ else
+ return NS_CLIENT;
+}
+
+QStringList CoreProtocol::extraNamespaces()
+{
+ QStringList list;
+ if(dialback) {
+ list += "db";
+ list += NS_DIALBACK;
+ }
+ return list;
+}
+
+void CoreProtocol::handleStreamOpen(const Parser::Event &pe)
+{
+ if(isIncoming()) {
+ QString ns = pe.nsprefix();
+ QString db;
+ if(server) {
+ db = pe.nsprefix("db");
+ if(!db.isEmpty())
+ dialback = true;
+ }
+
+ // verify namespace
+ if((!server && ns != NS_CLIENT) || (server && ns != NS_SERVER) || (dialback && db != NS_DIALBACK)) {
+ delayErrorAndClose(InvalidNamespace);
+ return;
+ }
+
+ // verify version
+ if(version.major < 1 && !dialback) {
+ delayErrorAndClose(UnsupportedVersion);
+ return;
+ }
+ }
+ else {
+ if(!dialback) {
+ if(version.major >= 1 && !oldOnly)
+ old = false;
+ else
+ old = true;
+ }
+ }
+}
+
+void CoreProtocol::elementSend(const QDomElement &e)
+{
+#ifdef XMPP_TEST
+ TD::outgoingXml(e);
+#endif
+}
+
+void CoreProtocol::elementRecv(const QDomElement &e)
+{
+#ifdef XMPP_TEST
+ TD::incomingXml(e);
+#endif
+}
+
+bool CoreProtocol::doStep2(const QDomElement &e)
+{
+ if(dialback)
+ return dialbackStep(e);
+ else
+ return normalStep(e);
+}
+
+bool CoreProtocol::isValidStanza(const QDomElement &e) const
+{
+ QString s = e.tagName();
+ if(e.namespaceURI() == (server ? NS_SERVER : NS_CLIENT) && (s == "message" || s == "presence" || s == "iq"))
+ return true;
+ else
+ return false;
+}
+
+bool CoreProtocol::grabPendingItem(const Jid &to, const Jid &from, int type, DBItem *item)
+{
+ for(QValueList<DBItem>::Iterator it = dbpending.begin(); it != dbpending.end(); ++it) {
+ const DBItem &i = *it;
+ if(i.type == type && i.to.compare(to) && i.from.compare(from)) {
+ const DBItem &i = (*it);
+ *item = i;
+ dbpending.remove(it);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CoreProtocol::dialbackStep(const QDomElement &e)
+{
+ if(step == Start) {
+ setReady(true);
+ step = Done;
+ event = EReady;
+ return true;
+ }
+
+ if(!dbrequests.isEmpty()) {
+ // process a request
+ DBItem i;
+ {
+ QValueList<DBItem>::Iterator it = dbrequests.begin();
+ i = (*it);
+ dbrequests.remove(it);
+ }
+
+ QDomElement r;
+ if(i.type == DBItem::ResultRequest) {
+ r = doc.createElementNS(NS_DIALBACK, "db:result");
+ r.setAttribute("to", i.to.full());
+ r.setAttribute("from", i.from.full());
+ r.appendChild(doc.createTextNode(i.key));
+ dbpending += i;
+ }
+ else if(i.type == DBItem::ResultGrant) {
+ r = doc.createElementNS(NS_DIALBACK, "db:result");
+ r.setAttribute("to", i.to.full());
+ r.setAttribute("from", i.from.full());
+ r.setAttribute("type", i.ok ? "valid" : "invalid");
+ if(i.ok) {
+ i.type = DBItem::Validated;
+ dbvalidated += i;
+ }
+ else {
+ // TODO: disconnect after writing element
+ }
+ }
+ else if(i.type == DBItem::VerifyRequest) {
+ r = doc.createElementNS(NS_DIALBACK, "db:verify");
+ r.setAttribute("to", i.to.full());
+ r.setAttribute("from", i.from.full());
+ r.setAttribute("id", i.id);
+ r.appendChild(doc.createTextNode(i.key));
+ dbpending += i;
+ }
+ // VerifyGrant
+ else {
+ r = doc.createElementNS(NS_DIALBACK, "db:verify");
+ r.setAttribute("to", i.to.full());
+ r.setAttribute("from", i.from.full());
+ r.setAttribute("id", i.id);
+ r.setAttribute("type", i.ok ? "valid" : "invalid");
+ }
+
+ writeElement(r, TypeElement, false);
+ event = ESend;
+ return true;
+ }
+
+ if(!e.isNull()) {
+ if(e.namespaceURI() == NS_DIALBACK) {
+ if(e.tagName() == "result") {
+ Jid to, from;
+ to.set(e.attribute("to"), "");
+ from.set(e.attribute("from"), "");
+ if(isIncoming()) {
+ QString key = e.text();
+ // TODO: report event
+ }
+ else {
+ bool ok = (e.attribute("type") == "valid") ? true: false;
+ DBItem i;
+ if(grabPendingItem(from, to, DBItem::ResultRequest, &i)) {
+ if(ok) {
+ i.type = DBItem::Validated;
+ i.ok = true;
+ dbvalidated += i;
+ // TODO: report event
+ }
+ else {
+ // TODO: report event
+ }
+ }
+ }
+ }
+ else if(e.tagName() == "verify") {
+ Jid to, from;
+ to.set(e.attribute("to"), "");
+ from.set(e.attribute("from"), "");
+ QString id = e.attribute("id");
+ if(isIncoming()) {
+ QString key = e.text();
+ // TODO: report event
+ }
+ else {
+ bool ok = (e.attribute("type") == "valid") ? true: false;
+ DBItem i;
+ if(grabPendingItem(from, to, DBItem::VerifyRequest, &i)) {
+ if(ok) {
+ // TODO: report event
+ }
+ else {
+ // TODO: report event
+ }
+ }
+ }
+ }
+ }
+ else {
+ if(isReady()) {
+ if(isValidStanza(e)) {
+ // TODO: disconnect if stanza is from unverified sender
+ // TODO: ignore packets from receiving servers
+ stanzaToRecv = e;
+ event = EStanzaReady;
+ return true;
+ }
+ }
+ }
+ }
+
+ need = NNotify;
+ notify |= NRecv;
+ return false;
+}
+
+bool CoreProtocol::normalStep(const QDomElement &e)
+{
+ if(step == Start) {
+ if(isIncoming()) {
+ need = NSASLMechs;
+ step = SendFeatures;
+ return false;
+ }
+ else {
+ if(old) {
+ if(doAuth)
+ step = HandleAuthGet;
+ else
+ return loginComplete();
+ }
+ else
+ step = GetFeatures;
+
+ return processStep();
+ }
+ }
+ else if(step == HandleFeatures) {
+ // deal with TLS?
+ if(doTLS && !tls_started && !sasl_authed && features.tls_supported) {
+ QDomElement e = doc.createElementNS(NS_TLS, "starttls");
+
+ send(e, true);
+ event = ESend;
+ step = GetTLSProceed;
+ return true;
+ }
+
+ // deal with SASL?
+ if(!sasl_authed) {
+ if(!features.sasl_supported) {
+ // SASL MUST be supported
+ event = EError;
+ errorCode = ErrProtocol;
+ return true;
+ }
+
+#ifdef XMPP_TEST
+ TD::msg("starting SASL authentication...");
+#endif
+ need = NSASLFirst;
+ step = GetSASLFirst;
+ return false;
+ }
+
+ if(server) {
+ return loginComplete();
+ }
+ else {
+ if(!doBinding)
+ return loginComplete();
+ }
+
+ // deal with bind
+ if(!features.bind_supported) {
+ // bind MUST be supported
+ event = EError;
+ errorCode = ErrProtocol;
+ return true;
+ }
+
+ QDomElement e = doc.createElement("iq");
+ e.setAttribute("type", "set");
+ e.setAttribute("id", "bind_1");
+ QDomElement b = doc.createElementNS(NS_BIND, "bind");
+
+ // request specific resource?
+ QString resource = jid.resource();
+ if(!resource.isEmpty()) {
+ QDomElement r = doc.createElement("resource");
+ r.appendChild(doc.createTextNode(jid.resource()));
+ b.appendChild(r);
+ }
+
+ e.appendChild(b);
+
+ send(e);
+ event = ESend;
+ step = GetBindResponse;
+ return true;
+ }
+ else if(step == GetSASLFirst) {
+ QDomElement e = doc.createElementNS(NS_SASL, "auth");
+ e.setAttribute("mechanism", sasl_mech);
+ if(!sasl_step.isEmpty()) {
+#ifdef XMPP_TEST
+ TD::msg(QString("SASL OUT: [%1]").arg(printArray(sasl_step)));
+#endif
+ e.appendChild(doc.createTextNode(Base64::arrayToString(sasl_step)));
+ }
+
+ send(e, true);
+ event = ESend;
+ step = GetSASLChallenge;
+ return true;
+ }
+ else if(step == GetSASLNext) {
+ if(isIncoming()) {
+ if(sasl_authed) {
+ QDomElement e = doc.createElementNS(NS_SASL, "success");
+ writeElement(e, TypeElement, false, true);
+ event = ESend;
+ step = IncHandleSASLSuccess;
+ return true;
+ }
+ else {
+ QByteArray stepData = sasl_step;
+ QDomElement e = doc.createElementNS(NS_SASL, "challenge");
+ if(!stepData.isEmpty())
+ e.appendChild(doc.createTextNode(Base64::arrayToString(stepData)));
+
+ writeElement(e, TypeElement, false, true);
+ event = ESend;
+ step = GetSASLResponse;
+ return true;
+ }
+ }
+ else {
+ QByteArray stepData = sasl_step;
+#ifdef XMPP_TEST
+ TD::msg(QString("SASL OUT: [%1]").arg(printArray(sasl_step)));
+#endif
+ QDomElement e = doc.createElementNS(NS_SASL, "response");
+ if(!stepData.isEmpty())
+ e.appendChild(doc.createTextNode(Base64::arrayToString(stepData)));
+
+ send(e, true);
+ event = ESend;
+ step = GetSASLChallenge;
+ return true;
+ }
+ }
+ else if(step == HandleSASLSuccess) {
+ need = NSASLLayer;
+ spare = resetStream();
+ step = Start;
+ return false;
+ }
+ else if(step == HandleAuthGet) {
+ QDomElement e = doc.createElement("iq");
+ e.setAttribute("to", to);
+ e.setAttribute("type", "get");
+ e.setAttribute("id", "auth_1");
+ QDomElement q = doc.createElementNS("jabber:iq:auth", "query");
+ QDomElement u = doc.createElement("username");
+ u.appendChild(doc.createTextNode(jid.node()));
+ q.appendChild(u);
+ e.appendChild(q);
+
+ send(e);
+ event = ESend;
+ step = GetAuthGetResponse;
+ return true;
+ }
+ else if(step == HandleAuthSet) {
+ QDomElement e = doc.createElement("iq");
+ e.setAttribute("to", to);
+ e.setAttribute("type", "set");
+ e.setAttribute("id", "auth_2");
+ QDomElement q = doc.createElementNS("jabber:iq:auth", "query");
+ QDomElement u = doc.createElement("username");
+ u.appendChild(doc.createTextNode(jid.node()));
+ q.appendChild(u);
+ QDomElement p;
+ if(digest) {
+ // need SHA1 here
+ if(!QCA::isSupported(QCA::CAP_SHA1))
+ QCA::insertProvider(createProviderHash());
+
+ p = doc.createElement("digest");
+ QCString cs = id.utf8() + password.utf8();
+ p.appendChild(doc.createTextNode(QCA::SHA1::hashToString(cs)));
+ }
+ else {
+ p = doc.createElement("password");
+ p.appendChild(doc.createTextNode(password));
+ }
+ q.appendChild(p);
+ QDomElement r = doc.createElement("resource");
+ r.appendChild(doc.createTextNode(jid.resource()));
+ q.appendChild(r);
+ e.appendChild(q);
+
+ send(e, true);
+ event = ESend;
+ step = GetAuthSetResponse;
+ return true;
+ }
+ // server
+ else if(step == SendFeatures) {
+ QDomElement f = doc.createElementNS(NS_ETHERX, "stream:features");
+ if(!tls_started && !sasl_authed) { // don't offer tls if we are already sasl'd
+ QDomElement tls = doc.createElementNS(NS_TLS, "starttls");
+ f.appendChild(tls);
+ }
+
+ if(sasl_authed) {
+ if(!server) {
+ QDomElement bind = doc.createElementNS(NS_BIND, "bind");
+ f.appendChild(bind);
+ }
+ }
+ else {
+ QDomElement mechs = doc.createElementNS(NS_SASL, "mechanisms");
+ for(QStringList::ConstIterator it = sasl_mechlist.begin(); it != sasl_mechlist.end(); ++it) {
+ QDomElement m = doc.createElement("mechanism");
+ m.appendChild(doc.createTextNode(*it));
+ mechs.appendChild(m);
+ }
+ f.appendChild(mechs);
+ }
+
+ writeElement(f, TypeElement, false);
+ event = ESend;
+ step = GetRequest;
+ return true;
+ }
+ // server
+ else if(step == HandleTLS) {
+ tls_started = true;
+ need = NStartTLS;
+ spare = resetStream();
+ step = Start;
+ return false;
+ }
+ // server
+ else if(step == IncHandleSASLSuccess) {
+ event = ESASLSuccess;
+ spare = resetStream();
+ step = Start;
+ printf("sasl success\n");
+ return true;
+ }
+ else if(step == GetFeatures) {
+ // we are waiting for stream features
+ if(e.namespaceURI() == NS_ETHERX && e.tagName() == "features") {
+ // extract features
+ StreamFeatures f;
+ QDomElement s = e.elementsByTagNameNS(NS_TLS, "starttls").item(0).toElement();
+ if(!s.isNull()) {
+ f.tls_supported = true;
+ f.tls_required = s.elementsByTagNameNS(NS_TLS, "required").count() > 0;
+ }
+ QDomElement m = e.elementsByTagNameNS(NS_SASL, "mechanisms").item(0).toElement();
+ if(!m.isNull()) {
+ f.sasl_supported = true;
+ QDomNodeList l = m.elementsByTagNameNS(NS_SASL, "mechanism");
+ for(uint n = 0; n < l.count(); ++n)
+ f.sasl_mechs += l.item(n).toElement().text();
+ }
+ QDomElement b = e.elementsByTagNameNS(NS_BIND, "bind").item(0).toElement();
+ if(!b.isNull())
+ f.bind_supported = true;
+
+ if(f.tls_supported) {
+#ifdef XMPP_TEST
+ QString s = "STARTTLS is available";
+ if(f.tls_required)
+ s += " (required)";
+ TD::msg(s);
+#endif
+ }
+ if(f.sasl_supported) {
+#ifdef XMPP_TEST
+ QString s = "SASL mechs:";
+ for(QStringList::ConstIterator it = f.sasl_mechs.begin(); it != f.sasl_mechs.end(); ++it)
+ s += QString(" [%1]").arg((*it));
+ TD::msg(s);
+#endif
+ }
+
+ if(doAuth) {
+ event = EFeatures;
+ features = f;
+ step = HandleFeatures;
+ return true;
+ }
+ else
+ return loginComplete();
+ }
+ else {
+ // ignore
+ }
+ }
+ else if(step == GetTLSProceed) {
+ // waiting for proceed to starttls
+ if(e.namespaceURI() == NS_TLS) {
+ if(e.tagName() == "proceed") {
+#ifdef XMPP_TEST
+ TD::msg("Server wants us to proceed with ssl handshake");
+#endif
+ tls_started = true;
+ need = NStartTLS;
+ spare = resetStream();
+ step = Start;
+ return false;
+ }
+ else if(e.tagName() == "failure") {
+ event = EError;
+ errorCode = ErrStartTLS;
+ return true;
+ }
+ else {
+ event = EError;
+ errorCode = ErrProtocol;
+ return true;
+ }
+ }
+ else {
+ // ignore
+ }
+ }
+ else if(step == GetSASLChallenge) {
+ // waiting for sasl challenge/success/fail
+ if(e.namespaceURI() == NS_SASL) {
+ if(e.tagName() == "challenge") {
+ QByteArray a = Base64::stringToArray(e.text());
+#ifdef XMPP_TEST
+ TD::msg(QString("SASL IN: [%1]").arg(printArray(a)));
+#endif
+ sasl_step = a;
+ need = NSASLNext;
+ step = GetSASLNext;
+ return false;
+ }
+ else if(e.tagName() == "success") {
+ sasl_authed = true;
+ event = ESASLSuccess;
+ step = HandleSASLSuccess;
+ return true;
+ }
+ else if(e.tagName() == "failure") {
+ QDomElement t = firstChildElement(e);
+ if(t.isNull() || t.namespaceURI() != NS_SASL)
+ errCond = -1;
+ else
+ errCond = stringToSASLCond(t.tagName());
+
+ event = EError;
+ errorCode = ErrAuth;
+ return true;
+ }
+ else {
+ event = EError;
+ errorCode = ErrProtocol;
+ return true;
+ }
+ }
+ }
+ else if(step == GetBindResponse) {
+ if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
+ QString type(e.attribute("type"));
+ QString id(e.attribute("id"));
+
+ if(id == "bind_1" && (type == "result" || type == "error")) {
+ if(type == "result") {
+ QDomElement b = e.elementsByTagNameNS(NS_BIND, "bind").item(0).toElement();
+ Jid j;
+ if(!b.isNull()) {
+ QDomElement je = e.elementsByTagName("jid").item(0).toElement();
+ j = je.text();
+ }
+ if(!j.isValid()) {
+ event = EError;
+ errorCode = ErrProtocol;
+ return true;
+ }
+ jid = j;
+ return loginComplete();
+ }
+ else {
+ errCond = -1;
+
+ QDomElement err = e.elementsByTagNameNS(NS_CLIENT, "error").item(0).toElement();
+ if(!err.isNull()) {
+ // get error condition
+ QDomNodeList nl = err.childNodes();
+ QDomElement t;
+ for(uint n = 0; n < nl.count(); ++n) {
+ QDomNode i = nl.item(n);
+ if(i.isElement()) {
+ t = i.toElement();
+ break;
+ }
+ }
+ if(!t.isNull() && t.namespaceURI() == NS_STANZAS) {
+ QString cond = t.tagName();
+ if(cond == "not-allowed")
+ errCond = BindNotAllowed;
+ else if(cond == "conflict")
+ errCond = BindConflict;
+ }
+ }
+
+ event = EError;
+ errorCode = ErrBind;
+ return true;
+ }
+ }
+ else {
+ // ignore
+ }
+ }
+ else {
+ // ignore
+ }
+ }
+ else if(step == GetAuthGetResponse) {
+ // waiting for an iq
+ if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
+ Jid from(e.attribute("from"));
+ QString type(e.attribute("type"));
+ QString id(e.attribute("id"));
+
+ bool okfrom = (from.isEmpty() || from.compare(Jid(to)));
+ if(okfrom && id == "auth_1" && (type == "result" || type == "error")) {
+ if(type == "result") {
+ QDomElement q = e.elementsByTagNameNS("jabber:iq:auth", "query").item(0).toElement();
+ if(q.isNull() || q.elementsByTagName("username").item(0).isNull() || q.elementsByTagName("resource").item(0).isNull()) {
+ event = EError;
+ errorCode = ErrProtocol;
+ return true;
+ }
+ bool plain_supported = !q.elementsByTagName("password").item(0).isNull();
+ bool digest_supported = !q.elementsByTagName("digest").item(0).isNull();
+
+ if(!digest_supported && !plain_supported) {
+ event = EError;
+ errorCode = ErrProtocol;
+ return true;
+ }
+
+ // plain text not allowed?
+ if(!digest_supported && !allowPlain) {
+ event = EError;
+ errorCode = ErrPlain;
+ return true;
+ }
+
+ digest = digest_supported;
+ need = NPassword;
+ step = HandleAuthSet;
+ return false;
+ }
+ else {
+ errCond = getOldErrorCode(e);
+
+ event = EError;
+ errorCode = ErrAuth;
+ return true;
+ }
+ }
+ else {
+ // ignore
+ }
+ }
+ else {
+ // ignore
+ }
+ }
+ else if(step == GetAuthSetResponse) {
+ // waiting for an iq
+ if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
+ Jid from(e.attribute("from"));
+ QString type(e.attribute("type"));
+ QString id(e.attribute("id"));
+
+ bool okfrom = (from.isEmpty() || from.compare(Jid(to)));
+ if(okfrom && id == "auth_2" && (type == "result" || type == "error")) {
+ if(type == "result") {
+ return loginComplete();
+ }
+ else {
+ errCond = getOldErrorCode(e);
+
+ event = EError;
+ errorCode = ErrAuth;
+ return true;
+ }
+ }
+ else {
+ // ignore
+ }
+ }
+ else {
+ // ignore
+ }
+ }
+ // server
+ else if(step == GetRequest) {
+ printf("get request: [%s], %s\n", e.namespaceURI().latin1(), e.tagName().latin1());
+ if(e.namespaceURI() == NS_TLS && e.localName() == "starttls") {
+ // TODO: don't let this be done twice
+
+ QDomElement e = doc.createElementNS(NS_TLS, "proceed");
+ writeElement(e, TypeElement, false, true);
+ event = ESend;
+ step = HandleTLS;
+ return true;
+ }
+ if(e.namespaceURI() == NS_SASL) {
+ if(e.localName() == "auth") {
+ if(sasl_started) {
+ // TODO
+ printf("error\n");
+ return false;
+ }
+
+ sasl_started = true;
+ sasl_mech = e.attribute("mechanism");
+ // TODO: if child text missing, don't pass it
+ sasl_step = Base64::stringToArray(e.text());
+ need = NSASLFirst;
+ step = GetSASLNext;
+ return false;
+ }
+ else {
+ // TODO
+ printf("unknown sasl tag\n");
+ return false;
+ }
+ }
+ if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
+ QDomElement b = e.elementsByTagNameNS(NS_BIND, "bind").item(0).toElement();
+ if(!b.isNull()) {
+ QDomElement res = b.elementsByTagName("resource").item(0).toElement();
+ QString resource = res.text();
+
+ QDomElement r = doc.createElement("iq");
+ r.setAttribute("type", "result");
+ r.setAttribute("id", e.attribute("id"));
+ QDomElement bind = doc.createElementNS(NS_BIND, "bind");
+ QDomElement jid = doc.createElement("jid");
+ Jid j = user + '@' + host + '/' + resource;
+ jid.appendChild(doc.createTextNode(j.full()));
+ bind.appendChild(jid);
+ r.appendChild(bind);
+
+ writeElement(r, TypeElement, false);
+ event = ESend;
+ // TODO
+ return true;
+ }
+ else {
+ // TODO
+ }
+ }
+ }
+ else if(step == GetSASLResponse) {
+ if(e.namespaceURI() == NS_SASL && e.localName() == "response") {
+ sasl_step = Base64::stringToArray(e.text());
+ need = NSASLNext;
+ step = GetSASLNext;
+ return false;
+ }
+ }
+
+ if(isReady()) {
+ if(!e.isNull() && isValidStanza(e)) {
+ stanzaToRecv = e;
+ event = EStanzaReady;
+ setIncomingAsExternal();
+ return true;
+ }
+ }
+
+ need = NNotify;
+ notify |= NRecv;
+ return false;
+}
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/protocol.h b/kopete/protocols/jabber/libiris/iris/xmpp-core/protocol.h
new file mode 100644
index 00000000..8511ce32
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/protocol.h
@@ -0,0 +1,355 @@
+/*
+ * protocol.h - XMPP-Core protocol state machine
+ * Copyright (C) 2004 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef PROTOCOL_H
+#define PROTOCOL_H
+
+#include<qpair.h>
+#include"xmlprotocol.h"
+#include"xmpp.h"
+
+#define NS_ETHERX "http://etherx.jabber.org/streams"
+#define NS_CLIENT "jabber:client"
+#define NS_SERVER "jabber:server"
+#define NS_DIALBACK "jabber:server:dialback"
+#define NS_STREAMS "urn:ietf:params:xml:ns:xmpp-streams"
+#define NS_TLS "urn:ietf:params:xml:ns:xmpp-tls"
+#define NS_SASL "urn:ietf:params:xml:ns:xmpp-sasl"
+#define NS_SESSION "urn:ietf:params:xml:ns:xmpp-session"
+#define NS_STANZAS "urn:ietf:params:xml:ns:xmpp-stanzas"
+#define NS_BIND "urn:ietf:params:xml:ns:xmpp-bind"
+#define NS_XHTML_IM "http://jabber.org/protocol/xhtml-im"
+#define NS_XHTML "http://www.w3.org/1999/xhtml"
+#define NS_CHATSTATES "http://jabber.org/protocol/chatstates"
+
+namespace XMPP
+{
+ class Version
+ {
+ public:
+ Version(int maj=0, int min=0);
+
+ int major, minor;
+ };
+
+ class StreamFeatures
+ {
+ public:
+ StreamFeatures();
+
+ bool tls_supported, sasl_supported, bind_supported;
+ bool tls_required;
+ QStringList sasl_mechs;
+ };
+
+ class BasicProtocol : public XmlProtocol
+ {
+ public:
+ // xmpp 1.0 error conditions
+ enum SASLCond {
+ Aborted,
+ IncorrectEncoding,
+ InvalidAuthzid,
+ InvalidMech,
+ MechTooWeak,
+ NotAuthorized,
+ TemporaryAuthFailure
+ };
+ enum StreamCond {
+ BadFormat,
+ BadNamespacePrefix,
+ Conflict,
+ ConnectionTimeout,
+ HostGone,
+ HostUnknown,
+ ImproperAddressing,
+ InternalServerError,
+ InvalidFrom,
+ InvalidId,
+ InvalidNamespace,
+ InvalidXml,
+ StreamNotAuthorized,
+ PolicyViolation,
+ RemoteConnectionFailed,
+ ResourceConstraint,
+ RestrictedXml,
+ SeeOtherHost,
+ SystemShutdown,
+ UndefinedCondition,
+ UnsupportedEncoding,
+ UnsupportedStanzaType,
+ UnsupportedVersion,
+ XmlNotWellFormed
+ };
+ enum BindCond {
+ BindBadRequest,
+ BindNotAllowed,
+ BindConflict
+ };
+
+ // extend the XmlProtocol enums
+ enum Need {
+ NSASLMechs = XmlProtocol::NCustom, // need SASL mechlist
+ NStartTLS, // need to switch on TLS layer
+ NSASLFirst, // need SASL first step
+ NSASLNext, // need SASL next step
+ NSASLLayer, // need to switch on SASL layer
+ NCustom = XmlProtocol::NCustom+10
+ };
+ enum Event {
+ EFeatures = XmlProtocol::ECustom, // breakpoint after features packet is received
+ ESASLSuccess, // breakpoint after successful sasl auth
+ EStanzaReady, // a stanza was received
+ EStanzaSent, // a stanza was sent
+ EReady, // stream is ready for stanza use
+ ECustom = XmlProtocol::ECustom+10
+ };
+ enum Error {
+ ErrProtocol = XmlProtocol::ErrCustom, // there was an error in the xmpp-core protocol exchange
+ ErrStream, // <stream:error>, see errCond, errText, and errAppSpec for details
+ ErrStartTLS, // server refused starttls
+ ErrAuth, // authorization error. errCond holds sasl condition (or numeric code for old-protocol)
+ ErrBind, // server refused resource bind
+ ErrCustom = XmlProtocol::ErrCustom+10
+ };
+
+ BasicProtocol();
+ ~BasicProtocol();
+
+ void reset();
+
+ // for outgoing xml
+ QDomDocument doc;
+
+ // sasl-related
+ QString saslMech() const;
+ QByteArray saslStep() const;
+ void setSASLMechList(const QStringList &list);
+ void setSASLFirst(const QString &mech, const QByteArray &step);
+ void setSASLNext(const QByteArray &step);
+ void setSASLAuthed();
+
+ // send / recv
+ void sendStanza(const QDomElement &e);
+ void sendDirect(const QString &s);
+ void sendWhitespace();
+ QDomElement recvStanza();
+
+ // shutdown
+ void shutdown();
+ void shutdownWithError(int cond, const QString &otherHost="");
+
+ // <stream> information
+ QString to, from, id, lang;
+ Version version;
+
+ // error output
+ int errCond;
+ QString errText;
+ QDomElement errAppSpec;
+ QString otherHost;
+
+ QByteArray spare; // filled with unprocessed data on NStartTLS and NSASLLayer
+
+ bool isReady() const;
+
+ enum { TypeElement, TypeStanza, TypeDirect, TypePing };
+
+ protected:
+ static int stringToSASLCond(const QString &s);
+ static int stringToStreamCond(const QString &s);
+ static QString saslCondToString(int);
+ static QString streamCondToString(int);
+
+ void send(const QDomElement &e, bool clip=false);
+ void sendStreamError(int cond, const QString &text="", const QDomElement &appSpec=QDomElement());
+ void sendStreamError(const QString &text); // old-style
+
+ bool errorAndClose(int cond, const QString &text="", const QDomElement &appSpec=QDomElement());
+ bool error(int code);
+ void delayErrorAndClose(int cond, const QString &text="", const QDomElement &appSpec=QDomElement());
+ void delayError(int code);
+
+ // reimplemented
+ QDomElement docElement();
+ void handleDocOpen(const Parser::Event &pe);
+ bool handleError();
+ bool handleCloseFinished();
+ bool doStep(const QDomElement &e);
+ void itemWritten(int id, int size);
+
+ virtual QString defaultNamespace();
+ virtual QStringList extraNamespaces(); // stringlist: prefix,uri,prefix,uri, [...]
+ virtual void handleStreamOpen(const Parser::Event &pe);
+ virtual bool doStep2(const QDomElement &e)=0;
+
+ void setReady(bool b);
+
+ QString sasl_mech;
+ QStringList sasl_mechlist;
+ QByteArray sasl_step;
+ bool sasl_authed;
+
+ QDomElement stanzaToRecv;
+
+ private:
+ struct SASLCondEntry
+ {
+ const char *str;
+ int cond;
+ };
+ static SASLCondEntry saslCondTable[];
+
+ struct StreamCondEntry
+ {
+ const char *str;
+ int cond;
+ };
+ static StreamCondEntry streamCondTable[];
+
+ struct SendItem
+ {
+ QDomElement stanzaToSend;
+ QString stringToSend;
+ bool doWhitespace;
+ };
+ QValueList<SendItem> sendList;
+
+ bool doShutdown, delayedError, closeError, ready;
+ int stanzasPending, stanzasWritten;
+
+ void init();
+ void extractStreamError(const QDomElement &e);
+ };
+
+ class CoreProtocol : public BasicProtocol
+ {
+ public:
+ enum {
+ NPassword = NCustom, // need password for old-mode
+ EDBVerify = ECustom, // breakpoint after db:verify request
+ ErrPlain = ErrCustom // server only supports plain, but allowPlain is false locally
+ };
+
+ CoreProtocol();
+ ~CoreProtocol();
+
+ void reset();
+
+ void startClientOut(const Jid &jid, bool oldOnly, bool tlsActive, bool doAuth);
+ void startServerOut(const QString &to);
+ void startDialbackOut(const QString &to, const QString &from);
+ void startDialbackVerifyOut(const QString &to, const QString &from, const QString &id, const QString &key);
+ void startClientIn(const QString &id);
+ void startServerIn(const QString &id);
+
+ void setLang(const QString &s);
+ void setAllowTLS(bool b);
+ void setAllowBind(bool b);
+ void setAllowPlain(bool b); // old-mode
+
+ void setPassword(const QString &s);
+ void setFrom(const QString &s);
+ void setDialbackKey(const QString &s);
+
+ // input
+ QString user, host;
+
+ // status
+ bool old;
+
+ StreamFeatures features;
+
+ //static QString xmlToString(const QDomElement &e, bool clip=false);
+
+ class DBItem
+ {
+ public:
+ enum { ResultRequest, ResultGrant, VerifyRequest, VerifyGrant, Validated };
+ int type;
+ Jid to, from;
+ QString key, id;
+ bool ok;
+ };
+
+ private:
+ enum Step {
+ Start,
+ Done,
+ SendFeatures,
+ GetRequest,
+ HandleTLS,
+ GetSASLResponse,
+ IncHandleSASLSuccess,
+ GetFeatures, // read features packet
+ HandleFeatures, // act on features, by initiating tls, sasl, or bind
+ GetTLSProceed, // read <proceed/> tls response
+ GetSASLFirst, // perform sasl first step using provided data
+ GetSASLChallenge, // read server sasl challenge
+ GetSASLNext, // perform sasl next step using provided data
+ HandleSASLSuccess, // handle what must be done after reporting sasl success
+ GetBindResponse, // read bind response
+ HandleAuthGet, // send old-protocol auth-get
+ GetAuthGetResponse, // read auth-get response
+ HandleAuthSet, // send old-protocol auth-set
+ GetAuthSetResponse // read auth-set response
+ };
+
+ QValueList<DBItem> dbrequests, dbpending, dbvalidated;
+
+ bool server, dialback, dialback_verify;
+ int step;
+
+ bool digest;
+ bool tls_started, sasl_started;
+
+ Jid jid;
+ bool oldOnly;
+ bool allowPlain;
+ bool doTLS, doAuth, doBinding;
+ QString password;
+
+ QString dialback_id, dialback_key;
+ QString self_from;
+
+ void init();
+ static int getOldErrorCode(const QDomElement &e);
+ bool loginComplete();
+
+ bool isValidStanza(const QDomElement &e) const;
+ bool grabPendingItem(const Jid &to, const Jid &from, int type, DBItem *item);
+ bool normalStep(const QDomElement &e);
+ bool dialbackStep(const QDomElement &e);
+
+ // reimplemented
+ bool stepAdvancesParser() const;
+ bool stepRequiresElement() const;
+ void stringSend(const QString &s);
+ void stringRecv(const QString &s);
+ QString defaultNamespace();
+ QStringList extraNamespaces();
+ void handleStreamOpen(const Parser::Event &pe);
+ bool doStep2(const QDomElement &e);
+ void elementSend(const QDomElement &e);
+ void elementRecv(const QDomElement &e);
+ };
+}
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/qcaprovider.h b/kopete/protocols/jabber/libiris/iris/xmpp-core/qcaprovider.h
new file mode 100644
index 00000000..a7f1805b
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/qcaprovider.h
@@ -0,0 +1,191 @@
+/*
+ * qcaprovider.h - QCA Plugin API
+ * 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 QCAPROVIDER_H
+#define QCAPROVIDER_H
+
+#include<qglobal.h>
+#include<qstring.h>
+#include<qdatetime.h>
+#include<qobject.h>
+#include<qhostaddress.h>
+#include"qca.h"
+
+#define QCA_PLUGIN_VERSION 1
+
+class QCAProvider
+{
+public:
+ QCAProvider() {}
+ virtual ~QCAProvider() {}
+
+ virtual void init()=0;
+ virtual int qcaVersion() const=0;
+ virtual int capabilities() const=0;
+ virtual void *context(int cap)=0;
+};
+
+class QCA_HashContext
+{
+public:
+ virtual ~QCA_HashContext() {}
+
+ virtual QCA_HashContext *clone()=0;
+ virtual void reset()=0;
+ virtual void update(const char *in, unsigned int len)=0;
+ virtual void final(QByteArray *out)=0;
+};
+
+class QCA_CipherContext
+{
+public:
+ virtual ~QCA_CipherContext() {}
+
+ virtual QCA_CipherContext *clone()=0;
+ virtual int keySize()=0;
+ virtual int blockSize()=0;
+ virtual bool generateKey(char *out, int keysize=-1)=0;
+ virtual bool generateIV(char *out)=0;
+
+ virtual bool setup(int dir, int mode, const char *key, int keysize, const char *iv, bool pad)=0;
+ virtual bool update(const char *in, unsigned int len)=0;
+ virtual bool final(QByteArray *out)=0;
+};
+
+class QCA_RSAKeyContext
+{
+public:
+ virtual ~QCA_RSAKeyContext() {}
+
+ virtual QCA_RSAKeyContext *clone() const=0;
+ virtual bool isNull() const=0;
+ virtual bool havePublic() const=0;
+ virtual bool havePrivate() const=0;
+ virtual bool createFromDER(const char *in, unsigned int len)=0;
+ virtual bool createFromPEM(const char *in, unsigned int len)=0;
+ virtual bool createFromNative(void *in)=0;
+ virtual bool generate(unsigned int bits)=0;
+ virtual bool toDER(QByteArray *out, bool publicOnly)=0;
+ virtual bool toPEM(QByteArray *out, bool publicOnly)=0;
+
+ virtual bool encrypt(const QByteArray &in, QByteArray *out, bool oaep)=0;
+ virtual bool decrypt(const QByteArray &in, QByteArray *out, bool oaep)=0;
+};
+
+struct QCA_CertProperty
+{
+ QString var;
+ QString val;
+};
+
+class QCA_CertContext
+{
+public:
+ virtual ~QCA_CertContext() {}
+
+ virtual QCA_CertContext *clone() const=0;
+ virtual bool isNull() const=0;
+ virtual bool createFromDER(const char *in, unsigned int len)=0;
+ virtual bool createFromPEM(const char *in, unsigned int len)=0;
+ virtual bool toDER(QByteArray *out)=0;
+ virtual bool toPEM(QByteArray *out)=0;
+
+ virtual QString serialNumber() const=0;
+ virtual QString subjectString() const=0;
+ virtual QString issuerString() const=0;
+ virtual QValueList<QCA_CertProperty> subject() const=0;
+ virtual QValueList<QCA_CertProperty> issuer() const=0;
+ virtual QDateTime notBefore() const=0;
+ virtual QDateTime notAfter() const=0;
+ virtual bool matchesAddress(const QString &realHost) const=0;
+};
+
+class QCA_TLSContext
+{
+public:
+ enum Result { Success, Error, Continue };
+ virtual ~QCA_TLSContext() {}
+
+ virtual void reset()=0;
+ virtual bool startClient(const QPtrList<QCA_CertContext> &store, const QCA_CertContext &cert, const QCA_RSAKeyContext &key)=0;
+ virtual bool startServer(const QPtrList<QCA_CertContext> &store, const QCA_CertContext &cert, const QCA_RSAKeyContext &key)=0;
+
+ virtual int handshake(const QByteArray &in, QByteArray *out)=0;
+ virtual int shutdown(const QByteArray &in, QByteArray *out)=0;
+ virtual bool encode(const QByteArray &plain, QByteArray *to_net, int *encoded)=0;
+ virtual bool decode(const QByteArray &from_net, QByteArray *plain, QByteArray *to_net)=0;
+ virtual bool eof() const=0;
+ virtual QByteArray unprocessed()=0;
+
+ virtual QCA_CertContext *peerCertificate() const=0;
+ virtual int validityResult() const=0;
+};
+
+struct QCA_SASLHostPort
+{
+ QHostAddress addr;
+ Q_UINT16 port;
+};
+
+struct QCA_SASLNeedParams
+{
+ bool user, authzid, pass, realm;
+};
+
+class QCA_SASLContext
+{
+public:
+ enum Result { Success, Error, NeedParams, AuthCheck, Continue };
+ virtual ~QCA_SASLContext() {}
+
+ // common
+ virtual void reset()=0;
+ virtual void setCoreProps(const QString &service, const QString &host, QCA_SASLHostPort *local, QCA_SASLHostPort *remote)=0;
+ virtual void setSecurityProps(bool noPlain, bool noActive, bool noDict, bool noAnon, bool reqForward, bool reqCreds, bool reqMutual, int ssfMin, int ssfMax, const QString &_ext_authid, int _ext_ssf)=0;
+ virtual int security() const=0;
+ virtual int errorCond() const=0;
+
+ // init / first step
+ virtual bool clientStart(const QStringList &mechlist)=0;
+ virtual int clientFirstStep(bool allowClientSendFirst)=0;
+ virtual bool serverStart(const QString &realm, QStringList *mechlist, const QString &name)=0;
+ virtual int serverFirstStep(const QString &mech, const QByteArray *in)=0;
+
+ // get / set params
+ virtual QCA_SASLNeedParams clientParamsNeeded() const=0;
+ virtual void setClientParams(const QString *user, const QString *authzid, const QString *pass, const QString *realm)=0;
+ virtual QString username() const=0;
+ virtual QString authzid() const=0;
+
+ // continue steps
+ virtual int nextStep(const QByteArray &in)=0;
+ virtual int tryAgain()=0;
+
+ // results
+ virtual QString mech() const=0;
+ virtual const QByteArray *clientInit() const=0;
+ virtual QByteArray result() const=0;
+
+ // security layer
+ virtual bool encode(const QByteArray &in, QByteArray *out)=0;
+ virtual bool decode(const QByteArray &in, QByteArray *out)=0;
+};
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/securestream.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-core/securestream.cpp
new file mode 100644
index 00000000..6bd902d9
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/securestream.cpp
@@ -0,0 +1,589 @@
+/*
+ * securestream.cpp - combines a ByteStream with TLS and SASL
+ * Copyright (C) 2004 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ Note: SecureStream depends on the underlying security layers to signal
+ plain-to-encrypted results immediately (as opposed to waiting for the
+ event loop) so that the user cannot add/remove security layers during
+ this conversion moment. QCA::TLS and QCA::SASL behave as expected,
+ but future layers might not.
+*/
+
+#include"securestream.h"
+
+#include<qguardedptr.h>
+#include<qvaluelist.h>
+#include<qtimer.h>
+
+#ifdef USE_TLSHANDLER
+#include"xmpp.h"
+#endif
+
+//----------------------------------------------------------------------------
+// LayerTracker
+//----------------------------------------------------------------------------
+class LayerTracker
+{
+public:
+ struct Item
+ {
+ int plain;
+ int encoded;
+ };
+
+ LayerTracker();
+
+ void reset();
+ void addPlain(int plain);
+ void specifyEncoded(int encoded, int plain);
+ int finished(int encoded);
+
+ int p;
+ QValueList<Item> list;
+};
+
+LayerTracker::LayerTracker()
+{
+ p = 0;
+}
+
+void LayerTracker::reset()
+{
+ p = 0;
+ list.clear();
+}
+
+void LayerTracker::addPlain(int plain)
+{
+ p += plain;
+}
+
+void LayerTracker::specifyEncoded(int encoded, int plain)
+{
+ // can't specify more bytes than we have
+ if(plain > p)
+ plain = p;
+ p -= plain;
+ Item i;
+ i.plain = plain;
+ i.encoded = encoded;
+ list += i;
+}
+
+int LayerTracker::finished(int encoded)
+{
+ int plain = 0;
+ for(QValueList<Item>::Iterator it = list.begin(); it != list.end();) {
+ Item &i = *it;
+
+ // not enough?
+ if(encoded < i.encoded) {
+ i.encoded -= encoded;
+ break;
+ }
+
+ encoded -= i.encoded;
+ plain += i.plain;
+ it = list.remove(it);
+ }
+ return plain;
+}
+
+//----------------------------------------------------------------------------
+// SecureStream
+//----------------------------------------------------------------------------
+class SecureLayer : public QObject
+{
+ Q_OBJECT
+public:
+ enum { TLS, SASL, TLSH };
+ int type;
+ union {
+ QCA::TLS *tls;
+ QCA::SASL *sasl;
+#ifdef USE_TLSHANDLER
+ XMPP::TLSHandler *tlsHandler;
+#endif
+ } p;
+ LayerTracker layer;
+ bool tls_done;
+ int prebytes;
+
+ SecureLayer(QCA::TLS *t)
+ {
+ type = TLS;
+ p.tls = t;
+ init();
+ connect(p.tls, SIGNAL(handshaken()), SLOT(tls_handshaken()));
+ connect(p.tls, SIGNAL(readyRead()), SLOT(tls_readyRead()));
+ connect(p.tls, SIGNAL(readyReadOutgoing(int)), SLOT(tls_readyReadOutgoing(int)));
+ connect(p.tls, SIGNAL(closed()), SLOT(tls_closed()));
+ connect(p.tls, SIGNAL(error(int)), SLOT(tls_error(int)));
+ }
+
+ SecureLayer(QCA::SASL *s)
+ {
+ type = SASL;
+ p.sasl = s;
+ init();
+ connect(p.sasl, SIGNAL(readyRead()), SLOT(sasl_readyRead()));
+ connect(p.sasl, SIGNAL(readyReadOutgoing(int)), SLOT(sasl_readyReadOutgoing(int)));
+ connect(p.sasl, SIGNAL(error(int)), SLOT(sasl_error(int)));
+ }
+
+#ifdef USE_TLSHANDLER
+ SecureLayer(XMPP::TLSHandler *t)
+ {
+ type = TLSH;
+ p.tlsHandler = t;
+ init();
+ connect(p.tlsHandler, SIGNAL(success()), SLOT(tlsHandler_success()));
+ connect(p.tlsHandler, SIGNAL(fail()), SLOT(tlsHandler_fail()));
+ connect(p.tlsHandler, SIGNAL(closed()), SLOT(tlsHandler_closed()));
+ connect(p.tlsHandler, SIGNAL(readyRead(const QByteArray &)), SLOT(tlsHandler_readyRead(const QByteArray &)));
+ connect(p.tlsHandler, SIGNAL(readyReadOutgoing(const QByteArray &, int)), SLOT(tlsHandler_readyReadOutgoing(const QByteArray &, int)));
+ }
+#endif
+
+ void init()
+ {
+ tls_done = false;
+ prebytes = 0;
+ }
+
+ void write(const QByteArray &a)
+ {
+ layer.addPlain(a.size());
+ switch(type) {
+ case TLS: { p.tls->write(a); break; }
+ case SASL: { p.sasl->write(a); break; }
+#ifdef USE_TLSHANDLER
+ case TLSH: { p.tlsHandler->write(a); break; }
+#endif
+ }
+ }
+
+ void writeIncoming(const QByteArray &a)
+ {
+ switch(type) {
+ case TLS: { p.tls->writeIncoming(a); break; }
+ case SASL: { p.sasl->writeIncoming(a); break; }
+#ifdef USE_TLSHANDLER
+ case TLSH: { p.tlsHandler->writeIncoming(a); break; }
+#endif
+ }
+ }
+
+ int finished(int plain)
+ {
+ int written = 0;
+
+ // deal with prebytes (bytes sent prior to this security layer)
+ if(prebytes > 0) {
+ if(prebytes >= plain) {
+ written += plain;
+ prebytes -= plain;
+ plain = 0;
+ }
+ else {
+ written += prebytes;
+ plain -= prebytes;
+ prebytes = 0;
+ }
+ }
+
+ // put remainder into the layer tracker
+ if(type == SASL || tls_done)
+ written += layer.finished(plain);
+
+ return written;
+ }
+
+signals:
+ void tlsHandshaken();
+ void tlsClosed(const QByteArray &);
+ void readyRead(const QByteArray &);
+ void needWrite(const QByteArray &);
+ void error(int);
+
+private slots:
+ void tls_handshaken()
+ {
+ tls_done = true;
+ tlsHandshaken();
+ }
+
+ void tls_readyRead()
+ {
+ QByteArray a = p.tls->read();
+ readyRead(a);
+ }
+
+ void tls_readyReadOutgoing(int plainBytes)
+ {
+ QByteArray a = p.tls->readOutgoing();
+ if(tls_done)
+ layer.specifyEncoded(a.size(), plainBytes);
+ needWrite(a);
+ }
+
+ void tls_closed()
+ {
+ QByteArray a = p.tls->readUnprocessed();
+ tlsClosed(a);
+ }
+
+ void tls_error(int x)
+ {
+ error(x);
+ }
+
+ void sasl_readyRead()
+ {
+ QByteArray a = p.sasl->read();
+ readyRead(a);
+ }
+
+ void sasl_readyReadOutgoing(int plainBytes)
+ {
+ QByteArray a = p.sasl->readOutgoing();
+ layer.specifyEncoded(a.size(), plainBytes);
+ needWrite(a);
+ }
+
+ void sasl_error(int x)
+ {
+ error(x);
+ }
+
+#ifdef USE_TLSHANDLER
+ void tlsHandler_success()
+ {
+ tls_done = true;
+ tlsHandshaken();
+ }
+
+ void tlsHandler_fail()
+ {
+ error(0);
+ }
+
+ void tlsHandler_closed()
+ {
+ tlsClosed(QByteArray());
+ }
+
+ void tlsHandler_readyRead(const QByteArray &a)
+ {
+ readyRead(a);
+ }
+
+ void tlsHandler_readyReadOutgoing(const QByteArray &a, int plainBytes)
+ {
+ if(tls_done)
+ layer.specifyEncoded(a.size(), plainBytes);
+ needWrite(a);
+ }
+#endif
+};
+
+#include"securestream.moc"
+
+class SecureStream::Private
+{
+public:
+ ByteStream *bs;
+ QPtrList<SecureLayer> layers;
+ int pending;
+ int errorCode;
+ bool active;
+ bool topInProgress;
+
+ bool haveTLS() const
+ {
+ QPtrListIterator<SecureLayer> it(layers);
+ for(SecureLayer *s; (s = it.current()); ++it) {
+ if(s->type == SecureLayer::TLS
+#ifdef USE_TLSHANDLER
+ || s->type == SecureLayer::TLSH
+#endif
+ ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool haveSASL() const
+ {
+ QPtrListIterator<SecureLayer> it(layers);
+ for(SecureLayer *s; (s = it.current()); ++it) {
+ if(s->type == SecureLayer::SASL)
+ return true;
+ }
+ return false;
+ }
+};
+
+SecureStream::SecureStream(ByteStream *s)
+:ByteStream(0)
+{
+ d = new Private;
+
+ d->bs = s;
+ connect(d->bs, SIGNAL(readyRead()), SLOT(bs_readyRead()));
+ connect(d->bs, SIGNAL(bytesWritten(int)), SLOT(bs_bytesWritten(int)));
+
+ d->layers.setAutoDelete(true);
+ d->pending = 0;
+ d->active = true;
+ d->topInProgress = false;
+}
+
+SecureStream::~SecureStream()
+{
+ delete d;
+}
+
+void SecureStream::linkLayer(QObject *s)
+{
+ connect(s, SIGNAL(tlsHandshaken()), SLOT(layer_tlsHandshaken()));
+ connect(s, SIGNAL(tlsClosed(const QByteArray &)), SLOT(layer_tlsClosed(const QByteArray &)));
+ connect(s, SIGNAL(readyRead(const QByteArray &)), SLOT(layer_readyRead(const QByteArray &)));
+ connect(s, SIGNAL(needWrite(const QByteArray &)), SLOT(layer_needWrite(const QByteArray &)));
+ connect(s, SIGNAL(error(int)), SLOT(layer_error(int)));
+}
+
+int SecureStream::calcPrebytes() const
+{
+ int x = 0;
+ QPtrListIterator<SecureLayer> it(d->layers);
+ for(SecureLayer *s; (s = it.current()); ++it)
+ x += s->prebytes;
+ return (d->pending - x);
+}
+
+void SecureStream::startTLSClient(QCA::TLS *t, const QByteArray &spare)
+{
+ if(!d->active || d->topInProgress || d->haveTLS())
+ return;
+
+ SecureLayer *s = new SecureLayer(t);
+ s->prebytes = calcPrebytes();
+ linkLayer(s);
+ d->layers.append(s);
+ d->topInProgress = true;
+
+ insertData(spare);
+}
+
+void SecureStream::startTLSServer(QCA::TLS *t, const QByteArray &spare)
+{
+ if(!d->active || d->topInProgress || d->haveTLS())
+ return;
+
+ SecureLayer *s = new SecureLayer(t);
+ s->prebytes = calcPrebytes();
+ linkLayer(s);
+ d->layers.append(s);
+ d->topInProgress = true;
+
+ insertData(spare);
+}
+
+void SecureStream::setLayerSASL(QCA::SASL *sasl, const QByteArray &spare)
+{
+ if(!d->active || d->topInProgress || d->haveSASL())
+ return;
+
+ SecureLayer *s = new SecureLayer(sasl);
+ s->prebytes = calcPrebytes();
+ linkLayer(s);
+ d->layers.append(s);
+
+ insertData(spare);
+}
+
+#ifdef USE_TLSHANDLER
+void SecureStream::startTLSClient(XMPP::TLSHandler *t, const QString &server, const QByteArray &spare)
+{
+ if(!d->active || d->topInProgress || d->haveTLS())
+ return;
+
+ SecureLayer *s = new SecureLayer(t);
+ s->prebytes = calcPrebytes();
+ linkLayer(s);
+ d->layers.append(s);
+ d->topInProgress = true;
+
+ // unlike QCA::TLS, XMPP::TLSHandler has no return value
+ s->p.tlsHandler->startClient(server);
+
+ insertData(spare);
+}
+#endif
+
+void SecureStream::closeTLS()
+{
+ SecureLayer *s = d->layers.getLast();
+ if(s) {
+ if(s->type == SecureLayer::TLS)
+ s->p.tls->close();
+ }
+}
+
+int SecureStream::errorCode() const
+{
+ return d->errorCode;
+}
+
+bool SecureStream::isOpen() const
+{
+ return d->active;
+}
+
+void SecureStream::write(const QByteArray &a)
+{
+ if(!isOpen())
+ return;
+
+ d->pending += a.size();
+
+ // send to the last layer
+ SecureLayer *s = d->layers.getLast();
+ if(s)
+ s->write(a);
+ else
+ writeRawData(a);
+}
+
+int SecureStream::bytesToWrite() const
+{
+ return d->pending;
+}
+
+void SecureStream::bs_readyRead()
+{
+ QByteArray a = d->bs->read();
+
+ // send to the first layer
+ SecureLayer *s = d->layers.getFirst();
+ if(s)
+ s->writeIncoming(a);
+ else
+ incomingData(a);
+}
+
+void SecureStream::bs_bytesWritten(int bytes)
+{
+ QPtrListIterator<SecureLayer> it(d->layers);
+ for(SecureLayer *s; (s = it.current()); ++it)
+ bytes = s->finished(bytes);
+
+ if(bytes > 0) {
+ d->pending -= bytes;
+ bytesWritten(bytes);
+ }
+}
+
+void SecureStream::layer_tlsHandshaken()
+{
+ d->topInProgress = false;
+ tlsHandshaken();
+}
+
+void SecureStream::layer_tlsClosed(const QByteArray &)
+{
+ d->active = false;
+ d->layers.clear();
+ tlsClosed();
+}
+
+void SecureStream::layer_readyRead(const QByteArray &a)
+{
+ SecureLayer *s = (SecureLayer *)sender();
+ QPtrListIterator<SecureLayer> it(d->layers);
+ while(it.current() != s)
+ ++it;
+
+ // pass upwards
+ ++it;
+ s = it.current();
+ if(s)
+ s->writeIncoming(a);
+ else
+ incomingData(a);
+}
+
+void SecureStream::layer_needWrite(const QByteArray &a)
+{
+ SecureLayer *s = (SecureLayer *)sender();
+ QPtrListIterator<SecureLayer> it(d->layers);
+ while(it.current() != s)
+ ++it;
+
+ // pass downwards
+ --it;
+ s = it.current();
+ if(s)
+ s->write(a);
+ else
+ writeRawData(a);
+}
+
+void SecureStream::layer_error(int x)
+{
+ SecureLayer *s = (SecureLayer *)sender();
+ int type = s->type;
+ d->errorCode = x;
+ d->active = false;
+ d->layers.clear();
+ if(type == SecureLayer::TLS)
+ error(ErrTLS);
+ else if(type == SecureLayer::SASL)
+ error(ErrSASL);
+#ifdef USE_TLSHANDLER
+ else if(type == SecureLayer::TLSH)
+ error(ErrTLS);
+#endif
+}
+
+void SecureStream::insertData(const QByteArray &a)
+{
+ if(!a.isEmpty()) {
+ SecureLayer *s = d->layers.getLast();
+ if(s)
+ s->writeIncoming(a);
+ else
+ incomingData(a);
+ }
+}
+
+void SecureStream::writeRawData(const QByteArray &a)
+{
+ d->bs->write(a);
+}
+
+void SecureStream::incomingData(const QByteArray &a)
+{
+ appendRead(a);
+ if(bytesAvailable())
+ readyRead();
+}
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/securestream.h b/kopete/protocols/jabber/libiris/iris/xmpp-core/securestream.h
new file mode 100644
index 00000000..c5787a2b
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/securestream.h
@@ -0,0 +1,84 @@
+/*
+ * securestream.h - combines a ByteStream with TLS and SASL
+ * Copyright (C) 2004 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef SECURESTREAM_H
+#define SECURESTREAM_H
+
+#include<qca.h>
+#include"bytestream.h"
+
+#define USE_TLSHANDLER
+
+#ifdef USE_TLSHANDLER
+namespace XMPP
+{
+ class TLSHandler;
+}
+#endif
+
+class SecureStream : public ByteStream
+{
+ Q_OBJECT
+public:
+ enum Error { ErrTLS = ErrCustom, ErrSASL };
+ SecureStream(ByteStream *s);
+ ~SecureStream();
+
+ void startTLSClient(QCA::TLS *t, const QByteArray &spare=QByteArray());
+ void startTLSServer(QCA::TLS *t, const QByteArray &spare=QByteArray());
+ void setLayerSASL(QCA::SASL *s, const QByteArray &spare=QByteArray());
+#ifdef USE_TLSHANDLER
+ void startTLSClient(XMPP::TLSHandler *t, const QString &server, const QByteArray &spare=QByteArray());
+#endif
+
+ void closeTLS();
+ int errorCode() const;
+
+ // reimplemented
+ bool isOpen() const;
+ void write(const QByteArray &);
+ int bytesToWrite() const;
+
+signals:
+ void tlsHandshaken();
+ void tlsClosed();
+
+private slots:
+ void bs_readyRead();
+ void bs_bytesWritten(int);
+
+ void layer_tlsHandshaken();
+ void layer_tlsClosed(const QByteArray &);
+ void layer_readyRead(const QByteArray &);
+ void layer_needWrite(const QByteArray &);
+ void layer_error(int);
+
+private:
+ void linkLayer(QObject *);
+ int calcPrebytes() const;
+ void insertData(const QByteArray &a);
+ void writeRawData(const QByteArray &a);
+ void incomingData(const QByteArray &a);
+
+ class Private;
+ Private *d;
+};
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/simplesasl.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-core/simplesasl.cpp
new file mode 100644
index 00000000..54c4f405
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/simplesasl.cpp
@@ -0,0 +1,459 @@
+/*
+ * simplesasl.cpp - Simple SASL implementation
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include"simplesasl.h"
+
+#include<qhostaddress.h>
+#include<qstringlist.h>
+#include<qptrlist.h>
+#include<qvaluelist.h>
+#include<qca.h>
+#include<stdlib.h>
+#include"base64.h"
+
+namespace XMPP
+{
+
+struct Prop
+{
+ QCString var, val;
+};
+
+class PropList : public QValueList<Prop>
+{
+public:
+ PropList() : QValueList<Prop>()
+ {
+ }
+
+ void set(const QCString &var, const QCString &val)
+ {
+ Prop p;
+ p.var = var;
+ p.val = val;
+ append(p);
+ }
+
+ QCString get(const QCString &var)
+ {
+ for(ConstIterator it = begin(); it != end(); ++it) {
+ if((*it).var == var)
+ return (*it).val;
+ }
+ return QCString();
+ }
+
+ QCString toString() const
+ {
+ QCString str;
+ bool first = true;
+ for(ConstIterator it = begin(); it != end(); ++it) {
+ if(!first)
+ str += ',';
+ str += (*it).var + "=\"" + (*it).val + '\"';
+ first = false;
+ }
+ return str;
+ }
+
+ bool fromString(const QCString &str)
+ {
+ PropList list;
+ int at = 0;
+ while(1) {
+ int n = str.find('=', at);
+ if(n == -1)
+ break;
+ QCString var, val;
+ var = str.mid(at, n-at);
+ at = n + 1;
+ if(str[at] == '\"') {
+ ++at;
+ n = str.find('\"', at);
+ if(n == -1)
+ break;
+ val = str.mid(at, n-at);
+ at = n + 1;
+ }
+ else {
+ n = str.find(',', at);
+ if(n != -1) {
+ val = str.mid(at, n-at);
+ at = n;
+ }
+ else {
+ val = str.mid(at);
+ at = str.length()-1;
+ }
+ }
+ Prop prop;
+ prop.var = var;
+ prop.val = val;
+ list.append(prop);
+
+ if(str[at] != ',')
+ break;
+ ++at;
+ }
+
+ // integrity check
+ if(list.varCount("nonce") != 1)
+ return false;
+ if(list.varCount("algorithm") != 1)
+ return false;
+ *this = list;
+ return true;
+ }
+
+ int varCount(const QCString &var)
+ {
+ int n = 0;
+ for(ConstIterator it = begin(); it != end(); ++it) {
+ if((*it).var == var)
+ ++n;
+ }
+ return n;
+ }
+
+ QStringList getValues(const QCString &var)
+ {
+ QStringList list;
+ for(ConstIterator it = begin(); it != end(); ++it) {
+ if((*it).var == var)
+ list += (*it).val;
+ }
+ return list;
+ }
+};
+
+class SimpleSASLContext : public QCA_SASLContext
+{
+public:
+ // core props
+ QString service, host;
+
+ // state
+ int step;
+ QByteArray in_buf;
+ QString out_mech;
+ QByteArray out_buf;
+ bool capable;
+ int err;
+
+ QCA_SASLNeedParams need;
+ QCA_SASLNeedParams have;
+ QString user, authz, pass, realm;
+
+ SimpleSASLContext()
+ {
+ reset();
+ }
+
+ ~SimpleSASLContext()
+ {
+ reset();
+ }
+
+ void reset()
+ {
+ resetState();
+ resetParams();
+ }
+
+ void resetState()
+ {
+ out_mech = QString();
+ out_buf.resize(0);
+ err = -1;
+ }
+
+ void resetParams()
+ {
+ capable = true;
+ need.user = false;
+ need.authzid = false;
+ need.pass = false;
+ need.realm = false;
+ have.user = false;
+ have.authzid = false;
+ have.pass = false;
+ have.realm = false;
+ user = QString();
+ authz = QString();
+ pass = QString();
+ realm = QString();
+ }
+
+ void setCoreProps(const QString &_service, const QString &_host, QCA_SASLHostPort *, QCA_SASLHostPort *)
+ {
+ service = _service;
+ host = _host;
+ }
+
+ void setSecurityProps(bool, bool, bool, bool, bool reqForward, bool reqCreds, bool reqMutual, int ssfMin, int, const QString &, int)
+ {
+ if(reqForward || reqCreds || reqMutual || ssfMin > 0)
+ capable = false;
+ else
+ capable = true;
+ }
+
+ int security() const
+ {
+ return 0;
+ }
+
+ int errorCond() const
+ {
+ return err;
+ }
+
+ bool clientStart(const QStringList &mechlist)
+ {
+ bool haveMech = false;
+ for(QStringList::ConstIterator it = mechlist.begin(); it != mechlist.end(); ++it) {
+ if((*it) == "DIGEST-MD5") {
+ haveMech = true;
+ break;
+ }
+ }
+ if(!capable || !haveMech) {
+ err = QCA::SASL::NoMech;
+ return false;
+ }
+
+ resetState();
+ step = 0;
+ return true;
+ }
+
+ int clientFirstStep(bool)
+ {
+ return clientTryAgain();
+ }
+
+ bool serverStart(const QString &, QStringList *, const QString &)
+ {
+ return false;
+ }
+
+ int serverFirstStep(const QString &, const QByteArray *)
+ {
+ return Error;
+ }
+
+ QCA_SASLNeedParams clientParamsNeeded() const
+ {
+ return need;
+ }
+
+ void setClientParams(const QString *_user, const QString *_authzid, const QString *_pass, const QString *_realm)
+ {
+ if(_user) {
+ user = *_user;
+ need.user = false;
+ have.user = true;
+ }
+ if(_authzid) {
+ authz = *_authzid;
+ need.authzid = false;
+ have.authzid = true;
+ }
+ if(_pass) {
+ pass = *_pass;
+ need.pass = false;
+ have.pass = true;
+ }
+ if(_realm) {
+ realm = *_realm;
+ need.realm = false;
+ have.realm = true;
+ }
+ }
+
+ QString username() const
+ {
+ return QString();
+ }
+
+ QString authzid() const
+ {
+ return QString();
+ }
+
+ int nextStep(const QByteArray &in)
+ {
+ in_buf = in.copy();
+ return tryAgain();
+ }
+
+ int tryAgain()
+ {
+ return clientTryAgain();
+ }
+
+ QString mech() const
+ {
+ return out_mech;
+ }
+
+ const QByteArray *clientInit() const
+ {
+ return 0;
+ }
+
+ QByteArray result() const
+ {
+ return out_buf;
+ }
+
+ int clientTryAgain()
+ {
+ if(step == 0) {
+ out_mech = "DIGEST-MD5";
+ ++step;
+ return Continue;
+ }
+ else if(step == 1) {
+ // if we still need params, then the app has failed us!
+ if(need.user || need.authzid || need.pass || need.realm) {
+ err = -1;
+ return Error;
+ }
+
+ // see if some params are needed
+ if(!have.user)
+ need.user = true;
+ if(!have.authzid)
+ need.authzid = true;
+ if(!have.pass)
+ need.pass = true;
+ if(need.user || need.authzid || need.pass)
+ return NeedParams;
+
+ // get props
+ QCString cs(in_buf.data(), in_buf.size()+1);
+ PropList in;
+ if(!in.fromString(cs)) {
+ err = QCA::SASL::BadProto;
+ return Error;
+ }
+
+ // make a cnonce
+ QByteArray a(32);
+ for(int n = 0; n < (int)a.size(); ++n)
+ a[n] = (char)(256.0*rand()/(RAND_MAX+1.0));
+ QCString cnonce = Base64::arrayToString(a).latin1();
+
+ // make other variables
+ realm = host;
+ QCString nonce = in.get("nonce");
+ QCString nc = "00000001";
+ QCString uri = service.utf8() + '/' + host.utf8();
+ QCString qop = "auth";
+
+ // build 'response'
+ QCString X = user.utf8() + ':' + realm.utf8() + ':' + pass.utf8();
+ QByteArray Y = QCA::MD5::hash(X);
+ QCString tmp = QCString(":") + nonce + ':' + cnonce + ':' + authz.utf8();
+ QByteArray A1(Y.size() + tmp.length());
+ memcpy(A1.data(), Y.data(), Y.size());
+ memcpy(A1.data() + Y.size(), tmp.data(), tmp.length());
+ QCString A2 = "AUTHENTICATE:" + uri;
+ QCString HA1 = QCA::MD5::hashToString(A1).latin1();
+ QCString HA2 = QCA::MD5::hashToString(A2).latin1();
+ QCString KD = HA1 + ':' + nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + HA2;
+ QCString Z = QCA::MD5::hashToString(KD).latin1();
+
+ // build output
+ PropList out;
+ out.set("username", user.utf8());
+ out.set("realm", host.utf8());
+ out.set("nonce", nonce);
+ out.set("cnonce", cnonce);
+ out.set("nc", nc);
+ out.set("serv-type", service.utf8());
+ out.set("host", host.utf8());
+ out.set("digest-uri", uri);
+ out.set("qop", qop);
+ out.set("response", Z);
+ out.set("charset", "utf-8");
+ out.set("authzid", authz.utf8());
+ QCString s = out.toString();
+
+ // done
+ out_buf.resize(s.length());
+ memcpy(out_buf.data(), s.data(), out_buf.size());
+ ++step;
+ return Continue;
+ }
+ else {
+ out_buf.resize(0);
+ return Success;
+ }
+ }
+
+ bool encode(const QByteArray &a, QByteArray *b)
+ {
+ *b = a.copy();
+ return true;
+ }
+
+ bool decode(const QByteArray &a, QByteArray *b)
+ {
+ *b = a.copy();
+ return true;
+ }
+};
+
+class QCASimpleSASL : public QCAProvider
+{
+public:
+ QCASimpleSASL() {}
+ ~QCASimpleSASL() {}
+
+ void init()
+ {
+ }
+
+ int qcaVersion() const
+ {
+ return QCA_PLUGIN_VERSION;
+ }
+
+ int capabilities() const
+ {
+ return QCA::CAP_SASL;
+ }
+
+ void *context(int cap)
+ {
+ if(cap == QCA::CAP_SASL)
+ return new SimpleSASLContext;
+ return 0;
+ }
+};
+
+QCAProvider *createProviderSimpleSASL()
+{
+ return (new QCASimpleSASL);
+}
+
+}
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/simplesasl.h b/kopete/protocols/jabber/libiris/iris/xmpp-core/simplesasl.h
new file mode 100644
index 00000000..12a08c0e
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/simplesasl.h
@@ -0,0 +1,31 @@
+/*
+ * simplesasl.h - Simple SASL implementation
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef SIMPLESASL_H
+#define SIMPLESASL_H
+
+#include"qcaprovider.h"
+
+namespace XMPP
+{
+ QCAProvider *createProviderSimpleSASL();
+}
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/stream.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-core/stream.cpp
new file mode 100644
index 00000000..bfcc218c
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/stream.cpp
@@ -0,0 +1,1762 @@
+/*
+ * stream.cpp - handles a client stream
+ * 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
+ *
+ */
+
+/*
+ Notes:
+ - For Non-SASL auth (JEP-0078), username and resource fields are required.
+
+ TODO:
+ - sasl needParams is totally jacked? PLAIN requires authzid, etc
+ - server error handling
+ - reply with protocol errors if the client send something wrong
+ - don't necessarily disconnect on protocol error. prepare for more.
+ - server function
+ - deal with stream 'to' attribute dynamically
+ - flag tls/sasl/binding support dynamically (have the ability to specify extra stream:features)
+ - inform the caller about the user authentication information
+ - sasl security settings
+ - resource-binding interaction
+ - timeouts
+ - allow exchanges of non-standard stanzas
+ - send </stream:stream> even if we close prematurely?
+ - ensure ClientStream and child classes are fully deletable after signals
+ - xml:lang in root (<stream>) element
+ - sasl external
+ - sasl anonymous
+*/
+
+#include"xmpp.h"
+
+#include<qtextstream.h>
+#include<qguardedptr.h>
+#include<qtimer.h>
+#include<qca.h>
+#include<stdlib.h>
+#include"bytestream.h"
+#include"base64.h"
+#include"hash.h"
+#include"simplesasl.h"
+#include"securestream.h"
+#include"protocol.h"
+
+#ifdef XMPP_TEST
+#include"td.h"
+#endif
+
+//#define XMPP_DEBUG
+
+using namespace XMPP;
+
+static Debug *debug_ptr = 0;
+void XMPP::setDebug(Debug *p)
+{
+ debug_ptr = p;
+}
+
+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;
+}
+
+static QString genId()
+{
+ // need SHA1 here
+ if(!QCA::isSupported(QCA::CAP_SHA1))
+ QCA::insertProvider(createProviderHash());
+
+ return QCA::SHA1::hashToString(randomArray(128));
+}
+
+//----------------------------------------------------------------------------
+// Stanza
+//----------------------------------------------------------------------------
+Stanza::Error::Error(int _type, int _condition, const QString &_text, const QDomElement &_appSpec)
+{
+ type = _type;
+ condition = _condition;
+ text = _text;
+ appSpec = _appSpec;
+}
+
+class Stanza::Private
+{
+public:
+ struct ErrorTypeEntry
+ {
+ const char *str;
+ int type;
+ };
+ static ErrorTypeEntry errorTypeTable[];
+
+ struct ErrorCondEntry
+ {
+ const char *str;
+ int cond;
+ };
+ static ErrorCondEntry errorCondTable[];
+
+ static int stringToKind(const QString &s)
+ {
+ if(s == "message")
+ return Message;
+ else if(s == "presence")
+ return Presence;
+ else if(s == "iq")
+ return IQ;
+ else
+ return -1;
+ }
+
+ static QString kindToString(Kind k)
+ {
+ if(k == Message)
+ return "message";
+ else if(k == Presence)
+ return "presence";
+ else
+ return "iq";
+ }
+
+ static int stringToErrorType(const QString &s)
+ {
+ for(int n = 0; errorTypeTable[n].str; ++n) {
+ if(s == errorTypeTable[n].str)
+ return errorTypeTable[n].type;
+ }
+ return -1;
+ }
+
+ static QString errorTypeToString(int x)
+ {
+ for(int n = 0; errorTypeTable[n].str; ++n) {
+ if(x == errorTypeTable[n].type)
+ return errorTypeTable[n].str;
+ }
+ return QString();
+ }
+
+ static int stringToErrorCond(const QString &s)
+ {
+ for(int n = 0; errorCondTable[n].str; ++n) {
+ if(s == errorCondTable[n].str)
+ return errorCondTable[n].cond;
+ }
+ return -1;
+ }
+
+ static QString errorCondToString(int x)
+ {
+ for(int n = 0; errorCondTable[n].str; ++n) {
+ if(x == errorCondTable[n].cond)
+ return errorCondTable[n].str;
+ }
+ return QString();
+ }
+
+ Stream *s;
+ QDomElement e;
+};
+
+Stanza::Private::ErrorTypeEntry Stanza::Private::errorTypeTable[] =
+{
+ { "cancel", Cancel },
+ { "continue", Continue },
+ { "modify", Modify },
+ { "auth", Auth },
+ { "wait", Wait },
+ { 0, 0 },
+};
+
+Stanza::Private::ErrorCondEntry Stanza::Private::errorCondTable[] =
+{
+ { "bad-request", BadRequest },
+ { "conflict", Conflict },
+ { "feature-not-implemented", FeatureNotImplemented },
+ { "forbidden", Forbidden },
+ { "internal-server-error", InternalServerError },
+ { "item-not-found", ItemNotFound },
+ { "jid-malformed", JidMalformed },
+ { "not-allowed", NotAllowed },
+ { "payment-required", PaymentRequired },
+ { "recipient-unavailable", RecipientUnavailable },
+ { "registration-required", RegistrationRequired },
+ { "remote-server-not-found", ServerNotFound },
+ { "remote-server-timeout", ServerTimeout },
+ { "resource-constraint", ResourceConstraint },
+ { "service-unavailable", ServiceUnavailable },
+ { "subscription-required", SubscriptionRequired },
+ { "undefined-condition", UndefinedCondition },
+ { "unexpected-request", UnexpectedRequest },
+ { 0, 0 },
+};
+
+Stanza::Stanza()
+{
+ d = 0;
+}
+
+Stanza::Stanza(Stream *s, Kind k, const Jid &to, const QString &type, const QString &id)
+{
+ d = new Private;
+
+ Kind kind;
+ if(k == Message || k == Presence || k == IQ)
+ kind = k;
+ else
+ kind = Message;
+
+ d->s = s;
+ d->e = d->s->doc().createElementNS(s->baseNS(), Private::kindToString(kind));
+ if(to.isValid())
+ setTo(to);
+ if(!type.isEmpty())
+ setType(type);
+ if(!id.isEmpty())
+ setId(id);
+}
+
+Stanza::Stanza(Stream *s, const QDomElement &e)
+{
+ d = 0;
+ if(e.namespaceURI() != s->baseNS())
+ return;
+ int x = Private::stringToKind(e.tagName());
+ if(x == -1)
+ return;
+ d = new Private;
+ d->s = s;
+ d->e = e;
+}
+
+Stanza::Stanza(const Stanza &from)
+{
+ d = 0;
+ *this = from;
+}
+
+Stanza & Stanza::operator=(const Stanza &from)
+{
+ delete d;
+ d = 0;
+ if(from.d)
+ d = new Private(*from.d);
+ return *this;
+}
+
+Stanza::~Stanza()
+{
+ delete d;
+}
+
+bool Stanza::isNull() const
+{
+ return (d ? false: true);
+}
+
+QDomElement Stanza::element() const
+{
+ return d->e;
+}
+
+QString Stanza::toString() const
+{
+ return Stream::xmlToString(d->e);
+}
+
+QDomDocument & Stanza::doc() const
+{
+ return d->s->doc();
+}
+
+QString Stanza::baseNS() const
+{
+ return d->s->baseNS();
+}
+
+QString Stanza::xhtmlImNS() const
+{
+ return d->s->xhtmlImNS();
+}
+
+QString Stanza::xhtmlNS() const
+{
+ return d->s->xhtmlNS();
+}
+
+QDomElement Stanza::createElement(const QString &ns, const QString &tagName)
+{
+ return d->s->doc().createElementNS(ns, tagName);
+}
+
+QDomElement Stanza::createTextElement(const QString &ns, const QString &tagName, const QString &text)
+{
+ QDomElement e = d->s->doc().createElementNS(ns, tagName);
+ e.appendChild(d->s->doc().createTextNode(text));
+ return e;
+}
+
+QDomElement Stanza::createXHTMLElement(const QString &xHTML)
+{
+ QDomDocument doc;
+
+ doc.setContent(xHTML, true);
+ QDomElement root = doc.documentElement();
+ //QDomElement e;
+ return (root);
+}
+
+void Stanza::appendChild(const QDomElement &e)
+{
+ d->e.appendChild(e);
+}
+
+Stanza::Kind Stanza::kind() const
+{
+ return (Kind)Private::stringToKind(d->e.tagName());
+}
+
+void Stanza::setKind(Kind k)
+{
+ d->e.setTagName(Private::kindToString(k));
+}
+
+Jid Stanza::to() const
+{
+ return Jid(d->e.attribute("to"));
+}
+
+Jid Stanza::from() const
+{
+ return Jid(d->e.attribute("from"));
+}
+
+QString Stanza::id() const
+{
+ return d->e.attribute("id");
+}
+
+QString Stanza::type() const
+{
+ return d->e.attribute("type");
+}
+
+QString Stanza::lang() const
+{
+ return d->e.attributeNS(NS_XML, "lang", QString());
+}
+
+void Stanza::setTo(const Jid &j)
+{
+ d->e.setAttribute("to", j.full());
+}
+
+void Stanza::setFrom(const Jid &j)
+{
+ d->e.setAttribute("from", j.full());
+}
+
+void Stanza::setId(const QString &id)
+{
+ d->e.setAttribute("id", id);
+}
+
+void Stanza::setType(const QString &type)
+{
+ d->e.setAttribute("type", type);
+}
+
+void Stanza::setLang(const QString &lang)
+{
+ d->e.setAttribute("xml:lang", lang);
+}
+
+Stanza::Error Stanza::error() const
+{
+ Error err;
+ QDomElement e = d->e.elementsByTagNameNS(d->s->baseNS(), "error").item(0).toElement();
+ if(e.isNull())
+ return err;
+
+ // type
+ int x = Private::stringToErrorType(e.attribute("type"));
+ if(x != -1)
+ err.type = x;
+
+ // condition: find first element
+ QDomNodeList nl = e.childNodes();
+ QDomElement t;
+ uint n;
+ for(n = 0; n < nl.count(); ++n) {
+ QDomNode i = nl.item(n);
+ if(i.isElement()) {
+ t = i.toElement();
+ break;
+ }
+ }
+ if(!t.isNull() && t.namespaceURI() == NS_STANZAS) {
+ x = Private::stringToErrorCond(t.tagName());
+ if(x != -1)
+ err.condition = x;
+ }
+
+ // text
+ t = e.elementsByTagNameNS(NS_STANZAS, "text").item(0).toElement();
+ if(!t.isNull())
+ err.text = t.text();
+ else
+ err.text = e.text();
+
+ // appspec: find first non-standard namespaced element
+ nl = e.childNodes();
+ for(n = 0; n < nl.count(); ++n) {
+ QDomNode i = nl.item(n);
+ if(i.isElement() && i.namespaceURI() != NS_STANZAS) {
+ err.appSpec = i.toElement();
+ break;
+ }
+ }
+ return err;
+}
+
+void Stanza::setError(const Error &err)
+{
+ // create the element if necessary
+ QDomElement errElem = d->e.elementsByTagNameNS(d->s->baseNS(), "error").item(0).toElement();
+ if(errElem.isNull()) {
+ errElem = d->e.ownerDocument().createElementNS(d->s->baseNS(), "error");
+ d->e.appendChild(errElem);
+ }
+
+ // error type/condition
+ if(d->s->old()) {
+ errElem.setAttribute("code", QString::number(err.condition));
+ }
+ else {
+ QString stype = Private::errorTypeToString(err.type);
+ if(stype.isEmpty())
+ return;
+ QString scond = Private::errorCondToString(err.condition);
+ if(scond.isEmpty())
+ return;
+
+ errElem.setAttribute("type", stype);
+ errElem.appendChild(d->e.ownerDocument().createElementNS(d->s->baseNS(), scond));
+ }
+
+ // text
+ if(d->s->old()) {
+ errElem.appendChild(d->e.ownerDocument().createTextNode(err.text));
+ }
+ else {
+ QDomElement te = d->e.ownerDocument().createElementNS(d->s->baseNS(), "text");
+ te.appendChild(d->e.ownerDocument().createTextNode(err.text));
+ errElem.appendChild(te);
+ }
+
+ // application specific
+ errElem.appendChild(err.appSpec);
+}
+
+void Stanza::clearError()
+{
+ QDomElement errElem = d->e.elementsByTagNameNS(d->s->baseNS(), "error").item(0).toElement();
+ if(!errElem.isNull())
+ d->e.removeChild(errElem);
+}
+
+//----------------------------------------------------------------------------
+// Stream
+//----------------------------------------------------------------------------
+static XmlProtocol *foo = 0;
+Stream::Stream(QObject *parent)
+:QObject(parent)
+{
+}
+
+Stream::~Stream()
+{
+}
+
+Stanza Stream::createStanza(Stanza::Kind k, const Jid &to, const QString &type, const QString &id)
+{
+ return Stanza(this, k, to, type, id);
+}
+
+Stanza Stream::createStanza(const QDomElement &e)
+{
+ return Stanza(this, e);
+}
+
+QString Stream::xmlToString(const QDomElement &e, bool clip)
+{
+ if(!foo)
+ foo = new CoreProtocol;
+ return foo->elementToString(e, clip);
+}
+
+//----------------------------------------------------------------------------
+// ClientStream
+//----------------------------------------------------------------------------
+enum {
+ Idle,
+ Connecting,
+ WaitVersion,
+ WaitTLS,
+ NeedParams,
+ Active,
+ Closing
+};
+
+enum {
+ Client,
+ Server
+};
+
+class ClientStream::Private
+{
+public:
+ Private()
+ {
+ conn = 0;
+ bs = 0;
+ ss = 0;
+ tlsHandler = 0;
+ tls = 0;
+ sasl = 0;
+ in.setAutoDelete(true);
+
+ oldOnly = false;
+ allowPlain = false;
+ mutualAuth = false;
+ haveLocalAddr = false;
+ minimumSSF = 0;
+ maximumSSF = 0;
+ doBinding = true;
+
+ in_rrsig = false;
+
+ reset();
+ }
+
+ void reset()
+ {
+ state = Idle;
+ notify = 0;
+ newStanzas = false;
+ sasl_ssf = 0;
+ tls_warned = false;
+ using_tls = false;
+ }
+
+ Jid jid;
+ QString server;
+ bool oldOnly;
+ bool allowPlain, mutualAuth;
+ bool haveLocalAddr;
+ QHostAddress localAddr;
+ Q_UINT16 localPort;
+ int minimumSSF, maximumSSF;
+ QString sasl_mech;
+ bool doBinding;
+
+ bool in_rrsig;
+
+ Connector *conn;
+ ByteStream *bs;
+ TLSHandler *tlsHandler;
+ QCA::TLS *tls;
+ QCA::SASL *sasl;
+ SecureStream *ss;
+ CoreProtocol client;
+ CoreProtocol srv;
+
+ QString defRealm;
+
+ int mode;
+ int state;
+ int notify;
+ bool newStanzas;
+ int sasl_ssf;
+ bool tls_warned, using_tls;
+ bool doAuth;
+
+ QStringList sasl_mechlist;
+
+ int errCond;
+ QString errText;
+ QDomElement errAppSpec;
+
+ QPtrList<Stanza> in;
+
+ QTimer noopTimer;
+ int noop_time;
+};
+
+ClientStream::ClientStream(Connector *conn, TLSHandler *tlsHandler, QObject *parent)
+:Stream(parent)
+{
+ d = new Private;
+ d->mode = Client;
+ d->conn = conn;
+ connect(d->conn, SIGNAL(connected()), SLOT(cr_connected()));
+ connect(d->conn, SIGNAL(error()), SLOT(cr_error()));
+
+ d->noop_time = 0;
+ connect(&d->noopTimer, SIGNAL(timeout()), SLOT(doNoop()));
+
+ d->tlsHandler = tlsHandler;
+}
+
+ClientStream::ClientStream(const QString &host, const QString &defRealm, ByteStream *bs, QCA::TLS *tls, QObject *parent)
+:Stream(parent)
+{
+ d = new Private;
+ d->mode = Server;
+ d->bs = bs;
+ connect(d->bs, SIGNAL(connectionClosed()), SLOT(bs_connectionClosed()));
+ connect(d->bs, SIGNAL(delayedCloseFinished()), SLOT(bs_delayedCloseFinished()));
+ connect(d->bs, SIGNAL(error(int)), SLOT(bs_error(int)));
+
+ QByteArray spare = d->bs->read();
+
+ d->ss = new SecureStream(d->bs);
+ connect(d->ss, SIGNAL(readyRead()), SLOT(ss_readyRead()));
+ connect(d->ss, SIGNAL(bytesWritten(int)), SLOT(ss_bytesWritten(int)));
+ connect(d->ss, SIGNAL(tlsHandshaken()), SLOT(ss_tlsHandshaken()));
+ connect(d->ss, SIGNAL(tlsClosed()), SLOT(ss_tlsClosed()));
+ connect(d->ss, SIGNAL(error(int)), SLOT(ss_error(int)));
+
+ d->server = host;
+ d->defRealm = defRealm;
+
+ d->tls = tls;
+
+ d->srv.startClientIn(genId());
+ //d->srv.startServerIn(genId());
+ //d->state = Connecting;
+ //d->jid = Jid();
+ //d->server = QString();
+}
+
+ClientStream::~ClientStream()
+{
+ reset();
+ delete d;
+}
+
+void ClientStream::reset(bool all)
+{
+ d->reset();
+ d->noopTimer.stop();
+
+ // delete securestream
+ delete d->ss;
+ d->ss = 0;
+
+ // reset sasl
+ delete d->sasl;
+ d->sasl = 0;
+
+ // client
+ if(d->mode == Client) {
+ // reset tls
+ if(d->tlsHandler)
+ d->tlsHandler->reset();
+
+ // reset connector
+ if(d->bs) {
+ d->bs->close();
+ d->bs = 0;
+ }
+ d->conn->done();
+
+ // reset state machine
+ d->client.reset();
+ }
+ // server
+ else {
+ if(d->tls)
+ d->tls->reset();
+
+ if(d->bs) {
+ d->bs->close();
+ d->bs = 0;
+ }
+
+ d->srv.reset();
+ }
+
+ if(all)
+ d->in.clear();
+}
+
+Jid ClientStream::jid() const
+{
+ return d->jid;
+}
+
+void ClientStream::connectToServer(const Jid &jid, bool auth)
+{
+ reset(true);
+ d->state = Connecting;
+ d->jid = jid;
+ d->doAuth = auth;
+ d->server = d->jid.domain();
+
+ d->conn->connectToServer(d->server);
+}
+
+void ClientStream::continueAfterWarning()
+{
+ if(d->state == WaitVersion) {
+ // if we don't have TLS yet, then we're never going to get it
+ if(!d->tls_warned && !d->using_tls) {
+ d->tls_warned = true;
+ d->state = WaitTLS;
+ warning(WarnNoTLS);
+ return;
+ }
+ d->state = Connecting;
+ processNext();
+ }
+ else if(d->state == WaitTLS) {
+ d->state = Connecting;
+ processNext();
+ }
+}
+
+void ClientStream::accept()
+{
+ d->srv.host = d->server;
+ processNext();
+}
+
+bool ClientStream::isActive() const
+{
+ return (d->state != Idle) ? true: false;
+}
+
+bool ClientStream::isAuthenticated() const
+{
+ return (d->state == Active) ? true: false;
+}
+
+void ClientStream::setUsername(const QString &s)
+{
+ if(d->sasl)
+ d->sasl->setUsername(s);
+}
+
+void ClientStream::setPassword(const QString &s)
+{
+ if(d->client.old) {
+ d->client.setPassword(s);
+ }
+ else {
+ if(d->sasl)
+ d->sasl->setPassword(s);
+ }
+}
+
+void ClientStream::setRealm(const QString &s)
+{
+ if(d->sasl)
+ d->sasl->setRealm(s);
+}
+
+void ClientStream::continueAfterParams()
+{
+ if(d->state == NeedParams) {
+ d->state = Connecting;
+ if(d->client.old) {
+ processNext();
+ }
+ else {
+ if(d->sasl)
+ d->sasl->continueAfterParams();
+ }
+ }
+}
+
+void ClientStream::setResourceBinding(bool b)
+{
+ d->doBinding = b;
+}
+
+void ClientStream::setNoopTime(int mills)
+{
+ d->noop_time = mills;
+
+ if(d->state != Active)
+ return;
+
+ if(d->noop_time == 0) {
+ d->noopTimer.stop();
+ return;
+ }
+ d->noopTimer.start(d->noop_time);
+}
+
+QString ClientStream::saslMechanism() const
+{
+ return d->client.saslMech();
+}
+
+int ClientStream::saslSSF() const
+{
+ return d->sasl_ssf;
+}
+
+void ClientStream::setSASLMechanism(const QString &s)
+{
+ d->sasl_mech = s;
+}
+
+void ClientStream::setLocalAddr(const QHostAddress &addr, Q_UINT16 port)
+{
+ d->haveLocalAddr = true;
+ d->localAddr = addr;
+ d->localPort = port;
+}
+
+int ClientStream::errorCondition() const
+{
+ return d->errCond;
+}
+
+QString ClientStream::errorText() const
+{
+ return d->errText;
+}
+
+QDomElement ClientStream::errorAppSpec() const
+{
+ return d->errAppSpec;
+}
+
+bool ClientStream::old() const
+{
+ return d->client.old;
+}
+
+void ClientStream::close()
+{
+ if(d->state == Active) {
+ d->state = Closing;
+ d->client.shutdown();
+ processNext();
+ }
+ else if(d->state != Idle && d->state != Closing) {
+ reset();
+ }
+}
+
+QDomDocument & ClientStream::doc() const
+{
+ return d->client.doc;
+}
+
+QString ClientStream::baseNS() const
+{
+ return NS_CLIENT;
+}
+
+QString ClientStream::xhtmlImNS() const
+{
+ return NS_XHTML_IM;
+}
+
+QString ClientStream::xhtmlNS() const
+{
+ return NS_XHTML;
+}
+
+void ClientStream::setAllowPlain(bool b)
+{
+ d->allowPlain = b;
+}
+
+void ClientStream::setRequireMutualAuth(bool b)
+{
+ d->mutualAuth = b;
+}
+
+void ClientStream::setSSFRange(int low, int high)
+{
+ d->minimumSSF = low;
+ d->maximumSSF = high;
+}
+
+void ClientStream::setOldOnly(bool b)
+{
+ d->oldOnly = b;
+}
+
+bool ClientStream::stanzaAvailable() const
+{
+ return (!d->in.isEmpty());
+}
+
+Stanza ClientStream::read()
+{
+ if(d->in.isEmpty())
+ return Stanza();
+ else {
+ Stanza *sp = d->in.getFirst();
+ Stanza s = *sp;
+ d->in.removeRef(sp);
+ return s;
+ }
+}
+
+void ClientStream::write(const Stanza &s)
+{
+ if(d->state == Active) {
+ d->client.sendStanza(s.element());
+ processNext();
+ }
+}
+
+void ClientStream::cr_connected()
+{
+ d->bs = d->conn->stream();
+ connect(d->bs, SIGNAL(connectionClosed()), SLOT(bs_connectionClosed()));
+ connect(d->bs, SIGNAL(delayedCloseFinished()), SLOT(bs_delayedCloseFinished()));
+
+ QByteArray spare = d->bs->read();
+
+ d->ss = new SecureStream(d->bs);
+ connect(d->ss, SIGNAL(readyRead()), SLOT(ss_readyRead()));
+ connect(d->ss, SIGNAL(bytesWritten(int)), SLOT(ss_bytesWritten(int)));
+ connect(d->ss, SIGNAL(tlsHandshaken()), SLOT(ss_tlsHandshaken()));
+ connect(d->ss, SIGNAL(tlsClosed()), SLOT(ss_tlsClosed()));
+ connect(d->ss, SIGNAL(error(int)), SLOT(ss_error(int)));
+
+ //d->client.startDialbackOut("andbit.net", "im.pyxa.org");
+ //d->client.startServerOut(d->server);
+
+ d->client.startClientOut(d->jid, d->oldOnly, d->conn->useSSL(), d->doAuth);
+ d->client.setAllowTLS(d->tlsHandler ? true: false);
+ d->client.setAllowBind(d->doBinding);
+ d->client.setAllowPlain(d->allowPlain);
+
+ /*d->client.jid = d->jid;
+ d->client.server = d->server;
+ d->client.allowPlain = d->allowPlain;
+ d->client.oldOnly = d->oldOnly;
+ d->client.sasl_mech = d->sasl_mech;
+ d->client.doTLS = d->tlsHandler ? true: false;
+ d->client.doBinding = d->doBinding;*/
+
+ QGuardedPtr<QObject> self = this;
+ connected();
+ if(!self)
+ return;
+
+ // immediate SSL?
+ if(d->conn->useSSL()) {
+ d->using_tls = true;
+ d->ss->startTLSClient(d->tlsHandler, d->server, spare);
+ }
+ else {
+ d->client.addIncomingData(spare);
+ processNext();
+ }
+}
+
+void ClientStream::cr_error()
+{
+ reset();
+ error(ErrConnection);
+}
+
+void ClientStream::bs_connectionClosed()
+{
+ reset();
+ connectionClosed();
+}
+
+void ClientStream::bs_delayedCloseFinished()
+{
+ // we don't care about this (we track all important data ourself)
+}
+
+void ClientStream::bs_error(int)
+{
+ // TODO
+}
+
+void ClientStream::ss_readyRead()
+{
+ QByteArray a = d->ss->read();
+
+#ifdef XMPP_DEBUG
+ QCString cs(a.data(), a.size()+1);
+ fprintf(stderr, "ClientStream: recv: %d [%s]\n", a.size(), cs.data());
+#endif
+
+ if(d->mode == Client)
+ d->client.addIncomingData(a);
+ else
+ d->srv.addIncomingData(a);
+ if(d->notify & CoreProtocol::NRecv) {
+#ifdef XMPP_DEBUG
+ printf("We needed data, so let's process it\n");
+#endif
+ processNext();
+ }
+}
+
+void ClientStream::ss_bytesWritten(int bytes)
+{
+ if(d->mode == Client)
+ d->client.outgoingDataWritten(bytes);
+ else
+ d->srv.outgoingDataWritten(bytes);
+
+ if(d->notify & CoreProtocol::NSend) {
+#ifdef XMPP_DEBUG
+ printf("We were waiting for data to be written, so let's process\n");
+#endif
+ processNext();
+ }
+}
+
+void ClientStream::ss_tlsHandshaken()
+{
+ QGuardedPtr<QObject> self = this;
+ securityLayerActivated(LayerTLS);
+ if(!self)
+ return;
+ processNext();
+}
+
+void ClientStream::ss_tlsClosed()
+{
+ reset();
+ connectionClosed();
+}
+
+void ClientStream::ss_error(int x)
+{
+ if(x == SecureStream::ErrTLS) {
+ reset();
+ d->errCond = TLSFail;
+ error(ErrTLS);
+ }
+ else {
+ reset();
+ error(ErrSecurityLayer);
+ }
+}
+
+void ClientStream::sasl_clientFirstStep(const QString &mech, const QByteArray *stepData)
+{
+ d->client.setSASLFirst(mech, stepData ? *stepData : QByteArray());
+ //d->client.sasl_mech = mech;
+ //d->client.sasl_firstStep = stepData ? true : false;
+ //d->client.sasl_step = stepData ? *stepData : QByteArray();
+
+ processNext();
+}
+
+void ClientStream::sasl_nextStep(const QByteArray &stepData)
+{
+ if(d->mode == Client)
+ d->client.setSASLNext(stepData);
+ //d->client.sasl_step = stepData;
+ else
+ d->srv.setSASLNext(stepData);
+ //d->srv.sasl_step = stepData;
+
+ processNext();
+}
+
+void ClientStream::sasl_needParams(bool user, bool authzid, bool pass, bool realm)
+{
+#ifdef XMPP_DEBUG
+ printf("need params: %d,%d,%d,%d\n", user, authzid, pass, realm);
+#endif
+ if(authzid && !user) {
+ d->sasl->setAuthzid(d->jid.bare());
+ //d->sasl->setAuthzid("infiniti.homelesshackers.org");
+ }
+ if(user || pass || realm) {
+ d->state = NeedParams;
+ needAuthParams(user, pass, realm);
+ }
+ else
+ d->sasl->continueAfterParams();
+}
+
+void ClientStream::sasl_authCheck(const QString &user, const QString &)
+{
+//#ifdef XMPP_DEBUG
+// printf("authcheck: [%s], [%s]\n", user.latin1(), authzid.latin1());
+//#endif
+ QString u = user;
+ int n = u.find('@');
+ if(n != -1)
+ u.truncate(n);
+ d->srv.user = u;
+ d->sasl->continueAfterAuthCheck();
+}
+
+void ClientStream::sasl_authenticated()
+{
+#ifdef XMPP_DEBUG
+ printf("sasl authed!!\n");
+#endif
+ d->sasl_ssf = d->sasl->ssf();
+
+ if(d->mode == Server) {
+ d->srv.setSASLAuthed();
+ processNext();
+ }
+}
+
+void ClientStream::sasl_error(int)
+{
+//#ifdef XMPP_DEBUG
+// printf("sasl error: %d\n", c);
+//#endif
+ // has to be auth error
+ int x = convertedSASLCond();
+ reset();
+ d->errCond = x;
+ error(ErrAuth);
+}
+
+void ClientStream::srvProcessNext()
+{
+ while(1) {
+ printf("Processing step...\n");
+ if(!d->srv.processStep()) {
+ int need = d->srv.need;
+ if(need == CoreProtocol::NNotify) {
+ d->notify = d->srv.notify;
+ if(d->notify & CoreProtocol::NSend)
+ printf("More data needs to be written to process next step\n");
+ if(d->notify & CoreProtocol::NRecv)
+ printf("More data is needed to process next step\n");
+ }
+ else if(need == CoreProtocol::NSASLMechs) {
+ if(!d->sasl) {
+ d->sasl = new QCA::SASL;
+ connect(d->sasl, SIGNAL(authCheck(const QString &, const QString &)), SLOT(sasl_authCheck(const QString &, const QString &)));
+ connect(d->sasl, SIGNAL(nextStep(const QByteArray &)), SLOT(sasl_nextStep(const QByteArray &)));
+ connect(d->sasl, SIGNAL(authenticated()), SLOT(sasl_authenticated()));
+ connect(d->sasl, SIGNAL(error(int)), SLOT(sasl_error(int)));
+
+ //d->sasl->setAllowAnonymous(false);
+ //d->sasl->setRequirePassCredentials(true);
+ //d->sasl->setExternalAuthID("localhost");
+
+ d->sasl->setMinimumSSF(0);
+ d->sasl->setMaximumSSF(256);
+
+ QStringList list;
+ // TODO: d->server is probably wrong here
+ if(!d->sasl->startServer("xmpp", d->server, d->defRealm, &list)) {
+ printf("Error initializing SASL\n");
+ return;
+ }
+ d->sasl_mechlist = list;
+ }
+ d->srv.setSASLMechList(d->sasl_mechlist);
+ continue;
+ }
+ else if(need == CoreProtocol::NStartTLS) {
+ printf("Need StartTLS\n");
+ if(!d->tls->startServer()) {
+ printf("unable to start server!\n");
+ // TODO
+ return;
+ }
+ QByteArray a = d->srv.spare;
+ d->ss->startTLSServer(d->tls, a);
+ }
+ else if(need == CoreProtocol::NSASLFirst) {
+ printf("Need SASL First Step\n");
+ QByteArray a = d->srv.saslStep();
+ d->sasl->putServerFirstStep(d->srv.saslMech(), a);
+ }
+ else if(need == CoreProtocol::NSASLNext) {
+ printf("Need SASL Next Step\n");
+ QByteArray a = d->srv.saslStep();
+ QCString cs(a.data(), a.size()+1);
+ printf("[%s]\n", cs.data());
+ d->sasl->putStep(a);
+ }
+ else if(need == CoreProtocol::NSASLLayer) {
+ }
+
+ // now we can announce stanzas
+ //if(!d->in.isEmpty())
+ // readyRead();
+ return;
+ }
+
+ d->notify = 0;
+
+ int event = d->srv.event;
+ printf("event: %d\n", event);
+ switch(event) {
+ case CoreProtocol::EError: {
+ printf("Error! Code=%d\n", d->srv.errorCode);
+ reset();
+ error(ErrProtocol);
+ //handleError();
+ return;
+ }
+ case CoreProtocol::ESend: {
+ QByteArray a = d->srv.takeOutgoingData();
+ QCString cs(a.size()+1);
+ memcpy(cs.data(), a.data(), a.size());
+ printf("Need Send: {%s}\n", cs.data());
+ d->ss->write(a);
+ break;
+ }
+ case CoreProtocol::ERecvOpen: {
+ printf("Break (RecvOpen)\n");
+
+ // calculate key
+ QCString str = QCA::SHA1::hashToString("secret").utf8();
+ str = QCA::SHA1::hashToString(str + "im.pyxa.org").utf8();
+ str = QCA::SHA1::hashToString(str + d->srv.id.utf8()).utf8();
+ d->srv.setDialbackKey(str);
+
+ //d->srv.setDialbackKey("3c5d721ea2fcc45b163a11420e4e358f87e3142a");
+
+ if(d->srv.to != d->server) {
+ // host-gone, host-unknown, see-other-host
+ d->srv.shutdownWithError(CoreProtocol::HostUnknown);
+ }
+ else
+ d->srv.setFrom(d->server);
+ break;
+ }
+ case CoreProtocol::ESASLSuccess: {
+ printf("Break SASL Success\n");
+ disconnect(d->sasl, SIGNAL(error(int)), this, SLOT(sasl_error(int)));
+ QByteArray a = d->srv.spare;
+ d->ss->setLayerSASL(d->sasl, a);
+ break;
+ }
+ case CoreProtocol::EPeerClosed: {
+ // TODO: this isn' an error
+ printf("peer closed\n");
+ reset();
+ error(ErrProtocol);
+ return;
+ }
+ }
+ }
+}
+
+void ClientStream::doReadyRead()
+{
+ //QGuardedPtr<QObject> self = this;
+ readyRead();
+ //if(!self)
+ // return;
+ //d->in_rrsig = false;
+}
+
+void ClientStream::processNext()
+{
+ if(d->mode == Server) {
+ srvProcessNext();
+ return;
+ }
+
+ QGuardedPtr<QObject> self = this;
+
+ while(1) {
+#ifdef XMPP_DEBUG
+ printf("Processing step...\n");
+#endif
+ bool ok = d->client.processStep();
+ // deal with send/received items
+ for(QValueList<XmlProtocol::TransferItem>::ConstIterator it = d->client.transferItemList.begin(); it != d->client.transferItemList.end(); ++it) {
+ const XmlProtocol::TransferItem &i = *it;
+ if(i.isExternal)
+ continue;
+ QString str;
+ if(i.isString) {
+ // skip whitespace pings
+ if(i.str.stripWhiteSpace().isEmpty())
+ continue;
+ str = i.str;
+ }
+ else
+ str = d->client.elementToString(i.elem);
+ if(i.isSent)
+ outgoingXml(str);
+ else
+ incomingXml(str);
+ }
+
+ if(!ok) {
+ bool cont = handleNeed();
+
+ // now we can announce stanzas
+ //if(!d->in_rrsig && !d->in.isEmpty()) {
+ if(!d->in.isEmpty()) {
+ //d->in_rrsig = true;
+ QTimer::singleShot(0, this, SLOT(doReadyRead()));
+ }
+
+ if(cont)
+ continue;
+ return;
+ }
+
+ int event = d->client.event;
+ d->notify = 0;
+ switch(event) {
+ case CoreProtocol::EError: {
+#ifdef XMPP_DEBUG
+ printf("Error! Code=%d\n", d->client.errorCode);
+#endif
+ handleError();
+ return;
+ }
+ case CoreProtocol::ESend: {
+ QByteArray a = d->client.takeOutgoingData();
+#ifdef XMPP_DEBUG
+ QCString cs(a.size()+1);
+ memcpy(cs.data(), a.data(), a.size());
+ printf("Need Send: {%s}\n", cs.data());
+#endif
+ d->ss->write(a);
+ break;
+ }
+ case CoreProtocol::ERecvOpen: {
+#ifdef XMPP_DEBUG
+ printf("Break (RecvOpen)\n");
+#endif
+
+#ifdef XMPP_TEST
+ QString s = QString("handshake success (lang=[%1]").arg(d->client.lang);
+ if(!d->client.from.isEmpty())
+ s += QString(", from=[%1]").arg(d->client.from);
+ s += ')';
+ TD::msg(s);
+#endif
+
+ if(d->client.old) {
+ d->state = WaitVersion;
+ warning(WarnOldVersion);
+ return;
+ }
+ break;
+ }
+ case CoreProtocol::EFeatures: {
+#ifdef XMPP_DEBUG
+ printf("Break (Features)\n");
+#endif
+ if(!d->tls_warned && !d->using_tls && !d->client.features.tls_supported) {
+ d->tls_warned = true;
+ d->state = WaitTLS;
+ warning(WarnNoTLS);
+ return;
+ }
+ break;
+ }
+ case CoreProtocol::ESASLSuccess: {
+#ifdef XMPP_DEBUG
+ printf("Break SASL Success\n");
+#endif
+ break;
+ }
+ case CoreProtocol::EReady: {
+#ifdef XMPP_DEBUG
+ printf("Done!\n");
+#endif
+ // grab the JID, in case it changed
+ // TODO: d->jid = d->client.jid;
+ d->state = Active;
+ setNoopTime(d->noop_time);
+ authenticated();
+ if(!self)
+ return;
+ break;
+ }
+ case CoreProtocol::EPeerClosed: {
+#ifdef XMPP_DEBUG
+ printf("DocumentClosed\n");
+#endif
+ reset();
+ connectionClosed();
+ return;
+ }
+ case CoreProtocol::EStanzaReady: {
+#ifdef XMPP_DEBUG
+ printf("StanzaReady\n");
+#endif
+ // store the stanza for now, announce after processing all events
+ Stanza s = createStanza(d->client.recvStanza());
+ if(s.isNull())
+ break;
+ d->in.append(new Stanza(s));
+ break;
+ }
+ case CoreProtocol::EStanzaSent: {
+#ifdef XMPP_DEBUG
+ printf("StanzasSent\n");
+#endif
+ stanzaWritten();
+ if(!self)
+ return;
+ break;
+ }
+ case CoreProtocol::EClosed: {
+#ifdef XMPP_DEBUG
+ printf("Closed\n");
+#endif
+ reset();
+ delayedCloseFinished();
+ return;
+ }
+ }
+ }
+}
+
+bool ClientStream::handleNeed()
+{
+ int need = d->client.need;
+ if(need == CoreProtocol::NNotify) {
+ d->notify = d->client.notify;
+#ifdef XMPP_DEBUG
+ if(d->notify & CoreProtocol::NSend)
+ printf("More data needs to be written to process next step\n");
+ if(d->notify & CoreProtocol::NRecv)
+ printf("More data is needed to process next step\n");
+#endif
+ return false;
+ }
+
+ d->notify = 0;
+ switch(need) {
+ case CoreProtocol::NStartTLS: {
+#ifdef XMPP_DEBUG
+ printf("Need StartTLS\n");
+#endif
+ d->using_tls = true;
+ d->ss->startTLSClient(d->tlsHandler, d->server, d->client.spare);
+ return false;
+ }
+ case CoreProtocol::NSASLFirst: {
+#ifdef XMPP_DEBUG
+ printf("Need SASL First Step\n");
+#endif
+ // no SASL plugin? fall back to Simple SASL
+ if(!QCA::isSupported(QCA::CAP_SASL)) {
+ // Simple SASL needs MD5. do we have that either?
+ if(!QCA::isSupported(QCA::CAP_MD5))
+ QCA::insertProvider(createProviderHash());
+ QCA::insertProvider(createProviderSimpleSASL());
+ }
+
+ d->sasl = new QCA::SASL;
+ connect(d->sasl, SIGNAL(clientFirstStep(const QString &, const QByteArray *)), SLOT(sasl_clientFirstStep(const QString &, const QByteArray *)));
+ connect(d->sasl, SIGNAL(nextStep(const QByteArray &)), SLOT(sasl_nextStep(const QByteArray &)));
+ connect(d->sasl, SIGNAL(needParams(bool, bool, bool, bool)), SLOT(sasl_needParams(bool, bool, bool, bool)));
+ connect(d->sasl, SIGNAL(authenticated()), SLOT(sasl_authenticated()));
+ connect(d->sasl, SIGNAL(error(int)), SLOT(sasl_error(int)));
+
+ if(d->haveLocalAddr)
+ d->sasl->setLocalAddr(d->localAddr, d->localPort);
+ if(d->conn->havePeerAddress())
+ d->sasl->setRemoteAddr(d->conn->peerAddress(), d->conn->peerPort());
+
+ d->sasl->setAllowAnonymous(false);
+
+ //d->sasl_mech = "ANONYMOUS";
+ //d->sasl->setRequirePassCredentials(true);
+
+ //d->sasl->setExternalAuthID("localhost");
+ //d->sasl->setExternalSSF(64);
+ //d->sasl_mech = "EXTERNAL";
+
+ d->sasl->setAllowPlain(d->allowPlain);
+ d->sasl->setRequireMutualAuth(d->mutualAuth);
+
+ d->sasl->setMinimumSSF(d->minimumSSF);
+ d->sasl->setMaximumSSF(d->maximumSSF);
+
+ QStringList ml;
+ if(!d->sasl_mech.isEmpty())
+ ml += d->sasl_mech;
+ else
+ ml = d->client.features.sasl_mechs;
+
+ if(!d->sasl->startClient("xmpp", d->server, ml, true)) {
+ int x = convertedSASLCond();
+ reset();
+ d->errCond = x;
+ error(ErrAuth);
+ return false;
+ }
+ return false;
+ }
+ case CoreProtocol::NSASLNext: {
+#ifdef XMPP_DEBUG
+ printf("Need SASL Next Step\n");
+#endif
+ QByteArray a = d->client.saslStep();
+ d->sasl->putStep(a);
+ return false;
+ }
+ case CoreProtocol::NSASLLayer: {
+ // SecureStream will handle the errors from this point
+ disconnect(d->sasl, SIGNAL(error(int)), this, SLOT(sasl_error(int)));
+ d->ss->setLayerSASL(d->sasl, d->client.spare);
+ if(d->sasl_ssf > 0) {
+ QGuardedPtr<QObject> self = this;
+ securityLayerActivated(LayerSASL);
+ if(!self)
+ return false;
+ }
+ break;
+ }
+ case CoreProtocol::NPassword: {
+#ifdef XMPP_DEBUG
+ printf("Need Password\n");
+#endif
+ d->state = NeedParams;
+ needAuthParams(false, true, false);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int ClientStream::convertedSASLCond() const
+{
+ int x = d->sasl->errorCondition();
+ if(x == QCA::SASL::NoMech)
+ return NoMech;
+ else if(x == QCA::SASL::BadProto)
+ return BadProto;
+ else if(x == QCA::SASL::BadServ)
+ return BadServ;
+ else if(x == QCA::SASL::TooWeak)
+ return MechTooWeak;
+ else
+ return GenericAuthError;
+}
+
+void ClientStream::doNoop()
+{
+ if(d->state == Active) {
+#ifdef XMPP_DEBUG
+ printf("doPing\n");
+#endif
+ d->client.sendWhitespace();
+ processNext();
+ }
+}
+
+void ClientStream::writeDirect(const QString &s)
+{
+ if(d->state == Active) {
+#ifdef XMPP_DEBUG
+ printf("writeDirect\n");
+#endif
+ d->client.sendDirect(s);
+ processNext();
+ }
+}
+
+void ClientStream::handleError()
+{
+ int c = d->client.errorCode;
+ if(c == CoreProtocol::ErrParse) {
+ reset();
+ error(ErrParse);
+ }
+ else if(c == CoreProtocol::ErrProtocol) {
+ reset();
+ error(ErrProtocol);
+ }
+ else if(c == CoreProtocol::ErrStream) {
+ int x = d->client.errCond;
+ QString text = d->client.errText;
+ QDomElement appSpec = d->client.errAppSpec;
+
+ int connErr = -1;
+ int strErr = -1;
+
+ switch(x) {
+ case CoreProtocol::BadFormat: { break; } // should NOT happen (we send the right format)
+ case CoreProtocol::BadNamespacePrefix: { break; } // should NOT happen (we send prefixes)
+ case CoreProtocol::Conflict: { strErr = Conflict; break; }
+ case CoreProtocol::ConnectionTimeout: { strErr = ConnectionTimeout; break; }
+ case CoreProtocol::HostGone: { connErr = HostGone; break; }
+ case CoreProtocol::HostUnknown: { connErr = HostUnknown; break; }
+ case CoreProtocol::ImproperAddressing: { break; } // should NOT happen (we aren't a server)
+ case CoreProtocol::InternalServerError: { strErr = InternalServerError; break; }
+ case CoreProtocol::InvalidFrom: { strErr = InvalidFrom; break; }
+ case CoreProtocol::InvalidId: { break; } // should NOT happen (clients don't specify id)
+ case CoreProtocol::InvalidNamespace: { break; } // should NOT happen (we set the right ns)
+ case CoreProtocol::InvalidXml: { strErr = InvalidXml; break; } // shouldn't happen either, but just in case ...
+ case CoreProtocol::StreamNotAuthorized: { break; } // should NOT happen (we're not stupid)
+ case CoreProtocol::PolicyViolation: { strErr = PolicyViolation; break; }
+ case CoreProtocol::RemoteConnectionFailed: { connErr = RemoteConnectionFailed; break; }
+ case CoreProtocol::ResourceConstraint: { strErr = ResourceConstraint; break; }
+ case CoreProtocol::RestrictedXml: { strErr = InvalidXml; break; } // group with this one
+ case CoreProtocol::SeeOtherHost: { connErr = SeeOtherHost; break; }
+ case CoreProtocol::SystemShutdown: { strErr = SystemShutdown; break; }
+ case CoreProtocol::UndefinedCondition: { break; } // leave as null error
+ case CoreProtocol::UnsupportedEncoding: { break; } // should NOT happen (we send good encoding)
+ case CoreProtocol::UnsupportedStanzaType: { break; } // should NOT happen (we're not stupid)
+ case CoreProtocol::UnsupportedVersion: { connErr = UnsupportedVersion; break; }
+ case CoreProtocol::XmlNotWellFormed: { strErr = InvalidXml; break; } // group with this one
+ default: { break; }
+ }
+
+ reset();
+
+ d->errText = text;
+ d->errAppSpec = appSpec;
+ if(connErr != -1) {
+ d->errCond = connErr;
+ error(ErrNeg);
+ }
+ else {
+ if(strErr != -1)
+ d->errCond = strErr;
+ else
+ d->errCond = GenericStreamError;
+ error(ErrStream);
+ }
+ }
+ else if(c == CoreProtocol::ErrStartTLS) {
+ reset();
+ d->errCond = TLSStart;
+ error(ErrTLS);
+ }
+ else if(c == CoreProtocol::ErrAuth) {
+ int x = d->client.errCond;
+ int r = GenericAuthError;
+ if(d->client.old) {
+ if(x == 401) // not authorized
+ r = NotAuthorized;
+ else if(x == 409) // conflict
+ r = GenericAuthError;
+ else if(x == 406) // not acceptable (this should NOT happen)
+ r = GenericAuthError;
+ }
+ else {
+ switch(x) {
+ case CoreProtocol::Aborted: { r = GenericAuthError; break; } // should NOT happen (we never send <abort/>)
+ case CoreProtocol::IncorrectEncoding: { r = GenericAuthError; break; } // should NOT happen
+ case CoreProtocol::InvalidAuthzid: { r = InvalidAuthzid; break; }
+ case CoreProtocol::InvalidMech: { r = InvalidMech; break; }
+ case CoreProtocol::MechTooWeak: { r = MechTooWeak; break; }
+ case CoreProtocol::NotAuthorized: { r = NotAuthorized; break; }
+ case CoreProtocol::TemporaryAuthFailure: { r = TemporaryAuthFailure; break; }
+ }
+ }
+ reset();
+ d->errCond = r;
+ error(ErrAuth);
+ }
+ else if(c == CoreProtocol::ErrPlain) {
+ reset();
+ d->errCond = NoMech;
+ error(ErrAuth);
+ }
+ else if(c == CoreProtocol::ErrBind) {
+ int r = -1;
+ if(d->client.errCond == CoreProtocol::BindBadRequest) {
+ // should NOT happen
+ }
+ else if(d->client.errCond == CoreProtocol::BindNotAllowed) {
+ r = BindNotAllowed;
+ }
+ else if(d->client.errCond == CoreProtocol::BindConflict) {
+ r = BindConflict;
+ }
+
+ if(r != -1) {
+ reset();
+ d->errCond = r;
+ error(ErrBind);
+ }
+ else {
+ reset();
+ error(ErrProtocol);
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+// Debug
+//----------------------------------------------------------------------------
+Debug::~Debug()
+{
+}
+
+#ifdef XMPP_TEST
+TD::TD()
+{
+}
+
+TD::~TD()
+{
+}
+
+void TD::msg(const QString &s)
+{
+ if(debug_ptr)
+ debug_ptr->msg(s);
+}
+
+void TD::outgoingTag(const QString &s)
+{
+ if(debug_ptr)
+ debug_ptr->outgoingTag(s);
+}
+
+void TD::incomingTag(const QString &s)
+{
+ if(debug_ptr)
+ debug_ptr->incomingTag(s);
+}
+
+void TD::outgoingXml(const QDomElement &e)
+{
+ if(debug_ptr)
+ debug_ptr->outgoingXml(e);
+}
+
+void TD::incomingXml(const QDomElement &e)
+{
+ if(debug_ptr)
+ debug_ptr->incomingXml(e);
+}
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/td.h b/kopete/protocols/jabber/libiris/iris/xmpp-core/td.h
new file mode 100644
index 00000000..b636e190
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/td.h
@@ -0,0 +1,20 @@
+#ifndef TESTDEBUG_H
+#define TESTDEBUG_H
+
+#include<qdom.h>
+
+class TD
+{
+public:
+ TD();
+ ~TD();
+
+ static void msg(const QString &);
+ static void outgoingTag(const QString &);
+ static void incomingTag(const QString &);
+ static void outgoingXml(const QDomElement &);
+ static void incomingXml(const QDomElement &);
+};
+
+#endif
+
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/tlshandler.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-core/tlshandler.cpp
new file mode 100644
index 00000000..f3ac0067
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/tlshandler.cpp
@@ -0,0 +1,138 @@
+/*
+ * tlshandler.cpp - abstract wrapper for TLS
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include"xmpp.h"
+
+#include<qtimer.h>
+#include"qca.h"
+
+using namespace XMPP;
+
+//----------------------------------------------------------------------------
+// TLSHandler
+//----------------------------------------------------------------------------
+TLSHandler::TLSHandler(QObject *parent)
+:QObject(parent)
+{
+}
+
+TLSHandler::~TLSHandler()
+{
+}
+
+
+//----------------------------------------------------------------------------
+// QCATLSHandler
+//----------------------------------------------------------------------------
+class QCATLSHandler::Private
+{
+public:
+ QCA::TLS *tls;
+ int state, err;
+};
+
+QCATLSHandler::QCATLSHandler(QCA::TLS *parent)
+:TLSHandler(parent)
+{
+ d = new Private;
+ d->tls = parent;
+ connect(d->tls, SIGNAL(handshaken()), SLOT(tls_handshaken()));
+ connect(d->tls, SIGNAL(readyRead()), SLOT(tls_readyRead()));
+ connect(d->tls, SIGNAL(readyReadOutgoing(int)), SLOT(tls_readyReadOutgoing(int)));
+ connect(d->tls, SIGNAL(closed()), SLOT(tls_closed()));
+ connect(d->tls, SIGNAL(error(int)), SLOT(tls_error(int)));
+ d->state = 0;
+ d->err = -1;
+}
+
+QCATLSHandler::~QCATLSHandler()
+{
+ delete d;
+}
+
+QCA::TLS *QCATLSHandler::tls() const
+{
+ return d->tls;
+}
+
+int QCATLSHandler::tlsError() const
+{
+ return d->err;
+}
+
+void QCATLSHandler::reset()
+{
+ d->tls->reset();
+ d->state = 0;
+}
+
+void QCATLSHandler::startClient(const QString &host)
+{
+ d->state = 0;
+ d->err = -1;
+ if(!d->tls->startClient(host))
+ QTimer::singleShot(0, this, SIGNAL(fail()));
+}
+
+void QCATLSHandler::write(const QByteArray &a)
+{
+ d->tls->write(a);
+}
+
+void QCATLSHandler::writeIncoming(const QByteArray &a)
+{
+ d->tls->writeIncoming(a);
+}
+
+void QCATLSHandler::continueAfterHandshake()
+{
+ if(d->state == 2) {
+ success();
+ d->state = 3;
+ }
+}
+
+void QCATLSHandler::tls_handshaken()
+{
+ d->state = 2;
+ tlsHandshaken();
+}
+
+void QCATLSHandler::tls_readyRead()
+{
+ readyRead(d->tls->read());
+}
+
+void QCATLSHandler::tls_readyReadOutgoing(int plainBytes)
+{
+ readyReadOutgoing(d->tls->readOutgoing(), plainBytes);
+}
+
+void QCATLSHandler::tls_closed()
+{
+ closed();
+}
+
+void QCATLSHandler::tls_error(int x)
+{
+ d->err = x;
+ d->state = 0;
+ fail();
+}
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/xmlprotocol.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-core/xmlprotocol.cpp
new file mode 100644
index 00000000..c70a04a9
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/xmlprotocol.cpp
@@ -0,0 +1,543 @@
+/*
+ * xmlprotocol.cpp - state machine for 'jabber-like' protocols
+ * Copyright (C) 2004 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include"xmlprotocol.h"
+
+#include"bytestream.h"
+
+using namespace XMPP;
+
+// stripExtraNS
+//
+// This function removes namespace information from various nodes for
+// display purposes only (the element is pretty much useless for processing
+// after this). We do this because QXml is a bit overzealous about outputting
+// redundant namespaces.
+static QDomElement stripExtraNS(const QDomElement &e)
+{
+ // find closest parent with a namespace
+ QDomNode par = e.parentNode();
+ while(!par.isNull() && par.namespaceURI().isNull())
+ par = par.parentNode();
+ bool noShowNS = false;
+ if(!par.isNull() && par.namespaceURI() == e.namespaceURI())
+ noShowNS = true;
+
+ // build qName (prefix:localName)
+ QString qName;
+ if(!e.prefix().isEmpty())
+ qName = e.prefix() + ':' + e.localName();
+ else
+ qName = e.tagName();
+
+ QDomElement i;
+ uint x;
+ if(noShowNS)
+ i = e.ownerDocument().createElement(qName);
+ else
+ i = e.ownerDocument().createElementNS(e.namespaceURI(), qName);
+
+ // copy attributes
+ QDomNamedNodeMap al = e.attributes();
+ for(x = 0; x < al.count(); ++x) {
+ QDomAttr a = al.item(x).cloneNode().toAttr();
+
+ // don't show xml namespace
+ if(a.namespaceURI() == NS_XML)
+ i.setAttribute(QString("xml:") + a.name(), a.value());
+ else
+ i.setAttributeNodeNS(a);
+ }
+
+ // copy children
+ QDomNodeList nl = e.childNodes();
+ for(x = 0; x < nl.count(); ++x) {
+ QDomNode n = nl.item(x);
+ if(n.isElement())
+ i.appendChild(stripExtraNS(n.toElement()));
+ else
+ i.appendChild(n.cloneNode());
+ }
+ return i;
+}
+
+// xmlToString
+//
+// This function converts a QDomElement into a QString, using stripExtraNS
+// to make it pretty.
+static QString xmlToString(const QDomElement &e, const QString &fakeNS, const QString &fakeQName, bool clip)
+{
+ QDomElement i = e.cloneNode().toElement();
+
+ // It seems QDom can only have one namespace attribute at a time (see docElement 'HACK').
+ // Fortunately we only need one kind depending on the input, so it is specified here.
+ QDomElement fake = e.ownerDocument().createElementNS(fakeNS, fakeQName);
+ fake.appendChild(i);
+ fake = stripExtraNS(fake);
+ QString out;
+ {
+ QTextStream ts(&out, IO_WriteOnly);
+ fake.firstChild().save(ts, 0);
+ }
+ // 'clip' means to remove any unwanted (and unneeded) characters, such as a trailing newline
+ if(clip) {
+ int n = out.findRev('>');
+ out.truncate(n+1);
+ }
+ return out;
+}
+
+// createRootXmlTags
+//
+// This function creates three QStrings, one being an <?xml .. ?> processing
+// instruction, and the others being the opening and closing tags of an
+// element, <foo> and </foo>. This basically allows us to get the raw XML
+// text needed to open/close an XML stream, without resorting to generating
+// the XML ourselves. This function uses QDom to do the generation, which
+// ensures proper encoding and entity output.
+static void createRootXmlTags(const QDomElement &root, QString *xmlHeader, QString *tagOpen, QString *tagClose)
+{
+ QDomElement e = root.cloneNode(false).toElement();
+
+ // insert a dummy element to ensure open and closing tags are generated
+ QDomElement dummy = e.ownerDocument().createElement("dummy");
+ e.appendChild(dummy);
+
+ // convert to xml->text
+ QString str;
+ {
+ QTextStream ts(&str, IO_WriteOnly);
+ e.save(ts, 0);
+ }
+
+ // parse the tags out
+ int n = str.find('<');
+ int n2 = str.find('>', n);
+ ++n2;
+ *tagOpen = str.mid(n, n2-n);
+ n2 = str.findRev('>');
+ n = str.findRev('<');
+ ++n2;
+ *tagClose = str.mid(n, n2-n);
+
+ // generate a nice xml processing header
+ *xmlHeader = "<?xml version=\"1.0\"?>";
+}
+
+//----------------------------------------------------------------------------
+// Protocol
+//----------------------------------------------------------------------------
+XmlProtocol::TransferItem::TransferItem()
+{
+}
+
+XmlProtocol::TransferItem::TransferItem(const QString &_str, bool sent, bool external)
+{
+ isString = true;
+ isSent = sent;
+ isExternal = external;
+ str = _str;
+}
+
+XmlProtocol::TransferItem::TransferItem(const QDomElement &_elem, bool sent, bool external)
+{
+ isString = false;
+ isSent = sent;
+ isExternal = external;
+ elem = _elem;
+}
+
+XmlProtocol::XmlProtocol()
+{
+ init();
+}
+
+XmlProtocol::~XmlProtocol()
+{
+}
+
+void XmlProtocol::init()
+{
+ incoming = false;
+ peerClosed = false;
+ closeWritten = false;
+}
+
+void XmlProtocol::reset()
+{
+ init();
+
+ elem = QDomElement();
+ tagOpen = QString();
+ tagClose = QString();
+ xml.reset();
+ outData.resize(0);
+ trackQueue.clear();
+ transferItemList.clear();
+}
+
+void XmlProtocol::addIncomingData(const QByteArray &a)
+{
+ xml.appendData(a);
+}
+
+QByteArray XmlProtocol::takeOutgoingData()
+{
+ QByteArray a = outData.copy();
+ outData.resize(0);
+ return a;
+}
+
+void XmlProtocol::outgoingDataWritten(int bytes)
+{
+ for(QValueList<TrackItem>::Iterator it = trackQueue.begin(); it != trackQueue.end();) {
+ TrackItem &i = *it;
+
+ // enough bytes?
+ if(bytes < i.size) {
+ i.size -= bytes;
+ break;
+ }
+ int type = i.type;
+ int id = i.id;
+ int size = i.size;
+ bytes -= i.size;
+ it = trackQueue.remove(it);
+
+ if(type == TrackItem::Raw) {
+ // do nothing
+ }
+ else if(type == TrackItem::Close) {
+ closeWritten = true;
+ }
+ else if(type == TrackItem::Custom) {
+ itemWritten(id, size);
+ }
+ }
+}
+
+bool XmlProtocol::processStep()
+{
+ Parser::Event pe;
+ notify = 0;
+ transferItemList.clear();
+
+ if(state != Closing && (state == RecvOpen || stepAdvancesParser())) {
+ // if we get here, then it's because we're in some step that advances the parser
+ pe = xml.readNext();
+ if(!pe.isNull()) {
+ // note: error/close events should be handled for ALL steps, so do them here
+ switch(pe.type()) {
+ case Parser::Event::DocumentOpen: {
+ transferItemList += TransferItem(pe.actualString(), false);
+
+ //stringRecv(pe.actualString());
+ break;
+ }
+ case Parser::Event::DocumentClose: {
+ transferItemList += TransferItem(pe.actualString(), false);
+
+ //stringRecv(pe.actualString());
+ if(incoming) {
+ sendTagClose();
+ event = ESend;
+ peerClosed = true;
+ state = Closing;
+ }
+ else {
+ event = EPeerClosed;
+ }
+ return true;
+ }
+ case Parser::Event::Element: {
+ transferItemList += TransferItem(pe.element(), false);
+
+ //elementRecv(pe.element());
+ break;
+ }
+ case Parser::Event::Error: {
+ if(incoming) {
+ // If we get a parse error during the initial element exchange,
+ // flip immediately into 'open' mode so that we can report an error.
+ if(state == RecvOpen) {
+ sendTagOpen();
+ state = Open;
+ }
+ return handleError();
+ }
+ else {
+ event = EError;
+ errorCode = ErrParse;
+ return true;
+ }
+ }
+ }
+ }
+ else {
+ if(state == RecvOpen || stepRequiresElement()) {
+ need = NNotify;
+ notify |= NRecv;
+ return false;
+ }
+ }
+ }
+
+ return baseStep(pe);
+}
+
+QString XmlProtocol::xmlEncoding() const
+{
+ return xml.encoding();
+}
+
+QString XmlProtocol::elementToString(const QDomElement &e, bool clip)
+{
+ if(elem.isNull())
+ elem = elemDoc.importNode(docElement(), true).toElement();
+
+ // Determine the appropriate 'fakeNS' to use
+ QString ns;
+
+ // first, check root namespace
+ QString pre = e.prefix();
+ if(pre.isNull())
+ pre = "";
+ if(pre == elem.prefix()) {
+ ns = elem.namespaceURI();
+ }
+ else {
+ // scan the root attributes for 'xmlns' (oh joyous hacks)
+ QDomNamedNodeMap al = elem.attributes();
+ uint n;
+ for(n = 0; n < al.count(); ++n) {
+ QDomAttr a = al.item(n).toAttr();
+ QString s = a.name();
+ int x = s.find(':');
+ if(x != -1)
+ s = s.mid(x+1);
+ else
+ s = "";
+ if(pre == s) {
+ ns = a.value();
+ break;
+ }
+ }
+ if(n >= al.count()) {
+ // if we get here, then no appropriate ns was found. use root then..
+ ns = elem.namespaceURI();
+ }
+ }
+
+ // build qName
+ QString qn;
+ if(!elem.prefix().isEmpty())
+ qn = elem.prefix() + ':';
+ qn += elem.localName();
+
+ // make the string
+ return xmlToString(e, ns, qn, clip);
+}
+
+bool XmlProtocol::stepRequiresElement() const
+{
+ // default returns false
+ return false;
+}
+
+void XmlProtocol::itemWritten(int, int)
+{
+ // default does nothing
+}
+
+void XmlProtocol::stringSend(const QString &)
+{
+ // default does nothing
+}
+
+void XmlProtocol::stringRecv(const QString &)
+{
+ // default does nothing
+}
+
+void XmlProtocol::elementSend(const QDomElement &)
+{
+ // default does nothing
+}
+
+void XmlProtocol::elementRecv(const QDomElement &)
+{
+ // default does nothing
+}
+
+void XmlProtocol::startConnect()
+{
+ incoming = false;
+ state = SendOpen;
+}
+
+void XmlProtocol::startAccept()
+{
+ incoming = true;
+ state = RecvOpen;
+}
+
+bool XmlProtocol::close()
+{
+ sendTagClose();
+ event = ESend;
+ state = Closing;
+ return true;
+}
+
+int XmlProtocol::writeString(const QString &s, int id, bool external)
+{
+ transferItemList += TransferItem(s, true, external);
+ return internalWriteString(s, TrackItem::Custom, id);
+}
+
+int XmlProtocol::writeElement(const QDomElement &e, int id, bool external, bool clip)
+{
+ if(e.isNull())
+ return 0;
+ transferItemList += TransferItem(e, true, external);
+
+ //elementSend(e);
+ QString out = elementToString(e, clip);
+ return internalWriteString(out, TrackItem::Custom, id);
+}
+
+QByteArray XmlProtocol::resetStream()
+{
+ // reset the state
+ if(incoming)
+ state = RecvOpen;
+ else
+ state = SendOpen;
+
+ // grab unprocessed data before resetting
+ QByteArray spare = xml.unprocessed();
+ xml.reset();
+ return spare;
+}
+
+int XmlProtocol::internalWriteData(const QByteArray &a, TrackItem::Type t, int id)
+{
+ TrackItem i;
+ i.type = t;
+ i.id = id;
+ i.size = a.size();
+ trackQueue += i;
+
+ ByteStream::appendArray(&outData, a);
+ return a.size();
+}
+
+int XmlProtocol::internalWriteString(const QString &s, TrackItem::Type t, int id)
+{
+ QCString cs = s.utf8();
+ QByteArray a(cs.length());
+ memcpy(a.data(), cs.data(), a.size());
+ return internalWriteData(a, t, id);
+}
+
+void XmlProtocol::sendTagOpen()
+{
+ if(elem.isNull())
+ elem = elemDoc.importNode(docElement(), true).toElement();
+
+ QString xmlHeader;
+ createRootXmlTags(elem, &xmlHeader, &tagOpen, &tagClose);
+
+ QString s;
+ s += xmlHeader + '\n';
+ s += tagOpen + '\n';
+
+ transferItemList += TransferItem(xmlHeader, true);
+ transferItemList += TransferItem(tagOpen, true);
+
+ //stringSend(xmlHeader);
+ //stringSend(tagOpen);
+ internalWriteString(s, TrackItem::Raw);
+}
+
+void XmlProtocol::sendTagClose()
+{
+ transferItemList += TransferItem(tagClose, true);
+
+ //stringSend(tagClose);
+ internalWriteString(tagClose, TrackItem::Close);
+}
+
+bool XmlProtocol::baseStep(const Parser::Event &pe)
+{
+ // Basic
+ if(state == SendOpen) {
+ sendTagOpen();
+ event = ESend;
+ if(incoming)
+ state = Open;
+ else
+ state = RecvOpen;
+ return true;
+ }
+ else if(state == RecvOpen) {
+ if(incoming)
+ state = SendOpen;
+ else
+ state = Open;
+
+ // note: event will always be DocumentOpen here
+ handleDocOpen(pe);
+ event = ERecvOpen;
+ return true;
+ }
+ else if(state == Open) {
+ QDomElement e;
+ if(pe.type() == Parser::Event::Element)
+ e = pe.element();
+ return doStep(e);
+ }
+ // Closing
+ else {
+ if(closeWritten) {
+ if(peerClosed) {
+ event = EPeerClosed;
+ return true;
+ }
+ else
+ return handleCloseFinished();
+ }
+
+ need = NNotify;
+ notify = NSend;
+ return false;
+ }
+}
+
+void XmlProtocol::setIncomingAsExternal()
+{
+ for(QValueList<TransferItem>::Iterator it = transferItemList.begin(); it != transferItemList.end(); ++it) {
+ TransferItem &i = *it;
+ // look for elements received
+ if(!i.isString && !i.isSent)
+ i.isExternal = true;
+ }
+}
+
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/xmlprotocol.h b/kopete/protocols/jabber/libiris/iris/xmpp-core/xmlprotocol.h
new file mode 100644
index 00000000..5bf2cbda
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/xmlprotocol.h
@@ -0,0 +1,145 @@
+/*
+ * xmlprotocol.h - state machine for 'jabber-like' protocols
+ * Copyright (C) 2004 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef XMLPROTOCOL_H
+#define XMLPROTOCOL_H
+
+#include<qdom.h>
+#include<qvaluelist.h>
+#include"parser.h"
+
+#define NS_XML "http://www.w3.org/XML/1998/namespace"
+
+namespace XMPP
+{
+ class XmlProtocol
+ {
+ public:
+ enum Need {
+ NNotify, // need a data send and/or recv update
+ NCustom = 10
+ };
+ enum Event {
+ EError, // unrecoverable error, see errorCode for details
+ ESend, // data needs to be sent, use takeOutgoingData()
+ ERecvOpen, // breakpoint after root element open tag is received
+ EPeerClosed, // root element close tag received
+ EClosed, // finished closing
+ ECustom = 10
+ };
+ enum Error {
+ ErrParse, // there was an error parsing the xml
+ ErrCustom = 10
+ };
+ enum Notify {
+ NSend = 0x01, // need to know if data has been written
+ NRecv = 0x02 // need incoming data
+ };
+
+ XmlProtocol();
+ virtual ~XmlProtocol();
+
+ virtual void reset();
+
+ // byte I/O for the stream
+ void addIncomingData(const QByteArray &);
+ QByteArray takeOutgoingData();
+ void outgoingDataWritten(int);
+
+ // advance the state machine
+ bool processStep();
+
+ // set these before returning from a step
+ int need, event, errorCode, notify;
+
+ inline bool isIncoming() const { return incoming; }
+ QString xmlEncoding() const;
+ QString elementToString(const QDomElement &e, bool clip=false);
+
+ class TransferItem
+ {
+ public:
+ TransferItem();
+ TransferItem(const QString &str, bool sent, bool external=false);
+ TransferItem(const QDomElement &elem, bool sent, bool external=false);
+
+ bool isSent; // else, received
+ bool isString; // else, is element
+ bool isExternal; // not owned by protocol
+ QString str;
+ QDomElement elem;
+ };
+ QValueList<TransferItem> transferItemList;
+ void setIncomingAsExternal();
+
+ protected:
+ virtual QDomElement docElement()=0;
+ virtual void handleDocOpen(const Parser::Event &pe)=0;
+ virtual bool handleError()=0;
+ virtual bool handleCloseFinished()=0;
+ virtual bool stepAdvancesParser() const=0;
+ virtual bool stepRequiresElement() const;
+ virtual bool doStep(const QDomElement &e)=0;
+ virtual void itemWritten(int id, int size);
+
+ // 'debug'
+ virtual void stringSend(const QString &s);
+ virtual void stringRecv(const QString &s);
+ virtual void elementSend(const QDomElement &e);
+ virtual void elementRecv(const QDomElement &e);
+
+ void startConnect();
+ void startAccept();
+ bool close();
+ int writeString(const QString &s, int id, bool external);
+ int writeElement(const QDomElement &e, int id, bool external, bool clip=false);
+ QByteArray resetStream();
+
+ private:
+ enum { SendOpen, RecvOpen, Open, Closing };
+ class TrackItem
+ {
+ public:
+ enum Type { Raw, Close, Custom };
+ int type, id, size;
+ };
+
+ bool incoming;
+ QDomDocument elemDoc;
+ QDomElement elem;
+ QString tagOpen, tagClose;
+ int state;
+ bool peerClosed;
+ bool closeWritten;
+
+ Parser xml;
+ QByteArray outData;
+ QValueList<TrackItem> trackQueue;
+
+ void init();
+ int internalWriteData(const QByteArray &a, TrackItem::Type t, int id=-1);
+ int internalWriteString(const QString &s, TrackItem::Type t, int id=-1);
+ void sendTagOpen();
+ void sendTagClose();
+ bool baseStep(const Parser::Event &pe);
+ };
+}
+
+#endif