summaryrefslogtreecommitdiffstats
path: root/kopete/protocols/jabber/libiris/iris
diff options
context:
space:
mode:
Diffstat (limited to 'kopete/protocols/jabber/libiris/iris')
-rw-r--r--kopete/protocols/jabber/libiris/iris/Makefile.am1
-rw-r--r--kopete/protocols/jabber/libiris/iris/TODO16
-rw-r--r--kopete/protocols/jabber/libiris/iris/include/Makefile.am7
-rw-r--r--kopete/protocols/jabber/libiris/iris/include/empty.cpp0
-rw-r--r--kopete/protocols/jabber/libiris/iris/include/im.h721
-rw-r--r--kopete/protocols/jabber/libiris/iris/include/xmpp.h553
-rw-r--r--kopete/protocols/jabber/libiris/iris/jabber/Makefile.am15
-rw-r--r--kopete/protocols/jabber/libiris/iris/jabber/all_mocs.cpp23
-rw-r--r--kopete/protocols/jabber/libiris/iris/jabber/filetransfer.cpp770
-rw-r--r--kopete/protocols/jabber/libiris/iris/jabber/filetransfer.h170
-rw-r--r--kopete/protocols/jabber/libiris/iris/jabber/s5b.cpp2538
-rw-r--r--kopete/protocols/jabber/libiris/iris/jabber/s5b.h341
-rw-r--r--kopete/protocols/jabber/libiris/iris/jabber/xmpp_ibb.cpp638
-rw-r--r--kopete/protocols/jabber/libiris/iris/jabber/xmpp_ibb.h145
-rw-r--r--kopete/protocols/jabber/libiris/iris/jabber/xmpp_jidlink.cpp318
-rw-r--r--kopete/protocols/jabber/libiris/iris/jabber/xmpp_jidlink.h114
-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
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-im/Makefile.am19
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-im/client.cpp1522
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-im/types.cpp1876
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_tasks.cpp2120
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_tasks.h485
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_vcard.cpp1241
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_vcard.h284
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_xmlcommon.cpp386
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_xmlcommon.h71
44 files changed, 23029 insertions, 0 deletions
diff --git a/kopete/protocols/jabber/libiris/iris/Makefile.am b/kopete/protocols/jabber/libiris/iris/Makefile.am
new file mode 100644
index 00000000..03e5818f
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = include jabber xmpp-core xmpp-im
diff --git a/kopete/protocols/jabber/libiris/iris/TODO b/kopete/protocols/jabber/libiris/iris/TODO
new file mode 100644
index 00000000..e6cf74c6
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/TODO
@@ -0,0 +1,16 @@
+- Stream::id(), Stream::lang()
+- whitespace pings (but disable when using http poll)
+- make stanza error conditions work for both 1.0 and old
+
+- xmpp-im (messages, roster, subscriptions, presence, privacy)
+- document xmpp-core
+- provide complete support for xmpp-core. this means all functionality from
+ the draft, and noting behavior issues (like IQ semantics) in the
+ library documentation.
+
+- SASL "EXTERNAL" w/ client certificate
+- SASL "ANONYMOUS" ?
+
+credits:
+ MD5 algorithm by Peter Deutsch (Aladdin Enterprises)
+
diff --git a/kopete/protocols/jabber/libiris/iris/include/Makefile.am b/kopete/protocols/jabber/libiris/iris/include/Makefile.am
new file mode 100644
index 00000000..6375392b
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/include/Makefile.am
@@ -0,0 +1,7 @@
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libiris.la
+INCLUDES = -Ixmpp-core -Ixmpp-im -I../cutestuff/util -I../cutestuff/network -I../qca/src $(all_includes)
+
+libiris_la_SOURCES = \
+ empty.cpp
diff --git a/kopete/protocols/jabber/libiris/iris/include/empty.cpp b/kopete/protocols/jabber/libiris/iris/include/empty.cpp
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/include/empty.cpp
diff --git a/kopete/protocols/jabber/libiris/iris/include/im.h b/kopete/protocols/jabber/libiris/iris/include/im.h
new file mode 100644
index 00000000..832ec62a
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/include/im.h
@@ -0,0 +1,721 @@
+/*
+ * im.h - XMPP "IM" library 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 XMPP_IM_H
+#define XMPP_IM_H
+
+#include<qdatetime.h>
+#include<qvaluelist.h>
+#include"xmpp.h"
+
+namespace XMPP
+{
+ class Url
+ {
+ public:
+ Url(const QString &url="", const QString &desc="");
+ Url(const Url &);
+ Url & operator=(const Url &);
+ ~Url();
+
+ QString url() const;
+ QString desc() const;
+
+ void setUrl(const QString &);
+ void setDesc(const QString &);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ typedef QValueList<Url> UrlList;
+ typedef QMap<QString, QString> StringMap;
+ typedef enum { OfflineEvent, DeliveredEvent, DisplayedEvent,
+ ComposingEvent, CancelEvent, InactiveEvent, GoneEvent } MsgEvent;
+
+ class Message
+ {
+ public:
+ Message(const Jid &to="");
+ Message(const Message &from);
+ Message & operator=(const Message &from);
+ ~Message();
+
+ Jid to() const;
+ Jid from() const;
+ QString id() const;
+ QString type() const;
+ QString lang() const;
+ QString subject(const QString &lang="") const;
+ QString body(const QString &lang="") const;
+ QString xHTMLBody(const QString &lang="") const;
+ QString thread() const;
+ Stanza::Error error() const;
+
+ void setTo(const Jid &j);
+ void setFrom(const Jid &j);
+ void setId(const QString &s);
+ void setType(const QString &s);
+ void setLang(const QString &s);
+ void setSubject(const QString &s, const QString &lang="");
+ void setBody(const QString &s, const QString &lang="");
+ void setXHTMLBody(const QString &s, const QString &lang="", const QString &attr = "");
+ void setThread(const QString &s);
+ void setError(const Stanza::Error &err);
+
+ // JEP-0091
+ QDateTime timeStamp() const;
+ void setTimeStamp(const QDateTime &ts);
+
+ // JEP-0066
+ UrlList urlList() const;
+ void urlAdd(const Url &u);
+ void urlsClear();
+ void setUrlList(const UrlList &list);
+
+ // JEP-0022
+ QString eventId() const;
+ void setEventId(const QString& id);
+ bool containsEvents() const;
+ bool containsEvent(MsgEvent e) const;
+ void addEvent(MsgEvent e);
+
+ // JEP-0027
+ QString xencrypted() const;
+ void setXEncrypted(const QString &s);
+
+ // Obsolete invitation
+ QString invite() const;
+ void setInvite(const QString &s);
+
+ // for compatibility. delete me later
+ bool spooled() const;
+ void setSpooled(bool);
+ bool wasEncrypted() const;
+ void setWasEncrypted(bool);
+
+ Stanza toStanza(Stream *stream) const;
+ bool fromStanza(const Stanza &s, int tzoffset);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ class Subscription
+ {
+ public:
+ enum SubType { None, To, From, Both, Remove };
+
+ Subscription(SubType type=None);
+
+ int type() const;
+
+ QString toString() const;
+ bool fromString(const QString &);
+
+ private:
+ SubType value;
+ };
+
+ class Status
+ {
+ public:
+ Status(const QString &show="", const QString &status="", int priority=0, bool available=true);
+ ~Status();
+
+ int priority() const;
+ const QString & show() const;
+ const QString & status() const;
+ QDateTime timeStamp() const;
+ const QString & keyID() const;
+ bool isAvailable() const;
+ bool isAway() const;
+ bool isInvisible() const;
+ bool hasError() const;
+ int errorCode() const;
+ const QString & errorString() const;
+
+ const QString & xsigned() const;
+ const QString & songTitle() const;
+ const QString & capsNode() const;
+ const QString & capsVersion() const;
+ const QString & capsExt() const;
+
+ void setPriority(int);
+ void setShow(const QString &);
+ void setStatus(const QString &);
+ void setTimeStamp(const QDateTime &);
+ void setKeyID(const QString &);
+ void setIsAvailable(bool);
+ void setIsInvisible(bool);
+ void setError(int, const QString &);
+ void setCapsNode(const QString&);
+ void setCapsVersion(const QString&);
+ void setCapsExt(const QString&);
+
+ void setXSigned(const QString &);
+ void setSongTitle(const QString &);
+
+ private:
+ int v_priority;
+ QString v_show, v_status, v_key;
+ QDateTime v_timeStamp;
+ bool v_isAvailable;
+ bool v_isInvisible;
+
+ QString v_xsigned;
+ // gabber song extension
+ QString v_songTitle;
+ QString v_capsNode, v_capsVersion, v_capsExt;
+
+ int ecode;
+ QString estr;
+
+ class Private;
+ Private *d;
+ };
+
+ class Resource
+ {
+ public:
+ Resource(const QString &name="", const Status &s=Status());
+ ~Resource();
+
+ const QString & name() const;
+ int priority() const;
+ const Status & status() const;
+
+ void setName(const QString &);
+ void setStatus(const Status &);
+
+ private:
+ QString v_name;
+ Status v_status;
+
+ class ResourcePrivate *d;
+ };
+
+ class ResourceList : public QValueList<Resource>
+ {
+ public:
+ ResourceList();
+ ~ResourceList();
+
+ ResourceList::Iterator find(const QString &);
+ ResourceList::Iterator priority();
+
+ ResourceList::ConstIterator find(const QString &) const;
+ ResourceList::ConstIterator priority() const;
+
+ private:
+ class ResourceListPrivate *d;
+ };
+
+ class RosterItem
+ {
+ public:
+ RosterItem(const Jid &jid="");
+ virtual ~RosterItem();
+
+ const Jid & jid() const;
+ const QString & name() const;
+ const QStringList & groups() const;
+ const Subscription & subscription() const;
+ const QString & ask() const;
+ bool isPush() const;
+ bool inGroup(const QString &) const;
+
+ virtual void setJid(const Jid &);
+ void setName(const QString &);
+ void setGroups(const QStringList &);
+ void setSubscription(const Subscription &);
+ void setAsk(const QString &);
+ void setIsPush(bool);
+ bool addGroup(const QString &);
+ bool removeGroup(const QString &);
+
+ QDomElement toXml(QDomDocument *) const;
+ bool fromXml(const QDomElement &);
+
+ private:
+ Jid v_jid;
+ QString v_name;
+ QStringList v_groups;
+ Subscription v_subscription;
+ QString v_ask;
+ bool v_push;
+
+ class RosterItemPrivate *d;
+ };
+
+ class Roster : public QValueList<RosterItem>
+ {
+ public:
+ Roster();
+ ~Roster();
+
+ Roster::Iterator find(const Jid &);
+ Roster::ConstIterator find(const Jid &) const;
+
+ private:
+ class RosterPrivate *d;
+ };
+
+ class Features
+ {
+ public:
+ Features();
+ Features(const QStringList &);
+ Features(const QString &);
+ ~Features();
+
+ QStringList list() const; // actual featurelist
+ void setList(const QStringList &);
+
+ // features
+ bool canRegister() const;
+ bool canSearch() const;
+ bool canGroupchat() const;
+ bool canVoice() const;
+ bool canDisco() const;
+ bool canXHTML() const;
+ bool isGateway() const;
+ bool haveVCard() const;
+
+ enum FeatureID {
+ FID_Invalid = -1,
+ FID_None,
+ FID_Register,
+ FID_Search,
+ FID_Groupchat,
+ FID_Disco,
+ FID_Gateway,
+ FID_VCard,
+ FID_Xhtml,
+
+ // private Psi actions
+ FID_Add
+ };
+
+ // useful functions
+ bool test(const QStringList &) const;
+
+ QString name() const;
+ static QString name(long id);
+ static QString name(const QString &feature);
+
+ long id() const;
+ static long id(const QString &feature);
+ static QString feature(long id);
+
+ class FeatureName;
+ private:
+ QStringList _list;
+ };
+
+ class AgentItem
+ {
+ public:
+ AgentItem() { }
+
+ const Jid & jid() const { return v_jid; }
+ const QString & name() const { return v_name; }
+ const QString & category() const { return v_category; }
+ const QString & type() const { return v_type; }
+ const Features & features() const { return v_features; }
+
+ void setJid(const Jid &j) { v_jid = j; }
+ void setName(const QString &n) { v_name = n; }
+ void setCategory(const QString &c) { v_category = c; }
+ void setType(const QString &t) { v_type = t; }
+ void setFeatures(const Features &f) { v_features = f; }
+
+ private:
+ Jid v_jid;
+ QString v_name, v_category, v_type;
+ Features v_features;
+ };
+
+ typedef QValueList<AgentItem> AgentList;
+
+ class DiscoItem
+ {
+ public:
+ DiscoItem();
+ ~DiscoItem();
+
+ const Jid &jid() const;
+ const QString &node() const;
+ const QString &name() const;
+
+ void setJid(const Jid &);
+ void setName(const QString &);
+ void setNode(const QString &);
+
+ enum Action {
+ None = 0,
+ Remove,
+ Update
+ };
+
+ Action action() const;
+ void setAction(Action);
+
+ const Features &features() const;
+ void setFeatures(const Features &);
+
+ struct Identity
+ {
+ QString category;
+ QString name;
+ QString type;
+ };
+
+ typedef QValueList<Identity> Identities;
+
+ const Identities &identities() const;
+ void setIdentities(const Identities &);
+
+ // some useful helper functions
+ static Action string2action(QString s);
+ static QString action2string(Action a);
+
+ DiscoItem & operator= (const DiscoItem &);
+ DiscoItem(const DiscoItem &);
+
+ operator AgentItem() const { return toAgentItem(); }
+ AgentItem toAgentItem() const;
+ void fromAgentItem(const AgentItem &);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ typedef QValueList<DiscoItem> DiscoList;
+
+ class FormField
+ {
+ public:
+ enum { username, nick, password, name, first, last, email, address, city, state, zip, phone, url, date, misc };
+ FormField(const QString &type="", const QString &value="");
+ ~FormField();
+
+ int type() const;
+ QString fieldName() const;
+ QString realName() const;
+ bool isSecret() const;
+ const QString & value() const;
+ void setType(int);
+ bool setType(const QString &);
+ void setValue(const QString &);
+
+ private:
+ int tagNameToType(const QString &) const;
+ QString typeToTagName(int) const;
+
+ int v_type;
+ QString v_value;
+
+ class Private;
+ Private *d;
+ };
+
+ class Form : public QValueList<FormField>
+ {
+ public:
+ Form(const Jid &j="");
+ ~Form();
+
+ Jid jid() const;
+ QString instructions() const;
+ QString key() const;
+ void setJid(const Jid &);
+ void setInstructions(const QString &);
+ void setKey(const QString &);
+
+ private:
+ Jid v_jid;
+ QString v_instructions, v_key;
+
+ class Private;
+ Private *d;
+ };
+
+ class SearchResult
+ {
+ public:
+ SearchResult(const Jid &jid="");
+ ~SearchResult();
+
+ const Jid & jid() const;
+ const QString & nick() const;
+ const QString & first() const;
+ const QString & last() const;
+ const QString & email() const;
+
+ void setJid(const Jid &);
+ void setNick(const QString &);
+ void setFirst(const QString &);
+ void setLast(const QString &);
+ void setEmail(const QString &);
+
+ private:
+ Jid v_jid;
+ QString v_nick, v_first, v_last, v_email;
+ };
+
+ class Client;
+ class LiveRosterItem;
+ class LiveRoster;
+ class S5BManager;
+ class IBBManager;
+ class JidLinkManager;
+ class FileTransferManager;
+
+ class Task : public QObject
+ {
+ Q_OBJECT
+ public:
+ enum { ErrDisc };
+ Task(Task *parent);
+ Task(Client *, bool isRoot);
+ virtual ~Task();
+
+ Task *parent() const;
+ Client *client() const;
+ QDomDocument *doc() const;
+ QString id() const;
+
+ bool success() const;
+ int statusCode() const;
+ const QString & statusString() const;
+
+ void go(bool autoDelete=false);
+ virtual bool take(const QDomElement &);
+ void safeDelete();
+
+ signals:
+ void finished();
+
+ protected:
+ virtual void onGo();
+ virtual void onDisconnect();
+ void send(const QDomElement &);
+ void setSuccess(int code=0, const QString &str="");
+ void setError(const QDomElement &);
+ void setError(int code=0, const QString &str="");
+ void debug(const char *, ...);
+ void debug(const QString &);
+ bool iqVerify(const QDomElement &x, const Jid &to, const QString &id, const QString &xmlns="");
+
+ private slots:
+ void clientDisconnected();
+ void done();
+
+ private:
+ void init();
+
+ class TaskPrivate;
+ TaskPrivate *d;
+ };
+
+ class Client : public QObject
+ {
+ Q_OBJECT
+
+ public:
+ Client(QObject *parent=0);
+ ~Client();
+
+ bool isActive() const;
+ void connectToServer(ClientStream *s, const Jid &j, bool auth=true);
+ void start(const QString &host, const QString &user, const QString &pass, const QString &resource);
+ void close(bool fast=false);
+
+ Stream & stream();
+ const LiveRoster & roster() const;
+ const ResourceList & resourceList() const;
+
+ void send(const QDomElement &);
+ void send(const QString &);
+
+ QString host() const;
+ QString user() const;
+ QString pass() const;
+ QString resource() const;
+ Jid jid() const;
+
+ void rosterRequest();
+ void sendMessage(const Message &);
+ void sendSubscription(const Jid &, const QString &);
+ void setPresence(const Status &);
+
+ void debug(const QString &);
+ QString genUniqueId();
+ Task *rootTask();
+ QDomDocument *doc() const;
+
+ QString OSName() const;
+ QString timeZone() const;
+ int timeZoneOffset() const;
+ QString clientName() const;
+ QString clientVersion() const;
+ QString capsNode() const;
+ QString capsVersion() const;
+ QString capsExt() const;
+
+ void setOSName(const QString &);
+ void setTimeZone(const QString &, int);
+ void setClientName(const QString &);
+ void setClientVersion(const QString &);
+ void setCapsNode(const QString &);
+ void setCapsVersion(const QString &);
+
+ void setIdentity(DiscoItem::Identity);
+ DiscoItem::Identity identity();
+
+ void addExtension(const QString& ext, const Features& f);
+ void removeExtension(const QString& ext);
+ const Features& extension(const QString& ext) const;
+ QStringList extensions() const;
+
+ S5BManager *s5bManager() const;
+ IBBManager *ibbManager() const;
+ JidLinkManager *jidLinkManager() const;
+
+ void setFileTransferEnabled(bool b);
+ FileTransferManager *fileTransferManager() const;
+
+ bool groupChatJoin(const QString &host, const QString &room, const QString &nick);
+ bool groupChatJoin(const QString &host, const QString &room, const QString &nick, const QString &password);
+ void groupChatSetStatus(const QString &host, const QString &room, const Status &);
+ void groupChatChangeNick(const QString &host, const QString &room, const QString &nick, const Status &);
+ void groupChatLeave(const QString &host, const QString &room);
+
+ signals:
+ void activated();
+ void disconnected();
+ //void authFinished(bool, int, const QString &);
+ void rosterRequestFinished(bool, int, const QString &);
+ void rosterItemAdded(const RosterItem &);
+ void rosterItemUpdated(const RosterItem &);
+ void rosterItemRemoved(const RosterItem &);
+ void resourceAvailable(const Jid &, const Resource &);
+ void resourceUnavailable(const Jid &, const Resource &);
+ void presenceError(const Jid &, int, const QString &);
+ void subscription(const Jid &, const QString &);
+ void messageReceived(const Message &);
+ void debugText(const QString &);
+ void xmlIncoming(const QString &);
+ void xmlOutgoing(const QString &);
+ void groupChatJoined(const Jid &);
+ void groupChatLeft(const Jid &);
+ void groupChatPresence(const Jid &, const Status &);
+ void groupChatError(const Jid &, int, const QString &);
+
+ void incomingJidLink();
+
+ private slots:
+ //void streamConnected();
+ //void streamHandshaken();
+ //void streamError(const StreamError &);
+ //void streamSSLCertificateReady(const QSSLCert &);
+ //void streamCloseFinished();
+ void streamError(int);
+ void streamReadyRead();
+ void streamIncomingXml(const QString &);
+ void streamOutgoingXml(const QString &);
+
+ void slotRosterRequestFinished();
+
+ // basic daemons
+ void ppSubscription(const Jid &, const QString &);
+ void ppPresence(const Jid &, const Status &);
+ void pmMessage(const Message &);
+ void prRoster(const Roster &);
+
+ void s5b_incomingReady();
+ void ibb_incomingReady();
+
+ public:
+ class GroupChat;
+ private:
+ void cleanup();
+ void distribute(const QDomElement &);
+ void importRoster(const Roster &);
+ void importRosterItem(const RosterItem &);
+ void updateSelfPresence(const Jid &, const Status &);
+ void updatePresence(LiveRosterItem *, const Jid &, const Status &);
+
+ class ClientPrivate;
+ ClientPrivate *d;
+ };
+
+ class LiveRosterItem : public RosterItem
+ {
+ public:
+ LiveRosterItem(const Jid &j="");
+ LiveRosterItem(const RosterItem &);
+ ~LiveRosterItem();
+
+ void setRosterItem(const RosterItem &);
+
+ ResourceList & resourceList();
+ ResourceList::Iterator priority();
+
+ const ResourceList & resourceList() const;
+ ResourceList::ConstIterator priority() const;
+
+ bool isAvailable() const;
+ const Status & lastUnavailableStatus() const;
+ bool flagForDelete() const;
+
+ void setLastUnavailableStatus(const Status &);
+ void setFlagForDelete(bool);
+
+ private:
+ ResourceList v_resourceList;
+ Status v_lastUnavailableStatus;
+ bool v_flagForDelete;
+
+ class LiveRosterItemPrivate;
+ LiveRosterItemPrivate *d;
+ };
+
+ class LiveRoster : public QValueList<LiveRosterItem>
+ {
+ public:
+ LiveRoster();
+ ~LiveRoster();
+
+ void flagAllForDelete();
+ LiveRoster::Iterator find(const Jid &, bool compareRes=true);
+ LiveRoster::ConstIterator find(const Jid &, bool compareRes=true) const;
+
+ private:
+ class LiveRosterPrivate;
+ LiveRosterPrivate *d;
+ };
+}
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/include/xmpp.h b/kopete/protocols/jabber/libiris/iris/include/xmpp.h
new file mode 100644
index 00000000..5636f963
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/include/xmpp.h
@@ -0,0 +1,553 @@
+/*
+ * xmpp.h - XMPP "core" library 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 XMPP_H
+#define XMPP_H
+
+#include<qobject.h>
+#include<qstring.h>
+#include<qhostaddress.h>
+#include<qstring.h>
+#include<qcstring.h>
+#include<qxml.h>
+#include<qdom.h>
+
+namespace QCA
+{
+ class TLS;
+}
+
+#ifndef CS_XMPP
+class ByteStream;
+#endif
+
+namespace XMPP
+{
+ // CS_IMPORT_BEGIN cutestuff/bytestream.h
+#ifdef CS_XMPP
+ class ByteStream;
+#endif
+ // CS_IMPORT_END
+
+ class Debug
+ {
+ public:
+ virtual ~Debug();
+
+ virtual void msg(const QString &)=0;
+ virtual void outgoingTag(const QString &)=0;
+ virtual void incomingTag(const QString &)=0;
+ virtual void outgoingXml(const QDomElement &)=0;
+ virtual void incomingXml(const QDomElement &)=0;
+ };
+
+ void setDebug(Debug *);
+
+ class Connector : public QObject
+ {
+ Q_OBJECT
+ public:
+ Connector(QObject *parent=0);
+ virtual ~Connector();
+
+ virtual void connectToServer(const QString &server)=0;
+ virtual ByteStream *stream() const=0;
+ virtual void done()=0;
+
+ bool useSSL() const;
+ bool havePeerAddress() const;
+ QHostAddress peerAddress() const;
+ Q_UINT16 peerPort() const;
+
+ signals:
+ void connected();
+ void error();
+
+ protected:
+ void setUseSSL(bool b);
+ void setPeerAddressNone();
+ void setPeerAddress(const QHostAddress &addr, Q_UINT16 port);
+
+ private:
+ bool ssl;
+ bool haveaddr;
+ QHostAddress addr;
+ Q_UINT16 port;
+ };
+
+ class AdvancedConnector : public Connector
+ {
+ Q_OBJECT
+ public:
+ enum Error { ErrConnectionRefused, ErrHostNotFound, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth, ErrStream };
+ AdvancedConnector(QObject *parent=0);
+ virtual ~AdvancedConnector();
+
+ class Proxy
+ {
+ public:
+ enum { None, HttpConnect, HttpPoll, Socks };
+ Proxy();
+ ~Proxy();
+
+ int type() const;
+ QString host() const;
+ Q_UINT16 port() const;
+ QString url() const;
+ QString user() const;
+ QString pass() const;
+ int pollInterval() const;
+
+ void setHttpConnect(const QString &host, Q_UINT16 port);
+ void setHttpPoll(const QString &host, Q_UINT16 port, const QString &url);
+ void setSocks(const QString &host, Q_UINT16 port);
+ void setUserPass(const QString &user, const QString &pass);
+ void setPollInterval(int secs);
+
+ private:
+ int t;
+ QString v_host, v_url;
+ Q_UINT16 v_port;
+ QString v_user, v_pass;
+ int v_poll;
+ };
+
+ void setProxy(const Proxy &proxy);
+ void setOptHostPort(const QString &host, Q_UINT16 port);
+ void setOptProbe(bool);
+ void setOptSSL(bool);
+
+ void changePollInterval(int secs);
+
+ void connectToServer(const QString &server);
+ ByteStream *stream() const;
+ void done();
+
+ int errorCode() const;
+
+ signals:
+ void srvLookup(const QString &server);
+ void srvResult(bool success);
+ void httpSyncStarted();
+ void httpSyncFinished();
+
+ private slots:
+ void dns_done();
+ void srv_done();
+ void bs_connected();
+ void bs_error(int);
+ void http_syncStarted();
+ void http_syncFinished();
+
+ private:
+ class Private;
+ Private *d;
+
+ void cleanup();
+ void do_resolve();
+ void do_connect();
+ void tryNextSrv();
+ };
+
+ class TLSHandler : public QObject
+ {
+ Q_OBJECT
+ public:
+ TLSHandler(QObject *parent=0);
+ virtual ~TLSHandler();
+
+ virtual void reset()=0;
+ virtual void startClient(const QString &host)=0;
+ virtual void write(const QByteArray &a)=0;
+ virtual void writeIncoming(const QByteArray &a)=0;
+
+ signals:
+ void success();
+ void fail();
+ void closed();
+ void readyRead(const QByteArray &a);
+ void readyReadOutgoing(const QByteArray &a, int plainBytes);
+ };
+
+ class QCATLSHandler : public TLSHandler
+ {
+ Q_OBJECT
+ public:
+ QCATLSHandler(QCA::TLS *parent);
+ ~QCATLSHandler();
+
+ QCA::TLS *tls() const;
+ int tlsError() const;
+
+ void reset();
+ void startClient(const QString &host);
+ void write(const QByteArray &a);
+ void writeIncoming(const QByteArray &a);
+
+ signals:
+ void tlsHandshaken();
+
+ public slots:
+ void continueAfterHandshake();
+
+ private slots:
+ void tls_handshaken();
+ void tls_readyRead();
+ void tls_readyReadOutgoing(int);
+ void tls_closed();
+ void tls_error(int);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ class Jid
+ {
+ public:
+ Jid();
+ ~Jid();
+
+ Jid(const QString &s);
+ Jid(const char *s);
+ Jid & operator=(const QString &s);
+ Jid & operator=(const char *s);
+
+ void set(const QString &s);
+ void set(const QString &domain, const QString &node, const QString &resource="");
+
+ void setDomain(const QString &s);
+ void setNode(const QString &s);
+ void setResource(const QString &s);
+
+ const QString & domain() const { return d; }
+ const QString & node() const { return n; }
+ const QString & resource() const { return r; }
+ const QString & bare() const { return b; }
+ const QString & full() const { return f; }
+
+ Jid withNode(const QString &s) const;
+ Jid withResource(const QString &s) const;
+
+ bool isValid() const;
+ bool isEmpty() const;
+ bool compare(const Jid &a, bool compareRes=true) const;
+
+ static bool validDomain(const QString &s, QString *norm=0);
+ static bool validNode(const QString &s, QString *norm=0);
+ static bool validResource(const QString &s, QString *norm=0);
+
+ // TODO: kill these later
+ const QString & host() const { return d; }
+ const QString & user() const { return n; }
+ const QString & userHost() const { return b; }
+
+ private:
+ void reset();
+ void update();
+
+ QString f, b, d, n, r;
+ bool valid;
+ };
+
+ class Stream;
+ class Stanza
+ {
+ public:
+ enum Kind { Message, Presence, IQ };
+ enum ErrorType { Cancel, Continue, Modify, Auth, Wait };
+ enum ErrorCond
+ {
+ BadRequest,
+ Conflict,
+ FeatureNotImplemented,
+ Forbidden,
+ InternalServerError,
+ ItemNotFound,
+ JidMalformed,
+ NotAllowed,
+ PaymentRequired,
+ RecipientUnavailable,
+ RegistrationRequired,
+ ServerNotFound,
+ ServerTimeout,
+ ResourceConstraint,
+ ServiceUnavailable,
+ SubscriptionRequired,
+ UndefinedCondition,
+ UnexpectedRequest
+ };
+
+ Stanza();
+ Stanza(const Stanza &from);
+ Stanza & operator=(const Stanza &from);
+ virtual ~Stanza();
+
+ class Error
+ {
+ public:
+ Error(int type=Cancel, int condition=UndefinedCondition, const QString &text="", const QDomElement &appSpec=QDomElement());
+
+ int type;
+ int condition;
+ QString text;
+ QDomElement appSpec;
+ };
+
+ bool isNull() const;
+
+ QDomElement element() const;
+ QString toString() const;
+
+ QDomDocument & doc() const;
+ QString baseNS() const;
+ QString xhtmlImNS() const;
+ QString xhtmlNS() const;
+ QDomElement createElement(const QString &ns, const QString &tagName);
+ QDomElement createTextElement(const QString &ns, const QString &tagName, const QString &text);
+ QDomElement createXHTMLElement(const QString &xHTML);
+ void appendChild(const QDomElement &e);
+
+ Kind kind() const;
+ void setKind(Kind k);
+
+ Jid to() const;
+ Jid from() const;
+ QString id() const;
+ QString type() const;
+ QString lang() const;
+
+ void setTo(const Jid &j);
+ void setFrom(const Jid &j);
+ void setId(const QString &id);
+ void setType(const QString &type);
+ void setLang(const QString &lang);
+
+ Error error() const;
+ void setError(const Error &err);
+ void clearError();
+
+ private:
+ friend class Stream;
+ Stanza(Stream *s, Kind k, const Jid &to, const QString &type, const QString &id);
+ Stanza(Stream *s, const QDomElement &e);
+
+ class Private;
+ Private *d;
+ };
+
+ class Stream : public QObject
+ {
+ Q_OBJECT
+ public:
+ enum Error { ErrParse, ErrProtocol, ErrStream, ErrCustom = 10 };
+ enum StreamCond {
+ GenericStreamError,
+ Conflict,
+ ConnectionTimeout,
+ InternalServerError,
+ InvalidFrom,
+ InvalidXml,
+ PolicyViolation,
+ ResourceConstraint,
+ SystemShutdown
+ };
+
+ Stream(QObject *parent=0);
+ virtual ~Stream();
+
+ virtual QDomDocument & doc() const=0;
+ virtual QString baseNS() const=0;
+ virtual QString xhtmlImNS() const=0;
+ virtual QString xhtmlNS() const=0;
+ virtual bool old() const=0;
+
+ virtual void close()=0;
+ virtual bool stanzaAvailable() const=0;
+ virtual Stanza read()=0;
+ virtual void write(const Stanza &s)=0;
+
+ virtual int errorCondition() const=0;
+ virtual QString errorText() const=0;
+ virtual QDomElement errorAppSpec() const=0;
+
+ Stanza createStanza(Stanza::Kind k, const Jid &to="", const QString &type="", const QString &id="");
+ Stanza createStanza(const QDomElement &e);
+
+ static QString xmlToString(const QDomElement &e, bool clip=false);
+
+ signals:
+ void connectionClosed();
+ void delayedCloseFinished();
+ void readyRead();
+ void stanzaWritten();
+ void error(int);
+ };
+
+ class ClientStream : public Stream
+ {
+ Q_OBJECT
+ public:
+ enum Error {
+ ErrConnection = ErrCustom, // Connection error, ask Connector-subclass what's up
+ ErrNeg, // Negotiation error, see condition
+ ErrTLS, // TLS error, see condition
+ ErrAuth, // Auth error, see condition
+ ErrSecurityLayer, // broken SASL security layer
+ ErrBind // Resource binding error
+ };
+ enum Warning {
+ WarnOldVersion, // server uses older XMPP/Jabber "0.9" protocol
+ WarnNoTLS // there is no chance for TLS at this point
+ };
+ enum NegCond {
+ HostGone, // host no longer hosted
+ HostUnknown, // unknown host
+ RemoteConnectionFailed, // unable to connect to a required remote resource
+ SeeOtherHost, // a 'redirect', see errorText() for other host
+ UnsupportedVersion // unsupported XMPP version
+ };
+ enum TLSCond {
+ TLSStart, // server rejected STARTTLS
+ TLSFail // TLS failed, ask TLSHandler-subclass what's up
+ };
+ enum SecurityLayer {
+ LayerTLS,
+ LayerSASL
+ };
+ enum AuthCond {
+ GenericAuthError, // all-purpose "can't login" error
+ NoMech, // No appropriate auth mech available
+ BadProto, // Bad SASL auth protocol
+ BadServ, // Server failed mutual auth
+ EncryptionRequired, // can't use mech without TLS
+ InvalidAuthzid, // bad input JID
+ InvalidMech, // bad mechanism
+ InvalidRealm, // bad realm
+ MechTooWeak, // can't use mech with this authzid
+ NotAuthorized, // bad user, bad password, bad creditials
+ TemporaryAuthFailure // please try again later!
+ };
+ enum BindCond {
+ BindNotAllowed, // not allowed to bind a resource
+ BindConflict // resource in-use
+ };
+
+ ClientStream(Connector *conn, TLSHandler *tlsHandler=0, QObject *parent=0);
+ ClientStream(const QString &host, const QString &defRealm, ByteStream *bs, QCA::TLS *tls=0, QObject *parent=0); // server
+ ~ClientStream();
+
+ Jid jid() const;
+ void connectToServer(const Jid &jid, bool auth=true);
+ void accept(); // server
+ bool isActive() const;
+ bool isAuthenticated() const;
+
+ // login params
+ void setUsername(const QString &s);
+ void setPassword(const QString &s);
+ void setRealm(const QString &s);
+ void continueAfterParams();
+
+ // SASL information
+ QString saslMechanism() const;
+ int saslSSF() const;
+
+ // binding
+ void setResourceBinding(bool);
+
+ // security options (old protocol only uses the first !)
+ void setAllowPlain(bool);
+ void setRequireMutualAuth(bool);
+ void setSSFRange(int low, int high);
+ void setOldOnly(bool);
+ void setSASLMechanism(const QString &s);
+ void setLocalAddr(const QHostAddress &addr, Q_UINT16 port);
+
+ // reimplemented
+ QDomDocument & doc() const;
+ QString baseNS() const;
+ QString xhtmlImNS() const;
+ QString xhtmlNS() const;
+ bool old() const;
+
+ void close();
+ bool stanzaAvailable() const;
+ Stanza read();
+ void write(const Stanza &s);
+
+ int errorCondition() const;
+ QString errorText() const;
+ QDomElement errorAppSpec() const;
+
+ // extra
+ void writeDirect(const QString &s);
+ void setNoopTime(int mills);
+
+ signals:
+ void connected();
+ void securityLayerActivated(int);
+ void needAuthParams(bool user, bool pass, bool realm);
+ void authenticated();
+ void warning(int);
+ void incomingXml(const QString &s);
+ void outgoingXml(const QString &s);
+
+ public slots:
+ void continueAfterWarning();
+
+ private slots:
+ void cr_connected();
+ void cr_error();
+
+ void bs_connectionClosed();
+ void bs_delayedCloseFinished();
+ void bs_error(int); // server only
+
+ void ss_readyRead();
+ void ss_bytesWritten(int);
+ void ss_tlsHandshaken();
+ void ss_tlsClosed();
+ void ss_error(int);
+
+ void sasl_clientFirstStep(const QString &mech, const QByteArray *clientInit);
+ void sasl_nextStep(const QByteArray &stepData);
+ void sasl_needParams(bool user, bool authzid, bool pass, bool realm);
+ void sasl_authCheck(const QString &user, const QString &authzid);
+ void sasl_authenticated();
+ void sasl_error(int);
+
+ void doNoop();
+ void doReadyRead();
+
+ private:
+ class Private;
+ Private *d;
+
+ void reset(bool all=false);
+ void processNext();
+ int convertedSASLCond() const;
+ bool handleNeed();
+ void handleError();
+ void srvProcessNext();
+ };
+}
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/jabber/Makefile.am b/kopete/protocols/jabber/libiris/iris/jabber/Makefile.am
new file mode 100644
index 00000000..d480984d
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/jabber/Makefile.am
@@ -0,0 +1,15 @@
+# we deal with s5b.moc separately since KDE's build system can't cope with Q_OBJECT in .cpp files
+METASOURCES = filetransfer.moc xmpp_ibb.moc xmpp_jidlink.moc
+
+noinst_LTLIBRARIES = libiris_jabber.la
+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_jabber_la_SOURCES = \
+ filetransfer.cpp s5b.cpp xmpp_ibb.cpp xmpp_jidlink.cpp all_mocs.cpp
+
+s5b.lo: s5b.moc
+
+CLEANFILES = s5b.moc
+s5b.moc: $(srcdir)/s5b.cpp $(srcdir)/s5b.h
+ ${MOC} $(srcdir)/s5b.h > $@
+ ${MOC} $(srcdir)/s5b.cpp >> $@
diff --git a/kopete/protocols/jabber/libiris/iris/jabber/all_mocs.cpp b/kopete/protocols/jabber/libiris/iris/jabber/all_mocs.cpp
new file mode 100644
index 00000000..f962a854
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/jabber/all_mocs.cpp
@@ -0,0 +1,23 @@
+/*
+ * all_mocs.cpp - #include all .moc files in this directory
+ * Copyright (C) 2004 Richard Smith
+ *
+ * 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 "filetransfer.moc"
+#include "xmpp_ibb.moc"
+#include "xmpp_jidlink.moc"
diff --git a/kopete/protocols/jabber/libiris/iris/jabber/filetransfer.cpp b/kopete/protocols/jabber/libiris/iris/jabber/filetransfer.cpp
new file mode 100644
index 00000000..1697b6a2
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/jabber/filetransfer.cpp
@@ -0,0 +1,770 @@
+/*
+ * filetransfer.cpp - File Transfer
+ * 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"filetransfer.h"
+
+#include<qtimer.h>
+#include<qptrlist.h>
+#include<qguardedptr.h>
+#include<qfileinfo.h>
+#include"xmpp_xmlcommon.h"
+#include"s5b.h"
+
+#define SENDBUFSIZE 65536
+
+using namespace XMPP;
+
+// 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();
+}
+
+//----------------------------------------------------------------------------
+// FileTransfer
+//----------------------------------------------------------------------------
+class FileTransfer::Private
+{
+public:
+ FileTransferManager *m;
+ JT_FT *ft;
+ Jid peer;
+ QString fname;
+ Q_LLONG size;
+ Q_LLONG sent;
+ QString desc;
+ bool rangeSupported;
+ Q_LLONG rangeOffset, rangeLength, length;
+ QString streamType;
+ bool needStream;
+ QString id, iq_id;
+ S5BConnection *c;
+ Jid proxy;
+ int state;
+ bool sender;
+};
+
+FileTransfer::FileTransfer(FileTransferManager *m, QObject *parent)
+:QObject(parent)
+{
+ d = new Private;
+ d->m = m;
+ d->ft = 0;
+ d->c = 0;
+ reset();
+}
+
+FileTransfer::~FileTransfer()
+{
+ reset();
+ delete d;
+}
+
+void FileTransfer::reset()
+{
+ d->m->unlink(this);
+
+ delete d->ft;
+ d->ft = 0;
+
+ delete d->c;
+ d->c = 0;
+
+ d->state = Idle;
+ d->needStream = false;
+ d->sent = 0;
+ d->sender = false;
+}
+
+void FileTransfer::setProxy(const Jid &proxy)
+{
+ d->proxy = proxy;
+}
+
+void FileTransfer::sendFile(const Jid &to, const QString &fname, Q_LLONG size, const QString &desc)
+{
+ d->state = Requesting;
+ d->peer = to;
+ d->fname = fname;
+ d->size = size;
+ d->desc = desc;
+ d->sender = true;
+ d->id = d->m->link(this);
+
+ d->ft = new JT_FT(d->m->client()->rootTask());
+ connect(d->ft, SIGNAL(finished()), SLOT(ft_finished()));
+ QStringList list;
+ list += "http://jabber.org/protocol/bytestreams";
+ d->ft->request(to, d->id, fname, size, desc, list);
+ d->ft->go(true);
+}
+
+int FileTransfer::dataSizeNeeded() const
+{
+ int pending = d->c->bytesToWrite();
+ if(pending >= SENDBUFSIZE)
+ return 0;
+ Q_LLONG left = d->length - (d->sent + pending);
+ int size = SENDBUFSIZE - pending;
+ if((Q_LLONG)size > left)
+ size = (int)left;
+ return size;
+}
+
+void FileTransfer::writeFileData(const QByteArray &a)
+{
+ int pending = d->c->bytesToWrite();
+ Q_LLONG left = d->length - (d->sent + pending);
+ if(left == 0)
+ return;
+
+ QByteArray block;
+ if((Q_LLONG)a.size() > left) {
+ block = a.copy();
+ block.resize((uint)left);
+ }
+ else
+ block = a;
+ d->c->write(block);
+}
+
+Jid FileTransfer::peer() const
+{
+ return d->peer;
+}
+
+QString FileTransfer::fileName() const
+{
+ return d->fname;
+}
+
+Q_LLONG FileTransfer::fileSize() const
+{
+ return d->size;
+}
+
+QString FileTransfer::description() const
+{
+ return d->desc;
+}
+
+bool FileTransfer::rangeSupported() const
+{
+ return d->rangeSupported;
+}
+
+Q_LLONG FileTransfer::offset() const
+{
+ return d->rangeOffset;
+}
+
+Q_LLONG FileTransfer::length() const
+{
+ return d->length;
+}
+
+void FileTransfer::accept(Q_LLONG offset, Q_LLONG length)
+{
+ d->state = Connecting;
+ d->rangeOffset = offset;
+ d->rangeLength = length;
+ if(length > 0)
+ d->length = length;
+ else
+ d->length = d->size;
+ d->streamType = "http://jabber.org/protocol/bytestreams";
+ d->m->con_accept(this);
+}
+
+void FileTransfer::close()
+{
+ if(d->state == Idle)
+ return;
+ if(d->state == WaitingForAccept)
+ d->m->con_reject(this);
+ else if(d->state == Active)
+ d->c->close();
+ reset();
+}
+
+S5BConnection *FileTransfer::s5bConnection() const
+{
+ return d->c;
+}
+
+void FileTransfer::ft_finished()
+{
+ JT_FT *ft = d->ft;
+ d->ft = 0;
+
+ if(ft->success()) {
+ d->state = Connecting;
+ d->rangeOffset = ft->rangeOffset();
+ d->length = ft->rangeLength();
+ if(d->length == 0)
+ d->length = d->size - d->rangeOffset;
+ d->streamType = ft->streamType();
+ d->c = d->m->client()->s5bManager()->createConnection();
+ connect(d->c, SIGNAL(connected()), SLOT(s5b_connected()));
+ connect(d->c, SIGNAL(connectionClosed()), SLOT(s5b_connectionClosed()));
+ connect(d->c, SIGNAL(bytesWritten(int)), SLOT(s5b_bytesWritten(int)));
+ connect(d->c, SIGNAL(error(int)), SLOT(s5b_error(int)));
+
+ if(d->proxy.isValid())
+ d->c->setProxy(d->proxy);
+ d->c->connectToJid(d->peer, d->id);
+ accepted();
+ }
+ else {
+ reset();
+ if(ft->statusCode() == 403)
+ error(ErrReject);
+ else
+ error(ErrNeg);
+ }
+}
+
+void FileTransfer::takeConnection(S5BConnection *c)
+{
+ d->c = c;
+ connect(d->c, SIGNAL(connected()), SLOT(s5b_connected()));
+ connect(d->c, SIGNAL(connectionClosed()), SLOT(s5b_connectionClosed()));
+ connect(d->c, SIGNAL(readyRead()), SLOT(s5b_readyRead()));
+ connect(d->c, SIGNAL(error(int)), SLOT(s5b_error(int)));
+ if(d->proxy.isValid())
+ d->c->setProxy(d->proxy);
+ accepted();
+ QTimer::singleShot(0, this, SLOT(doAccept()));
+}
+
+void FileTransfer::s5b_connected()
+{
+ d->state = Active;
+ connected();
+}
+
+void FileTransfer::s5b_connectionClosed()
+{
+ reset();
+ error(ErrStream);
+}
+
+void FileTransfer::s5b_readyRead()
+{
+ QByteArray a = d->c->read();
+ Q_LLONG need = d->length - d->sent;
+ if((Q_LLONG)a.size() > need)
+ a.resize((uint)need);
+ d->sent += a.size();
+ if(d->sent == d->length)
+ reset();
+ readyRead(a);
+}
+
+void FileTransfer::s5b_bytesWritten(int x)
+{
+ d->sent += x;
+ if(d->sent == d->length)
+ reset();
+ bytesWritten(x);
+}
+
+void FileTransfer::s5b_error(int x)
+{
+ reset();
+ if(x == S5BConnection::ErrRefused || x == S5BConnection::ErrConnect)
+ error(ErrConnect);
+ else if(x == S5BConnection::ErrProxy)
+ error(ErrProxy);
+ else
+ error(ErrStream);
+}
+
+void FileTransfer::man_waitForAccept(const FTRequest &req)
+{
+ d->state = WaitingForAccept;
+ d->peer = req.from;
+ d->id = req.id;
+ d->iq_id = req.iq_id;
+ d->fname = req.fname;
+ d->size = req.size;
+ d->desc = req.desc;
+ d->rangeSupported = req.rangeSupported;
+}
+
+void FileTransfer::doAccept()
+{
+ d->c->accept();
+}
+
+//----------------------------------------------------------------------------
+// FileTransferManager
+//----------------------------------------------------------------------------
+class FileTransferManager::Private
+{
+public:
+ Client *client;
+ QPtrList<FileTransfer> list, incoming;
+ JT_PushFT *pft;
+};
+
+FileTransferManager::FileTransferManager(Client *client)
+:QObject(client)
+{
+ d = new Private;
+ d->client = client;
+
+ d->pft = new JT_PushFT(d->client->rootTask());
+ connect(d->pft, SIGNAL(incoming(const FTRequest &)), SLOT(pft_incoming(const FTRequest &)));
+}
+
+FileTransferManager::~FileTransferManager()
+{
+ d->incoming.setAutoDelete(true);
+ d->incoming.clear();
+ delete d->pft;
+ delete d;
+}
+
+Client *FileTransferManager::client() const
+{
+ return d->client;
+}
+
+FileTransfer *FileTransferManager::createTransfer()
+{
+ FileTransfer *ft = new FileTransfer(this);
+ return ft;
+}
+
+FileTransfer *FileTransferManager::takeIncoming()
+{
+ if(d->incoming.isEmpty())
+ return 0;
+
+ FileTransfer *ft = d->incoming.getFirst();
+ d->incoming.removeRef(ft);
+
+ // move to active list
+ d->list.append(ft);
+ return ft;
+}
+
+void FileTransferManager::pft_incoming(const FTRequest &req)
+{
+ bool found = false;
+ for(QStringList::ConstIterator it = req.streamTypes.begin(); it != req.streamTypes.end(); ++it) {
+ if((*it) == "http://jabber.org/protocol/bytestreams") {
+ found = true;
+ break;
+ }
+ }
+ if(!found) {
+ d->pft->respondError(req.from, req.iq_id, 400, "No valid stream types");
+ return;
+ }
+ if(!d->client->s5bManager()->isAcceptableSID(req.from, req.id)) {
+ d->pft->respondError(req.from, req.iq_id, 400, "SID in use");
+ return;
+ }
+
+ FileTransfer *ft = new FileTransfer(this);
+ ft->man_waitForAccept(req);
+ d->incoming.append(ft);
+ incomingReady();
+}
+
+void FileTransferManager::s5b_incomingReady(S5BConnection *c)
+{
+ QPtrListIterator<FileTransfer> it(d->list);
+ FileTransfer *ft = 0;
+ for(FileTransfer *i; (i = it.current()); ++it) {
+ if(i->d->needStream && i->d->peer.compare(c->peer()) && i->d->id == c->sid()) {
+ ft = i;
+ break;
+ }
+ }
+ if(!ft) {
+ c->close();
+ delete c;
+ return;
+ }
+ ft->takeConnection(c);
+}
+
+QString FileTransferManager::link(FileTransfer *ft)
+{
+ d->list.append(ft);
+ return d->client->s5bManager()->genUniqueSID(ft->d->peer);
+}
+
+void FileTransferManager::con_accept(FileTransfer *ft)
+{
+ ft->d->needStream = true;
+ d->pft->respondSuccess(ft->d->peer, ft->d->iq_id, ft->d->rangeOffset, ft->d->rangeLength, ft->d->streamType);
+}
+
+void FileTransferManager::con_reject(FileTransfer *ft)
+{
+ d->pft->respondError(ft->d->peer, ft->d->iq_id, 403, "Declined");
+}
+
+void FileTransferManager::unlink(FileTransfer *ft)
+{
+ d->list.removeRef(ft);
+}
+
+//----------------------------------------------------------------------------
+// JT_FT
+//----------------------------------------------------------------------------
+class JT_FT::Private
+{
+public:
+ QDomElement iq;
+ Jid to;
+ Q_LLONG size, rangeOffset, rangeLength;
+ QString streamType;
+ QStringList streamTypes;
+};
+
+JT_FT::JT_FT(Task *parent)
+:Task(parent)
+{
+ d = new Private;
+}
+
+JT_FT::~JT_FT()
+{
+ delete d;
+}
+
+void JT_FT::request(const Jid &to, const QString &_id, const QString &fname, Q_LLONG size, const QString &desc, const QStringList &streamTypes)
+{
+ QDomElement iq;
+ d->to = to;
+ iq = createIQ(doc(), "set", to.full(), id());
+ QDomElement si = doc()->createElement("si");
+ si.setAttribute("xmlns", "http://jabber.org/protocol/si");
+ si.setAttribute("id", _id);
+ si.setAttribute("profile", "http://jabber.org/protocol/si/profile/file-transfer");
+
+ QDomElement file = doc()->createElement("file");
+ file.setAttribute("xmlns", "http://jabber.org/protocol/si/profile/file-transfer");
+ file.setAttribute("name", fname);
+ file.setAttribute("size", QString::number(size));
+ if(!desc.isEmpty()) {
+ QDomElement de = doc()->createElement("desc");
+ de.appendChild(doc()->createTextNode(desc));
+ file.appendChild(de);
+ }
+ QDomElement range = doc()->createElement("range");
+ file.appendChild(range);
+ si.appendChild(file);
+
+ QDomElement feature = doc()->createElement("feature");
+ feature.setAttribute("xmlns", "http://jabber.org/protocol/feature-neg");
+ QDomElement x = doc()->createElement("x");
+ x.setAttribute("xmlns", "jabber:x:data");
+ x.setAttribute("type", "form");
+
+ QDomElement field = doc()->createElement("field");
+ field.setAttribute("var", "stream-method");
+ field.setAttribute("type", "list-single");
+ for(QStringList::ConstIterator it = streamTypes.begin(); it != streamTypes.end(); ++it) {
+ QDomElement option = doc()->createElement("option");
+ QDomElement value = doc()->createElement("value");
+ value.appendChild(doc()->createTextNode(*it));
+ option.appendChild(value);
+ field.appendChild(option);
+ }
+
+ x.appendChild(field);
+ feature.appendChild(x);
+
+ si.appendChild(feature);
+ iq.appendChild(si);
+
+ d->streamTypes = streamTypes;
+ d->size = size;
+ d->iq = iq;
+}
+
+Q_LLONG JT_FT::rangeOffset() const
+{
+ return d->rangeOffset;
+}
+
+Q_LLONG JT_FT::rangeLength() const
+{
+ return d->rangeLength;
+}
+
+QString JT_FT::streamType() const
+{
+ return d->streamType;
+}
+
+void JT_FT::onGo()
+{
+ send(d->iq);
+}
+
+bool JT_FT::take(const QDomElement &x)
+{
+ if(!iqVerify(x, d->to, id()))
+ return false;
+
+ if(x.attribute("type") == "result") {
+ QDomElement si = firstChildElement(x);
+ if(si.attribute("xmlns") != "http://jabber.org/protocol/si" || si.tagName() != "si") {
+ setError(900, "");
+ return true;
+ }
+
+ QString id = si.attribute("id");
+
+ Q_LLONG range_offset = 0;
+ Q_LLONG range_length = 0;
+
+ QDomElement file = si.elementsByTagName("file").item(0).toElement();
+ if(!file.isNull()) {
+ QDomElement range = file.elementsByTagName("range").item(0).toElement();
+ if(!range.isNull()) {
+ int x;
+ bool ok;
+ if(range.hasAttribute("offset")) {
+#if QT_VERSION >= 0x030200
+ x = range.attribute("offset").toLongLong(&ok);
+#else
+ x = range.attribute("offset").toLong(&ok);
+#endif
+ if(!ok || x < 0) {
+ setError(900, "");
+ return true;
+ }
+ range_offset = x;
+ }
+ if(range.hasAttribute("length")) {
+#if QT_VERSION >= 0x030200
+ x = range.attribute("length").toLongLong(&ok);
+#else
+ x = range.attribute("length").toLong(&ok);
+#endif
+ if(!ok || x < 0) {
+ setError(900, "");
+ return true;
+ }
+ range_length = x;
+ }
+ }
+ }
+
+ if(range_offset > d->size || (range_length > (d->size - range_offset))) {
+ setError(900, "");
+ return true;
+ }
+
+ QString streamtype;
+ QDomElement feature = si.elementsByTagName("feature").item(0).toElement();
+ if(!feature.isNull() && feature.attribute("xmlns") == "http://jabber.org/protocol/feature-neg") {
+ QDomElement x = feature.elementsByTagName("x").item(0).toElement();
+ if(!x.isNull() && x.attribute("type") == "submit") {
+ QDomElement field = x.elementsByTagName("field").item(0).toElement();
+ if(!field.isNull() && field.attribute("var") == "stream-method") {
+ QDomElement value = field.elementsByTagName("value").item(0).toElement();
+ if(!value.isNull())
+ streamtype = value.text();
+ }
+ }
+ }
+
+ // must be one of the offered streamtypes
+ bool found = false;
+ for(QStringList::ConstIterator it = d->streamTypes.begin(); it != d->streamTypes.end(); ++it) {
+ if((*it) == streamtype) {
+ found = true;
+ break;
+ }
+ }
+ if(!found)
+ return true;
+
+ d->rangeOffset = range_offset;
+ d->rangeLength = range_length;
+ d->streamType = streamtype;
+ setSuccess();
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// JT_PushFT
+//----------------------------------------------------------------------------
+JT_PushFT::JT_PushFT(Task *parent)
+:Task(parent)
+{
+}
+
+JT_PushFT::~JT_PushFT()
+{
+}
+
+void JT_PushFT::respondSuccess(const Jid &to, const QString &id, Q_LLONG rangeOffset, Q_LLONG rangeLength, const QString &streamType)
+{
+ QDomElement iq = createIQ(doc(), "result", to.full(), id);
+ QDomElement si = doc()->createElement("si");
+ si.setAttribute("xmlns", "http://jabber.org/protocol/si");
+
+ if(rangeOffset != 0 || rangeLength != 0) {
+ QDomElement file = doc()->createElement("file");
+ file.setAttribute("xmlns", "http://jabber.org/protocol/si/profile/file-transfer");
+ QDomElement range = doc()->createElement("range");
+ if(rangeOffset > 0)
+ range.setAttribute("offset", QString::number(rangeOffset));
+ if(rangeLength > 0)
+ range.setAttribute("length", QString::number(rangeLength));
+ file.appendChild(range);
+ si.appendChild(file);
+ }
+
+ QDomElement feature = doc()->createElement("feature");
+ feature.setAttribute("xmlns", "http://jabber.org/protocol/feature-neg");
+ QDomElement x = doc()->createElement("x");
+ x.setAttribute("xmlns", "jabber:x:data");
+ x.setAttribute("type", "submit");
+
+ QDomElement field = doc()->createElement("field");
+ field.setAttribute("var", "stream-method");
+ QDomElement value = doc()->createElement("value");
+ value.appendChild(doc()->createTextNode(streamType));
+ field.appendChild(value);
+
+ x.appendChild(field);
+ feature.appendChild(x);
+
+ si.appendChild(feature);
+ iq.appendChild(si);
+ send(iq);
+}
+
+void JT_PushFT::respondError(const Jid &to, const QString &id, int code, const QString &str)
+{
+ QDomElement iq = createIQ(doc(), "error", to.full(), id);
+ QDomElement err = textTag(doc(), "error", str);
+ err.setAttribute("code", QString::number(code));
+ iq.appendChild(err);
+ send(iq);
+}
+
+bool JT_PushFT::take(const QDomElement &e)
+{
+ // must be an iq-set tag
+ if(e.tagName() != "iq")
+ return false;
+ if(e.attribute("type") != "set")
+ return false;
+
+ QDomElement si = firstChildElement(e);
+ if(si.attribute("xmlns") != "http://jabber.org/protocol/si" || si.tagName() != "si")
+ return false;
+ if(si.attribute("profile") != "http://jabber.org/protocol/si/profile/file-transfer")
+ return false;
+
+ Jid from(e.attribute("from"));
+ QString id = si.attribute("id");
+
+ QDomElement file = si.elementsByTagName("file").item(0).toElement();
+ if(file.isNull())
+ return true;
+
+ QString fname = file.attribute("name");
+ if(fname.isEmpty()) {
+ respondError(from, id, 400, "Bad file name");
+ return true;
+ }
+
+ // ensure kosher
+ {
+ QFileInfo fi(fname);
+ fname = fi.fileName();
+ }
+
+ bool ok;
+#if QT_VERSION >= 0x030200
+ Q_LLONG size = file.attribute("size").toLongLong(&ok);
+#else
+ Q_LLONG size = file.attribute("size").toLong(&ok);
+#endif
+ if(!ok || size < 0) {
+ respondError(from, id, 400, "Bad file size");
+ return true;
+ }
+
+ QString desc;
+ QDomElement de = file.elementsByTagName("desc").item(0).toElement();
+ if(!de.isNull())
+ desc = de.text();
+
+ bool rangeSupported = false;
+ QDomElement range = file.elementsByTagName("range").item(0).toElement();
+ if(!range.isNull())
+ rangeSupported = true;
+
+ QStringList streamTypes;
+ QDomElement feature = si.elementsByTagName("feature").item(0).toElement();
+ if(!feature.isNull() && feature.attribute("xmlns") == "http://jabber.org/protocol/feature-neg") {
+ QDomElement x = feature.elementsByTagName("x").item(0).toElement();
+ if(!x.isNull() /*&& x.attribute("type") == "form"*/) {
+ QDomElement field = x.elementsByTagName("field").item(0).toElement();
+ if(!field.isNull() && field.attribute("var") == "stream-method" && field.attribute("type") == "list-single") {
+ QDomNodeList nl = field.elementsByTagName("option");
+ for(uint n = 0; n < nl.count(); ++n) {
+ QDomElement e = nl.item(n).toElement();
+ QDomElement value = e.elementsByTagName("value").item(0).toElement();
+ if(!value.isNull())
+ streamTypes += value.text();
+ }
+ }
+ }
+ }
+
+ FTRequest r;
+ r.from = from;
+ r.iq_id = e.attribute("id");
+ r.id = id;
+ r.fname = fname;
+ r.size = size;
+ r.desc = desc;
+ r.rangeSupported = rangeSupported;
+ r.streamTypes = streamTypes;
+
+ incoming(r);
+ return true;
+}
diff --git a/kopete/protocols/jabber/libiris/iris/jabber/filetransfer.h b/kopete/protocols/jabber/libiris/iris/jabber/filetransfer.h
new file mode 100644
index 00000000..9ad4d403
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/jabber/filetransfer.h
@@ -0,0 +1,170 @@
+/*
+ * filetransfer.h - File Transfer
+ * 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 XMPP_FILETRANSFER_H
+#define XMPP_FILETRANSFER_H
+
+#include"im.h"
+
+#if QT_VERSION < 0x030200
+typedef long int Q_LLONG;
+#endif
+
+namespace XMPP
+{
+ class S5BConnection;
+ struct FTRequest;
+
+ class FileTransfer : public QObject
+ {
+ Q_OBJECT
+ public:
+ enum { ErrReject, ErrNeg, ErrConnect, ErrProxy, ErrStream };
+ enum { Idle, Requesting, Connecting, WaitingForAccept, Active };
+ ~FileTransfer();
+
+ void setProxy(const Jid &proxy);
+
+ // send
+ void sendFile(const Jid &to, const QString &fname, Q_LLONG size, const QString &desc);
+ Q_LLONG offset() const;
+ Q_LLONG length() const;
+ int dataSizeNeeded() const;
+ void writeFileData(const QByteArray &a);
+
+ // receive
+ Jid peer() const;
+ QString fileName() const;
+ Q_LLONG fileSize() const;
+ QString description() const;
+ bool rangeSupported() const;
+ void accept(Q_LLONG offset=0, Q_LLONG length=0);
+
+ // both
+ void close(); // reject, or stop sending/receiving
+ S5BConnection *s5bConnection() const; // active link
+
+ signals:
+ void accepted(); // indicates S5BConnection has started
+ void connected();
+ void readyRead(const QByteArray &a);
+ void bytesWritten(int);
+ void error(int);
+
+ private slots:
+ void ft_finished();
+ void s5b_connected();
+ void s5b_connectionClosed();
+ void s5b_readyRead();
+ void s5b_bytesWritten(int);
+ void s5b_error(int);
+ void doAccept();
+
+ private:
+ class Private;
+ Private *d;
+
+ void reset();
+
+ friend class FileTransferManager;
+ FileTransfer(FileTransferManager *, QObject *parent=0);
+ void man_waitForAccept(const FTRequest &req);
+ void takeConnection(S5BConnection *c);
+ };
+
+ class FileTransferManager : public QObject
+ {
+ Q_OBJECT
+ public:
+ FileTransferManager(Client *);
+ ~FileTransferManager();
+
+ Client *client() const;
+ FileTransfer *createTransfer();
+ FileTransfer *takeIncoming();
+
+ signals:
+ void incomingReady();
+
+ private slots:
+ void pft_incoming(const FTRequest &req);
+
+ private:
+ class Private;
+ Private *d;
+
+ friend class Client;
+ void s5b_incomingReady(S5BConnection *);
+
+ friend class FileTransfer;
+ QString link(FileTransfer *);
+ void con_accept(FileTransfer *);
+ void con_reject(FileTransfer *);
+ void unlink(FileTransfer *);
+ };
+
+ class JT_FT : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_FT(Task *parent);
+ ~JT_FT();
+
+ void request(const Jid &to, const QString &id, const QString &fname, Q_LLONG size, const QString &desc, const QStringList &streamTypes);
+ Q_LLONG rangeOffset() const;
+ Q_LLONG rangeLength() const;
+ QString streamType() const;
+
+ void onGo();
+ bool take(const QDomElement &);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ struct FTRequest
+ {
+ Jid from;
+ QString iq_id, id;
+ QString fname;
+ Q_LLONG size;
+ QString desc;
+ bool rangeSupported;
+ QStringList streamTypes;
+ };
+ class JT_PushFT : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_PushFT(Task *parent);
+ ~JT_PushFT();
+
+ void respondSuccess(const Jid &to, const QString &id, Q_LLONG rangeOffset, Q_LLONG rangeLength, const QString &streamType);
+ void respondError(const Jid &to, const QString &id, int code, const QString &str);
+
+ bool take(const QDomElement &);
+
+ signals:
+ void incoming(const FTRequest &req);
+ };
+}
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/jabber/s5b.cpp b/kopete/protocols/jabber/libiris/iris/jabber/s5b.cpp
new file mode 100644
index 00000000..b4b9be44
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/jabber/s5b.cpp
@@ -0,0 +1,2538 @@
+/*
+ * s5b.cpp - direct connection protocol via tcp
+ * 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 <config.h>
+
+#include"s5b.h"
+
+#include<qtimer.h>
+#include<qguardedptr.h>
+#include<stdlib.h>
+#include<qca.h>
+#include"xmpp_xmlcommon.h"
+#include"hash.h"
+#include"socks.h"
+#include"safedelete.h"
+
+#ifdef Q_OS_WIN
+# include <windows.h>
+#else
+# ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+# endif
+# include <netinet/in.h>
+#endif
+
+#define MAXSTREAMHOSTS 5
+
+//#define S5B_DEBUG
+
+namespace XMPP {
+
+static QString makeKey(const QString &sid, const Jid &initiator, const Jid &target)
+{
+ QString str = sid + initiator.full() + target.full();
+ return QCA::SHA1::hashToString(str.utf8());
+}
+
+static bool haveHost(const StreamHostList &list, const Jid &j)
+{
+ for(StreamHostList::ConstIterator it = list.begin(); it != list.end(); ++it) {
+ if((*it).jid().compare(j))
+ return true;
+ }
+ return false;
+}
+
+class S5BManager::Item : public QObject
+{
+ Q_OBJECT
+public:
+ enum { Idle, Initiator, Target, Active };
+ enum { ErrRefused, ErrConnect, ErrWrongHost, ErrProxy };
+ enum { Unknown, Fast, NotFast };
+ S5BManager *m;
+ int state;
+ QString sid, key, out_key, out_id, in_id;
+ Jid self, peer;
+ StreamHostList in_hosts;
+ JT_S5B *task, *proxy_task;
+ SocksClient *client, *client_out;
+ SocksUDP *client_udp, *client_out_udp;
+ S5BConnector *conn, *proxy_conn;
+ bool wantFast;
+ StreamHost proxy;
+ int targetMode; // initiator sets this once it figures it out
+ bool fast; // target sets this
+ bool activated;
+ bool lateProxy;
+ bool connSuccess;
+ bool localFailed, remoteFailed;
+ bool allowIncoming;
+ bool udp;
+ int statusCode;
+ Jid activatedStream;
+
+ Item(S5BManager *manager);
+ ~Item();
+
+ void reset();
+ void startInitiator(const QString &_sid, const Jid &_self, const Jid &_peer, bool fast, bool udp);
+ void startTarget(const QString &_sid, const Jid &_self, const Jid &_peer, const StreamHostList &hosts, const QString &iq_id, bool fast, bool udp);
+ void handleFast(const StreamHostList &hosts, const QString &iq_id);
+
+ void doOutgoing();
+ void doIncoming();
+ void setIncomingClient(SocksClient *sc);
+ void incomingActivate(const Jid &streamHost);
+
+signals:
+ void accepted();
+ void tryingHosts(const StreamHostList &list);
+ void proxyConnect();
+ void waitingForActivation();
+ void connected();
+ void error(int);
+
+private slots:
+ void jt_finished();
+ void conn_result(bool b);
+ void proxy_result(bool b);
+ void proxy_finished();
+ void sc_readyRead();
+ void sc_bytesWritten(int);
+ void sc_error(int);
+
+private:
+ void doConnectError();
+ void tryActivation();
+ void checkForActivation();
+ void checkFailure();
+ void finished();
+};
+
+//----------------------------------------------------------------------------
+// S5BDatagram
+//----------------------------------------------------------------------------
+S5BDatagram::S5BDatagram()
+{
+ _source = 0;
+ _dest = 0;
+}
+
+S5BDatagram::S5BDatagram(int source, int dest, const QByteArray &data)
+{
+ _source = source;
+ _dest = dest;
+ _buf = data;
+}
+
+int S5BDatagram::sourcePort() const
+{
+ return _source;
+}
+
+int S5BDatagram::destPort() const
+{
+ return _dest;
+}
+
+QByteArray S5BDatagram::data() const
+{
+ return _buf;
+}
+
+//----------------------------------------------------------------------------
+// S5BConnection
+//----------------------------------------------------------------------------
+class S5BConnection::Private
+{
+public:
+ S5BManager *m;
+ SocksClient *sc;
+ SocksUDP *su;
+ int state;
+ Jid peer;
+ QString sid;
+ bool remote;
+ bool switched;
+ bool notifyRead, notifyClose;
+ int id;
+ S5BRequest req;
+ Jid proxy;
+ Mode mode;
+ QPtrList<S5BDatagram> dglist;
+};
+
+static int id_conn = 0;
+static int num_conn = 0;
+
+S5BConnection::S5BConnection(S5BManager *m, QObject *parent)
+:ByteStream(parent)
+{
+ d = new Private;
+ d->m = m;
+ d->sc = 0;
+ d->su = 0;
+
+ ++num_conn;
+ d->id = id_conn++;
+#ifdef S5B_DEBUG
+ printf("S5BConnection[%d]: constructing, count=%d, %p\n", d->id, num_conn, this);
+#endif
+
+ reset();
+}
+
+S5BConnection::~S5BConnection()
+{
+ reset(true);
+
+ --num_conn;
+#ifdef S5B_DEBUG
+ printf("S5BConnection[%d]: destructing, count=%d\n", d->id, num_conn);
+#endif
+
+ delete d;
+}
+
+void S5BConnection::reset(bool clear)
+{
+ d->m->con_unlink(this);
+ if(clear && d->sc) {
+ delete d->sc;
+ d->sc = 0;
+ }
+ delete d->su;
+ d->su = 0;
+ if(clear) {
+ d->dglist.setAutoDelete(true);
+ d->dglist.clear();
+ d->dglist.setAutoDelete(false);
+ }
+ d->state = Idle;
+ d->peer = Jid();
+ d->sid = QString();
+ d->remote = false;
+ d->switched = false;
+ d->notifyRead = false;
+ d->notifyClose = false;
+}
+
+Jid S5BConnection::proxy() const
+{
+ return d->proxy;
+}
+
+void S5BConnection::setProxy(const Jid &proxy)
+{
+ d->proxy = proxy;
+}
+
+void S5BConnection::connectToJid(const Jid &peer, const QString &sid, Mode m)
+{
+ reset(true);
+ if(!d->m->isAcceptableSID(peer, sid))
+ return;
+
+ d->peer = peer;
+ d->sid = sid;
+ d->state = Requesting;
+ d->mode = m;
+#ifdef S5B_DEBUG
+ printf("S5BConnection[%d]: connecting %s [%s]\n", d->id, d->peer.full().latin1(), d->sid.latin1());
+#endif
+ d->m->con_connect(this);
+}
+
+void S5BConnection::accept()
+{
+ if(d->state != WaitingForAccept)
+ return;
+
+ d->state = Connecting;
+#ifdef S5B_DEBUG
+ printf("S5BConnection[%d]: accepting %s [%s]\n", d->id, d->peer.full().latin1(), d->sid.latin1());
+#endif
+ d->m->con_accept(this);
+}
+
+void S5BConnection::close()
+{
+ if(d->state == Idle)
+ return;
+
+ if(d->state == WaitingForAccept)
+ d->m->con_reject(this);
+ else if(d->state == Active)
+ d->sc->close();
+#ifdef S5B_DEBUG
+ printf("S5BConnection[%d]: closing %s [%s]\n", d->id, d->peer.full().latin1(), d->sid.latin1());
+#endif
+ reset();
+}
+
+Jid S5BConnection::peer() const
+{
+ return d->peer;
+}
+
+QString S5BConnection::sid() const
+{
+ return d->sid;
+}
+
+bool S5BConnection::isRemote() const
+{
+ return d->remote;
+}
+
+S5BConnection::Mode S5BConnection::mode() const
+{
+ return d->mode;
+}
+
+int S5BConnection::state() const
+{
+ return d->state;
+}
+
+bool S5BConnection::isOpen() const
+{
+ if(d->state == Active)
+ return true;
+ else
+ return false;
+}
+
+void S5BConnection::write(const QByteArray &buf)
+{
+ if(d->state == Active && d->mode == Stream)
+ d->sc->write(buf);
+}
+
+QByteArray S5BConnection::read(int bytes)
+{
+ if(d->sc)
+ return d->sc->read(bytes);
+ else
+ return QByteArray();
+}
+
+int S5BConnection::bytesAvailable() const
+{
+ if(d->sc)
+ return d->sc->bytesAvailable();
+ else
+ return 0;
+}
+
+int S5BConnection::bytesToWrite() const
+{
+ if(d->state == Active)
+ return d->sc->bytesToWrite();
+ else
+ return 0;
+}
+
+void S5BConnection::writeDatagram(const S5BDatagram &i)
+{
+ QByteArray buf(i.data().size() + 4);
+ ushort ssp = htons(i.sourcePort());
+ ushort sdp = htons(i.destPort());
+ QByteArray data = i.data();
+ memcpy(buf.data(), &ssp, 2);
+ memcpy(buf.data() + 2, &sdp, 2);
+ memcpy(buf.data() + 4, data.data(), data.size());
+ sendUDP(buf);
+}
+
+S5BDatagram S5BConnection::readDatagram()
+{
+ if(d->dglist.isEmpty())
+ return S5BDatagram();
+ S5BDatagram *i = d->dglist.getFirst();
+ d->dglist.removeRef(i);
+ S5BDatagram val = *i;
+ delete i;
+ return val;
+}
+
+int S5BConnection::datagramsAvailable() const
+{
+ return d->dglist.count();
+}
+
+void S5BConnection::man_waitForAccept(const S5BRequest &r)
+{
+ d->state = WaitingForAccept;
+ d->remote = true;
+ d->req = r;
+ d->peer = r.from;
+ d->sid = r.sid;
+ d->mode = r.udp ? Datagram : Stream;
+}
+
+void S5BConnection::man_clientReady(SocksClient *sc, SocksUDP *sc_udp)
+{
+ d->sc = sc;
+ connect(d->sc, SIGNAL(connectionClosed()), SLOT(sc_connectionClosed()));
+ connect(d->sc, SIGNAL(delayedCloseFinished()), SLOT(sc_delayedCloseFinished()));
+ connect(d->sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
+ connect(d->sc, SIGNAL(bytesWritten(int)), SLOT(sc_bytesWritten(int)));
+ connect(d->sc, SIGNAL(error(int)), SLOT(sc_error(int)));
+
+ if(sc_udp) {
+ d->su = sc_udp;
+ connect(d->su, SIGNAL(packetReady(const QByteArray &)), SLOT(su_packetReady(const QByteArray &)));
+ }
+
+ d->state = Active;
+#ifdef S5B_DEBUG
+ printf("S5BConnection[%d]: %s [%s] <<< success >>>\n", d->id, d->peer.full().latin1(), d->sid.latin1());
+#endif
+
+ // bytes already in the stream?
+ if(d->sc->bytesAvailable()) {
+#ifdef S5B_DEBUG
+ printf("Stream has %d bytes in it.\n", d->sc->bytesAvailable());
+#endif
+ d->notifyRead = true;
+ }
+ // closed before it got here?
+ if(!d->sc->isOpen()) {
+#ifdef S5B_DEBUG
+ printf("Stream was closed before S5B request finished?\n");
+#endif
+ d->notifyClose = true;
+ }
+ if(d->notifyRead || d->notifyClose)
+ QTimer::singleShot(0, this, SLOT(doPending()));
+ connected();
+}
+
+void S5BConnection::doPending()
+{
+ if(d->notifyRead) {
+ if(d->notifyClose)
+ QTimer::singleShot(0, this, SLOT(doPending()));
+ sc_readyRead();
+ }
+ else if(d->notifyClose)
+ sc_connectionClosed();
+}
+
+void S5BConnection::man_udpReady(const QByteArray &buf)
+{
+ handleUDP(buf);
+}
+
+void S5BConnection::man_failed(int x)
+{
+ reset(true);
+ if(x == S5BManager::Item::ErrRefused)
+ error(ErrRefused);
+ if(x == S5BManager::Item::ErrConnect)
+ error(ErrConnect);
+ if(x == S5BManager::Item::ErrWrongHost)
+ error(ErrConnect);
+ if(x == S5BManager::Item::ErrProxy)
+ error(ErrProxy);
+}
+
+void S5BConnection::sc_connectionClosed()
+{
+ // if we have a pending read notification, postpone close
+ if(d->notifyRead) {
+#ifdef S5B_DEBUG
+ printf("closed while pending read\n");
+#endif
+ d->notifyClose = true;
+ return;
+ }
+ d->notifyClose = false;
+ reset();
+ connectionClosed();
+}
+
+void S5BConnection::sc_delayedCloseFinished()
+{
+ // echo
+ delayedCloseFinished();
+}
+
+void S5BConnection::sc_readyRead()
+{
+ if(d->mode == Datagram) {
+ // throw the data away
+ d->sc->read();
+ return;
+ }
+
+ d->notifyRead = false;
+ // echo
+ readyRead();
+}
+
+void S5BConnection::sc_bytesWritten(int x)
+{
+ // echo
+ bytesWritten(x);
+}
+
+void S5BConnection::sc_error(int)
+{
+ reset();
+ error(ErrSocket);
+}
+
+void S5BConnection::su_packetReady(const QByteArray &buf)
+{
+ handleUDP(buf);
+}
+
+void S5BConnection::handleUDP(const QByteArray &buf)
+{
+ // must be at least 4 bytes, to accomodate virtual ports
+ if(buf.size() < 4)
+ return; // drop
+
+ ushort ssp, sdp;
+ memcpy(&ssp, buf.data(), 2);
+ memcpy(&sdp, buf.data() + 2, 2);
+ int source = ntohs(ssp);
+ int dest = ntohs(sdp);
+ QByteArray data(buf.size() - 4);
+ memcpy(data.data(), buf.data() + 4, data.size());
+ d->dglist.append(new S5BDatagram(source, dest, data));
+
+ datagramReady();
+}
+
+void S5BConnection::sendUDP(const QByteArray &buf)
+{
+ if(d->su)
+ d->su->write(buf);
+ else
+ d->m->con_sendUDP(this, buf);
+}
+
+//----------------------------------------------------------------------------
+// S5BManager
+//----------------------------------------------------------------------------
+class S5BManager::Entry
+{
+public:
+ Entry()
+ {
+ i = 0;
+ query = 0;
+ udp_init = false;
+ }
+
+ ~Entry()
+ {
+ delete query;
+ }
+
+ S5BConnection *c;
+ Item *i;
+ QString sid;
+ JT_S5B *query;
+ StreamHost proxyInfo;
+ QGuardedPtr<S5BServer> relatedServer;
+
+ bool udp_init;
+ QHostAddress udp_addr;
+ int udp_port;
+};
+
+class S5BManager::Private
+{
+public:
+ Client *client;
+ S5BServer *serv;
+ QPtrList<Entry> activeList;
+ S5BConnectionList incomingConns;
+ JT_PushS5B *ps;
+};
+
+S5BManager::S5BManager(Client *parent)
+:QObject(parent)
+{
+ // S5B needs SHA1
+ if(!QCA::isSupported(QCA::CAP_SHA1))
+ QCA::insertProvider(createProviderHash());
+
+ d = new Private;
+ d->client = parent;
+ d->serv = 0;
+ d->activeList.setAutoDelete(true);
+
+ d->ps = new JT_PushS5B(d->client->rootTask());
+ connect(d->ps, SIGNAL(incoming(const S5BRequest &)), SLOT(ps_incoming(const S5BRequest &)));
+ connect(d->ps, SIGNAL(incomingUDPSuccess(const Jid &, const QString &)), SLOT(ps_incomingUDPSuccess(const Jid &, const QString &)));
+ connect(d->ps, SIGNAL(incomingActivate(const Jid &, const QString &, const Jid &)), SLOT(ps_incomingActivate(const Jid &, const QString &, const Jid &)));
+}
+
+S5BManager::~S5BManager()
+{
+ setServer(0);
+ d->incomingConns.setAutoDelete(true);
+ d->incomingConns.clear();
+ delete d->ps;
+ delete d;
+}
+
+Client *S5BManager::client() const
+{
+ return d->client;
+}
+
+S5BServer *S5BManager::server() const
+{
+ return d->serv;
+}
+
+void S5BManager::setServer(S5BServer *serv)
+{
+ if(d->serv) {
+ d->serv->unlink(this);
+ d->serv = 0;
+ }
+
+ if(serv) {
+ d->serv = serv;
+ d->serv->link(this);
+ }
+}
+
+S5BConnection *S5BManager::createConnection()
+{
+ S5BConnection *c = new S5BConnection(this);
+ return c;
+}
+
+S5BConnection *S5BManager::takeIncoming()
+{
+ if(d->incomingConns.isEmpty())
+ return 0;
+
+ S5BConnection *c = d->incomingConns.getFirst();
+ d->incomingConns.removeRef(c);
+
+ // move to activeList
+ Entry *e = new Entry;
+ e->c = c;
+ e->sid = c->d->sid;
+ d->activeList.append(e);
+
+ return c;
+}
+
+void S5BManager::ps_incoming(const S5BRequest &req)
+{
+#ifdef S5B_DEBUG
+ printf("S5BManager: incoming from %s\n", req.from.full().latin1());
+#endif
+
+ bool ok = false;
+ // ensure we don't already have an incoming connection from this peer+sid
+ S5BConnection *c = findIncoming(req.from, req.sid);
+ if(!c) {
+ // do we have an active entry with this sid already?
+ Entry *e = findEntryBySID(req.from, req.sid);
+ if(e) {
+ if(e->i) {
+ // loopback
+ if(req.from.compare(d->client->jid()) && (req.id == e->i->out_id)) {
+#ifdef S5B_DEBUG
+ printf("ALLOWED: loopback\n");
+#endif
+ ok = true;
+ }
+ // allowed by 'fast mode'
+ else if(e->i->state == Item::Initiator && e->i->targetMode == Item::Unknown) {
+#ifdef S5B_DEBUG
+ printf("ALLOWED: fast-mode\n");
+#endif
+ e->i->handleFast(req.hosts, req.id);
+ return;
+ }
+ }
+ }
+ else {
+#ifdef S5B_DEBUG
+ printf("ALLOWED: we don't have it\n");
+#endif
+ ok = true;
+ }
+ }
+ if(!ok) {
+ d->ps->respondError(req.from, req.id, 406, "SID in use");
+ return;
+ }
+
+ // create an incoming connection
+ c = new S5BConnection(this);
+ c->man_waitForAccept(req);
+ d->incomingConns.append(c);
+ incomingReady();
+}
+
+void S5BManager::ps_incomingUDPSuccess(const Jid &from, const QString &key)
+{
+ Entry *e = findEntryByHash(key);
+ if(e && e->i) {
+ if(e->i->conn)
+ e->i->conn->man_udpSuccess(from);
+ else if(e->i->proxy_conn)
+ e->i->proxy_conn->man_udpSuccess(from);
+ }
+}
+
+void S5BManager::ps_incomingActivate(const Jid &from, const QString &sid, const Jid &streamHost)
+{
+ Entry *e = findEntryBySID(from, sid);
+ if(e && e->i)
+ e->i->incomingActivate(streamHost);
+}
+
+void S5BManager::doSuccess(const Jid &peer, const QString &id, const Jid &streamHost)
+{
+ d->ps->respondSuccess(peer, id, streamHost);
+}
+
+void S5BManager::doError(const Jid &peer, const QString &id, int code, const QString &str)
+{
+ d->ps->respondError(peer, id, code, str);
+}
+
+void S5BManager::doActivate(const Jid &peer, const QString &sid, const Jid &streamHost)
+{
+ d->ps->sendActivate(peer, sid, streamHost);
+}
+
+QString S5BManager::genUniqueSID(const Jid &peer) const
+{
+ // get unused key
+ QString sid;
+ do {
+ sid = "s5b_";
+ for(int i = 0; i < 4; ++i) {
+ int word = rand() & 0xffff;
+ for(int n = 0; n < 4; ++n) {
+ QString s;
+ s.sprintf("%x", (word >> (n * 4)) & 0xf);
+ sid.append(s);
+ }
+ }
+ } while(!isAcceptableSID(peer, sid));
+ return sid;
+}
+
+bool S5BManager::isAcceptableSID(const Jid &peer, const QString &sid) const
+{
+ QString key = makeKey(sid, d->client->jid(), peer);
+ QString key_out = makeKey(sid, peer, d->client->jid());
+
+ // if we have a server, then check through it
+ if(d->serv) {
+ if(findServerEntryByHash(key) || findServerEntryByHash(key_out))
+ return false;
+ }
+ else {
+ if(findEntryByHash(key) || findEntryByHash(key_out))
+ return false;
+ }
+ return true;
+}
+
+S5BConnection *S5BManager::findIncoming(const Jid &from, const QString &sid) const
+{
+ QPtrListIterator<S5BConnection> it(d->incomingConns);
+ for(S5BConnection *c; (c = it.current()); ++it) {
+ if(c->d->peer.compare(from) && c->d->sid == sid)
+ return c;
+ }
+ return 0;
+}
+
+S5BManager::Entry *S5BManager::findEntry(S5BConnection *c) const
+{
+ QPtrListIterator<Entry> it(d->activeList);
+ for(Entry *e; (e = it.current()); ++it) {
+ if(e->c == c)
+ return e;
+ }
+ return 0;
+}
+
+S5BManager::Entry *S5BManager::findEntry(Item *i) const
+{
+ QPtrListIterator<Entry> it(d->activeList);
+ for(Entry *e; (e = it.current()); ++it) {
+ if(e->i == i)
+ return e;
+ }
+ return 0;
+}
+
+S5BManager::Entry *S5BManager::findEntryByHash(const QString &key) const
+{
+ QPtrListIterator<Entry> it(d->activeList);
+ for(Entry *e; (e = it.current()); ++it) {
+ if(e->i && e->i->key == key)
+ return e;
+ }
+ return 0;
+}
+
+S5BManager::Entry *S5BManager::findEntryBySID(const Jid &peer, const QString &sid) const
+{
+ QPtrListIterator<Entry> it(d->activeList);
+ for(Entry *e; (e = it.current()); ++it) {
+ if(e->i && e->i->peer.compare(peer) && e->sid == sid)
+ return e;
+ }
+ return 0;
+}
+
+S5BManager::Entry *S5BManager::findServerEntryByHash(const QString &key) const
+{
+ const QPtrList<S5BManager> &manList = d->serv->managerList();
+ QPtrListIterator<S5BManager> it(manList);
+ for(S5BManager *m; (m = it.current()); ++it) {
+ Entry *e = m->findEntryByHash(key);
+ if(e)
+ return e;
+ }
+ return 0;
+}
+
+bool S5BManager::srv_ownsHash(const QString &key) const
+{
+ if(findEntryByHash(key))
+ return true;
+ return false;
+}
+
+void S5BManager::srv_incomingReady(SocksClient *sc, const QString &key)
+{
+ Entry *e = findEntryByHash(key);
+ if(!e->i->allowIncoming) {
+ sc->requestDeny();
+ SafeDelete::deleteSingle(sc);
+ return;
+ }
+ if(e->c->d->mode == S5BConnection::Datagram)
+ sc->grantUDPAssociate("", 0);
+ else
+ sc->grantConnect();
+ e->relatedServer = (S5BServer *)sender();
+ e->i->setIncomingClient(sc);
+}
+
+void S5BManager::srv_incomingUDP(bool init, const QHostAddress &addr, int port, const QString &key, const QByteArray &data)
+{
+ Entry *e = findEntryByHash(key);
+ if(!e->c->d->mode != S5BConnection::Datagram)
+ return; // this key isn't in udp mode? drop!
+
+ if(init) {
+ if(e->udp_init)
+ return; // only init once
+
+ // lock on to this sender
+ e->udp_addr = addr;
+ e->udp_port = port;
+ e->udp_init = true;
+
+ // reply that initialization was successful
+ d->ps->sendUDPSuccess(e->c->d->peer, key);
+ return;
+ }
+
+ // not initialized yet? something went wrong
+ if(!e->udp_init)
+ return;
+
+ // must come from same source as when initialized
+ if(addr.toString() != e->udp_addr.toString() || port != e->udp_port)
+ return;
+
+ e->c->man_udpReady(data);
+}
+
+void S5BManager::srv_unlink()
+{
+ d->serv = 0;
+}
+
+void S5BManager::con_connect(S5BConnection *c)
+{
+ if(findEntry(c))
+ return;
+ Entry *e = new Entry;
+ e->c = c;
+ e->sid = c->d->sid;
+ d->activeList.append(e);
+
+ if(c->d->proxy.isValid()) {
+ queryProxy(e);
+ return;
+ }
+ entryContinue(e);
+}
+
+void S5BManager::con_accept(S5BConnection *c)
+{
+ Entry *e = findEntry(c);
+ if(!e)
+ return;
+
+ if(e->c->d->req.fast) {
+ if(targetShouldOfferProxy(e)) {
+ queryProxy(e);
+ return;
+ }
+ }
+ entryContinue(e);
+}
+
+void S5BManager::con_reject(S5BConnection *c)
+{
+ d->ps->respondError(c->d->peer, c->d->req.id, 406, "Not acceptable");
+}
+
+void S5BManager::con_unlink(S5BConnection *c)
+{
+ Entry *e = findEntry(c);
+ if(!e)
+ return;
+
+ // active incoming request? cancel it
+ if(e->i && e->i->conn)
+ d->ps->respondError(e->i->peer, e->i->out_id, 406, "Not acceptable");
+ delete e->i;
+ d->activeList.removeRef(e);
+}
+
+void S5BManager::con_sendUDP(S5BConnection *c, const QByteArray &buf)
+{
+ Entry *e = findEntry(c);
+ if(!e)
+ return;
+ if(!e->udp_init)
+ return;
+
+ if(e->relatedServer)
+ e->relatedServer->writeUDP(e->udp_addr, e->udp_port, buf);
+}
+
+void S5BManager::item_accepted()
+{
+ Item *i = (Item *)sender();
+ Entry *e = findEntry(i);
+
+ e->c->accepted(); // signal
+}
+
+void S5BManager::item_tryingHosts(const StreamHostList &list)
+{
+ Item *i = (Item *)sender();
+ Entry *e = findEntry(i);
+
+ e->c->tryingHosts(list); // signal
+}
+
+void S5BManager::item_proxyConnect()
+{
+ Item *i = (Item *)sender();
+ Entry *e = findEntry(i);
+
+ e->c->proxyConnect(); // signal
+}
+
+void S5BManager::item_waitingForActivation()
+{
+ Item *i = (Item *)sender();
+ Entry *e = findEntry(i);
+
+ e->c->waitingForActivation(); // signal
+}
+
+void S5BManager::item_connected()
+{
+ Item *i = (Item *)sender();
+ Entry *e = findEntry(i);
+
+ // grab the client
+ SocksClient *client = i->client;
+ i->client = 0;
+ SocksUDP *client_udp = i->client_udp;
+ i->client_udp = 0;
+
+ // give it to the connection
+ e->c->man_clientReady(client, client_udp);
+}
+
+void S5BManager::item_error(int x)
+{
+ Item *i = (Item *)sender();
+ Entry *e = findEntry(i);
+
+ e->c->man_failed(x);
+}
+
+void S5BManager::entryContinue(Entry *e)
+{
+ e->i = new Item(this);
+ e->i->proxy = e->proxyInfo;
+
+ connect(e->i, SIGNAL(accepted()), SLOT(item_accepted()));
+ connect(e->i, SIGNAL(tryingHosts(const StreamHostList &)), SLOT(item_tryingHosts(const StreamHostList &)));
+ connect(e->i, SIGNAL(proxyConnect()), SLOT(item_proxyConnect()));
+ connect(e->i, SIGNAL(waitingForActivation()), SLOT(item_waitingForActivation()));
+ connect(e->i, SIGNAL(connected()), SLOT(item_connected()));
+ connect(e->i, SIGNAL(error(int)), SLOT(item_error(int)));
+
+ if(e->c->isRemote()) {
+ const S5BRequest &req = e->c->d->req;
+ e->i->startTarget(e->sid, d->client->jid(), e->c->d->peer, req.hosts, req.id, req.fast, req.udp);
+ }
+ else {
+ e->i->startInitiator(e->sid, d->client->jid(), e->c->d->peer, true, e->c->d->mode == S5BConnection::Datagram ? true: false);
+ e->c->requesting(); // signal
+ }
+}
+
+void S5BManager::queryProxy(Entry *e)
+{
+ QGuardedPtr<QObject> self = this;
+ e->c->proxyQuery(); // signal
+ if(!self)
+ return;
+
+#ifdef S5B_DEBUG
+ printf("querying proxy: [%s]\n", e->c->d->proxy.full().latin1());
+#endif
+ e->query = new JT_S5B(d->client->rootTask());
+ connect(e->query, SIGNAL(finished()), SLOT(query_finished()));
+ e->query->requestProxyInfo(e->c->d->proxy);
+ e->query->go(true);
+}
+
+void S5BManager::query_finished()
+{
+ JT_S5B *query = (JT_S5B *)sender();
+ Entry *e;
+ bool found = false;
+ QPtrListIterator<Entry> it(d->activeList);
+ for(; (e = it.current()); ++it) {
+ if(e->query == query) {
+ found = true;
+ break;
+ }
+ }
+ if(!found)
+ return;
+ e->query = 0;
+
+#ifdef S5B_DEBUG
+ printf("query finished: ");
+#endif
+ if(query->success()) {
+ e->proxyInfo = query->proxyInfo();
+#ifdef S5B_DEBUG
+ printf("host/ip=[%s] port=[%d]\n", e->proxyInfo.host().latin1(), e->proxyInfo.port());
+#endif
+ }
+ else {
+#ifdef S5B_DEBUG
+ printf("fail\n");
+#endif
+ }
+
+ QGuardedPtr<QObject> self = this;
+ e->c->proxyResult(query->success()); // signal
+ if(!self)
+ return;
+
+ entryContinue(e);
+}
+
+bool S5BManager::targetShouldOfferProxy(Entry *e)
+{
+ if(!e->c->d->proxy.isValid())
+ return false;
+
+ // if target, don't offer any proxy if the initiator already did
+ const StreamHostList &hosts = e->c->d->req.hosts;
+ for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) {
+ if((*it).isProxy())
+ return false;
+ }
+
+ // ensure we don't offer the same proxy as the initiator
+ if(haveHost(hosts, e->c->d->proxy))
+ return false;
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// S5BManager::Item
+//----------------------------------------------------------------------------
+S5BManager::Item::Item(S5BManager *manager) : QObject(0)
+{
+ m = manager;
+ task = 0;
+ proxy_task = 0;
+ conn = 0;
+ proxy_conn = 0;
+ client_udp = 0;
+ client = 0;
+ client_out_udp = 0;
+ client_out = 0;
+ reset();
+}
+
+S5BManager::Item::~Item()
+{
+ reset();
+}
+
+void S5BManager::Item::reset()
+{
+ delete task;
+ task = 0;
+
+ delete proxy_task;
+ proxy_task = 0;
+
+ delete conn;
+ conn = 0;
+
+ delete proxy_conn;
+ proxy_conn = 0;
+
+ delete client_udp;
+ client_udp = 0;
+
+ delete client;
+ client = 0;
+
+ delete client_out_udp;
+ client_out_udp = 0;
+
+ delete client_out;
+ client_out = 0;
+
+ state = Idle;
+ wantFast = false;
+ targetMode = Unknown;
+ fast = false;
+ activated = false;
+ lateProxy = false;
+ connSuccess = false;
+ localFailed = false;
+ remoteFailed = false;
+ allowIncoming = false;
+ udp = false;
+}
+
+void S5BManager::Item::startInitiator(const QString &_sid, const Jid &_self, const Jid &_peer, bool fast, bool _udp)
+{
+ sid = _sid;
+ self = _self;
+ peer = _peer;
+ key = makeKey(sid, self, peer);
+ out_key = makeKey(sid, peer, self);
+ wantFast = fast;
+ udp = _udp;
+
+#ifdef S5B_DEBUG
+ printf("S5BManager::Item initiating request %s [%s]\n", peer.full().latin1(), sid.latin1());
+#endif
+ state = Initiator;
+ doOutgoing();
+}
+
+void S5BManager::Item::startTarget(const QString &_sid, const Jid &_self, const Jid &_peer, const StreamHostList &hosts, const QString &iq_id, bool _fast, bool _udp)
+{
+ sid = _sid;
+ peer = _peer;
+ self = _self;
+ in_hosts = hosts;
+ in_id = iq_id;
+ fast = _fast;
+ key = makeKey(sid, self, peer);
+ out_key = makeKey(sid, peer, self);
+ udp = _udp;
+
+#ifdef S5B_DEBUG
+ printf("S5BManager::Item incoming request %s [%s]\n", peer.full().latin1(), sid.latin1());
+#endif
+ state = Target;
+ if(fast)
+ doOutgoing();
+ doIncoming();
+}
+
+void S5BManager::Item::handleFast(const StreamHostList &hosts, const QString &iq_id)
+{
+ targetMode = Fast;
+
+ QGuardedPtr<QObject> self = this;
+ accepted();
+ if(!self)
+ return;
+
+ // if we already have a stream, then bounce this request
+ if(client) {
+ m->doError(peer, iq_id, 406, "Not acceptable");
+ }
+ else {
+ in_hosts = hosts;
+ in_id = iq_id;
+ doIncoming();
+ }
+}
+
+void S5BManager::Item::doOutgoing()
+{
+ StreamHostList hosts;
+ S5BServer *serv = m->server();
+ if(serv && serv->isActive() && !haveHost(in_hosts, m->client()->jid())) {
+ QStringList hostList = serv->hostList();
+ for(QStringList::ConstIterator it = hostList.begin(); it != hostList.end(); ++it) {
+ StreamHost h;
+ h.setJid(m->client()->jid());
+ h.setHost(*it);
+ h.setPort(serv->port());
+ hosts += h;
+ }
+ }
+
+ // if the proxy is valid, then it's ok to add (the manager already ensured that it doesn't conflict)
+ if(proxy.jid().isValid())
+ hosts += proxy;
+
+ // if we're the target and we have no streamhosts of our own, then don't even bother with fast-mode
+ if(state == Target && hosts.isEmpty()) {
+ fast = false;
+ return;
+ }
+
+ allowIncoming = true;
+
+ task = new JT_S5B(m->client()->rootTask());
+ connect(task, SIGNAL(finished()), SLOT(jt_finished()));
+ task->request(peer, sid, hosts, state == Initiator ? wantFast : false, udp);
+ out_id = task->id();
+ task->go(true);
+}
+
+void S5BManager::Item::doIncoming()
+{
+ if(in_hosts.isEmpty()) {
+ doConnectError();
+ return;
+ }
+
+ StreamHostList list;
+ if(lateProxy) {
+ // take just the proxy streamhosts
+ for(StreamHostList::ConstIterator it = in_hosts.begin(); it != in_hosts.end(); ++it) {
+ if((*it).isProxy())
+ list += *it;
+ }
+ lateProxy = false;
+ }
+ else {
+ // only try doing the late proxy trick if using fast mode AND we did not offer a proxy
+ if((state == Initiator || (state == Target && fast)) && !proxy.jid().isValid()) {
+ // take just the non-proxy streamhosts
+ bool hasProxies = false;
+ for(StreamHostList::ConstIterator it = in_hosts.begin(); it != in_hosts.end(); ++it) {
+ if((*it).isProxy())
+ hasProxies = true;
+ else
+ list += *it;
+ }
+ if(hasProxies) {
+ lateProxy = true;
+
+ // no regular streamhosts? wait for remote error
+ if(list.isEmpty())
+ return;
+ }
+ }
+ else
+ list = in_hosts;
+ }
+
+ conn = new S5BConnector;
+ connect(conn, SIGNAL(result(bool)), SLOT(conn_result(bool)));
+
+ QGuardedPtr<QObject> self = this;
+ tryingHosts(list);
+ if(!self)
+ return;
+
+ conn->start(m->client()->jid(), list, out_key, udp, lateProxy ? 10 : 30);
+}
+
+void S5BManager::Item::setIncomingClient(SocksClient *sc)
+{
+#ifdef S5B_DEBUG
+ printf("S5BManager::Item: %s [%s] successful incoming connection\n", peer.full().latin1(), sid.latin1());
+#endif
+
+ connect(sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
+ connect(sc, SIGNAL(bytesWritten(int)), SLOT(sc_bytesWritten(int)));
+ connect(sc, SIGNAL(error(int)), SLOT(sc_error(int)));
+
+ client = sc;
+ allowIncoming = false;
+}
+
+void S5BManager::Item::incomingActivate(const Jid &streamHost)
+{
+ if(!activated) {
+ activatedStream = streamHost;
+ checkForActivation();
+ }
+}
+
+void S5BManager::Item::jt_finished()
+{
+ JT_S5B *j = task;
+ task = 0;
+
+#ifdef S5B_DEBUG
+ printf("jt_finished: state=%s, %s\n", state == Initiator ? "initiator" : "target", j->success() ? "ok" : "fail");
+#endif
+
+ if(state == Initiator) {
+ if(targetMode == Unknown) {
+ targetMode = NotFast;
+ QGuardedPtr<QObject> self = this;
+ accepted();
+ if(!self)
+ return;
+ }
+ }
+
+ // if we've already reported successfully connecting to them, then this response doesn't matter
+ if(state == Initiator && connSuccess) {
+ tryActivation();
+ return;
+ }
+
+ if(j->success()) {
+ // stop connecting out
+ if(conn || lateProxy) {
+ delete conn;
+ conn = 0;
+ doConnectError();
+ }
+
+ Jid streamHost = j->streamHostUsed();
+
+ // they connected to us?
+ if(streamHost.compare(self)) {
+ if(client) {
+ if(state == Initiator) {
+ activatedStream = streamHost;
+ tryActivation();
+ }
+ else
+ checkForActivation();
+ }
+ else {
+#ifdef S5B_DEBUG
+ printf("S5BManager::Item %s claims to have connected to us, but we don't see this\n", peer.full().latin1());
+#endif
+ reset();
+ error(ErrWrongHost);
+ }
+ }
+ else if(streamHost.compare(proxy.jid())) {
+ // toss out any direct incoming, since it won't be used
+ delete client;
+ client = 0;
+ allowIncoming = false;
+
+#ifdef S5B_DEBUG
+ printf("attempting to connect to proxy\n");
+#endif
+ // connect to the proxy
+ proxy_conn = new S5BConnector;
+ connect(proxy_conn, SIGNAL(result(bool)), SLOT(proxy_result(bool)));
+ StreamHostList list;
+ list += proxy;
+
+ QGuardedPtr<QObject> self = this;
+ proxyConnect();
+ if(!self)
+ return;
+
+ proxy_conn->start(m->client()->jid(), list, key, udp, 30);
+ }
+ else {
+#ifdef S5B_DEBUG
+ printf("S5BManager::Item %s claims to have connected to a streamhost we never offered\n", peer.full().latin1());
+#endif
+ reset();
+ error(ErrWrongHost);
+ }
+ }
+ else {
+#ifdef S5B_DEBUG
+ printf("S5BManager::Item %s [%s] error\n", peer.full().latin1(), sid.latin1());
+#endif
+ remoteFailed = true;
+ statusCode = j->statusCode();
+
+ if(lateProxy) {
+ if(!conn)
+ doIncoming();
+ }
+ else {
+ // if connSuccess is true at this point, then we're a Target
+ if(connSuccess)
+ checkForActivation();
+ else
+ checkFailure();
+ }
+ }
+}
+
+void S5BManager::Item::conn_result(bool b)
+{
+ if(b) {
+ SocksClient *sc = conn->takeClient();
+ SocksUDP *sc_udp = conn->takeUDP();
+ StreamHost h = conn->streamHostUsed();
+ delete conn;
+ conn = 0;
+ connSuccess = true;
+
+#ifdef S5B_DEBUG
+ printf("S5BManager::Item: %s [%s] successful outgoing connection\n", peer.full().latin1(), sid.latin1());
+#endif
+
+ connect(sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
+ connect(sc, SIGNAL(bytesWritten(int)), SLOT(sc_bytesWritten(int)));
+ connect(sc, SIGNAL(error(int)), SLOT(sc_error(int)));
+
+ m->doSuccess(peer, in_id, h.jid());
+
+ // if the first batch works, don't try proxy
+ lateProxy = false;
+
+ // if initiator, run with this one
+ if(state == Initiator) {
+ // if we had an incoming one, toss it
+ delete client_udp;
+ client_udp = sc_udp;
+ delete client;
+ client = sc;
+ allowIncoming = false;
+ activatedStream = peer;
+ tryActivation();
+ }
+ else {
+ client_out_udp = sc_udp;
+ client_out = sc;
+ checkForActivation();
+ }
+ }
+ else {
+ delete conn;
+ conn = 0;
+
+ // if we delayed the proxies for later, try now
+ if(lateProxy) {
+ if(remoteFailed)
+ doIncoming();
+ }
+ else
+ doConnectError();
+ }
+}
+
+void S5BManager::Item::proxy_result(bool b)
+{
+#ifdef S5B_DEBUG
+ printf("proxy_result: %s\n", b ? "ok" : "fail");
+#endif
+ if(b) {
+ SocksClient *sc = proxy_conn->takeClient();
+ SocksUDP *sc_udp = proxy_conn->takeUDP();
+ delete proxy_conn;
+ proxy_conn = 0;
+
+ connect(sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
+ connect(sc, SIGNAL(bytesWritten(int)), SLOT(sc_bytesWritten(int)));
+ connect(sc, SIGNAL(error(int)), SLOT(sc_error(int)));
+
+ client = sc;
+ client_udp = sc_udp;
+
+ // activate
+#ifdef S5B_DEBUG
+ printf("activating proxy stream\n");
+#endif
+ proxy_task = new JT_S5B(m->client()->rootTask());
+ connect(proxy_task, SIGNAL(finished()), SLOT(proxy_finished()));
+ proxy_task->requestActivation(proxy.jid(), sid, peer);
+ proxy_task->go(true);
+ }
+ else {
+ delete proxy_conn;
+ proxy_conn = 0;
+ reset();
+ error(ErrProxy);
+ }
+}
+
+void S5BManager::Item::proxy_finished()
+{
+ JT_S5B *j = proxy_task;
+ proxy_task = 0;
+
+ if(j->success()) {
+#ifdef S5B_DEBUG
+ printf("proxy stream activated\n");
+#endif
+ if(state == Initiator) {
+ activatedStream = proxy.jid();
+ tryActivation();
+ }
+ else
+ checkForActivation();
+ }
+ else {
+ reset();
+ error(ErrProxy);
+ }
+}
+
+void S5BManager::Item::sc_readyRead()
+{
+#ifdef S5B_DEBUG
+ printf("sc_readyRead\n");
+#endif
+ // only targets check for activation, and only should do it if there is no pending outgoing iq-set
+ if(state == Target && !task && !proxy_task)
+ checkForActivation();
+}
+
+void S5BManager::Item::sc_bytesWritten(int)
+{
+#ifdef S5B_DEBUG
+ printf("sc_bytesWritten\n");
+#endif
+ // this should only happen to the initiator, and should always be 1 byte (the '\r' sent earlier)
+ finished();
+}
+
+void S5BManager::Item::sc_error(int)
+{
+#ifdef S5B_DEBUG
+ printf("sc_error\n");
+#endif
+ reset();
+ error(ErrConnect);
+}
+
+void S5BManager::Item::doConnectError()
+{
+ localFailed = true;
+ m->doError(peer, in_id, 404, "Could not connect to given hosts");
+ checkFailure();
+}
+
+void S5BManager::Item::tryActivation()
+{
+#ifdef S5B_DEBUG
+ printf("tryActivation\n");
+#endif
+ if(activated) {
+#ifdef S5B_DEBUG
+ printf("already activated !?\n");
+#endif
+ return;
+ }
+
+ if(targetMode == NotFast) {
+#ifdef S5B_DEBUG
+ printf("tryActivation: NotFast\n");
+#endif
+ // nothing to activate, we're done
+ finished();
+ }
+ else if(targetMode == Fast) {
+ // with fast mode, we don't wait for the iq reply, so delete the task (if any)
+ delete task;
+ task = 0;
+
+ activated = true;
+
+ // if udp, activate using special stanza
+ if(udp) {
+ m->doActivate(peer, sid, activatedStream);
+ }
+ else {
+#ifdef S5B_DEBUG
+ printf("sending extra CR\n");
+#endif
+ // must send [CR] to activate target streamhost
+ QByteArray a(1);
+ a[0] = '\r';
+ client->write(a);
+ }
+ }
+}
+
+void S5BManager::Item::checkForActivation()
+{
+ QPtrList<SocksClient> clientList;
+ if(client)
+ clientList.append(client);
+ if(client_out)
+ clientList.append(client_out);
+ QPtrListIterator<SocksClient> it(clientList);
+ for(SocksClient *sc; (sc = it.current()); ++it) {
+#ifdef S5B_DEBUG
+ printf("checking for activation\n");
+#endif
+ if(fast) {
+ bool ok = false;
+ if(udp) {
+ if((sc == client_out && activatedStream.compare(self)) || (sc == client && !activatedStream.compare(self))) {
+ clientList.removeRef(sc);
+ ok = true;
+ }
+ }
+ else {
+#ifdef S5B_DEBUG
+ printf("need CR\n");
+#endif
+ if(sc->bytesAvailable() >= 1) {
+ clientList.removeRef(sc);
+ QByteArray a = sc->read(1);
+ if(a[0] != '\r') {
+ delete sc;
+ return;
+ }
+ ok = true;
+ }
+ }
+
+ if(ok) {
+ SocksUDP *sc_udp = 0;
+ if(sc == client) {
+ delete client_out_udp;
+ client_out_udp = 0;
+ sc_udp = client_udp;
+ }
+ else if(sc == client_out) {
+ delete client_udp;
+ client_udp = 0;
+ sc_udp = client_out_udp;
+ }
+
+ sc->disconnect(this);
+ clientList.setAutoDelete(true);
+ clientList.clear();
+ client = sc;
+ client_out = 0;
+ client_udp = sc_udp;
+ activated = true;
+#ifdef S5B_DEBUG
+ printf("activation success\n");
+#endif
+ break;
+ }
+ }
+ else {
+#ifdef S5B_DEBUG
+ printf("not fast mode, no need to wait for anything\n");
+#endif
+ clientList.removeRef(sc);
+ sc->disconnect(this);
+ clientList.setAutoDelete(true);
+ clientList.clear();
+ client = sc;
+ client_out = 0;
+ activated = true;
+ break;
+ }
+ }
+
+ if(activated) {
+ finished();
+ }
+ else {
+ // only emit waitingForActivation if there is nothing left to do
+ if((connSuccess || localFailed) && !proxy_task && !proxy_conn)
+ waitingForActivation();
+ }
+}
+
+void S5BManager::Item::checkFailure()
+{
+ bool failed = false;
+ if(state == Initiator) {
+ if(remoteFailed) {
+ if((localFailed && targetMode == Fast) || targetMode == NotFast)
+ failed = true;
+ }
+ }
+ else {
+ if(localFailed) {
+ if((remoteFailed && fast) || !fast)
+ failed = true;
+ }
+ }
+
+ if(failed) {
+ if(state == Initiator) {
+ reset();
+ if(statusCode == 404)
+ error(ErrConnect);
+ else
+ error(ErrRefused);
+ }
+ else {
+ reset();
+ error(ErrConnect);
+ }
+ }
+}
+
+void S5BManager::Item::finished()
+{
+ client->disconnect(this);
+ state = Active;
+#ifdef S5B_DEBUG
+ printf("S5BManager::Item %s [%s] linked successfully\n", peer.full().latin1(), sid.latin1());
+#endif
+ connected();
+}
+
+//----------------------------------------------------------------------------
+// S5BConnector
+//----------------------------------------------------------------------------
+class S5BConnector::Item : public QObject
+{
+ Q_OBJECT
+public:
+ SocksClient *client;
+ SocksUDP *client_udp;
+ StreamHost host;
+ QString key;
+ bool udp;
+ int udp_tries;
+ QTimer t;
+ Jid jid;
+
+ Item(const Jid &self, const StreamHost &_host, const QString &_key, bool _udp) : QObject(0)
+ {
+ jid = self;
+ host = _host;
+ key = _key;
+ udp = _udp;
+ client = new SocksClient;
+ client_udp = 0;
+ connect(client, SIGNAL(connected()), SLOT(sc_connected()));
+ connect(client, SIGNAL(error(int)), SLOT(sc_error(int)));
+ connect(&t, SIGNAL(timeout()), SLOT(trySendUDP()));
+ }
+
+ ~Item()
+ {
+ cleanup();
+ }
+
+ void start()
+ {
+ client->connectToHost(host.host(), host.port(), key, 0, udp);
+ }
+
+ void udpSuccess()
+ {
+ t.stop();
+ client_udp->change(key, 0); // flip over to the data port
+ success();
+ }
+
+signals:
+ void result(bool);
+
+private slots:
+ void sc_connected()
+ {
+ // if udp, need to send init packet before we are good
+ if(udp) {
+ // port 1 is init
+ client_udp = client->createUDP(key, 1, client->peerAddress(), client->peerPort());
+ udp_tries = 0;
+ t.start(5000);
+ trySendUDP();
+ return;
+ }
+
+ success();
+ }
+
+ void sc_error(int)
+ {
+#ifdef S5B_DEBUG
+ printf("S5BConnector[%s]: error\n", host.host().latin1());
+#endif
+ cleanup();
+ result(false);
+ }
+
+ void trySendUDP()
+ {
+ if(udp_tries == 5) {
+ t.stop();
+ cleanup();
+ result(false);
+ return;
+ }
+
+ // send initialization with our JID
+ QCString cs = jid.full().utf8();
+ QByteArray a(cs.length());
+ memcpy(a.data(), cs.data(), a.size());
+ client_udp->write(a);
+ ++udp_tries;
+ }
+
+private:
+ void cleanup()
+ {
+ delete client_udp;
+ client_udp = 0;
+ delete client;
+ client = 0;
+ }
+
+ void success()
+ {
+#ifdef S5B_DEBUG
+ printf("S5BConnector[%s]: success\n", host.host().latin1());
+#endif
+ client->disconnect(this);
+ result(true);
+ }
+};
+
+class S5BConnector::Private
+{
+public:
+ SocksClient *active;
+ SocksUDP *active_udp;
+ QPtrList<Item> itemList;
+ QString key;
+ StreamHost activeHost;
+ QTimer t;
+};
+
+S5BConnector::S5BConnector(QObject *parent)
+:QObject(parent)
+{
+ d = new Private;
+ d->active = 0;
+ d->active_udp = 0;
+ d->itemList.setAutoDelete(true);
+ connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout()));
+}
+
+S5BConnector::~S5BConnector()
+{
+ reset();
+ delete d;
+}
+
+void S5BConnector::reset()
+{
+ d->t.stop();
+ delete d->active_udp;
+ d->active_udp = 0;
+ delete d->active;
+ d->active = 0;
+ d->itemList.clear();
+}
+
+void S5BConnector::start(const Jid &self, const StreamHostList &hosts, const QString &key, bool udp, int timeout)
+{
+ reset();
+
+#ifdef S5B_DEBUG
+ printf("S5BConnector: starting [%p]!\n", this);
+#endif
+ for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) {
+ Item *i = new Item(self, *it, key, udp);
+ connect(i, SIGNAL(result(bool)), SLOT(item_result(bool)));
+ d->itemList.append(i);
+ i->start();
+ }
+ d->t.start(timeout * 1000);
+}
+
+SocksClient *S5BConnector::takeClient()
+{
+ SocksClient *c = d->active;
+ d->active = 0;
+ return c;
+}
+
+SocksUDP *S5BConnector::takeUDP()
+{
+ SocksUDP *c = d->active_udp;
+ d->active_udp = 0;
+ return c;
+}
+
+StreamHost S5BConnector::streamHostUsed() const
+{
+ return d->activeHost;
+}
+
+void S5BConnector::item_result(bool b)
+{
+ Item *i = (Item *)sender();
+ if(b) {
+ d->active = i->client;
+ i->client = 0;
+ d->active_udp = i->client_udp;
+ i->client_udp = 0;
+ d->activeHost = i->host;
+ d->itemList.clear();
+ d->t.stop();
+#ifdef S5B_DEBUG
+ printf("S5BConnector: complete! [%p]\n", this);
+#endif
+ result(true);
+ }
+ else {
+ d->itemList.removeRef(i);
+ if(d->itemList.isEmpty()) {
+ d->t.stop();
+#ifdef S5B_DEBUG
+ printf("S5BConnector: failed! [%p]\n", this);
+#endif
+ result(false);
+ }
+ }
+}
+
+void S5BConnector::t_timeout()
+{
+ reset();
+#ifdef S5B_DEBUG
+ printf("S5BConnector: failed! (timeout)\n");
+#endif
+ result(false);
+}
+
+void S5BConnector::man_udpSuccess(const Jid &streamHost)
+{
+ // was anyone sending to this streamhost?
+ QPtrListIterator<Item> it(d->itemList);
+ for(Item *i; (i = it.current()); ++it) {
+ if(i->host.jid().compare(streamHost) && i->client_udp) {
+ i->udpSuccess();
+ return;
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+// S5BServer
+//----------------------------------------------------------------------------
+class S5BServer::Item : public QObject
+{
+ Q_OBJECT
+public:
+ SocksClient *client;
+ QString host;
+ QTimer expire;
+
+ Item(SocksClient *c) : QObject(0)
+ {
+ client = c;
+ connect(client, SIGNAL(incomingMethods(int)), SLOT(sc_incomingMethods(int)));
+ connect(client, SIGNAL(incomingConnectRequest(const QString &, int)), SLOT(sc_incomingConnectRequest(const QString &, int)));
+ connect(client, SIGNAL(error(int)), SLOT(sc_error(int)));
+
+ connect(&expire, SIGNAL(timeout()), SLOT(doError()));
+ resetExpiration();
+ }
+
+ ~Item()
+ {
+ delete client;
+ }
+
+ void resetExpiration()
+ {
+ expire.start(30000);
+ }
+
+signals:
+ void result(bool);
+
+private slots:
+ void doError()
+ {
+ expire.stop();
+ delete client;
+ client = 0;
+ result(false);
+ }
+
+ void sc_incomingMethods(int m)
+ {
+ if(m & SocksClient::AuthNone)
+ client->chooseMethod(SocksClient::AuthNone);
+ else
+ doError();
+ }
+
+ void sc_incomingConnectRequest(const QString &_host, int port)
+ {
+ if(port == 0) {
+ host = _host;
+ client->disconnect(this);
+ result(true);
+ }
+ else
+ doError();
+ }
+
+ void sc_error(int)
+ {
+ doError();
+ }
+};
+
+class S5BServer::Private
+{
+public:
+ SocksServer serv;
+ QStringList hostList;
+ QPtrList<S5BManager> manList;
+ QPtrList<Item> itemList;
+};
+
+S5BServer::S5BServer(QObject *parent)
+:QObject(parent)
+{
+ d = new Private;
+ d->itemList.setAutoDelete(true);
+ connect(&d->serv, SIGNAL(incomingReady()), SLOT(ss_incomingReady()));
+ connect(&d->serv, SIGNAL(incomingUDP(const QString &, int, const QHostAddress &, int, const QByteArray &)), SLOT(ss_incomingUDP(const QString &, int, const QHostAddress &, int, const QByteArray &)));
+}
+
+S5BServer::~S5BServer()
+{
+ unlinkAll();
+ delete d;
+}
+
+bool S5BServer::isActive() const
+{
+ return d->serv.isActive();
+}
+
+bool S5BServer::start(int port)
+{
+ d->serv.stop();
+ return d->serv.listen(port, true);
+}
+
+void S5BServer::stop()
+{
+ d->serv.stop();
+}
+
+void S5BServer::setHostList(const QStringList &list)
+{
+ d->hostList = list;
+}
+
+QStringList S5BServer::hostList() const
+{
+ return d->hostList;
+}
+
+int S5BServer::port() const
+{
+ return d->serv.port();
+}
+
+void S5BServer::ss_incomingReady()
+{
+ Item *i = new Item(d->serv.takeIncoming());
+#ifdef S5B_DEBUG
+ printf("S5BServer: incoming connection from %s:%d\n", i->client->peerAddress().toString().latin1(), i->client->peerPort());
+#endif
+ connect(i, SIGNAL(result(bool)), SLOT(item_result(bool)));
+ d->itemList.append(i);
+}
+
+void S5BServer::ss_incomingUDP(const QString &host, int port, const QHostAddress &addr, int sourcePort, const QByteArray &data)
+{
+ if(port != 0 || port != 1)
+ return;
+
+ QPtrListIterator<S5BManager> it(d->manList);
+ for(S5BManager *m; (m = it.current()); ++it) {
+ if(m->srv_ownsHash(host)) {
+ m->srv_incomingUDP(port == 1 ? true : false, addr, sourcePort, host, data);
+ return;
+ }
+ }
+}
+
+void S5BServer::item_result(bool b)
+{
+ Item *i = (Item *)sender();
+#ifdef S5B_DEBUG
+ printf("S5BServer item result: %d\n", b);
+#endif
+ if(!b) {
+ d->itemList.removeRef(i);
+ return;
+ }
+
+ SocksClient *c = i->client;
+ i->client = 0;
+ QString key = i->host;
+ d->itemList.removeRef(i);
+
+ // find the appropriate manager for this incoming connection
+ QPtrListIterator<S5BManager> it(d->manList);
+ for(S5BManager *m; (m = it.current()); ++it) {
+ if(m->srv_ownsHash(key)) {
+ m->srv_incomingReady(c, key);
+ return;
+ }
+ }
+
+ // throw it away
+ delete c;
+}
+
+void S5BServer::link(S5BManager *m)
+{
+ d->manList.append(m);
+}
+
+void S5BServer::unlink(S5BManager *m)
+{
+ d->manList.removeRef(m);
+}
+
+void S5BServer::unlinkAll()
+{
+ QPtrListIterator<S5BManager> it(d->manList);
+ for(S5BManager *m; (m = it.current()); ++it)
+ m->srv_unlink();
+ d->manList.clear();
+}
+
+const QPtrList<S5BManager> & S5BServer::managerList() const
+{
+ return d->manList;
+}
+
+void S5BServer::writeUDP(const QHostAddress &addr, int port, const QByteArray &data)
+{
+ d->serv.writeUDP(addr, port, data);
+}
+
+//----------------------------------------------------------------------------
+// JT_S5B
+//----------------------------------------------------------------------------
+class JT_S5B::Private
+{
+public:
+ QDomElement iq;
+ Jid to;
+ Jid streamHost;
+ StreamHost proxyInfo;
+ int mode;
+ QTimer t;
+};
+
+JT_S5B::JT_S5B(Task *parent)
+:Task(parent)
+{
+ d = new Private;
+ d->mode = -1;
+ connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout()));
+}
+
+JT_S5B::~JT_S5B()
+{
+ delete d;
+}
+
+void JT_S5B::request(const Jid &to, const QString &sid, const StreamHostList &hosts, bool fast, bool udp)
+{
+ d->mode = 0;
+
+ QDomElement iq;
+ d->to = to;
+ iq = createIQ(doc(), "set", to.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "http://jabber.org/protocol/bytestreams");
+ query.setAttribute("sid", sid);
+ query.setAttribute("mode", udp ? "udp" : "tcp" );
+ iq.appendChild(query);
+ for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) {
+ QDomElement shost = doc()->createElement("streamhost");
+ shost.setAttribute("jid", (*it).jid().full());
+ shost.setAttribute("host", (*it).host());
+ shost.setAttribute("port", QString::number((*it).port()));
+ if((*it).isProxy()) {
+ QDomElement p = doc()->createElement("proxy");
+ p.setAttribute("xmlns", "http://affinix.com/jabber/stream");
+ shost.appendChild(p);
+ }
+ query.appendChild(shost);
+ }
+ if(fast) {
+ QDomElement e = doc()->createElement("fast");
+ e.setAttribute("xmlns", "http://affinix.com/jabber/stream");
+ query.appendChild(e);
+ }
+ d->iq = iq;
+}
+
+void JT_S5B::requestProxyInfo(const Jid &to)
+{
+ d->mode = 1;
+
+ QDomElement iq;
+ d->to = to;
+ iq = createIQ(doc(), "get", to.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "http://jabber.org/protocol/bytestreams");
+ iq.appendChild(query);
+ d->iq = iq;
+}
+
+void JT_S5B::requestActivation(const Jid &to, const QString &sid, const Jid &target)
+{
+ d->mode = 2;
+
+ QDomElement iq;
+ d->to = to;
+ iq = createIQ(doc(), "set", to.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "http://jabber.org/protocol/bytestreams");
+ query.setAttribute("sid", sid);
+ iq.appendChild(query);
+ QDomElement act = doc()->createElement("activate");
+ act.appendChild(doc()->createTextNode(target.full()));
+ query.appendChild(act);
+ d->iq = iq;
+}
+
+void JT_S5B::onGo()
+{
+ if(d->mode == 1)
+ d->t.start(15000, true);
+ send(d->iq);
+}
+
+void JT_S5B::onDisconnect()
+{
+ d->t.stop();
+}
+
+bool JT_S5B::take(const QDomElement &x)
+{
+ if(d->mode == -1)
+ return false;
+
+ if(!iqVerify(x, d->to, id()))
+ return false;
+
+ d->t.stop();
+
+ if(x.attribute("type") == "result") {
+ QDomElement q = queryTag(x);
+ if(d->mode == 0) {
+ d->streamHost = "";
+ if(!q.isNull()) {
+ QDomElement shost = q.elementsByTagName("streamhost-used").item(0).toElement();
+ if(!shost.isNull())
+ d->streamHost = shost.attribute("jid");
+ }
+
+ setSuccess();
+ }
+ else if(d->mode == 1) {
+ if(!q.isNull()) {
+ QDomElement shost = q.elementsByTagName("streamhost").item(0).toElement();
+ if(!shost.isNull()) {
+ Jid j = shost.attribute("jid");
+ if(j.isValid()) {
+ QString host = shost.attribute("host");
+ if(!host.isEmpty()) {
+ int port = shost.attribute("port").toInt();
+ StreamHost h;
+ h.setJid(j);
+ h.setHost(host);
+ h.setPort(port);
+ h.setIsProxy(true);
+ d->proxyInfo = h;
+ }
+ }
+ }
+ }
+
+ setSuccess();
+ }
+ else {
+ setSuccess();
+ }
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+}
+
+void JT_S5B::t_timeout()
+{
+ d->mode = -1;
+ setError(500, "Timed out");
+}
+
+Jid JT_S5B::streamHostUsed() const
+{
+ return d->streamHost;
+}
+
+StreamHost JT_S5B::proxyInfo() const
+{
+ return d->proxyInfo;
+}
+
+//----------------------------------------------------------------------------
+// JT_PushS5B
+//----------------------------------------------------------------------------
+JT_PushS5B::JT_PushS5B(Task *parent)
+:Task(parent)
+{
+}
+
+JT_PushS5B::~JT_PushS5B()
+{
+}
+
+int JT_PushS5B::priority() const
+{
+ return 1;
+}
+
+bool JT_PushS5B::take(const QDomElement &e)
+{
+ // look for udpsuccess
+ if(e.tagName() == "message") {
+ QDomElement x = e.elementsByTagName("udpsuccess").item(0).toElement();
+ if(!x.isNull() && x.attribute("xmlns") == "http://jabber.org/protocol/bytestreams") {
+ incomingUDPSuccess(Jid(x.attribute("from")), x.attribute("dstaddr"));
+ return true;
+ }
+ x = e.elementsByTagName("activate").item(0).toElement();
+ if(!x.isNull() && x.attribute("xmlns") == "http://affinix.com/jabber/stream") {
+ incomingActivate(Jid(x.attribute("from")), x.attribute("sid"), Jid(x.attribute("jid")));
+ return true;
+ }
+ return false;
+ }
+
+ // must be an iq-set tag
+ if(e.tagName() != "iq")
+ return false;
+ if(e.attribute("type") != "set")
+ return false;
+ if(queryNS(e) != "http://jabber.org/protocol/bytestreams")
+ return false;
+
+ Jid from(e.attribute("from"));
+ QDomElement q = queryTag(e);
+ QString sid = q.attribute("sid");
+
+ StreamHostList hosts;
+ QDomNodeList nl = q.elementsByTagName("streamhost");
+ for(uint n = 0; n < nl.count(); ++n) {
+ QDomElement shost = nl.item(n).toElement();
+ if(hosts.count() < MAXSTREAMHOSTS) {
+ Jid j = shost.attribute("jid");
+ if(!j.isValid())
+ continue;
+ QString host = shost.attribute("host");
+ if(host.isEmpty())
+ continue;
+ int port = shost.attribute("port").toInt();
+ QDomElement p = shost.elementsByTagName("proxy").item(0).toElement();
+ bool isProxy = false;
+ if(!p.isNull() && p.attribute("xmlns") == "http://affinix.com/jabber/stream")
+ isProxy = true;
+
+ StreamHost h;
+ h.setJid(j);
+ h.setHost(host);
+ h.setPort(port);
+ h.setIsProxy(isProxy);
+ hosts += h;
+ }
+ }
+
+ bool fast = false;
+ QDomElement t;
+ t = q.elementsByTagName("fast").item(0).toElement();
+ if(!t.isNull() && t.attribute("xmlns") == "http://affinix.com/jabber/stream")
+ fast = true;
+
+ S5BRequest r;
+ r.from = from;
+ r.id = e.attribute("id");
+ r.sid = sid;
+ r.hosts = hosts;
+ r.fast = fast;
+ r.udp = q.attribute("mode") == "udp" ? true: false;
+
+ incoming(r);
+ return true;
+}
+
+void JT_PushS5B::respondSuccess(const Jid &to, const QString &id, const Jid &streamHost)
+{
+ QDomElement iq = createIQ(doc(), "result", to.full(), id);
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "http://jabber.org/protocol/bytestreams");
+ iq.appendChild(query);
+ QDomElement shost = doc()->createElement("streamhost-used");
+ shost.setAttribute("jid", streamHost.full());
+ query.appendChild(shost);
+ send(iq);
+}
+
+void JT_PushS5B::respondError(const Jid &to, const QString &id, int code, const QString &str)
+{
+ QDomElement iq = createIQ(doc(), "error", to.full(), id);
+ QDomElement err = textTag(doc(), "error", str);
+ err.setAttribute("code", QString::number(code));
+ iq.appendChild(err);
+ send(iq);
+}
+
+void JT_PushS5B::sendUDPSuccess(const Jid &to, const QString &dstaddr)
+{
+ QDomElement m = doc()->createElement("message");
+ m.setAttribute("to", to.full());
+ QDomElement u = doc()->createElement("udpsuccess");
+ u.setAttribute("xmlns", "http://jabber.org/protocol/bytestreams");
+ u.setAttribute("dstaddr", dstaddr);
+ m.appendChild(u);
+ send(m);
+}
+
+void JT_PushS5B::sendActivate(const Jid &to, const QString &sid, const Jid &streamHost)
+{
+ QDomElement m = doc()->createElement("message");
+ m.setAttribute("to", to.full());
+ QDomElement act = doc()->createElement("activate");
+ act.setAttribute("xmlns", "http://affinix.com/jabber/stream");
+ act.setAttribute("sid", sid);
+ act.setAttribute("jid", streamHost.full());
+ m.appendChild(act);
+ send(m);
+}
+
+//----------------------------------------------------------------------------
+// StreamHost
+//----------------------------------------------------------------------------
+StreamHost::StreamHost()
+{
+ v_port = -1;
+ proxy = false;
+}
+
+const Jid & StreamHost::jid() const
+{
+ return j;
+}
+
+const QString & StreamHost::host() const
+{
+ return v_host;
+}
+
+int StreamHost::port() const
+{
+ return v_port;
+}
+
+bool StreamHost::isProxy() const
+{
+ return proxy;
+}
+
+void StreamHost::setJid(const Jid &_j)
+{
+ j = _j;
+}
+
+void StreamHost::setHost(const QString &host)
+{
+ v_host = host;
+}
+
+void StreamHost::setPort(int port)
+{
+ v_port = port;
+}
+
+void StreamHost::setIsProxy(bool b)
+{
+ proxy = b;
+}
+
+}
+
+#include"s5b.moc"
diff --git a/kopete/protocols/jabber/libiris/iris/jabber/s5b.h b/kopete/protocols/jabber/libiris/iris/jabber/s5b.h
new file mode 100644
index 00000000..dec06969
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/jabber/s5b.h
@@ -0,0 +1,341 @@
+/*
+ * s5b.h - direct connection protocol via tcp
+ * 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 XMPP_S5B_H
+#define XMPP_S5B_H
+
+#include<qobject.h>
+#include<qcstring.h>
+#include<qptrlist.h>
+#include<qvaluelist.h>
+#include"im.h"
+#include"bytestream.h"
+
+class SocksClient;
+class SocksUDP;
+
+namespace XMPP
+{
+ class StreamHost;
+ class S5BConnection;
+ class S5BManager;
+ class S5BServer;
+ struct S5BRequest;
+ typedef QValueList<StreamHost> StreamHostList;
+ typedef QPtrList<S5BConnection> S5BConnectionList;
+ typedef QPtrListIterator<S5BConnection> S5BConnectionListIt;
+
+ class S5BDatagram
+ {
+ public:
+ S5BDatagram();
+ S5BDatagram(int source, int dest, const QByteArray &data);
+
+ int sourcePort() const;
+ int destPort() const;
+ QByteArray data() const;
+
+ private:
+ int _source, _dest;
+ QByteArray _buf;
+ };
+
+ class S5BConnection : public ByteStream
+ {
+ Q_OBJECT
+ public:
+ enum Mode { Stream, Datagram };
+ enum Error { ErrRefused, ErrConnect, ErrProxy, ErrSocket };
+ enum State { Idle, Requesting, Connecting, WaitingForAccept, Active };
+ ~S5BConnection();
+
+ Jid proxy() const;
+ void setProxy(const Jid &proxy);
+
+ void connectToJid(const Jid &peer, const QString &sid, Mode m = Stream);
+ void accept();
+ void close();
+
+ Jid peer() const;
+ QString sid() const;
+ bool isRemote() const;
+ Mode mode() const;
+ int state() const;
+
+ bool isOpen() const;
+ void write(const QByteArray &);
+ QByteArray read(int bytes=0);
+ int bytesAvailable() const;
+ int bytesToWrite() const;
+
+ void writeDatagram(const S5BDatagram &);
+ S5BDatagram readDatagram();
+ int datagramsAvailable() const;
+
+ signals:
+ void proxyQuery(); // querying proxy for streamhost information
+ void proxyResult(bool b); // query success / fail
+ void requesting(); // sent actual S5B request (initiator only)
+ void accepted(); // target accepted (initiator only
+ void tryingHosts(const StreamHostList &hosts); // currently connecting to these hosts
+ void proxyConnect(); // connecting to proxy
+ void waitingForActivation(); // waiting for activation (target only)
+ void connected(); // connection active
+ void datagramReady();
+
+ private slots:
+ void doPending();
+
+ void sc_connectionClosed();
+ void sc_delayedCloseFinished();
+ void sc_readyRead();
+ void sc_bytesWritten(int);
+ void sc_error(int);
+
+ void su_packetReady(const QByteArray &buf);
+
+ private:
+ class Private;
+ Private *d;
+
+ void reset(bool clear=false);
+ void handleUDP(const QByteArray &buf);
+ void sendUDP(const QByteArray &buf);
+
+ friend class S5BManager;
+ void man_waitForAccept(const S5BRequest &r);
+ void man_clientReady(SocksClient *, SocksUDP *);
+ void man_udpReady(const QByteArray &buf);
+ void man_failed(int);
+ S5BConnection(S5BManager *, QObject *parent=0);
+ };
+
+ class S5BManager : public QObject
+ {
+ Q_OBJECT
+ public:
+ S5BManager(Client *);
+ ~S5BManager();
+
+ Client *client() const;
+ S5BServer *server() const;
+ void setServer(S5BServer *s);
+
+ bool isAcceptableSID(const Jid &peer, const QString &sid) const;
+ QString genUniqueSID(const Jid &peer) const;
+
+ S5BConnection *createConnection();
+ S5BConnection *takeIncoming();
+
+ class Item;
+ class Entry;
+
+ signals:
+ void incomingReady();
+
+ private slots:
+ void ps_incoming(const S5BRequest &req);
+ void ps_incomingUDPSuccess(const Jid &from, const QString &dstaddr);
+ void ps_incomingActivate(const Jid &from, const QString &sid, const Jid &streamHost);
+ void item_accepted();
+ void item_tryingHosts(const StreamHostList &list);
+ void item_proxyConnect();
+ void item_waitingForActivation();
+ void item_connected();
+ void item_error(int);
+ void query_finished();
+
+ private:
+ class Private;
+ Private *d;
+
+ S5BConnection *findIncoming(const Jid &from, const QString &sid) const;
+ Entry *findEntry(S5BConnection *) const;
+ Entry *findEntry(Item *) const;
+ Entry *findEntryByHash(const QString &key) const;
+ Entry *findEntryBySID(const Jid &peer, const QString &sid) const;
+ Entry *findServerEntryByHash(const QString &key) const;
+
+ void entryContinue(Entry *e);
+ void queryProxy(Entry *e);
+ bool targetShouldOfferProxy(Entry *e);
+
+ friend class S5BConnection;
+ void con_connect(S5BConnection *);
+ void con_accept(S5BConnection *);
+ void con_reject(S5BConnection *);
+ void con_unlink(S5BConnection *);
+ void con_sendUDP(S5BConnection *, const QByteArray &buf);
+
+ friend class S5BServer;
+ bool srv_ownsHash(const QString &key) const;
+ void srv_incomingReady(SocksClient *sc, const QString &key);
+ void srv_incomingUDP(bool init, const QHostAddress &addr, int port, const QString &key, const QByteArray &data);
+ void srv_unlink();
+
+ friend class Item;
+ void doSuccess(const Jid &peer, const QString &id, const Jid &streamHost);
+ void doError(const Jid &peer, const QString &id, int, const QString &);
+ void doActivate(const Jid &peer, const QString &sid, const Jid &streamHost);
+ };
+
+ class S5BConnector : public QObject
+ {
+ Q_OBJECT
+ public:
+ S5BConnector(QObject *parent=0);
+ ~S5BConnector();
+
+ void reset();
+ void start(const Jid &self, const StreamHostList &hosts, const QString &key, bool udp, int timeout);
+ SocksClient *takeClient();
+ SocksUDP *takeUDP();
+ StreamHost streamHostUsed() const;
+
+ class Item;
+
+ signals:
+ void result(bool);
+
+ private slots:
+ void item_result(bool);
+ void t_timeout();
+
+ private:
+ class Private;
+ Private *d;
+
+ friend class S5BManager;
+ void man_udpSuccess(const Jid &streamHost);
+ };
+
+ // listens on a port for serving
+ class S5BServer : public QObject
+ {
+ Q_OBJECT
+ public:
+ S5BServer(QObject *par=0);
+ ~S5BServer();
+
+ bool isActive() const;
+ bool start(int port);
+ void stop();
+ int port() const;
+ void setHostList(const QStringList &);
+ QStringList hostList() const;
+
+ class Item;
+
+ private slots:
+ void ss_incomingReady();
+ void ss_incomingUDP(const QString &host, int port, const QHostAddress &addr, int sourcePort, const QByteArray &data);
+ void item_result(bool);
+
+ private:
+ class Private;
+ Private *d;
+
+ friend class S5BManager;
+ void link(S5BManager *);
+ void unlink(S5BManager *);
+ void unlinkAll();
+ const QPtrList<S5BManager> & managerList() const;
+ void writeUDP(const QHostAddress &addr, int port, const QByteArray &data);
+ };
+
+ class JT_S5B : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_S5B(Task *);
+ ~JT_S5B();
+
+ void request(const Jid &to, const QString &sid, const StreamHostList &hosts, bool fast, bool udp=false);
+ void requestProxyInfo(const Jid &to);
+ void requestActivation(const Jid &to, const QString &sid, const Jid &target);
+
+ void onGo();
+ void onDisconnect();
+ bool take(const QDomElement &);
+
+ Jid streamHostUsed() const;
+ StreamHost proxyInfo() const;
+
+ private slots:
+ void t_timeout();
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ struct S5BRequest
+ {
+ Jid from;
+ QString id, sid;
+ StreamHostList hosts;
+ bool fast;
+ bool udp;
+ };
+ class JT_PushS5B : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_PushS5B(Task *);
+ ~JT_PushS5B();
+
+ int priority() const;
+
+ void respondSuccess(const Jid &to, const QString &id, const Jid &streamHost);
+ void respondError(const Jid &to, const QString &id, int code, const QString &str);
+ void sendUDPSuccess(const Jid &to, const QString &dstaddr);
+ void sendActivate(const Jid &to, const QString &sid, const Jid &streamHost);
+
+ bool take(const QDomElement &);
+
+ signals:
+ void incoming(const S5BRequest &req);
+ void incomingUDPSuccess(const Jid &from, const QString &dstaddr);
+ void incomingActivate(const Jid &from, const QString &sid, const Jid &streamHost);
+ };
+
+ class StreamHost
+ {
+ public:
+ StreamHost();
+
+ const Jid & jid() const;
+ const QString & host() const;
+ int port() const;
+ bool isProxy() const;
+ void setJid(const Jid &);
+ void setHost(const QString &);
+ void setPort(int);
+ void setIsProxy(bool);
+
+ private:
+ Jid j;
+ QString v_host;
+ int v_port;
+ bool proxy;
+ };
+}
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/jabber/xmpp_ibb.cpp b/kopete/protocols/jabber/libiris/iris/jabber/xmpp_ibb.cpp
new file mode 100644
index 00000000..813157bf
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/jabber/xmpp_ibb.cpp
@@ -0,0 +1,638 @@
+/*
+ * ibb.cpp - Inband bytestream
+ * Copyright (C) 2001, 2002 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include"xmpp_ibb.h"
+
+#include<qtimer.h>
+#include"xmpp_xmlcommon.h"
+#include"base64.h"
+
+#include<stdlib.h>
+
+#define IBB_PACKET_SIZE 4096
+#define IBB_PACKET_DELAY 0
+
+using namespace XMPP;
+
+static int num_conn = 0;
+static int id_conn = 0;
+
+//----------------------------------------------------------------------------
+// IBBConnection
+//----------------------------------------------------------------------------
+class IBBConnection::Private
+{
+public:
+ Private() {}
+
+ int state;
+ Jid peer;
+ QString sid;
+ IBBManager *m;
+ JT_IBB *j;
+ QDomElement comment;
+ QString iq_id;
+
+ int blockSize;
+ QByteArray recvbuf, sendbuf;
+ bool closePending, closing;
+
+ int id;
+};
+
+IBBConnection::IBBConnection(IBBManager *m)
+:ByteStream(m)
+{
+ d = new Private;
+ d->m = m;
+ d->j = 0;
+ reset();
+
+ ++num_conn;
+ d->id = id_conn++;
+ QString dstr; dstr.sprintf("IBBConnection[%d]: constructing, count=%d\n", d->id, num_conn);
+ d->m->client()->debug(dstr);
+}
+
+void IBBConnection::reset(bool clear)
+{
+ d->m->unlink(this);
+ d->state = Idle;
+ d->closePending = false;
+ d->closing = false;
+
+ delete d->j;
+ d->j = 0;
+
+ d->sendbuf.resize(0);
+ if(clear)
+ d->recvbuf.resize(0);
+}
+
+IBBConnection::~IBBConnection()
+{
+ reset(true);
+
+ --num_conn;
+ QString dstr; dstr.sprintf("IBBConnection[%d]: destructing, count=%d\n", d->id, num_conn);
+ d->m->client()->debug(dstr);
+
+ delete d;
+}
+
+void IBBConnection::connectToJid(const Jid &peer, const QDomElement &comment)
+{
+ close();
+ reset(true);
+
+ d->state = Requesting;
+ d->peer = peer;
+ d->comment = comment;
+
+ QString dstr; dstr.sprintf("IBBConnection[%d]: initiating request to %s\n", d->id, peer.full().latin1());
+ d->m->client()->debug(dstr);
+
+ d->j = new JT_IBB(d->m->client()->rootTask());
+ connect(d->j, SIGNAL(finished()), SLOT(ibb_finished()));
+ d->j->request(d->peer, comment);
+ d->j->go(true);
+}
+
+void IBBConnection::accept()
+{
+ if(d->state != WaitingForAccept)
+ return;
+
+ QString dstr; dstr.sprintf("IBBConnection[%d]: accepting %s [%s]\n", d->id, d->peer.full().latin1(), d->sid.latin1());
+ d->m->client()->debug(dstr);
+
+ d->m->doAccept(this, d->iq_id);
+ d->state = Active;
+ d->m->link(this);
+}
+
+void IBBConnection::close()
+{
+ if(d->state == Idle)
+ return;
+
+ if(d->state == WaitingForAccept) {
+ d->m->doReject(this, d->iq_id, 403, "Rejected");
+ reset();
+ return;
+ }
+
+ QString dstr; dstr.sprintf("IBBConnection[%d]: closing\n", d->id);
+ d->m->client()->debug(dstr);
+
+ if(d->state == Active) {
+ // if there is data pending to be written, then pend the closing
+ if(bytesToWrite() > 0) {
+ d->closePending = true;
+ trySend();
+ return;
+ }
+
+ // send a close packet
+ JT_IBB *j = new JT_IBB(d->m->client()->rootTask());
+ j->sendData(d->peer, d->sid, QByteArray(), true);
+ j->go(true);
+ }
+
+ reset();
+}
+
+int IBBConnection::state() const
+{
+ return d->state;
+}
+
+Jid IBBConnection::peer() const
+{
+ return d->peer;
+}
+
+QString IBBConnection::streamid() const
+{
+ return d->sid;
+}
+
+QDomElement IBBConnection::comment() const
+{
+ return d->comment;
+}
+
+bool IBBConnection::isOpen() const
+{
+ if(d->state == Active)
+ return true;
+ else
+ return false;
+}
+
+void IBBConnection::write(const QByteArray &a)
+{
+ if(d->state != Active || d->closePending || d->closing)
+ return;
+
+ // append to the end of our send buffer
+ int oldsize = d->sendbuf.size();
+ d->sendbuf.resize(oldsize + a.size());
+ memcpy(d->sendbuf.data() + oldsize, a.data(), a.size());
+
+ trySend();
+}
+
+QByteArray IBBConnection::read(int)
+{
+ // TODO: obey argument
+ QByteArray a = d->recvbuf.copy();
+ d->recvbuf.resize(0);
+ return a;
+}
+
+int IBBConnection::bytesAvailable() const
+{
+ return d->recvbuf.size();
+}
+
+int IBBConnection::bytesToWrite() const
+{
+ return d->sendbuf.size();
+}
+
+void IBBConnection::waitForAccept(const Jid &peer, const QString &sid, const QDomElement &comment, const QString &iq_id)
+{
+ close();
+ reset(true);
+
+ d->state = WaitingForAccept;
+ d->peer = peer;
+ d->sid = sid;
+ d->comment = comment;
+ d->iq_id = iq_id;
+}
+
+void IBBConnection::takeIncomingData(const QByteArray &a, bool close)
+{
+ // append to the end of our recv buffer
+ int oldsize = d->recvbuf.size();
+ d->recvbuf.resize(oldsize + a.size());
+ memcpy(d->recvbuf.data() + oldsize, a.data(), a.size());
+
+ readyRead();
+
+ if(close) {
+ reset();
+ connectionClosed();
+ }
+}
+
+void IBBConnection::ibb_finished()
+{
+ JT_IBB *j = d->j;
+ d->j = 0;
+
+ if(j->success()) {
+ if(j->mode() == JT_IBB::ModeRequest) {
+ d->sid = j->streamid();
+
+ QString dstr; dstr.sprintf("IBBConnection[%d]: %s [%s] accepted.\n", d->id, d->peer.full().latin1(), d->sid.latin1());
+ d->m->client()->debug(dstr);
+
+ d->state = Active;
+ d->m->link(this);
+ connected();
+ }
+ else {
+ bytesWritten(d->blockSize);
+
+ if(d->closing) {
+ reset();
+ delayedCloseFinished();
+ }
+
+ if(!d->sendbuf.isEmpty() || d->closePending)
+ QTimer::singleShot(IBB_PACKET_DELAY, this, SLOT(trySend()));
+ }
+ }
+ else {
+ if(j->mode() == JT_IBB::ModeRequest) {
+ QString dstr; dstr.sprintf("IBBConnection[%d]: %s refused.\n", d->id, d->peer.full().latin1());
+ d->m->client()->debug(dstr);
+
+ reset(true);
+ error(ErrRequest);
+ }
+ else {
+ reset(true);
+ error(ErrData);
+ }
+ }
+}
+
+void IBBConnection::trySend()
+{
+ // if we already have an active task, then don't do anything
+ if(d->j)
+ return;
+
+ QByteArray a;
+ if(!d->sendbuf.isEmpty()) {
+ // take a chunk
+ if(d->sendbuf.size() < IBB_PACKET_SIZE)
+ a.resize(d->sendbuf.size());
+ else
+ a.resize(IBB_PACKET_SIZE);
+ memcpy(a.data(), d->sendbuf.data(), a.size());
+ d->sendbuf.resize(d->sendbuf.size() - a.size());
+ }
+
+ bool doClose = false;
+ if(d->sendbuf.isEmpty() && d->closePending)
+ doClose = true;
+
+ // null operation?
+ if(a.isEmpty() && !doClose)
+ return;
+
+ printf("IBBConnection[%d]: sending [%d] bytes ", d->id, a.size());
+ if(doClose)
+ printf("and closing.\n");
+ else
+ printf("(%d bytes left)\n", d->sendbuf.size());
+
+ if(doClose) {
+ d->closePending = false;
+ d->closing = true;
+ }
+
+ d->blockSize = a.size();
+ d->j = new JT_IBB(d->m->client()->rootTask());
+ connect(d->j, SIGNAL(finished()), SLOT(ibb_finished()));
+ d->j->sendData(d->peer, d->sid, a, doClose);
+ d->j->go(true);
+}
+
+
+//----------------------------------------------------------------------------
+// IBBManager
+//----------------------------------------------------------------------------
+class IBBManager::Private
+{
+public:
+ Private() {}
+
+ Client *client;
+ IBBConnectionList activeConns;
+ IBBConnectionList incomingConns;
+ JT_IBB *ibb;
+};
+
+IBBManager::IBBManager(Client *parent)
+:QObject(parent)
+{
+ d = new Private;
+ d->client = parent;
+
+ d->ibb = new JT_IBB(d->client->rootTask(), true);
+ connect(d->ibb, SIGNAL(incomingRequest(const Jid &, const QString &, const QDomElement &)), SLOT(ibb_incomingRequest(const Jid &, const QString &, const QDomElement &)));
+ connect(d->ibb, SIGNAL(incomingData(const Jid &, const QString &, const QString &, const QByteArray &, bool)), SLOT(ibb_incomingData(const Jid &, const QString &, const QString &, const QByteArray &, bool)));
+}
+
+IBBManager::~IBBManager()
+{
+ d->incomingConns.setAutoDelete(true);
+ d->incomingConns.clear();
+ delete d->ibb;
+ delete d;
+}
+
+Client *IBBManager::client() const
+{
+ return d->client;
+}
+
+IBBConnection *IBBManager::takeIncoming()
+{
+ if(d->incomingConns.isEmpty())
+ return 0;
+
+ IBBConnection *c = d->incomingConns.getFirst();
+ d->incomingConns.removeRef(c);
+ return c;
+}
+
+void IBBManager::ibb_incomingRequest(const Jid &from, const QString &id, const QDomElement &comment)
+{
+ QString sid = genUniqueKey();
+
+ // create a "waiting" connection
+ IBBConnection *c = new IBBConnection(this);
+ c->waitForAccept(from, sid, comment, id);
+ d->incomingConns.append(c);
+ incomingReady();
+}
+
+void IBBManager::ibb_incomingData(const Jid &from, const QString &streamid, const QString &id, const QByteArray &data, bool close)
+{
+ IBBConnection *c = findConnection(streamid, from);
+ if(!c) {
+ d->ibb->respondError(from, id, 404, "No such stream");
+ }
+ else {
+ d->ibb->respondAck(from, id);
+ c->takeIncomingData(data, close);
+ }
+}
+
+QString IBBManager::genKey() const
+{
+ QString key = "ibb_";
+
+ for(int i = 0; i < 4; ++i) {
+ int word = rand() & 0xffff;
+ for(int n = 0; n < 4; ++n) {
+ QString s;
+ s.sprintf("%x", (word >> (n * 4)) & 0xf);
+ key.append(s);
+ }
+ }
+
+ return key;
+}
+
+QString IBBManager::genUniqueKey() const
+{
+ // get unused key
+ QString key;
+ while(1) {
+ key = genKey();
+
+ if(!findConnection(key))
+ break;
+ }
+
+ return key;
+}
+
+void IBBManager::link(IBBConnection *c)
+{
+ d->activeConns.append(c);
+}
+
+void IBBManager::unlink(IBBConnection *c)
+{
+ d->activeConns.removeRef(c);
+}
+
+IBBConnection *IBBManager::findConnection(const QString &sid, const Jid &peer) const
+{
+ IBBConnectionListIt it(d->activeConns);
+ for(IBBConnection *c; (c = it.current()); ++it) {
+ if(c->streamid() == sid && (peer.isEmpty() || c->peer().compare(peer)) )
+ return c;
+ }
+ return 0;
+}
+
+void IBBManager::doAccept(IBBConnection *c, const QString &id)
+{
+ d->ibb->respondSuccess(c->peer(), id, c->streamid());
+}
+
+void IBBManager::doReject(IBBConnection *c, const QString &id, int code, const QString &str)
+{
+ d->ibb->respondError(c->peer(), id, code, str);
+}
+
+
+//----------------------------------------------------------------------------
+// JT_IBB
+//----------------------------------------------------------------------------
+class JT_IBB::Private
+{
+public:
+ Private() {}
+
+ QDomElement iq;
+ int mode;
+ bool serve;
+ Jid to;
+ QString streamid;
+};
+
+JT_IBB::JT_IBB(Task *parent, bool serve)
+:Task(parent)
+{
+ d = new Private;
+ d->serve = serve;
+}
+
+JT_IBB::~JT_IBB()
+{
+ delete d;
+}
+
+void JT_IBB::request(const Jid &to, const QDomElement &comment)
+{
+ d->mode = ModeRequest;
+ QDomElement iq;
+ d->to = to;
+ iq = createIQ(doc(), "set", to.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "http://jabber.org/protocol/ibb");
+ iq.appendChild(query);
+ query.appendChild(comment);
+ d->iq = iq;
+}
+
+void JT_IBB::sendData(const Jid &to, const QString &streamid, const QByteArray &a, bool close)
+{
+ d->mode = ModeSendData;
+ QDomElement iq;
+ d->to = to;
+ iq = createIQ(doc(), "set", to.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "http://jabber.org/protocol/ibb");
+ iq.appendChild(query);
+ query.appendChild(textTag(doc(), "streamid", streamid));
+ if(!a.isEmpty())
+ query.appendChild(textTag(doc(), "data", Base64::arrayToString(a)));
+ if(close) {
+ QDomElement c = doc()->createElement("close");
+ query.appendChild(c);
+ }
+ d->iq = iq;
+}
+
+void JT_IBB::respondSuccess(const Jid &to, const QString &id, const QString &streamid)
+{
+ QDomElement iq = createIQ(doc(), "result", to.full(), id);
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "http://jabber.org/protocol/ibb");
+ iq.appendChild(query);
+ query.appendChild(textTag(doc(), "streamid", streamid));
+ send(iq);
+}
+
+void JT_IBB::respondError(const Jid &to, const QString &id, int code, const QString &str)
+{
+ QDomElement iq = createIQ(doc(), "error", to.full(), id);
+ QDomElement err = textTag(doc(), "error", str);
+ err.setAttribute("code", QString::number(code));
+ iq.appendChild(err);
+ send(iq);
+}
+
+void JT_IBB::respondAck(const Jid &to, const QString &id)
+{
+ QDomElement iq = createIQ(doc(), "result", to.full(), id);
+ send(iq);
+}
+
+void JT_IBB::onGo()
+{
+ send(d->iq);
+}
+
+bool JT_IBB::take(const QDomElement &e)
+{
+ if(d->serve) {
+ // must be an iq-set tag
+ if(e.tagName() != "iq" || e.attribute("type") != "set")
+ return false;
+
+ if(queryNS(e) != "http://jabber.org/protocol/ibb")
+ return false;
+
+ Jid from(e.attribute("from"));
+ QString id = e.attribute("id");
+ QDomElement q = queryTag(e);
+
+ bool found;
+ QDomElement s = findSubTag(q, "streamid", &found);
+ if(!found) {
+ QDomElement comment = findSubTag(q, "comment", &found);
+ incomingRequest(from, id, comment);
+ }
+ else {
+ QString sid = tagContent(s);
+ QByteArray a;
+ bool close = false;
+ s = findSubTag(q, "data", &found);
+ if(found)
+ a = Base64::stringToArray(tagContent(s));
+ s = findSubTag(q, "close", &found);
+ if(found)
+ close = true;
+
+ incomingData(from, sid, id, a, close);
+ }
+
+ return true;
+ }
+ else {
+ Jid from(e.attribute("from"));
+ if(e.attribute("id") != id() || !d->to.compare(from))
+ return false;
+
+ if(e.attribute("type") == "result") {
+ QDomElement q = queryTag(e);
+
+ // request
+ if(d->mode == ModeRequest) {
+ bool found;
+ QDomElement s = findSubTag(q, "streamid", &found);
+ if(found)
+ d->streamid = tagContent(s);
+ else
+ d->streamid = "";
+ setSuccess();
+ }
+ // sendData
+ else {
+ // thank you for the ack, kind sir
+ setSuccess();
+ }
+ }
+ else {
+ setError(e);
+ }
+
+ return true;
+ }
+}
+
+QString JT_IBB::streamid() const
+{
+ return d->streamid;
+}
+
+Jid JT_IBB::jid() const
+{
+ return d->to;
+}
+
+int JT_IBB::mode() const
+{
+ return d->mode;
+}
+
diff --git a/kopete/protocols/jabber/libiris/iris/jabber/xmpp_ibb.h b/kopete/protocols/jabber/libiris/iris/jabber/xmpp_ibb.h
new file mode 100644
index 00000000..73de4ac4
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/jabber/xmpp_ibb.h
@@ -0,0 +1,145 @@
+/*
+ * ibb.h - Inband bytestream
+ * Copyright (C) 2001, 2002 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef JABBER_IBB_H
+#define JABBER_IBB_H
+
+#include<qobject.h>
+#include<qdom.h>
+#include<qstring.h>
+#include<qptrlist.h>
+#include"bytestream.h"
+#include"im.h"
+
+namespace XMPP
+{
+ class Client;
+ class IBBManager;
+
+ // this is an IBB connection. use it much like a qsocket
+ class IBBConnection : public ByteStream
+ {
+ Q_OBJECT
+ public:
+ enum { ErrRequest, ErrData };
+ enum { Idle, Requesting, WaitingForAccept, Active };
+ IBBConnection(IBBManager *);
+ ~IBBConnection();
+
+ void connectToJid(const Jid &peer, const QDomElement &comment);
+ void accept();
+ void close();
+
+ int state() const;
+ Jid peer() const;
+ QString streamid() const;
+ QDomElement comment() const;
+
+ bool isOpen() const;
+ void write(const QByteArray &);
+ QByteArray read(int bytes=0);
+ int bytesAvailable() const;
+ int bytesToWrite() const;
+
+ signals:
+ void connected();
+
+ private slots:
+ void ibb_finished();
+ void trySend();
+
+ private:
+ class Private;
+ Private *d;
+
+ void reset(bool clear=false);
+
+ friend class IBBManager;
+ void waitForAccept(const Jid &peer, const QString &sid, const QDomElement &comment, const QString &iq_id);
+ void takeIncomingData(const QByteArray &, bool close);
+ };
+
+ typedef QPtrList<IBBConnection> IBBConnectionList;
+ typedef QPtrListIterator<IBBConnection> IBBConnectionListIt;
+ class IBBManager : public QObject
+ {
+ Q_OBJECT
+ public:
+ IBBManager(Client *);
+ ~IBBManager();
+
+ Client *client() const;
+
+ IBBConnection *takeIncoming();
+
+ signals:
+ void incomingReady();
+
+ private slots:
+ void ibb_incomingRequest(const Jid &from, const QString &id, const QDomElement &);
+ void ibb_incomingData(const Jid &from, const QString &streamid, const QString &id, const QByteArray &data, bool close);
+
+ private:
+ class Private;
+ Private *d;
+
+ QString genKey() const;
+
+ friend class IBBConnection;
+ IBBConnection *findConnection(const QString &sid, const Jid &peer="") const;
+ QString genUniqueKey() const;
+ void link(IBBConnection *);
+ void unlink(IBBConnection *);
+ void doAccept(IBBConnection *c, const QString &id);
+ void doReject(IBBConnection *c, const QString &id, int, const QString &);
+ };
+
+ class JT_IBB : public Task
+ {
+ Q_OBJECT
+ public:
+ enum { ModeRequest, ModeSendData };
+ JT_IBB(Task *, bool serve=false);
+ ~JT_IBB();
+
+ void request(const Jid &, const QDomElement &comment);
+ void sendData(const Jid &, const QString &streamid, const QByteArray &data, bool close);
+ void respondSuccess(const Jid &, const QString &id, const QString &streamid);
+ void respondError(const Jid &, const QString &id, int code, const QString &str);
+ void respondAck(const Jid &to, const QString &id);
+
+ void onGo();
+ bool take(const QDomElement &);
+
+ QString streamid() const;
+ Jid jid() const;
+ int mode() const;
+
+ signals:
+ void incomingRequest(const Jid &from, const QString &id, const QDomElement &);
+ void incomingData(const Jid &from, const QString &streamid, const QString &id, const QByteArray &data, bool close);
+
+ private:
+ class Private;
+ Private *d;
+ };
+}
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/jabber/xmpp_jidlink.cpp b/kopete/protocols/jabber/libiris/iris/jabber/xmpp_jidlink.cpp
new file mode 100644
index 00000000..eb140880
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/jabber/xmpp_jidlink.cpp
@@ -0,0 +1,318 @@
+/*
+ * jidlink.cpp - establish a link between Jabber IDs
+ * Copyright (C) 2001, 2002 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include"xmpp_jidlink.h"
+
+#include<qdom.h>
+#include<qtimer.h>
+#include"im.h"
+#include"s5b.h"
+#include"xmpp_ibb.h"
+
+using namespace XMPP;
+
+//----------------------------------------------------------------------------
+// JidLink
+//----------------------------------------------------------------------------
+class JidLink::Private
+{
+public:
+ Client *client;
+ ByteStream *bs;
+ int type;
+ int state;
+ Jid peer;
+};
+
+JidLink::JidLink(Client *client)
+:QObject(client->jidLinkManager())
+{
+ d = new Private;
+ d->client = client;
+ d->bs = 0;
+
+ reset();
+}
+
+JidLink::~JidLink()
+{
+ reset(true);
+
+ delete d;
+}
+
+void JidLink::reset(bool clear)
+{
+ d->type = None;
+ d->state = Idle;
+
+ if(d->bs) {
+ unlink();
+ d->bs->close();
+ if(clear) {
+ delete d->bs;
+ d->bs = 0;
+ }
+ }
+}
+
+void JidLink::connectToJid(const Jid &jid, int type, const QDomElement &comment)
+{
+ reset(true);
+ if(type == DTCP)
+ d->bs = d->client->s5bManager()->createConnection();
+ else if(type == IBB)
+ d->bs = new IBBConnection(d->client->ibbManager());
+ else
+ return;
+
+ d->type = type;
+ d->peer = jid;
+ d->state = Connecting;
+
+ link();
+
+ if(type == DTCP) {
+ S5BConnection *c = (S5BConnection *)d->bs;
+ status(StatDTCPRequesting);
+ c->connectToJid(jid, d->client->s5bManager()->genUniqueSID(jid));
+ }
+ else {
+ IBBConnection *c = (IBBConnection *)d->bs;
+ status(StatIBBRequesting);
+ c->connectToJid(jid, comment);
+ }
+}
+
+void JidLink::link()
+{
+ if(d->type == DTCP) {
+ S5BConnection *c = (S5BConnection *)d->bs;
+ connect(c, SIGNAL(connected()), SLOT(dtcp_connected()));
+ connect(c, SIGNAL(accepted()), SLOT(dtcp_accepted()));
+ }
+ else {
+ IBBConnection *c = (IBBConnection *)d->bs;
+ connect(c, SIGNAL(connected()), SLOT(ibb_connected()));
+ }
+
+ connect(d->bs, SIGNAL(connectionClosed()), SLOT(bs_connectionClosed()));
+ connect(d->bs, SIGNAL(error(int)), SLOT(bs_error(int)));
+ connect(d->bs, SIGNAL(bytesWritten(int)), SLOT(bs_bytesWritten(int)));
+ connect(d->bs, SIGNAL(readyRead()), SLOT(bs_readyRead()));
+}
+
+void JidLink::unlink()
+{
+ d->bs->disconnect(this);
+}
+
+void JidLink::accept()
+{
+ if(d->state != WaitingForAccept)
+ return;
+
+ QTimer::singleShot(0, this, SLOT(doRealAccept()));
+}
+
+void JidLink::doRealAccept()
+{
+ if(d->type == DTCP) {
+ ((S5BConnection *)d->bs)->accept();
+ d->state = Connecting;
+ dtcp_accepted();
+ }
+ else {
+ ((IBBConnection *)d->bs)->accept();
+ d->state = Active;
+ connected();
+ }
+}
+
+bool JidLink::setStream(ByteStream *bs)
+{
+ reset(true);
+ int type = None;
+ if(bs->inherits("XMPP::S5BConnection"))
+ type = DTCP;
+ else if(bs->inherits("XMPP::IBBConnection"))
+ type = IBB;
+
+ if(type == None)
+ return false;
+
+ d->type = type;
+ d->bs = bs;
+ d->state = WaitingForAccept;
+
+ link();
+
+ if(d->type == DTCP)
+ d->peer = ((S5BConnection *)d->bs)->peer();
+ else
+ d->peer = ((IBBConnection *)d->bs)->peer();
+
+ return true;
+}
+
+int JidLink::type() const
+{
+ return d->type;
+}
+
+Jid JidLink::peer() const
+{
+ return d->peer;
+}
+
+int JidLink::state() const
+{
+ return d->state;
+}
+
+bool JidLink::isOpen() const
+{
+ if(d->state == Active)
+ return true;
+ else
+ return false;
+}
+
+void JidLink::close()
+{
+ if(d->state == Idle)
+ return;
+ reset();
+}
+
+void JidLink::write(const QByteArray &a)
+{
+ if(d->state == Active)
+ d->bs->write(a);
+}
+
+QByteArray JidLink::read(int bytes)
+{
+ if(d->bs)
+ return d->bs->read(bytes);
+ else
+ return QByteArray();
+}
+
+int JidLink::bytesAvailable() const
+{
+ if(d->bs)
+ return d->bs->bytesAvailable();
+ else
+ return 0;
+}
+
+int JidLink::bytesToWrite() const
+{
+ if(d->state == Active)
+ return d->bs->bytesToWrite();
+ else
+ return 0;
+}
+
+void JidLink::dtcp_accepted()
+{
+ status(StatDTCPAccepted);
+}
+
+void JidLink::dtcp_connected()
+{
+ d->state = Active;
+ status(StatDTCPConnected);
+ connected();
+}
+
+void JidLink::ibb_connected()
+{
+ d->state = Active;
+ status(StatIBBConnected);
+ connected();
+}
+
+void JidLink::bs_connectionClosed()
+{
+ reset();
+ connectionClosed();
+}
+
+void JidLink::bs_error(int)
+{
+ reset();
+ error(ErrConnect);
+}
+
+void JidLink::bs_readyRead()
+{
+ readyRead();
+}
+
+void JidLink::bs_bytesWritten(int x)
+{
+ bytesWritten(x);
+}
+
+
+//----------------------------------------------------------------------------
+// JidLinkManager
+//----------------------------------------------------------------------------
+class JidLinkManager::Private
+{
+public:
+ Private() {}
+
+ Client *client;
+ QPtrList<JidLink> incomingList;
+};
+
+JidLinkManager::JidLinkManager(Client *par)
+:QObject(par)
+{
+ d = new Private;
+ d->client = par;
+}
+
+JidLinkManager::~JidLinkManager()
+{
+ d->incomingList.setAutoDelete(true);
+ d->incomingList.clear();
+ delete d;
+}
+
+JidLink *JidLinkManager::takeIncoming()
+{
+ if(d->incomingList.isEmpty())
+ return 0;
+
+ JidLink *j = d->incomingList.getFirst();
+ d->incomingList.removeRef(j);
+ return j;
+}
+
+void JidLinkManager::insertStream(ByteStream *bs)
+{
+ JidLink *j = new JidLink(d->client);
+ if(j->setStream(bs))
+ d->incomingList.append(j);
+}
diff --git a/kopete/protocols/jabber/libiris/iris/jabber/xmpp_jidlink.h b/kopete/protocols/jabber/libiris/iris/jabber/xmpp_jidlink.h
new file mode 100644
index 00000000..955fce50
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/jabber/xmpp_jidlink.h
@@ -0,0 +1,114 @@
+/*
+ * jidlink.h - establish a link between Jabber IDs
+ * Copyright (C) 2001, 2002 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ NOTE: this is not to be confused with JEP-0041
+*/
+
+#ifndef JABBER_JIDLINK_H
+#define JABBER_JIDLINK_H
+
+#include<qobject.h>
+#include<qstring.h>
+#include"xmpp.h"
+
+class ByteStream;
+
+namespace XMPP
+{
+ class Client;
+
+ class JidLink : public QObject
+ {
+ Q_OBJECT
+ public:
+ enum { None, DTCP, IBB };
+ enum { Idle, Connecting, WaitingForAccept, Active };
+ enum { ErrConnect, ErrStream };
+ enum { StatDTCPRequesting, StatDTCPAccepted, StatDTCPConnected, StatIBBRequesting, StatIBBConnected };
+ JidLink(Client *client);
+ ~JidLink();
+
+ void connectToJid(const Jid &jid, int type, const QDomElement &comment);
+ void accept();
+
+ int type() const;
+ Jid peer() const;
+ int state() const;
+
+ bool isOpen() const;
+ void close();
+ void write(const QByteArray &);
+ QByteArray read(int bytes=0);
+ int bytesAvailable() const;
+ int bytesToWrite() const;
+
+ signals:
+ void connected();
+ void connectionClosed();
+ void readyRead();
+ void bytesWritten(int);
+ void error(int);
+ void status(int);
+
+ private slots:
+ void dtcp_connected();
+ void dtcp_accepted();
+ void ibb_connected();
+
+ void bs_connectionClosed();
+ void bs_error(int);
+ void bs_readyRead();
+ void bs_bytesWritten(int);
+
+ void doRealAccept();
+
+ private:
+ class Private;
+ Private *d;
+
+ void reset(bool clear=false);
+
+ void link();
+ void unlink();
+
+ friend class JidLinkManager;
+ bool setStream(ByteStream *);
+ };
+
+ // the job of JidLinkManager is to keep track of streams and properly shut them down
+ class JidLinkManager : public QObject
+ {
+ Q_OBJECT
+ public:
+ JidLinkManager(Client *);
+ ~JidLinkManager();
+
+ JidLink *takeIncoming();
+
+ void insertStream(ByteStream *);
+
+ private:
+ class Private;
+ Private *d;
+ };
+}
+
+#endif
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
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-im/Makefile.am b/kopete/protocols/jabber/libiris/iris/xmpp-im/Makefile.am
new file mode 100644
index 00000000..c6dff330
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-im/Makefile.am
@@ -0,0 +1,19 @@
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libiris_xmpp_im.la
+INCLUDES = -I$(srcdir)/../include -I$(srcdir)/../xmpp-core -I$(srcdir)/../xmpp-im -I$(srcdir)/../jabber -I$(srcdir)/../../cutestuff/util -I$(srcdir)/../../cutestuff/network -I$(srcdir)/../../qca/src $(all_includes)
+
+libiris_xmpp_im_la_SOURCES = \
+ client.cpp \
+ types.cpp \
+ xmpp_tasks.cpp \
+ xmpp_vcard.cpp \
+ xmpp_xmlcommon.cpp
+
+CLEANFILES = types.moc
+types.lo: types.moc
+types.moc: $(top_builddir)/kopete/protocols/jabber/libiris/iris/xmpp-im/Makefile
+ ${MOC} -o types.moc $(srcdir)/../xmpp-im/types.cpp
+
+KDE_OPTIONS = nofinal
+
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-im/client.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-im/client.cpp
new file mode 100644
index 00000000..0baeb820
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-im/client.cpp
@@ -0,0 +1,1522 @@
+/*
+ * client.cpp - IM Client
+ * 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"im.h"
+#include"safedelete.h"
+
+//! \class Client client.h
+//! \brief Communicates with the Jabber network. Start here.
+//!
+//! Client controls an active Jabber connection. It allows you to connect,
+//! authenticate, manipulate the roster, and send / receive messages and
+//! presence. It is the centerpiece of this library, and all Tasks must pass
+//! through it.
+//!
+//! For convenience, many Tasks are handled internally to Client (such as
+//! JT_Auth). However, for accessing features beyond the basics provided by
+//! Client, you will need to manually invoke Tasks. Fortunately, the
+//! process is very simple.
+//!
+//! The entire Task system is heavily founded on Qt. All Tasks have a parent,
+//! except for the root Task, and are considered QObjects. By using Qt's RTTI
+//! facilities (QObject::sender(), QObject::isA(), etc), you can use a
+//! "fire and forget" approach with Tasks.
+//!
+//! \code
+//! #include "client.h"
+//! using namespace Jabber;
+//!
+//! ...
+//!
+//! Client *client;
+//!
+//! Session::Session()
+//! {
+//! client = new Client;
+//! connect(client, SIGNAL(handshaken()), SLOT(clientHandshaken()));
+//! connect(client, SIGNAL(authFinished(bool, int, const QString &)), SLOT(authFinished(bool, int, const QString &)));
+//! client->connectToHost("jabber.org");
+//! }
+//!
+//! void Session::clientHandshaken()
+//! {
+//! client->authDigest("jabtest", "12345", "Psi");
+//! }
+//!
+//! void Session::authFinished(bool success, int, const QString &err)
+//! {
+//! if(success)
+//! printf("Login success!");
+//! else
+//! printf("Login failed. Here's why: %s\n", err.latin1());
+//! }
+//! \endcode
+
+#include<stdarg.h>
+#include<qmap.h>
+#include<qobjectlist.h>
+#include<qtimer.h>
+#include<qguardedptr.h>
+#include"xmpp_tasks.h"
+#include"xmpp_xmlcommon.h"
+#include"s5b.h"
+#include"xmpp_ibb.h"
+#include"xmpp_jidlink.h"
+#include"filetransfer.h"
+
+/*#include<stdio.h>
+#include<stdarg.h>
+#include<qstring.h>
+#include<qdom.h>
+#include<qobjectlist.h>
+#include<qtimer.h>
+#include"xmpp_stream.h"
+#include"xmpp_tasks.h"
+#include"xmpp_xmlcommon.h"
+#include"xmpp_dtcp.h"
+#include"xmpp_ibb.h"
+#include"xmpp_jidlink.h"
+
+using namespace Jabber;*/
+
+#ifdef Q_WS_WIN
+#define vsnprintf _vsnprintf
+#endif
+
+namespace XMPP
+{
+
+//----------------------------------------------------------------------------
+// Client
+//----------------------------------------------------------------------------
+class Client::GroupChat
+{
+public:
+ enum { Connecting, Connected, Closing };
+ GroupChat() {}
+
+ Jid j;
+ int status;
+};
+
+class Client::ClientPrivate
+{
+public:
+ ClientPrivate() {}
+
+ ClientStream *stream;
+ QDomDocument doc;
+ int id_seed;
+ Task *root;
+ QString host, user, pass, resource;
+ QString osname, tzname, clientName, clientVersion, capsNode, capsVersion, capsExt;
+ DiscoItem::Identity identity;
+ QMap<QString,Features> extension_features;
+ int tzoffset;
+ bool active;
+
+ LiveRoster roster;
+ ResourceList resourceList;
+ S5BManager *s5bman;
+ IBBManager *ibbman;
+ JidLinkManager *jlman;
+ FileTransferManager *ftman;
+ bool ftEnabled;
+ QValueList<GroupChat> groupChatList;
+};
+
+
+Client::Client(QObject *par)
+:QObject(par)
+{
+ d = new ClientPrivate;
+ d->tzoffset = 0;
+ d->active = false;
+ d->osname = "N/A";
+ d->clientName = "N/A";
+ d->clientVersion = "0.0";
+ d->capsNode = "";
+ d->capsVersion = "";
+ d->capsExt = "";
+
+ d->id_seed = 0xaaaa;
+ d->root = new Task(this, true);
+
+ d->stream = 0;
+
+ d->s5bman = new S5BManager(this);
+ connect(d->s5bman, SIGNAL(incomingReady()), SLOT(s5b_incomingReady()));
+
+ d->ibbman = new IBBManager(this);
+ connect(d->ibbman, SIGNAL(incomingReady()), SLOT(ibb_incomingReady()));
+
+ d->jlman = new JidLinkManager(this);
+
+ d->ftman = 0;
+}
+
+Client::~Client()
+{
+ close(true);
+
+ delete d->ftman;
+ delete d->jlman;
+ delete d->ibbman;
+ delete d->s5bman;
+ delete d->root;
+ //delete d->stream;
+ delete d;
+}
+
+void Client::connectToServer(ClientStream *s, const Jid &j, bool auth)
+{
+ d->stream = s;
+ //connect(d->stream, SIGNAL(connected()), SLOT(streamConnected()));
+ //connect(d->stream, SIGNAL(handshaken()), SLOT(streamHandshaken()));
+ connect(d->stream, SIGNAL(error(int)), SLOT(streamError(int)));
+ //connect(d->stream, SIGNAL(sslCertificateReady(const QSSLCert &)), SLOT(streamSSLCertificateReady(const QSSLCert &)));
+ connect(d->stream, SIGNAL(readyRead()), SLOT(streamReadyRead()));
+ //connect(d->stream, SIGNAL(closeFinished()), SLOT(streamCloseFinished()));
+ connect(d->stream, SIGNAL(incomingXml(const QString &)), SLOT(streamIncomingXml(const QString &)));
+ connect(d->stream, SIGNAL(outgoingXml(const QString &)), SLOT(streamOutgoingXml(const QString &)));
+
+ d->stream->connectToServer(j, auth);
+}
+
+void Client::start(const QString &host, const QString &user, const QString &pass, const QString &_resource)
+{
+ // TODO
+ d->host = host;
+ d->user = user;
+ d->pass = pass;
+ d->resource = _resource;
+
+ Status stat;
+ stat.setIsAvailable(false);
+ d->resourceList += Resource(resource(), stat);
+
+ JT_PushPresence *pp = new JT_PushPresence(rootTask());
+ connect(pp, SIGNAL(subscription(const Jid &, const QString &)), SLOT(ppSubscription(const Jid &, const QString &)));
+ connect(pp, SIGNAL(presence(const Jid &, const Status &)), SLOT(ppPresence(const Jid &, const Status &)));
+
+ JT_PushMessage *pm = new JT_PushMessage(rootTask());
+ connect(pm, SIGNAL(message(const Message &)), SLOT(pmMessage(const Message &)));
+
+ JT_PushRoster *pr = new JT_PushRoster(rootTask());
+ connect(pr, SIGNAL(roster(const Roster &)), SLOT(prRoster(const Roster &)));
+
+ new JT_ServInfo(rootTask());
+
+ d->active = true;
+}
+
+void Client::setFileTransferEnabled(bool b)
+{
+ if(b) {
+ if(!d->ftman)
+ d->ftman = new FileTransferManager(this);
+ }
+ else {
+ if(d->ftman) {
+ delete d->ftman;
+ d->ftman = 0;
+ }
+ }
+}
+
+FileTransferManager *Client::fileTransferManager() const
+{
+ return d->ftman;
+}
+
+JidLinkManager *Client::jidLinkManager() const
+{
+ return d->jlman;
+}
+
+S5BManager *Client::s5bManager() const
+{
+ return d->s5bman;
+}
+
+IBBManager *Client::ibbManager() const
+{
+ return d->ibbman;
+}
+
+bool Client::isActive() const
+{
+ return d->active;
+}
+
+void Client::groupChatChangeNick(const QString &host, const QString &room, const QString &nick, const Status &_s)
+{
+ Jid jid(room + "@" + host + "/" + nick);
+ for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) {
+ GroupChat &i = *it;
+ if(i.j.compare(jid, false)) {
+ i.j = jid;
+
+ Status s = _s;
+ s.setIsAvailable(true);
+
+ JT_Presence *j = new JT_Presence(rootTask());
+ j->pres(jid, s);
+ j->go(true);
+
+ break;
+ }
+ }
+}
+
+bool Client::groupChatJoin(const QString &host, const QString &room, const QString &nick)
+{
+ Jid jid(room + "@" + host + "/" + nick);
+ for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end();) {
+ GroupChat &i = *it;
+ if(i.j.compare(jid, false)) {
+ // if this room is shutting down, then free it up
+ if(i.status == GroupChat::Closing)
+ it = d->groupChatList.remove(it);
+ else
+ return false;
+ }
+ else
+ ++it;
+ }
+
+ debug(QString("Client: Joined: [%1]\n").arg(jid.full()));
+ GroupChat i;
+ i.j = jid;
+ i.status = GroupChat::Connecting;
+ d->groupChatList += i;
+
+ JT_Presence *j = new JT_Presence(rootTask());
+ j->pres(jid, Status());
+ j->go(true);
+
+ return true;
+}
+
+bool Client::groupChatJoin(const QString &host, const QString &room, const QString &nick, const QString &password)
+{
+ Jid jid(room + "@" + host + "/" + nick);
+ for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end();) {
+ GroupChat &i = *it;
+ if(i.j.compare(jid, false)) {
+ // if this room is shutting down, then free it up
+ if(i.status == GroupChat::Closing)
+ it = d->groupChatList.remove(it);
+ else
+ return false;
+ }
+ else
+ ++it;
+ }
+
+ debug(QString("Client: Joined: [%1]\n").arg(jid.full()));
+ GroupChat i;
+ i.j = jid;
+ i.status = GroupChat::Connecting;
+ d->groupChatList += i;
+
+ JT_MucPresence *j = new JT_MucPresence(rootTask());
+ j->pres(jid, Status(), password);
+ j->go(true);
+
+ return true;
+}
+
+void Client::groupChatSetStatus(const QString &host, const QString &room, const Status &_s)
+{
+ Jid jid(room + "@" + host);
+ bool found = false;
+ for(QValueList<GroupChat>::ConstIterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) {
+ const GroupChat &i = *it;
+ if(i.j.compare(jid, false)) {
+ found = true;
+ jid = i.j;
+ break;
+ }
+ }
+ if(!found)
+ return;
+
+ Status s = _s;
+ s.setIsAvailable(true);
+
+ JT_Presence *j = new JT_Presence(rootTask());
+ j->pres(jid, s);
+ j->go(true);
+}
+
+void Client::groupChatLeave(const QString &host, const QString &room)
+{
+ Jid jid(room + "@" + host);
+ for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) {
+ GroupChat &i = *it;
+
+ if(!i.j.compare(jid, false))
+ continue;
+
+ i.status = GroupChat::Closing;
+ debug(QString("Client: Leaving: [%1]\n").arg(i.j.full()));
+
+ JT_Presence *j = new JT_Presence(rootTask());
+ Status s;
+ s.setIsAvailable(false);
+ j->pres(i.j, s);
+ j->go(true);
+ }
+}
+
+/*void Client::start()
+{
+ if(d->stream->old()) {
+ // old has no activation step
+ d->active = true;
+ activated();
+ }
+ else {
+ // TODO: IM session
+ }
+}*/
+
+// TODO: fast close
+void Client::close(bool)
+{
+ if(d->stream) {
+ if(d->active) {
+ for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) {
+ GroupChat &i = *it;
+ i.status = GroupChat::Closing;
+
+ JT_Presence *j = new JT_Presence(rootTask());
+ Status s;
+ s.setIsAvailable(false);
+ j->pres(i.j, s);
+ j->go(true);
+ }
+ }
+
+ d->stream->disconnect(this);
+ d->stream->close();
+ d->stream = 0;
+ }
+ disconnected();
+ cleanup();
+}
+
+void Client::cleanup()
+{
+ d->active = false;
+ //d->authed = false;
+ d->groupChatList.clear();
+}
+
+/*void Client::continueAfterCert()
+{
+ d->stream->continueAfterCert();
+}
+
+void Client::streamConnected()
+{
+ connected();
+}
+
+void Client::streamHandshaken()
+{
+ handshaken();
+}*/
+
+void Client::streamError(int)
+{
+ //StreamError e = err;
+ //error(e);
+
+ //if(!e.isWarning()) {
+ disconnected();
+ cleanup();
+ //}
+}
+
+/*void Client::streamSSLCertificateReady(const QSSLCert &cert)
+{
+ sslCertReady(cert);
+}
+
+void Client::streamCloseFinished()
+{
+ closeFinished();
+}*/
+
+static QDomElement oldStyleNS(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;
+
+ QDomElement i;
+ uint x;
+ //if(noShowNS)
+ i = e.ownerDocument().createElement(e.tagName());
+ //else
+ // i = e.ownerDocument().createElementNS(e.namespaceURI(), e.tagName());
+
+ // copy attributes
+ QDomNamedNodeMap al = e.attributes();
+ for(x = 0; x < al.count(); ++x)
+ i.setAttributeNode(al.item(x).cloneNode().toAttr());
+
+ if(!noShowNS)
+ i.setAttribute("xmlns", e.namespaceURI());
+
+ // copy children
+ QDomNodeList nl = e.childNodes();
+ for(x = 0; x < nl.count(); ++x) {
+ QDomNode n = nl.item(x);
+ if(n.isElement())
+ i.appendChild(oldStyleNS(n.toElement()));
+ else
+ i.appendChild(n.cloneNode());
+ }
+ return i;
+}
+
+void Client::streamReadyRead()
+{
+ // HACK HACK HACK
+ QGuardedPtr<ClientStream> pstream = d->stream;
+
+ while(pstream && d->stream->stanzaAvailable()) {
+ Stanza s = d->stream->read();
+
+ QString out = s.toString();
+ debug(QString("Client: incoming: [\n%1]\n").arg(out));
+ xmlIncoming(out);
+
+ QDomElement x = oldStyleNS(s.element());
+ distribute(x);
+ }
+}
+
+void Client::streamIncomingXml(const QString &s)
+{
+ QString str = s;
+ if(str.at(str.length()-1) != '\n')
+ str += '\n';
+ xmlIncoming(str);
+}
+
+void Client::streamOutgoingXml(const QString &s)
+{
+ QString str = s;
+ if(str.at(str.length()-1) != '\n')
+ str += '\n';
+ xmlOutgoing(str);
+}
+
+void Client::debug(const QString &str)
+{
+ debugText(str);
+}
+
+QString Client::genUniqueId()
+{
+ QString s;
+ s.sprintf("a%x", d->id_seed);
+ d->id_seed += 0x10;
+ return s;
+}
+
+Task *Client::rootTask()
+{
+ return d->root;
+}
+
+QDomDocument *Client::doc() const
+{
+ return &d->doc;
+}
+
+void Client::distribute(const QDomElement &x)
+{
+ if(x.hasAttribute("from")) {
+ Jid j(x.attribute("from"));
+ if(!j.isValid()) {
+ debug("Client: bad 'from' JID\n");
+ return;
+ }
+ }
+
+ if(!rootTask()->take(x)) {
+ debug("Client: packet was ignored.\n");
+ }
+}
+
+static QDomElement addCorrectNS(const QDomElement &e)
+{
+ uint x;
+
+ // grab child nodes
+ /*QDomDocumentFragment frag = e.ownerDocument().createDocumentFragment();
+ QDomNodeList nl = e.childNodes();
+ for(x = 0; x < nl.count(); ++x)
+ frag.appendChild(nl.item(x).cloneNode());*/
+
+ // find closest xmlns
+ QDomNode n = e;
+ while(!n.isNull() && !n.toElement().hasAttribute("xmlns"))
+ n = n.parentNode();
+ QString ns;
+ if(n.isNull() || !n.toElement().hasAttribute("xmlns"))
+ ns = "jabber:client";
+ else
+ ns = n.toElement().attribute("xmlns");
+
+ // make a new node
+ QDomElement i = e.ownerDocument().createElementNS(ns, e.tagName());
+
+ // copy attributes
+ QDomNamedNodeMap al = e.attributes();
+ for(x = 0; x < al.count(); ++x) {
+ QDomAttr a = al.item(x).toAttr();
+ if(a.name() != "xmlns")
+ i.setAttributeNodeNS(a.cloneNode().toAttr());
+ }
+
+ // copy children
+ QDomNodeList nl = e.childNodes();
+ for(x = 0; x < nl.count(); ++x) {
+ QDomNode n = nl.item(x);
+ if(n.isElement())
+ i.appendChild(addCorrectNS(n.toElement()));
+ else
+ i.appendChild(n.cloneNode());
+ }
+
+ //i.appendChild(frag);
+ return i;
+}
+
+void Client::send(const QDomElement &x)
+{
+ if(!d->stream)
+ return;
+
+ //QString out;
+ //QTextStream ts(&out, IO_WriteOnly);
+ //x.save(ts, 0);
+
+ //QString out = Stream::xmlToString(x);
+ //debug(QString("Client: outgoing: [\n%1]\n").arg(out));
+ //xmlOutgoing(out);
+
+ QDomElement e = addCorrectNS(x);
+ Stanza s = d->stream->createStanza(e);
+ if(s.isNull()) {
+ //printf("bad stanza??\n");
+ return;
+ }
+
+ QString out = s.toString();
+ debug(QString("Client: outgoing: [\n%1]\n").arg(out));
+ xmlOutgoing(out);
+
+ //printf("x[%s] x2[%s] s[%s]\n", Stream::xmlToString(x).latin1(), Stream::xmlToString(e).latin1(), s.toString().latin1());
+ d->stream->write(s);
+}
+
+void Client::send(const QString &str)
+{
+ if(!d->stream)
+ return;
+
+ debug(QString("Client: outgoing: [\n%1]\n").arg(str));
+ xmlOutgoing(str);
+ static_cast<ClientStream*>(d->stream)->writeDirect(str);
+}
+
+Stream & Client::stream()
+{
+ return *d->stream;
+}
+
+const LiveRoster & Client::roster() const
+{
+ return d->roster;
+}
+
+const ResourceList & Client::resourceList() const
+{
+ return d->resourceList;
+}
+
+QString Client::host() const
+{
+ return d->host;
+}
+
+QString Client::user() const
+{
+ return d->user;
+}
+
+QString Client::pass() const
+{
+ return d->pass;
+}
+
+QString Client::resource() const
+{
+ return d->resource;
+}
+
+Jid Client::jid() const
+{
+ QString s;
+ if(!d->user.isEmpty())
+ s += d->user + '@';
+ s += d->host;
+ if(!d->resource.isEmpty()) {
+ s += '/';
+ s += d->resource;
+ }
+
+ return Jid(s);
+}
+
+void Client::ppSubscription(const Jid &j, const QString &s)
+{
+ subscription(j, s);
+}
+
+void Client::ppPresence(const Jid &j, const Status &s)
+{
+ if(s.isAvailable())
+ debug(QString("Client: %1 is available.\n").arg(j.full()));
+ else
+ debug(QString("Client: %1 is unavailable.\n").arg(j.full()));
+
+ for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) {
+ GroupChat &i = *it;
+
+ if(i.j.compare(j, false)) {
+ bool us = (i.j.resource() == j.resource() || j.resource().isEmpty()) ? true: false;
+
+ debug(QString("for groupchat i=[%1] pres=[%2], [us=%3].\n").arg(i.j.full()).arg(j.full()).arg(us));
+ switch(i.status) {
+ case GroupChat::Connecting:
+ if(us && s.hasError()) {
+ Jid j = i.j;
+ d->groupChatList.remove(it);
+ groupChatError(j, s.errorCode(), s.errorString());
+ }
+ else {
+ // don't signal success unless it is a non-error presence
+ if(!s.hasError()) {
+ i.status = GroupChat::Connected;
+ groupChatJoined(i.j);
+ }
+ groupChatPresence(j, s);
+ }
+ break;
+ case GroupChat::Connected:
+ groupChatPresence(j, s);
+ break;
+ case GroupChat::Closing:
+ if(us && !s.isAvailable()) {
+ Jid j = i.j;
+ d->groupChatList.remove(it);
+ groupChatLeft(j);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return;
+ }
+ }
+
+ if(s.hasError()) {
+ presenceError(j, s.errorCode(), s.errorString());
+ return;
+ }
+
+ // is it me?
+ if(j.compare(jid(), false)) {
+ updateSelfPresence(j, s);
+ }
+ else {
+ // update all relavent roster entries
+ for(LiveRoster::Iterator it = d->roster.begin(); it != d->roster.end(); ++it) {
+ LiveRosterItem &i = *it;
+
+ if(!i.jid().compare(j, false))
+ continue;
+
+ // roster item has its own resource?
+ if(!i.jid().resource().isEmpty()) {
+ if(i.jid().resource() != j.resource())
+ continue;
+ }
+
+ updatePresence(&i, j, s);
+ }
+ }
+}
+
+void Client::updateSelfPresence(const Jid &j, const Status &s)
+{
+ ResourceList::Iterator rit = d->resourceList.find(j.resource());
+ bool found = (rit == d->resourceList.end()) ? false: true;
+
+ // unavailable? remove the resource
+ if(!s.isAvailable()) {
+ if(found) {
+ debug(QString("Client: Removing self resource: name=[%1]\n").arg(j.resource()));
+ (*rit).setStatus(s);
+ resourceUnavailable(j, *rit);
+ d->resourceList.remove(rit);
+ }
+ }
+ // available? add/update the resource
+ else {
+ Resource r;
+ if(!found) {
+ r = Resource(j.resource(), s);
+ d->resourceList += r;
+ debug(QString("Client: Adding self resource: name=[%1]\n").arg(j.resource()));
+ }
+ else {
+ (*rit).setStatus(s);
+ r = *rit;
+ debug(QString("Client: Updating self resource: name=[%1]\n").arg(j.resource()));
+ }
+
+ resourceAvailable(j, r);
+ }
+}
+
+void Client::updatePresence(LiveRosterItem *i, const Jid &j, const Status &s)
+{
+ ResourceList::Iterator rit = i->resourceList().find(j.resource());
+ bool found = (rit == i->resourceList().end()) ? false: true;
+
+ // unavailable? remove the resource
+ if(!s.isAvailable()) {
+ if(found) {
+ (*rit).setStatus(s);
+ debug(QString("Client: Removing resource from [%1]: name=[%2]\n").arg(i->jid().full()).arg(j.resource()));
+ resourceUnavailable(j, *rit);
+ i->resourceList().remove(rit);
+ i->setLastUnavailableStatus(s);
+ }
+ }
+ // available? add/update the resource
+ else {
+ Resource r;
+ if(!found) {
+ r = Resource(j.resource(), s);
+ i->resourceList() += r;
+ debug(QString("Client: Adding resource to [%1]: name=[%2]\n").arg(i->jid().full()).arg(j.resource()));
+ }
+ else {
+ (*rit).setStatus(s);
+ r = *rit;
+ debug(QString("Client: Updating resource to [%1]: name=[%2]\n").arg(i->jid().full()).arg(j.resource()));
+ }
+
+ resourceAvailable(j, r);
+ }
+}
+
+void Client::pmMessage(const Message &m)
+{
+ debug(QString("Client: Message from %1\n").arg(m.from().full()));
+
+ if(m.type() == "groupchat") {
+ for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) {
+ const GroupChat &i = *it;
+
+ if(!i.j.compare(m.from(), false))
+ continue;
+
+ if(i.status == GroupChat::Connected)
+ messageReceived(m);
+ }
+ }
+ else
+ messageReceived(m);
+}
+
+void Client::prRoster(const Roster &r)
+{
+ importRoster(r);
+}
+
+void Client::rosterRequest()
+{
+ if(!d->active)
+ return;
+
+ JT_Roster *r = new JT_Roster(rootTask());
+ connect(r, SIGNAL(finished()), SLOT(slotRosterRequestFinished()));
+ r->get();
+ d->roster.flagAllForDelete(); // mod_groups patch
+ r->go(true);
+}
+
+void Client::slotRosterRequestFinished()
+{
+ JT_Roster *r = (JT_Roster *)sender();
+ // on success, let's take it
+ if(r->success()) {
+ //d->roster.flagAllForDelete(); // mod_groups patch
+
+ importRoster(r->roster());
+
+ for(LiveRoster::Iterator it = d->roster.begin(); it != d->roster.end();) {
+ LiveRosterItem &i = *it;
+ if(i.flagForDelete()) {
+ rosterItemRemoved(i);
+ it = d->roster.remove(it);
+ }
+ else
+ ++it;
+ }
+ }
+ else {
+ // don't report a disconnect. Client::error() will do that.
+ if(r->statusCode() == Task::ErrDisc)
+ return;
+ }
+
+ // report success / fail
+ rosterRequestFinished(r->success(), r->statusCode(), r->statusString());
+}
+
+void Client::importRoster(const Roster &r)
+{
+ for(Roster::ConstIterator it = r.begin(); it != r.end(); ++it) {
+ importRosterItem(*it);
+ }
+}
+
+void Client::importRosterItem(const RosterItem &item)
+{
+ QString substr;
+ switch(item.subscription().type()) {
+ case Subscription::Both:
+ substr = "<-->"; break;
+ case Subscription::From:
+ substr = " ->"; break;
+ case Subscription::To:
+ substr = "<- "; break;
+ case Subscription::Remove:
+ substr = "xxxx"; break;
+ case Subscription::None:
+ default:
+ substr = "----"; break;
+ }
+
+ QString dstr, str;
+ str.sprintf(" %s %-32s", substr.latin1(), item.jid().full().latin1());
+ if(!item.name().isEmpty())
+ str += QString(" [") + item.name() + "]";
+ str += '\n';
+
+ // Remove
+ if(item.subscription().type() == Subscription::Remove) {
+ LiveRoster::Iterator it = d->roster.find(item.jid());
+ if(it != d->roster.end()) {
+ rosterItemRemoved(*it);
+ d->roster.remove(it);
+ }
+ dstr = "Client: (Removed) ";
+ }
+ // Add/Update
+ else {
+ LiveRoster::Iterator it = d->roster.find(item.jid());
+ if(it != d->roster.end()) {
+ LiveRosterItem &i = *it;
+ i.setFlagForDelete(false);
+ i.setRosterItem(item);
+ rosterItemUpdated(i);
+ dstr = "Client: (Updated) ";
+ }
+ else {
+ LiveRosterItem i(item);
+ d->roster += i;
+
+ // signal it
+ rosterItemAdded(i);
+ dstr = "Client: (Added) ";
+ }
+ }
+
+ debug(dstr + str);
+}
+
+void Client::sendMessage(const Message &m)
+{
+ JT_Message *j = new JT_Message(rootTask(), m);
+ j->go(true);
+}
+
+void Client::sendSubscription(const Jid &jid, const QString &type)
+{
+ JT_Presence *j = new JT_Presence(rootTask());
+ j->sub(jid, type);
+ j->go(true);
+}
+
+void Client::setPresence(const Status &s)
+{
+ JT_Presence *j = new JT_Presence(rootTask());
+ j->pres(s);
+ j->go(true);
+
+ // update our resourceList
+ ppPresence(jid(), s);
+ //ResourceList::Iterator rit = d->resourceList.find(resource());
+ //Resource &r = *rit;
+ //r.setStatus(s);
+}
+
+QString Client::OSName() const
+{
+ return d->osname;
+}
+
+QString Client::timeZone() const
+{
+ return d->tzname;
+}
+
+int Client::timeZoneOffset() const
+{
+ return d->tzoffset;
+}
+
+QString Client::clientName() const
+{
+ return d->clientName;
+}
+
+QString Client::clientVersion() const
+{
+ return d->clientVersion;
+}
+
+QString Client::capsNode() const
+{
+ return d->capsNode;
+}
+
+QString Client::capsVersion() const
+{
+ return d->capsVersion;
+}
+
+QString Client::capsExt() const
+{
+ return d->capsExt;
+}
+
+void Client::setOSName(const QString &name)
+{
+ d->osname = name;
+}
+
+void Client::setTimeZone(const QString &name, int offset)
+{
+ d->tzname = name;
+ d->tzoffset = offset;
+}
+
+void Client::setClientName(const QString &s)
+{
+ d->clientName = s;
+}
+
+void Client::setClientVersion(const QString &s)
+{
+ d->clientVersion = s;
+}
+
+void Client::setCapsNode(const QString &s)
+{
+ d->capsNode = s;
+}
+
+void Client::setCapsVersion(const QString &s)
+{
+ d->capsVersion = s;
+}
+
+DiscoItem::Identity Client::identity()
+{
+ return d->identity;
+}
+
+void Client::setIdentity(DiscoItem::Identity identity)
+{
+ d->identity = identity;
+}
+
+void Client::addExtension(const QString& ext, const Features& features)
+{
+ if (!ext.isEmpty()) {
+ d->extension_features[ext] = features;
+ d->capsExt = extensions().join(" ");
+ }
+}
+
+void Client::removeExtension(const QString& ext)
+{
+ if (d->extension_features.contains(ext)) {
+ d->extension_features.remove(ext);
+ d->capsExt = extensions().join(" ");
+ }
+}
+
+QStringList Client::extensions() const
+{
+ return d->extension_features.keys();
+}
+
+const Features& Client::extension(const QString& ext) const
+{
+ return d->extension_features[ext];
+}
+
+void Client::s5b_incomingReady()
+{
+ S5BConnection *c = d->s5bman->takeIncoming();
+ if(!c)
+ return;
+ if(!d->ftman) {
+ c->close();
+ c->deleteLater();
+ return;
+ }
+ d->ftman->s5b_incomingReady(c);
+ //d->jlman->insertStream(c);
+ //incomingJidLink();
+}
+
+void Client::ibb_incomingReady()
+{
+ IBBConnection *c = d->ibbman->takeIncoming();
+ if(!c)
+ return;
+ c->deleteLater();
+ //d->jlman->insertStream(c);
+ //incomingJidLink();
+}
+
+//----------------------------------------------------------------------------
+// Task
+//----------------------------------------------------------------------------
+class Task::TaskPrivate
+{
+public:
+ TaskPrivate() {}
+
+ QString id;
+ bool success;
+ int statusCode;
+ QString statusString;
+ Client *client;
+ bool insig, deleteme, autoDelete;
+ bool done;
+};
+
+Task::Task(Task *parent)
+:QObject(parent)
+{
+ init();
+
+ d->client = parent->client();
+ d->id = client()->genUniqueId();
+ connect(d->client, SIGNAL(disconnected()), SLOT(clientDisconnected()));
+}
+
+Task::Task(Client *parent, bool)
+:QObject(0)
+{
+ init();
+
+ d->client = parent;
+ connect(d->client, SIGNAL(disconnected()), SLOT(clientDisconnected()));
+}
+
+Task::~Task()
+{
+ delete d;
+}
+
+void Task::init()
+{
+ d = new TaskPrivate;
+ d->success = false;
+ d->insig = false;
+ d->deleteme = false;
+ d->autoDelete = false;
+ d->done = false;
+}
+
+Task *Task::parent() const
+{
+ return (Task *)QObject::parent();
+}
+
+Client *Task::client() const
+{
+ return d->client;
+}
+
+QDomDocument *Task::doc() const
+{
+ return client()->doc();
+}
+
+QString Task::id() const
+{
+ return d->id;
+}
+
+bool Task::success() const
+{
+ return d->success;
+}
+
+int Task::statusCode() const
+{
+ return d->statusCode;
+}
+
+const QString & Task::statusString() const
+{
+ return d->statusString;
+}
+
+void Task::go(bool autoDelete)
+{
+ d->autoDelete = autoDelete;
+
+ onGo();
+}
+
+bool Task::take(const QDomElement &x)
+{
+ const QObjectList *p = children();
+ if(!p)
+ return false;
+
+ // pass along the xml
+ QObjectListIt it(*p);
+ Task *t;
+ for(; it.current(); ++it) {
+ QObject *obj = it.current();
+ if(!obj->inherits("XMPP::Task"))
+ continue;
+
+ t = static_cast<Task*>(obj);
+ if(t->take(x))
+ return true;
+ }
+
+ return false;
+}
+
+void Task::safeDelete()
+{
+ if(d->deleteme)
+ return;
+
+ d->deleteme = true;
+ if(!d->insig)
+ SafeDelete::deleteSingle(this);
+}
+
+void Task::onGo()
+{
+}
+
+void Task::onDisconnect()
+{
+ if(!d->done) {
+ d->success = false;
+ d->statusCode = ErrDisc;
+ d->statusString = tr("Disconnected");
+
+ // delay this so that tasks that react don't block the shutdown
+ QTimer::singleShot(0, this, SLOT(done()));
+ }
+}
+
+void Task::send(const QDomElement &x)
+{
+ client()->send(x);
+}
+
+void Task::setSuccess(int code, const QString &str)
+{
+ if(!d->done) {
+ d->success = true;
+ d->statusCode = code;
+ d->statusString = str;
+ done();
+ }
+}
+
+void Task::setError(const QDomElement &e)
+{
+ if(!d->done) {
+ d->success = false;
+ getErrorFromElement(e, &d->statusCode, &d->statusString);
+ done();
+ }
+}
+
+void Task::setError(int code, const QString &str)
+{
+ if(!d->done) {
+ d->success = false;
+ d->statusCode = code;
+ d->statusString = str;
+ done();
+ }
+}
+
+void Task::done()
+{
+ if(d->done || d->insig)
+ return;
+ d->done = true;
+
+ if(d->deleteme || d->autoDelete)
+ d->deleteme = true;
+
+ d->insig = true;
+ finished();
+ d->insig = false;
+
+ if(d->deleteme)
+ SafeDelete::deleteSingle(this);
+}
+
+void Task::clientDisconnected()
+{
+ onDisconnect();
+}
+
+void Task::debug(const char *fmt, ...)
+{
+ char *buf;
+ QString str;
+ int size = 1024;
+ int r;
+
+ do {
+ buf = new char[size];
+ va_list ap;
+ va_start(ap, fmt);
+ r = vsnprintf(buf, size, fmt, ap);
+ va_end(ap);
+
+ if(r != -1)
+ str = QString(buf);
+
+ delete [] buf;
+
+ size *= 2;
+ } while(r == -1);
+
+ debug(str);
+}
+
+void Task::debug(const QString &str)
+{
+ client()->debug(QString("%1: ").arg(className()) + str);
+}
+
+bool Task::iqVerify(const QDomElement &x, const Jid &to, const QString &id, const QString &xmlns)
+{
+ if(x.tagName() != "iq")
+ return false;
+
+ Jid from(x.attribute("from"));
+ Jid local = client()->jid();
+ Jid server = client()->host();
+
+ // empty 'from' ?
+ if(from.isEmpty()) {
+ // allowed if we are querying the server
+ if(!to.isEmpty() && !to.compare(server))
+ return false;
+ }
+ // from ourself?
+ else if(from.compare(local, false)) {
+ // allowed if we are querying ourself or the server
+ if(!to.isEmpty() && !to.compare(local, false) && !to.compare(server))
+ return false;
+ }
+ // from anywhere else?
+ else {
+ if(!from.compare(to))
+ return false;
+ }
+
+ if(!id.isEmpty()) {
+ if(x.attribute("id") != id)
+ return false;
+ }
+
+ if(!xmlns.isEmpty()) {
+ if(queryNS(x) != xmlns)
+ return false;
+ }
+
+ return true;
+}
+
+//---------------------------------------------------------------------------
+// LiveRosterItem
+//---------------------------------------------------------------------------
+LiveRosterItem::LiveRosterItem(const Jid &jid)
+:RosterItem(jid)
+{
+ setFlagForDelete(false);
+}
+
+LiveRosterItem::LiveRosterItem(const RosterItem &i)
+{
+ setRosterItem(i);
+ setFlagForDelete(false);
+}
+
+LiveRosterItem::~LiveRosterItem()
+{
+}
+
+void LiveRosterItem::setRosterItem(const RosterItem &i)
+{
+ setJid(i.jid());
+ setName(i.name());
+ setGroups(i.groups());
+ setSubscription(i.subscription());
+ setAsk(i.ask());
+ setIsPush(i.isPush());
+}
+
+ResourceList & LiveRosterItem::resourceList()
+{
+ return v_resourceList;
+}
+
+ResourceList::Iterator LiveRosterItem::priority()
+{
+ return v_resourceList.priority();
+}
+
+const ResourceList & LiveRosterItem::resourceList() const
+{
+ return v_resourceList;
+}
+
+ResourceList::ConstIterator LiveRosterItem::priority() const
+{
+ return v_resourceList.priority();
+}
+
+bool LiveRosterItem::isAvailable() const
+{
+ if(v_resourceList.count() > 0)
+ return true;
+ return false;
+}
+
+const Status & LiveRosterItem::lastUnavailableStatus() const
+{
+ return v_lastUnavailableStatus;
+}
+
+bool LiveRosterItem::flagForDelete() const
+{
+ return v_flagForDelete;
+}
+
+void LiveRosterItem::setLastUnavailableStatus(const Status &s)
+{
+ v_lastUnavailableStatus = s;
+}
+
+void LiveRosterItem::setFlagForDelete(bool b)
+{
+ v_flagForDelete = b;
+}
+
+//---------------------------------------------------------------------------
+// LiveRoster
+//---------------------------------------------------------------------------
+LiveRoster::LiveRoster()
+:QValueList<LiveRosterItem>()
+{
+}
+
+LiveRoster::~LiveRoster()
+{
+}
+
+void LiveRoster::flagAllForDelete()
+{
+ for(Iterator it = begin(); it != end(); ++it)
+ (*it).setFlagForDelete(true);
+}
+
+LiveRoster::Iterator LiveRoster::find(const Jid &j, bool compareRes)
+{
+ Iterator it;
+ for(it = begin(); it != end(); ++it) {
+ if((*it).jid().compare(j, compareRes))
+ break;
+ }
+ return it;
+}
+
+LiveRoster::ConstIterator LiveRoster::find(const Jid &j, bool compareRes) const
+{
+ ConstIterator it;
+ for(it = begin(); it != end(); ++it) {
+ if((*it).jid().compare(j, compareRes))
+ break;
+ }
+ return it;
+}
+
+}
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-im/types.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-im/types.cpp
new file mode 100644
index 00000000..1e457584
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-im/types.cpp
@@ -0,0 +1,1876 @@
+/*
+ * types.cpp - IM data types
+ * 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"im.h"
+#include "protocol.h"
+#include<qmap.h>
+#include<qapplication.h>
+
+#define NS_XML "http://www.w3.org/XML/1998/namespace"
+
+static QDomElement textTag(QDomDocument *doc, const QString &name, const QString &content)
+{
+ QDomElement tag = doc->createElement(name);
+ QDomText text = doc->createTextNode(content);
+ tag.appendChild(text);
+
+ return tag;
+}
+
+static QString tagContent(const QDomElement &e)
+{
+ // look for some tag content
+ for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomText i = n.toText();
+ if(i.isNull())
+ continue;
+ return i.data();
+ }
+
+ return "";
+}
+
+static QDateTime stamp2TS(const QString &ts)
+{
+ if(ts.length() != 17)
+ return QDateTime();
+
+ int year = ts.mid(0,4).toInt();
+ int month = ts.mid(4,2).toInt();
+ int day = ts.mid(6,2).toInt();
+
+ int hour = ts.mid(9,2).toInt();
+ int min = ts.mid(12,2).toInt();
+ int sec = ts.mid(15,2).toInt();
+
+ QDate xd;
+ xd.setYMD(year, month, day);
+ if(!xd.isValid())
+ return QDateTime();
+
+ QTime xt;
+ xt.setHMS(hour, min, sec);
+ if(!xt.isValid())
+ return QDateTime();
+
+ return QDateTime(xd, xt);
+}
+
+/*static QString TS2stamp(const QDateTime &d)
+{
+ QString str;
+
+ str.sprintf("%04d%02d%02dT%02d:%02d:%02d",
+ d.date().year(),
+ d.date().month(),
+ d.date().day(),
+ d.time().hour(),
+ d.time().minute(),
+ d.time().second());
+
+ return str;
+}*/
+
+namespace XMPP
+{
+
+//----------------------------------------------------------------------------
+// Url
+//----------------------------------------------------------------------------
+class Url::Private
+{
+public:
+ QString url;
+ QString desc;
+};
+
+//! \brief Construct Url object with a given URL and Description.
+//!
+//! This function will construct a Url object.
+//! \param QString - url (default: empty string)
+//! \param QString - description of url (default: empty string)
+//! \sa setUrl() setDesc()
+Url::Url(const QString &url, const QString &desc)
+{
+ d = new Private;
+ d->url = url;
+ d->desc = desc;
+}
+
+//! \brief Construct Url object.
+//!
+//! Overloaded constructor which will constructs a exact copy of the Url object that was passed to the constructor.
+//! \param Url - Url Object
+Url::Url(const Url &from)
+{
+ d = new Private;
+ *this = from;
+}
+
+//! \brief operator overloader needed for d pointer (Internel).
+Url & Url::operator=(const Url &from)
+{
+ *d = *from.d;
+ return *this;
+}
+
+//! \brief destroy Url object.
+Url::~Url()
+{
+ delete d;
+}
+
+//! \brief Get url information.
+//!
+//! Returns url information.
+QString Url::url() const
+{
+ return d->url;
+}
+
+//! \brief Get Description information.
+//!
+//! Returns desction of the URL.
+QString Url::desc() const
+{
+ return d->desc;
+}
+
+//! \brief Set Url information.
+//!
+//! Set url information.
+//! \param url - url string (eg: http://psi.affinix.com/)
+void Url::setUrl(const QString &url)
+{
+ d->url = url;
+}
+
+//! \brief Set Description information.
+//!
+//! Set description of the url.
+//! \param desc - description of url
+void Url::setDesc(const QString &desc)
+{
+ d->desc = desc;
+}
+
+//----------------------------------------------------------------------------
+// Message
+//----------------------------------------------------------------------------
+class Message::Private
+{
+public:
+ Jid to, from;
+ QString id, type, lang;
+
+ StringMap subject, body, xHTMLBody;
+
+ QString thread;
+ Stanza::Error error;
+
+ // extensions
+ QDateTime timeStamp;
+ UrlList urlList;
+ QValueList<MsgEvent> eventList;
+ QString eventId;
+ QString xencrypted, invite;
+
+ bool spooled, wasEncrypted;
+};
+
+//! \brief Constructs Message with given Jid information.
+//!
+//! This function will construct a Message container.
+//! \param to - specify reciver (default: empty string)
+Message::Message(const Jid &to)
+{
+ d = new Private;
+ d->to = to;
+ d->spooled = false;
+ d->wasEncrypted = false;
+ /*d->flag = false;
+ d->spooled = false;
+ d->wasEncrypted = false;
+ d->errorCode = -1;*/
+}
+
+//! \brief Constructs a copy of Message object
+//!
+//! Overloaded constructor which will constructs a exact copy of the Message
+//! object that was passed to the constructor.
+//! \param from - Message object you want to copy
+Message::Message(const Message &from)
+{
+ d = new Private;
+ *this = from;
+}
+
+//! \brief Required for internel use.
+Message & Message::operator=(const Message &from)
+{
+ *d = *from.d;
+ return *this;
+}
+
+//! \brief Destroy Message object.
+Message::~Message()
+{
+ delete d;
+}
+
+//! \brief Return receiver's Jid information.
+Jid Message::to() const
+{
+ return d->to;
+}
+
+//! \brief Return sender's Jid information.
+Jid Message::from() const
+{
+ return d->from;
+}
+
+QString Message::id() const
+{
+ return d->id;
+}
+
+//! \brief Return type information
+QString Message::type() const
+{
+ return d->type;
+}
+
+QString Message::lang() const
+{
+ return d->lang;
+}
+
+//! \brief Return subject information.
+QString Message::subject(const QString &lang) const
+{
+ return d->subject[lang];
+}
+
+//! \brief Return body information.
+//!
+//! This function will return a plain text or the Richtext version if it
+//! it exists.
+//! \param rich - Returns richtext if true and plain text if false. (default: false)
+//! \note Richtext is in Qt's richtext format and not in xhtml.
+QString Message::body(const QString &lang) const
+{
+ return d->body[lang];
+}
+
+QString Message::xHTMLBody(const QString &lang) const
+{
+ return d->xHTMLBody[lang];
+}
+
+QString Message::thread() const
+{
+ return d->thread;
+}
+
+Stanza::Error Message::error() const
+{
+ return d->error;
+}
+
+//! \brief Set receivers information
+//!
+//! \param to - Receivers Jabber id
+void Message::setTo(const Jid &j)
+{
+ d->to = j;
+ //d->flag = false;
+}
+
+void Message::setFrom(const Jid &j)
+{
+ d->from = j;
+ //d->flag = false;
+}
+
+void Message::setId(const QString &s)
+{
+ d->id = s;
+}
+
+//! \brief Set Type of message
+//!
+//! \param type - type of message your going to send
+void Message::setType(const QString &s)
+{
+ d->type = s;
+ //d->flag = false;
+}
+
+void Message::setLang(const QString &s)
+{
+ d->lang = s;
+}
+
+//! \brief Set subject
+//!
+//! \param subject - Subject information
+void Message::setSubject(const QString &s, const QString &lang)
+{
+ d->subject[lang] = s;
+ //d->flag = false;
+}
+
+//! \brief Set body
+//!
+//! \param body - body information
+//! \param rich - set richtext if true and set plaintext if false.
+//! \note Richtext support will be implemented in the future... Sorry.
+void Message::setBody(const QString &s, const QString &lang)
+{
+ d->body[lang] = s;
+}
+
+void Message::setXHTMLBody(const QString &s, const QString &lang, const QString &attr)
+{
+ //ugly but needed if s is not a node but a list of leaf
+
+ QString content = "<body xmlns='" + QString(NS_XHTML) + "' "+attr+" >\n" + s +"\n</body>";
+ d->xHTMLBody[lang] = content;
+}
+
+void Message::setThread(const QString &s)
+{
+ d->thread = s;
+}
+
+void Message::setError(const Stanza::Error &err)
+{
+ d->error = err;
+}
+
+QDateTime Message::timeStamp() const
+{
+ return d->timeStamp;
+}
+
+void Message::setTimeStamp(const QDateTime &ts)
+{
+ d->timeStamp = ts;
+}
+
+//! \brief Return list of urls attached to message.
+UrlList Message::urlList() const
+{
+ return d->urlList;
+}
+
+//! \brief Add Url to the url list.
+//!
+//! \param url - url to append
+void Message::urlAdd(const Url &u)
+{
+ d->urlList += u;
+}
+
+//! \brief clear out the url list.
+void Message::urlsClear()
+{
+ d->urlList.clear();
+}
+
+//! \brief Set urls to send
+//!
+//! \param urlList - list of urls to send
+void Message::setUrlList(const UrlList &list)
+{
+ d->urlList = list;
+}
+
+QString Message::eventId() const
+{
+ return d->eventId;
+}
+
+void Message::setEventId(const QString& id)
+{
+ d->eventId = id;
+}
+
+bool Message::containsEvents() const
+{
+ return !d->eventList.isEmpty();
+}
+
+bool Message::containsEvent(MsgEvent e) const
+{
+ return d->eventList.contains(e);
+}
+
+void Message::addEvent(MsgEvent e)
+{
+ if (!d->eventList.contains(e)) {
+ if (e == CancelEvent || containsEvent(CancelEvent))
+ d->eventList.clear(); // Reset list
+ d->eventList += e;
+ }
+}
+
+QString Message::xencrypted() const
+{
+ return d->xencrypted;
+}
+
+void Message::setXEncrypted(const QString &s)
+{
+ d->xencrypted = s;
+}
+
+QString Message::invite() const
+{
+ return d->invite;
+}
+
+void Message::setInvite(const QString &s)
+{
+ d->invite = s;
+}
+
+bool Message::spooled() const
+{
+ return d->spooled;
+}
+
+void Message::setSpooled(bool b)
+{
+ d->spooled = b;
+}
+
+bool Message::wasEncrypted() const
+{
+ return d->wasEncrypted;
+}
+
+void Message::setWasEncrypted(bool b)
+{
+ d->wasEncrypted = b;
+}
+
+Stanza Message::toStanza(Stream *stream) const
+{
+ Stanza s = stream->createStanza(Stanza::Message, d->to, d->type);
+ if(!d->from.isEmpty())
+ s.setFrom(d->from);
+ if(!d->id.isEmpty())
+ s.setId(d->id);
+ if(!d->lang.isEmpty())
+ s.setLang(d->lang);
+
+ StringMap::ConstIterator it;
+ for(it = d->subject.begin(); it != d->subject.end(); ++it) {
+ const QString &str = it.data();
+ if(!str.isEmpty()) {
+ QDomElement e = s.createTextElement(s.baseNS(), "subject", str);
+ if(!it.key().isEmpty())
+ e.setAttributeNS(NS_XML, "xml:lang", it.key());
+ s.appendChild(e);
+ }
+ }
+ for(it = d->body.begin(); it != d->body.end(); ++it) {
+ const QString &str = it.data();
+ if(!str.isEmpty()) {
+ QDomElement e = s.createTextElement(s.baseNS(), "body", str);
+ if(!it.key().isEmpty())
+ e.setAttributeNS(NS_XML, "xml:lang", it.key());
+ s.appendChild(e);
+ }
+ }
+ if ( !d->xHTMLBody.isEmpty()) {
+ QDomElement parent = s.createElement(s.xhtmlImNS(), "html");
+ for(it = d->xHTMLBody.begin(); it != d->xHTMLBody.end(); ++it) {
+ const QString &str = it.data();
+ if(!str.isEmpty()) {
+ QDomElement child = s.createXHTMLElement(str);
+ if(!it.key().isEmpty())
+ child.setAttributeNS(NS_XML, "xml:lang", it.key());
+ parent.appendChild(child);
+ }
+ }
+ s.appendChild(parent);
+ }
+ if(d->type == "error")
+ s.setError(d->error);
+
+ // timestamp
+ /*if(!d->timeStamp.isNull()) {
+ QDomElement e = s.createElement("jabber:x:delay", "x");
+ e.setAttribute("stamp", TS2stamp(d->timeStamp));
+ s.appendChild(e);
+ }*/
+
+ // urls
+ for(QValueList<Url>::ConstIterator uit = d->urlList.begin(); uit != d->urlList.end(); ++uit) {
+ QDomElement x = s.createElement("jabber:x:oob", "x");
+ x.appendChild(s.createTextElement("jabber:x:oob", "url", (*uit).url()));
+ if(!(*uit).desc().isEmpty())
+ x.appendChild(s.createTextElement("jabber:x:oob", "desc", (*uit).desc()));
+ s.appendChild(x);
+ }
+
+ // events
+ if (!d->eventList.isEmpty()) {
+ QDomElement x = s.createElement("jabber:x:event", "x");
+
+ if (d->body.isEmpty()) {
+ if (d->eventId.isEmpty())
+ x.appendChild(s.createElement("jabber:x:event","id"));
+ else
+ x.appendChild(s.createTextElement("jabber:x:event","id",d->eventId));
+ }
+ else if (d->type=="chat" || d->type=="groupchat")
+ s.appendChild( s.createElement(NS_CHATSTATES , "active" ) );
+
+ bool need_x_event=false;
+ for(QValueList<MsgEvent>::ConstIterator ev = d->eventList.begin(); ev != d->eventList.end(); ++ev) {
+ switch (*ev) {
+ case OfflineEvent:
+ x.appendChild(s.createElement("jabber:x:event", "offline"));
+ need_x_event=true;
+ break;
+ case DeliveredEvent:
+ x.appendChild(s.createElement("jabber:x:event", "delivered"));
+ need_x_event=true;
+ break;
+ case DisplayedEvent:
+ x.appendChild(s.createElement("jabber:x:event", "displayed"));
+ need_x_event=true;
+ break;
+ case ComposingEvent:
+ x.appendChild(s.createElement("jabber:x:event", "composing"));
+ need_x_event=true;
+ if (d->body.isEmpty() && (d->type=="chat" || d->type=="groupchat") )
+ s.appendChild( s.createElement(NS_CHATSTATES , "composing" ) );
+ break;
+ case CancelEvent:
+ need_x_event=true;
+ if (d->body.isEmpty() && (d->type=="chat" || d->type=="groupchat") )
+ s.appendChild( s.createElement(NS_CHATSTATES , "paused" ) );
+ break;
+ case InactiveEvent:
+ if (d->body.isEmpty() && (d->type=="chat" || d->type=="groupchat") )
+ s.appendChild( s.createElement(NS_CHATSTATES , "inactive" ) );
+ break;
+ case GoneEvent:
+ if (d->body.isEmpty() && (d->type=="chat" || d->type=="groupchat") )
+ s.appendChild( s.createElement(NS_CHATSTATES , "gone" ) );
+ break;
+ }
+ }
+ if(need_x_event) //we don't need to have the (empty) x:event element if this is only <gone> or <inactive>
+ s.appendChild(x);
+ }
+
+
+ // xencrypted
+ if(!d->xencrypted.isEmpty())
+ s.appendChild(s.createTextElement("jabber:x:encrypted", "x", d->xencrypted));
+
+ // invite
+ if(!d->invite.isEmpty()) {
+ QDomElement e = s.createElement("jabber:x:conference", "x");
+ e.setAttribute("jid", d->invite);
+ s.appendChild(e);
+ }
+
+ return s;
+}
+
+bool Message::fromStanza(const Stanza &s, int timeZoneOffset)
+{
+ if(s.kind() != Stanza::Message)
+ return false;
+
+ setTo(s.to());
+ setFrom(s.from());
+ setId(s.id());
+ setType(s.type());
+ setLang(s.lang());
+
+ d->subject.clear();
+ d->body.clear();
+ d->thread = QString();
+ d->eventList.clear();
+
+ QDomElement root = s.element();
+
+ QDomNodeList nl = root.childNodes();
+ uint n;
+ for(n = 0; n < nl.count(); ++n) {
+ QDomNode i = nl.item(n);
+ if(i.isElement()) {
+ QDomElement e = i.toElement();
+ if(e.namespaceURI() == s.baseNS()) {
+ if(e.tagName() == "subject") {
+ QString lang = e.attributeNS(NS_XML, "lang", "");
+ d->subject[lang] = e.text();
+ }
+ else if(e.tagName() == "body") {
+ QString lang = e.attributeNS(NS_XML, "lang", "");
+ d->body[lang] = e.text();
+ }
+ else if(e.tagName() == "thread")
+ d->thread = e.text();
+ }
+ else if (e.namespaceURI() == s.xhtmlImNS()) {
+ if (e.tagName() == "html") {
+ QDomNodeList htmlNL= e.childNodes();
+ for (unsigned int x = 0; x < htmlNL.count(); x++) {
+ QDomElement i = htmlNL.item(x).toElement();
+
+ if (i.tagName() == "body") {
+ QDomDocument RichText;
+ QString lang = i.attributeNS(NS_XML, "lang", "");
+ RichText.appendChild(i);
+ d-> xHTMLBody[lang] = RichText.toString();
+ }
+ }
+ }
+ }
+ else if (e.namespaceURI() == NS_CHATSTATES)
+ {
+ if(e.tagName() == "active")
+ {
+ //like in JEP-0022 we let the client know that we can receive ComposingEvent
+ // (we can do that according to §4.6 of the JEP-0085)
+ d->eventList += ComposingEvent;
+ d->eventList += InactiveEvent;
+ d->eventList += GoneEvent;
+ }
+ else if (e.tagName() == "composing")
+ {
+ d->eventList += ComposingEvent;
+ }
+ else if (e.tagName() == "paused")
+ {
+ d->eventList += CancelEvent;
+ }
+ else if (e.tagName() == "inactive")
+ {
+ d->eventList += InactiveEvent;
+ }
+ else if (e.tagName() == "gone")
+ {
+ d->eventList += GoneEvent;
+ }
+ }
+ else {
+ //printf("extension element: [%s]\n", e.tagName().latin1());
+ }
+ }
+ }
+
+ if(s.type() == "error")
+ d->error = s.error();
+
+ // timestamp
+ QDomElement t = root.elementsByTagNameNS("jabber:x:delay", "x").item(0).toElement();
+ if(!t.isNull()) {
+ d->timeStamp = stamp2TS(t.attribute("stamp"));
+ d->timeStamp = d->timeStamp.addSecs(timeZoneOffset * 3600);
+ d->spooled = true;
+ }
+ else {
+ d->timeStamp = QDateTime::currentDateTime();
+ d->spooled = false;
+ }
+
+ // urls
+ d->urlList.clear();
+ nl = root.elementsByTagNameNS("jabber:x:oob", "x");
+ for(n = 0; n < nl.count(); ++n) {
+ QDomElement t = nl.item(n).toElement();
+ Url u;
+ u.setUrl(t.elementsByTagName("url").item(0).toElement().text());
+ u.setDesc(t.elementsByTagName("desc").item(0).toElement().text());
+ d->urlList += u;
+ }
+
+ // events
+ nl = root.elementsByTagNameNS("jabber:x:event", "x");
+ if (nl.count()) {
+ nl = nl.item(0).childNodes();
+ for(n = 0; n < nl.count(); ++n) {
+ QString evtag = nl.item(n).toElement().tagName();
+ if (evtag == "id") {
+ d->eventId = nl.item(n).toElement().text();
+ }
+ else if (evtag == "displayed")
+ d->eventList += DisplayedEvent;
+ else if (evtag == "composing")
+ d->eventList += ComposingEvent;
+ else if (evtag == "delivered")
+ d->eventList += DeliveredEvent;
+ else if (evtag == "offline")
+ d->eventList += OfflineEvent;
+ }
+ if (d->eventList.isEmpty())
+ d->eventList += CancelEvent;
+ }
+
+ // xencrypted
+ t = root.elementsByTagNameNS("jabber:x:encrypted", "x").item(0).toElement();
+ if(!t.isNull())
+ d->xencrypted = t.text();
+ else
+ d->xencrypted = QString();
+
+ // invite
+ t = root.elementsByTagNameNS("jabber:x:conference", "x").item(0).toElement();
+ if(!t.isNull())
+ d->invite = t.attribute("jid");
+ else
+ d->invite = QString();
+
+ return true;
+}
+
+//---------------------------------------------------------------------------
+// Subscription
+//---------------------------------------------------------------------------
+Subscription::Subscription(SubType type)
+{
+ value = type;
+}
+
+int Subscription::type() const
+{
+ return value;
+}
+
+QString Subscription::toString() const
+{
+ switch(value) {
+ case Remove:
+ return "remove";
+ case Both:
+ return "both";
+ case From:
+ return "from";
+ case To:
+ return "to";
+ case None:
+ default:
+ return "none";
+ }
+}
+
+bool Subscription::fromString(const QString &s)
+{
+ if(s == "remove")
+ value = Remove;
+ else if(s == "both")
+ value = Both;
+ else if(s == "from")
+ value = From;
+ else if(s == "to")
+ value = To;
+ else if(s == "none")
+ value = None;
+ else
+ return false;
+
+ return true;
+}
+
+
+//---------------------------------------------------------------------------
+// Status
+//---------------------------------------------------------------------------
+Status::Status(const QString &show, const QString &status, int priority, bool available)
+{
+ v_isAvailable = available;
+ v_show = show;
+ v_status = status;
+ v_priority = priority;
+ v_timeStamp = QDateTime::currentDateTime();
+ v_isInvisible = false;
+ ecode = -1;
+}
+
+Status::~Status()
+{
+}
+
+bool Status::hasError() const
+{
+ return (ecode != -1);
+}
+
+void Status::setError(int code, const QString &str)
+{
+ ecode = code;
+ estr = str;
+}
+
+void Status::setIsAvailable(bool available)
+{
+ v_isAvailable = available;
+}
+
+void Status::setIsInvisible(bool invisible)
+{
+ v_isInvisible = invisible;
+}
+
+void Status::setPriority(int x)
+{
+ v_priority = x;
+}
+
+void Status::setShow(const QString & _show)
+{
+ v_show = _show;
+}
+
+void Status::setStatus(const QString & _status)
+{
+ v_status = _status;
+}
+
+void Status::setTimeStamp(const QDateTime & _timestamp)
+{
+ v_timeStamp = _timestamp;
+}
+
+void Status::setKeyID(const QString &key)
+{
+ v_key = key;
+}
+
+void Status::setXSigned(const QString &s)
+{
+ v_xsigned = s;
+}
+
+void Status::setSongTitle(const QString & _songtitle)
+{
+ v_songTitle = _songtitle;
+}
+
+void Status::setCapsNode(const QString & _capsNode)
+{
+ v_capsNode = _capsNode;
+}
+
+void Status::setCapsVersion(const QString & _capsVersion)
+{
+ v_capsVersion = _capsVersion;
+}
+
+void Status::setCapsExt(const QString & _capsExt)
+{
+ v_capsExt = _capsExt;
+}
+
+bool Status::isAvailable() const
+{
+ return v_isAvailable;
+}
+
+bool Status::isAway() const
+{
+ if(v_show == "away" || v_show == "xa" || v_show == "dnd")
+ return true;
+
+ return false;
+}
+
+bool Status::isInvisible() const
+{
+ return v_isInvisible;
+}
+
+int Status::priority() const
+{
+ return v_priority;
+}
+
+const QString & Status::show() const
+{
+ return v_show;
+}
+const QString & Status::status() const
+{
+ return v_status;
+}
+
+QDateTime Status::timeStamp() const
+{
+ return v_timeStamp;
+}
+
+const QString & Status::keyID() const
+{
+ return v_key;
+}
+
+const QString & Status::xsigned() const
+{
+ return v_xsigned;
+}
+
+const QString & Status::songTitle() const
+{
+ return v_songTitle;
+}
+
+const QString & Status::capsNode() const
+{
+ return v_capsNode;
+}
+
+const QString & Status::capsVersion() const
+{
+ return v_capsVersion;
+}
+
+const QString & Status::capsExt() const
+{
+ return v_capsExt;
+}
+
+int Status::errorCode() const
+{
+ return ecode;
+}
+
+const QString & Status::errorString() const
+{
+ return estr;
+}
+
+
+//---------------------------------------------------------------------------
+// Resource
+//---------------------------------------------------------------------------
+Resource::Resource(const QString &name, const Status &stat)
+{
+ v_name = name;
+ v_status = stat;
+}
+
+Resource::~Resource()
+{
+}
+
+const QString & Resource::name() const
+{
+ return v_name;
+}
+
+int Resource::priority() const
+{
+ return v_status.priority();
+}
+
+const Status & Resource::status() const
+{
+ return v_status;
+}
+
+void Resource::setName(const QString & _name)
+{
+ v_name = _name;
+}
+
+void Resource::setStatus(const Status & _status)
+{
+ v_status = _status;
+}
+
+
+//---------------------------------------------------------------------------
+// ResourceList
+//---------------------------------------------------------------------------
+ResourceList::ResourceList()
+:QValueList<Resource>()
+{
+}
+
+ResourceList::~ResourceList()
+{
+}
+
+ResourceList::Iterator ResourceList::find(const QString & _find)
+{
+ for(ResourceList::Iterator it = begin(); it != end(); ++it) {
+ if((*it).name() == _find)
+ return it;
+ }
+
+ return end();
+}
+
+ResourceList::Iterator ResourceList::priority()
+{
+ ResourceList::Iterator highest = end();
+
+ for(ResourceList::Iterator it = begin(); it != end(); ++it) {
+ if(highest == end() || (*it).priority() > (*highest).priority())
+ highest = it;
+ }
+
+ return highest;
+}
+
+ResourceList::ConstIterator ResourceList::find(const QString & _find) const
+{
+ for(ResourceList::ConstIterator it = begin(); it != end(); ++it) {
+ if((*it).name() == _find)
+ return it;
+ }
+
+ return end();
+}
+
+ResourceList::ConstIterator ResourceList::priority() const
+{
+ ResourceList::ConstIterator highest = end();
+
+ for(ResourceList::ConstIterator it = begin(); it != end(); ++it) {
+ if(highest == end() || (*it).priority() > (*highest).priority())
+ highest = it;
+ }
+
+ return highest;
+}
+
+
+//---------------------------------------------------------------------------
+// RosterItem
+//---------------------------------------------------------------------------
+RosterItem::RosterItem(const Jid &_jid)
+{
+ v_jid = _jid;
+}
+
+RosterItem::~RosterItem()
+{
+}
+
+const Jid & RosterItem::jid() const
+{
+ return v_jid;
+}
+
+const QString & RosterItem::name() const
+{
+ return v_name;
+}
+
+const QStringList & RosterItem::groups() const
+{
+ return v_groups;
+}
+
+const Subscription & RosterItem::subscription() const
+{
+ return v_subscription;
+}
+
+const QString & RosterItem::ask() const
+{
+ return v_ask;
+}
+
+bool RosterItem::isPush() const
+{
+ return v_push;
+}
+
+bool RosterItem::inGroup(const QString &g) const
+{
+ for(QStringList::ConstIterator it = v_groups.begin(); it != v_groups.end(); ++it) {
+ if(*it == g)
+ return true;
+ }
+ return false;
+}
+
+void RosterItem::setJid(const Jid &_jid)
+{
+ v_jid = _jid;
+}
+
+void RosterItem::setName(const QString &_name)
+{
+ v_name = _name;
+}
+
+void RosterItem::setGroups(const QStringList &_groups)
+{
+ v_groups = _groups;
+}
+
+void RosterItem::setSubscription(const Subscription &type)
+{
+ v_subscription = type;
+}
+
+void RosterItem::setAsk(const QString &_ask)
+{
+ v_ask = _ask;
+}
+
+void RosterItem::setIsPush(bool b)
+{
+ v_push = b;
+}
+
+bool RosterItem::addGroup(const QString &g)
+{
+ if(inGroup(g))
+ return false;
+
+ v_groups += g;
+ return true;
+}
+
+bool RosterItem::removeGroup(const QString &g)
+{
+ for(QStringList::Iterator it = v_groups.begin(); it != v_groups.end(); ++it) {
+ if(*it == g) {
+ v_groups.remove(it);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+QDomElement RosterItem::toXml(QDomDocument *doc) const
+{
+ QDomElement item = doc->createElement("item");
+ item.setAttribute("jid", v_jid.full());
+ item.setAttribute("name", v_name);
+ item.setAttribute("subscription", v_subscription.toString());
+ if(!v_ask.isEmpty())
+ item.setAttribute("ask", v_ask);
+ for(QStringList::ConstIterator it = v_groups.begin(); it != v_groups.end(); ++it)
+ item.appendChild(textTag(doc, "group", *it));
+
+ return item;
+}
+
+bool RosterItem::fromXml(const QDomElement &item)
+{
+ if(item.tagName() != "item")
+ return false;
+ Jid j(item.attribute("jid"));
+ if(!j.isValid())
+ return false;
+ QString na = item.attribute("name");
+ Subscription s;
+ if(!s.fromString(item.attribute("subscription")) )
+ return false;
+ QStringList g;
+ for(QDomNode n = item.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+ if(i.tagName() == "group")
+ g += tagContent(i);
+ }
+ QString a = item.attribute("ask");
+
+ v_jid = j;
+ v_name = na;
+ v_subscription = s;
+ v_groups = g;
+ v_ask = a;
+
+ return true;
+}
+
+
+//---------------------------------------------------------------------------
+// Roster
+//---------------------------------------------------------------------------
+Roster::Roster()
+:QValueList<RosterItem>()
+{
+}
+
+Roster::~Roster()
+{
+}
+
+Roster::Iterator Roster::find(const Jid &j)
+{
+ for(Roster::Iterator it = begin(); it != end(); ++it) {
+ if((*it).jid().compare(j))
+ return it;
+ }
+
+ return end();
+}
+
+Roster::ConstIterator Roster::find(const Jid &j) const
+{
+ for(Roster::ConstIterator it = begin(); it != end(); ++it) {
+ if((*it).jid().compare(j))
+ return it;
+ }
+
+ return end();
+}
+
+
+//---------------------------------------------------------------------------
+// FormField
+//---------------------------------------------------------------------------
+FormField::FormField(const QString &type, const QString &value)
+{
+ v_type = misc;
+ if(!type.isEmpty()) {
+ int x = tagNameToType(type);
+ if(x != -1)
+ v_type = x;
+ }
+ v_value = value;
+}
+
+FormField::~FormField()
+{
+}
+
+int FormField::type() const
+{
+ return v_type;
+}
+
+QString FormField::realName() const
+{
+ return typeToTagName(v_type);
+}
+
+QString FormField::fieldName() const
+{
+ switch(v_type) {
+ case username: return QObject::tr("Username");
+ case nick: return QObject::tr("Nickname");
+ case password: return QObject::tr("Password");
+ case name: return QObject::tr("Name");
+ case first: return QObject::tr("First Name");
+ case last: return QObject::tr("Last Name");
+ case email: return QObject::tr("E-mail");
+ case address: return QObject::tr("Address");
+ case city: return QObject::tr("City");
+ case state: return QObject::tr("State");
+ case zip: return QObject::tr("Zipcode");
+ case phone: return QObject::tr("Phone");
+ case url: return QObject::tr("URL");
+ case date: return QObject::tr("Date");
+ case misc: return QObject::tr("Misc");
+ default: return "";
+ };
+}
+
+bool FormField::isSecret() const
+{
+ return (type() == password);
+}
+
+const QString & FormField::value() const
+{
+ return v_value;
+}
+
+void FormField::setType(int x)
+{
+ v_type = x;
+}
+
+bool FormField::setType(const QString &in)
+{
+ int x = tagNameToType(in);
+ if(x == -1)
+ return false;
+
+ v_type = x;
+ return true;
+}
+
+void FormField::setValue(const QString &in)
+{
+ v_value = in;
+}
+
+int FormField::tagNameToType(const QString &in) const
+{
+ if(!in.compare("username")) return username;
+ if(!in.compare("nick")) return nick;
+ if(!in.compare("password")) return password;
+ if(!in.compare("name")) return name;
+ if(!in.compare("first")) return first;
+ if(!in.compare("last")) return last;
+ if(!in.compare("email")) return email;
+ if(!in.compare("address")) return address;
+ if(!in.compare("city")) return city;
+ if(!in.compare("state")) return state;
+ if(!in.compare("zip")) return zip;
+ if(!in.compare("phone")) return phone;
+ if(!in.compare("url")) return url;
+ if(!in.compare("date")) return date;
+ if(!in.compare("misc")) return misc;
+
+ return -1;
+}
+
+QString FormField::typeToTagName(int type) const
+{
+ switch(type) {
+ case username: return "username";
+ case nick: return "nick";
+ case password: return "password";
+ case name: return "name";
+ case first: return "first";
+ case last: return "last";
+ case email: return "email";
+ case address: return "address";
+ case city: return "city";
+ case state: return "state";
+ case zip: return "zipcode";
+ case phone: return "phone";
+ case url: return "url";
+ case date: return "date";
+ case misc: return "misc";
+ default: return "";
+ };
+}
+
+
+//---------------------------------------------------------------------------
+// Form
+//---------------------------------------------------------------------------
+Form::Form(const Jid &j)
+:QValueList<FormField>()
+{
+ setJid(j);
+}
+
+Form::~Form()
+{
+}
+
+Jid Form::jid() const
+{
+ return v_jid;
+}
+
+QString Form::instructions() const
+{
+ return v_instructions;
+}
+
+QString Form::key() const
+{
+ return v_key;
+}
+
+void Form::setJid(const Jid &j)
+{
+ v_jid = j;
+}
+
+void Form::setInstructions(const QString &s)
+{
+ v_instructions = s;
+}
+
+void Form::setKey(const QString &s)
+{
+ v_key = s;
+}
+
+
+//---------------------------------------------------------------------------
+// SearchResult
+//---------------------------------------------------------------------------
+SearchResult::SearchResult(const Jid &jid)
+{
+ setJid(jid);
+}
+
+SearchResult::~SearchResult()
+{
+}
+
+const Jid & SearchResult::jid() const
+{
+ return v_jid;
+}
+
+const QString & SearchResult::nick() const
+{
+ return v_nick;
+}
+
+const QString & SearchResult::first() const
+{
+ return v_first;
+}
+
+const QString & SearchResult::last() const
+{
+ return v_last;
+}
+
+const QString & SearchResult::email() const
+{
+ return v_email;
+}
+
+void SearchResult::setJid(const Jid &jid)
+{
+ v_jid = jid;
+}
+
+void SearchResult::setNick(const QString &nick)
+{
+ v_nick = nick;
+}
+
+void SearchResult::setFirst(const QString &first)
+{
+ v_first = first;
+}
+
+void SearchResult::setLast(const QString &last)
+{
+ v_last = last;
+}
+
+void SearchResult::setEmail(const QString &email)
+{
+ v_email = email;
+}
+
+//---------------------------------------------------------------------------
+// Features
+//---------------------------------------------------------------------------
+
+Features::Features()
+{
+}
+
+Features::Features(const QStringList &l)
+{
+ setList(l);
+}
+
+Features::Features(const QString &str)
+{
+ QStringList l;
+ l << str;
+
+ setList(l);
+}
+
+Features::~Features()
+{
+}
+
+QStringList Features::list() const
+{
+ return _list;
+}
+
+void Features::setList(const QStringList &l)
+{
+ _list = l;
+}
+
+bool Features::test(const QStringList &ns) const
+{
+ QStringList::ConstIterator it = ns.begin();
+ for ( ; it != ns.end(); ++it)
+ if ( _list.find( *it ) != _list.end() )
+ return true;
+
+ return false;
+}
+
+#define FID_REGISTER "jabber:iq:register"
+bool Features::canRegister() const
+{
+ QStringList ns;
+ ns << FID_REGISTER;
+
+ return test(ns);
+}
+
+#define FID_SEARCH "jabber:iq:search"
+bool Features::canSearch() const
+{
+ QStringList ns;
+ ns << FID_SEARCH;
+
+ return test(ns);
+}
+
+#define FID_XHTML "http://jabber.org/protocol/xhtml-im"
+bool Features::canXHTML() const
+{
+ QStringList ns;
+
+ ns << FID_XHTML;
+
+ return test(ns);
+}
+
+#define FID_GROUPCHAT "jabber:iq:conference"
+bool Features::canGroupchat() const
+{
+ QStringList ns;
+ ns << "http://jabber.org/protocol/muc";
+ ns << FID_GROUPCHAT;
+
+ return test(ns);
+}
+
+#define FID_VOICE "http://www.google.com/xmpp/protocol/voice/v1"
+bool Features::canVoice() const
+{
+ QStringList ns;
+ ns << FID_VOICE;
+
+ return test(ns);
+}
+
+#define FID_GATEWAY "jabber:iq:gateway"
+bool Features::isGateway() const
+{
+ QStringList ns;
+ ns << FID_GATEWAY;
+
+ return test(ns);
+}
+
+#define FID_DISCO "http://jabber.org/protocol/disco"
+bool Features::canDisco() const
+{
+ QStringList ns;
+ ns << FID_DISCO;
+ ns << "http://jabber.org/protocol/disco#info";
+ ns << "http://jabber.org/protocol/disco#items";
+
+ return test(ns);
+}
+
+#define FID_VCARD "vcard-temp"
+bool Features::haveVCard() const
+{
+ QStringList ns;
+ ns << FID_VCARD;
+
+ return test(ns);
+}
+
+// custom Psi acitons
+#define FID_ADD "psi:add"
+
+class Features::FeatureName : public QObject
+{
+ Q_OBJECT
+public:
+ FeatureName()
+ : QObject(qApp)
+ {
+ id2s[FID_Invalid] = tr("ERROR: Incorrect usage of Features class");
+ id2s[FID_None] = tr("None");
+ id2s[FID_Register] = tr("Register");
+ id2s[FID_Search] = tr("Search");
+ id2s[FID_Groupchat] = tr("Groupchat");
+ id2s[FID_Gateway] = tr("Gateway");
+ id2s[FID_Disco] = tr("Service Discovery");
+ id2s[FID_VCard] = tr("VCard");
+
+ // custom Psi actions
+ id2s[FID_Add] = tr("Add to roster");
+
+ // compute reverse map
+ //QMap<QString, long>::Iterator it = id2s.begin();
+ //for ( ; it != id2s.end(); ++it)
+ // s2id[it.data()] = it.key();
+
+ id2f[FID_Register] = FID_REGISTER;
+ id2f[FID_Search] = FID_SEARCH;
+ id2f[FID_Groupchat] = FID_GROUPCHAT;
+ id2f[FID_Gateway] = FID_GATEWAY;
+ id2f[FID_Disco] = FID_DISCO;
+ id2f[FID_VCard] = FID_VCARD;
+
+ // custom Psi actions
+ id2f[FID_Add] = FID_ADD;
+ }
+
+ //QMap<QString, long> s2id;
+ QMap<long, QString> id2s;
+ QMap<long, QString> id2f;
+};
+
+static Features::FeatureName *featureName = 0;
+
+long Features::id() const
+{
+ if ( _list.count() > 1 )
+ return FID_Invalid;
+ else if ( canRegister() )
+ return FID_Register;
+ else if ( canSearch() )
+ return FID_Search;
+ else if ( canGroupchat() )
+ return FID_Groupchat;
+ else if ( isGateway() )
+ return FID_Gateway;
+ else if ( canDisco() )
+ return FID_Disco;
+ else if ( haveVCard() )
+ return FID_VCard;
+ else if ( test(FID_ADD) )
+ return FID_Add;
+
+ return FID_None;
+}
+
+long Features::id(const QString &feature)
+{
+ Features f(feature);
+ return f.id();
+}
+
+QString Features::feature(long id)
+{
+ if ( !featureName )
+ featureName = new FeatureName();
+
+ return featureName->id2f[id];
+}
+
+QString Features::name(long id)
+{
+ if ( !featureName )
+ featureName = new FeatureName();
+
+ return featureName->id2s[id];
+}
+
+QString Features::name() const
+{
+ return name(id());
+}
+
+QString Features::name(const QString &feature)
+{
+ Features f(feature);
+ return f.name(f.id());
+}
+
+//---------------------------------------------------------------------------
+// DiscoItem
+//---------------------------------------------------------------------------
+class DiscoItem::Private
+{
+public:
+ Private()
+ {
+ action = None;
+ }
+
+ Jid jid;
+ QString name;
+ QString node;
+ Action action;
+
+ Features features;
+ Identities identities;
+};
+
+DiscoItem::DiscoItem()
+{
+ d = new Private;
+}
+
+DiscoItem::DiscoItem(const DiscoItem &from)
+{
+ d = new Private;
+ *this = from;
+}
+
+DiscoItem & DiscoItem::operator= (const DiscoItem &from)
+{
+ d->jid = from.d->jid;
+ d->name = from.d->name;
+ d->node = from.d->node;
+ d->action = from.d->action;
+ d->features = from.d->features;
+ d->identities = from.d->identities;
+
+ return *this;
+}
+
+DiscoItem::~DiscoItem()
+{
+ delete d;
+}
+
+AgentItem DiscoItem::toAgentItem() const
+{
+ AgentItem ai;
+
+ ai.setJid( jid() );
+ ai.setName( name() );
+
+ Identity id;
+ if ( !identities().isEmpty() )
+ id = identities().first();
+
+ ai.setCategory( id.category );
+ ai.setType( id.type );
+
+ ai.setFeatures( d->features );
+
+ return ai;
+}
+
+void DiscoItem::fromAgentItem(const AgentItem &ai)
+{
+ setJid( ai.jid() );
+ setName( ai.name() );
+
+ Identity id;
+ id.category = ai.category();
+ id.type = ai.type();
+ id.name = ai.name();
+
+ Identities idList;
+ idList << id;
+
+ setIdentities( idList );
+
+ setFeatures( ai.features() );
+}
+
+const Jid &DiscoItem::jid() const
+{
+ return d->jid;
+}
+
+void DiscoItem::setJid(const Jid &j)
+{
+ d->jid = j;
+}
+
+const QString &DiscoItem::name() const
+{
+ return d->name;
+}
+
+void DiscoItem::setName(const QString &n)
+{
+ d->name = n;
+}
+
+const QString &DiscoItem::node() const
+{
+ return d->node;
+}
+
+void DiscoItem::setNode(const QString &n)
+{
+ d->node = n;
+}
+
+DiscoItem::Action DiscoItem::action() const
+{
+ return d->action;
+}
+
+void DiscoItem::setAction(Action a)
+{
+ d->action = a;
+}
+
+const Features &DiscoItem::features() const
+{
+ return d->features;
+}
+
+void DiscoItem::setFeatures(const Features &f)
+{
+ d->features = f;
+}
+
+const DiscoItem::Identities &DiscoItem::identities() const
+{
+ return d->identities;
+}
+
+void DiscoItem::setIdentities(const Identities &i)
+{
+ d->identities = i;
+
+ if ( name().isEmpty() && i.count() )
+ setName( i.first().name );
+}
+
+
+DiscoItem::Action DiscoItem::string2action(QString s)
+{
+ Action a;
+
+ if ( s == "update" )
+ a = Update;
+ else if ( s == "remove" )
+ a = Remove;
+ else
+ a = None;
+
+ return a;
+}
+
+QString DiscoItem::action2string(Action a)
+{
+ QString s;
+
+ if ( a == Update )
+ s = "update";
+ else if ( a == Remove )
+ s = "remove";
+ else
+ s = QString::null;
+
+ return s;
+}
+
+}
+
+#include"types.moc"
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_tasks.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_tasks.cpp
new file mode 100644
index 00000000..ffd7e6ae
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_tasks.cpp
@@ -0,0 +1,2120 @@
+/*
+ * tasks.cpp - basic tasks
+ * Copyright (C) 2001, 2002 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include"xmpp_tasks.h"
+
+#include"base64.h"
+//#include"sha1.h"
+#include"xmpp_xmlcommon.h"
+//#include"xmpp_stream.h"
+//#include"xmpp_types.h"
+#include"xmpp_vcard.h"
+
+#include<qregexp.h>
+#include<qvaluelist.h>
+
+using namespace XMPP;
+
+
+static QString lineEncode(QString str)
+{
+ str.replace(QRegExp("\\\\"), "\\\\"); // backslash to double-backslash
+ str.replace(QRegExp("\\|"), "\\p"); // pipe to \p
+ str.replace(QRegExp("\n"), "\\n"); // newline to \n
+ return str;
+}
+
+static QString lineDecode(const QString &str)
+{
+ QString ret;
+
+ for(unsigned int n = 0; n < str.length(); ++n) {
+ if(str.at(n) == '\\') {
+ ++n;
+ if(n >= str.length())
+ break;
+
+ if(str.at(n) == 'n')
+ ret.append('\n');
+ if(str.at(n) == 'p')
+ ret.append('|');
+ if(str.at(n) == '\\')
+ ret.append('\\');
+ }
+ else {
+ ret.append(str.at(n));
+ }
+ }
+
+ return ret;
+}
+
+static Roster xmlReadRoster(const QDomElement &q, bool push)
+{
+ Roster r;
+
+ for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+
+ if(i.tagName() == "item") {
+ RosterItem item;
+ item.fromXml(i);
+
+ if(push)
+ item.setIsPush(true);
+
+ r += item;
+ }
+ }
+
+ return r;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_Register
+//----------------------------------------------------------------------------
+class JT_Register::Private
+{
+public:
+ Private() {}
+
+ Form form;
+ Jid jid;
+ int type;
+};
+
+JT_Register::JT_Register(Task *parent)
+:Task(parent)
+{
+ d = new Private;
+ d->type = -1;
+}
+
+JT_Register::~JT_Register()
+{
+ delete d;
+}
+
+void JT_Register::reg(const QString &user, const QString &pass)
+{
+ d->type = 0;
+ to = client()->host();
+ iq = createIQ(doc(), "set", to.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:register");
+ iq.appendChild(query);
+ query.appendChild(textTag(doc(), "username", user));
+ query.appendChild(textTag(doc(), "password", pass));
+}
+
+void JT_Register::changepw(const QString &pass)
+{
+ d->type = 1;
+ to = client()->host();
+ iq = createIQ(doc(), "set", to.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:register");
+ iq.appendChild(query);
+ query.appendChild(textTag(doc(), "username", client()->user()));
+ query.appendChild(textTag(doc(), "password", pass));
+}
+
+void JT_Register::unreg(const Jid &j)
+{
+ d->type = 2;
+ to = j.isEmpty() ? client()->host() : j.full();
+ iq = createIQ(doc(), "set", to.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:register");
+ iq.appendChild(query);
+
+ // this may be useful
+ if(!d->form.key().isEmpty())
+ query.appendChild(textTag(doc(), "key", d->form.key()));
+
+ query.appendChild(doc()->createElement("remove"));
+}
+
+void JT_Register::getForm(const Jid &j)
+{
+ d->type = 3;
+ to = j;
+ iq = createIQ(doc(), "get", to.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:register");
+ iq.appendChild(query);
+}
+
+void JT_Register::setForm(const Form &form)
+{
+ d->type = 4;
+ to = form.jid();
+ iq = createIQ(doc(), "set", to.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:register");
+ iq.appendChild(query);
+
+ // key?
+ if(!form.key().isEmpty())
+ query.appendChild(textTag(doc(), "key", form.key()));
+
+ // fields
+ for(Form::ConstIterator it = form.begin(); it != form.end(); ++it) {
+ const FormField &f = *it;
+ query.appendChild(textTag(doc(), f.realName(), f.value()));
+ }
+}
+
+const Form & JT_Register::form() const
+{
+ return d->form;
+}
+
+void JT_Register::onGo()
+{
+ send(iq);
+}
+
+bool JT_Register::take(const QDomElement &x)
+{
+ if(!iqVerify(x, to, id()))
+ return false;
+
+ Jid from(x.attribute("from"));
+ if(x.attribute("type") == "result") {
+ if(d->type == 3) {
+ d->form.clear();
+ d->form.setJid(from);
+
+ QDomElement q = queryTag(x);
+ for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+
+ if(i.tagName() == "instructions")
+ d->form.setInstructions(tagContent(i));
+ else if(i.tagName() == "key")
+ d->form.setKey(tagContent(i));
+ else {
+ FormField f;
+ if(f.setType(i.tagName())) {
+ f.setValue(tagContent(i));
+ d->form += f;
+ }
+ }
+ }
+ }
+
+ setSuccess();
+ }
+ else
+ setError(x);
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// JT_UnRegister
+//----------------------------------------------------------------------------
+class JT_UnRegister::Private
+{
+public:
+ Private() { }
+
+ Jid j;
+ JT_Register *jt_reg;
+};
+
+JT_UnRegister::JT_UnRegister(Task *parent)
+: Task(parent)
+{
+ d = new Private;
+ d->jt_reg = 0;
+}
+
+JT_UnRegister::~JT_UnRegister()
+{
+ delete d->jt_reg;
+ delete d;
+}
+
+void JT_UnRegister::unreg(const Jid &j)
+{
+ d->j = j;
+}
+
+void JT_UnRegister::onGo()
+{
+ delete d->jt_reg;
+
+ d->jt_reg = new JT_Register(this);
+ d->jt_reg->getForm(d->j);
+ connect(d->jt_reg, SIGNAL(finished()), SLOT(getFormFinished()));
+ d->jt_reg->go(false);
+}
+
+void JT_UnRegister::getFormFinished()
+{
+ disconnect(d->jt_reg, 0, this, 0);
+
+ d->jt_reg->unreg(d->j);
+ connect(d->jt_reg, SIGNAL(finished()), SLOT(unregFinished()));
+ d->jt_reg->go(false);
+}
+
+void JT_UnRegister::unregFinished()
+{
+ if ( d->jt_reg->success() )
+ setSuccess();
+ else
+ setError(d->jt_reg->statusCode(), d->jt_reg->statusString());
+
+ delete d->jt_reg;
+ d->jt_reg = 0;
+}
+
+//----------------------------------------------------------------------------
+// JT_Roster
+//----------------------------------------------------------------------------
+class JT_Roster::Private
+{
+public:
+ Private() {}
+
+ Roster roster;
+ QValueList<QDomElement> itemList;
+};
+
+JT_Roster::JT_Roster(Task *parent)
+:Task(parent)
+{
+ type = -1;
+ d = new Private;
+}
+
+JT_Roster::~JT_Roster()
+{
+ delete d;
+}
+
+void JT_Roster::get()
+{
+ type = 0;
+ //to = client()->host();
+ iq = createIQ(doc(), "get", to.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:roster");
+ iq.appendChild(query);
+}
+
+void JT_Roster::set(const Jid &jid, const QString &name, const QStringList &groups)
+{
+ type = 1;
+ //to = client()->host();
+ QDomElement item = doc()->createElement("item");
+ item.setAttribute("jid", jid.full());
+ if(!name.isEmpty())
+ item.setAttribute("name", name);
+ for(QStringList::ConstIterator it = groups.begin(); it != groups.end(); ++it)
+ item.appendChild(textTag(doc(), "group", *it));
+ d->itemList += item;
+}
+
+void JT_Roster::remove(const Jid &jid)
+{
+ type = 1;
+ //to = client()->host();
+ QDomElement item = doc()->createElement("item");
+ item.setAttribute("jid", jid.full());
+ item.setAttribute("subscription", "remove");
+ d->itemList += item;
+}
+
+void JT_Roster::onGo()
+{
+ if(type == 0)
+ send(iq);
+ else if(type == 1) {
+ //to = client()->host();
+ iq = createIQ(doc(), "set", to.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:roster");
+ iq.appendChild(query);
+ for(QValueList<QDomElement>::ConstIterator it = d->itemList.begin(); it != d->itemList.end(); ++it)
+ query.appendChild(*it);
+ send(iq);
+ }
+}
+
+const Roster & JT_Roster::roster() const
+{
+ return d->roster;
+}
+
+QString JT_Roster::toString() const
+{
+ if(type != 1)
+ return "";
+
+ QDomElement i = doc()->createElement("request");
+ i.setAttribute("type", "JT_Roster");
+ for(QValueList<QDomElement>::ConstIterator it = d->itemList.begin(); it != d->itemList.end(); ++it)
+ i.appendChild(*it);
+ return lineEncode(Stream::xmlToString(i));
+ return "";
+}
+
+bool JT_Roster::fromString(const QString &str)
+{
+ QDomDocument *dd = new QDomDocument;
+ if(!dd->setContent(lineDecode(str).utf8()))
+ return false;
+ QDomElement e = doc()->importNode(dd->documentElement(), true).toElement();
+ delete dd;
+
+ if(e.tagName() != "request" || e.attribute("type") != "JT_Roster")
+ return false;
+
+ type = 1;
+ d->itemList.clear();
+ for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+ d->itemList += i;
+ }
+
+ return true;
+}
+
+bool JT_Roster::take(const QDomElement &x)
+{
+ if(!iqVerify(x, client()->host(), id()))
+ return false;
+
+ // get
+ if(type == 0) {
+ if(x.attribute("type") == "result") {
+ QDomElement q = queryTag(x);
+ d->roster = xmlReadRoster(q, false);
+ setSuccess();
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+ }
+ // set
+ else if(type == 1) {
+ if(x.attribute("type") == "result")
+ setSuccess();
+ else
+ setError(x);
+
+ return true;
+ }
+ // remove
+ else if(type == 2) {
+ setSuccess();
+ return true;
+ }
+
+ return false;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_PushRoster
+//----------------------------------------------------------------------------
+JT_PushRoster::JT_PushRoster(Task *parent)
+:Task(parent)
+{
+}
+
+JT_PushRoster::~JT_PushRoster()
+{
+}
+
+bool JT_PushRoster::take(const QDomElement &e)
+{
+ // must be an iq-set tag
+ if(e.tagName() != "iq" || e.attribute("type") != "set")
+ return false;
+
+ if(!iqVerify(e, client()->host(), "", "jabber:iq:roster"))
+ return false;
+
+ roster(xmlReadRoster(queryTag(e), true));
+
+ return true;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_Presence
+//----------------------------------------------------------------------------
+JT_Presence::JT_Presence(Task *parent)
+:Task(parent)
+{
+ type = -1;
+}
+
+JT_Presence::~JT_Presence()
+{
+}
+
+void JT_Presence::pres(const Status &s)
+{
+ type = 0;
+
+ tag = doc()->createElement("presence");
+ if(!s.isAvailable()) {
+ tag.setAttribute("type", "unavailable");
+ if(!s.status().isEmpty())
+ tag.appendChild(textTag(doc(), "status", s.status()));
+ }
+ else {
+ if(s.isInvisible())
+ tag.setAttribute("type", "invisible");
+
+ if(!s.show().isEmpty())
+ tag.appendChild(textTag(doc(), "show", s.show()));
+ if(!s.status().isEmpty())
+ tag.appendChild(textTag(doc(), "status", s.status()));
+
+ tag.appendChild( textTag(doc(), "priority", QString("%1").arg(s.priority()) ) );
+
+ if(!s.keyID().isEmpty()) {
+ QDomElement x = textTag(doc(), "x", s.keyID());
+ x.setAttribute("xmlns", "http://jabber.org/protocol/e2e");
+ tag.appendChild(x);
+ }
+ if(!s.xsigned().isEmpty()) {
+ QDomElement x = textTag(doc(), "x", s.xsigned());
+ x.setAttribute("xmlns", "jabber:x:signed");
+ tag.appendChild(x);
+ }
+
+ if(!s.capsNode().isEmpty() && !s.capsVersion().isEmpty()) {
+ QDomElement c = doc()->createElement("c");
+ c.setAttribute("xmlns","http://jabber.org/protocol/caps");
+ c.setAttribute("node",s.capsNode());
+ c.setAttribute("ver",s.capsVersion());
+ if (!s.capsExt().isEmpty())
+ c.setAttribute("ext",s.capsExt());
+ tag.appendChild(c);
+ }
+ }
+}
+
+void JT_Presence::pres(const Jid &to, const Status &s)
+{
+ pres(s);
+ tag.setAttribute("to", to.full());
+}
+
+void JT_Presence::sub(const Jid &to, const QString &subType)
+{
+ type = 1;
+
+ tag = doc()->createElement("presence");
+ tag.setAttribute("to", to.full());
+ tag.setAttribute("type", subType);
+}
+
+void JT_Presence::onGo()
+{
+ send(tag);
+ setSuccess();
+}
+
+
+//----------------------------------------------------------------------------
+// JT_PushPresence
+//----------------------------------------------------------------------------
+JT_PushPresence::JT_PushPresence(Task *parent)
+:Task(parent)
+{
+}
+
+JT_PushPresence::~JT_PushPresence()
+{
+}
+
+bool JT_PushPresence::take(const QDomElement &e)
+{
+ if(e.tagName() != "presence")
+ return false;
+
+ Jid j(e.attribute("from"));
+ Status p;
+
+ if(e.hasAttribute("type")) {
+ QString type = e.attribute("type");
+ if(type == "unavailable") {
+ p.setIsAvailable(false);
+ }
+ else if(type == "error") {
+ QString str = "";
+ int code = 0;
+ getErrorFromElement(e, &code, &str);
+ p.setError(code, str);
+ }
+ else {
+ subscription(j, type);
+ return true;
+ }
+ }
+
+ QDomElement tag;
+ bool found;
+
+ tag = findSubTag(e, "status", &found);
+ if(found)
+ p.setStatus(tagContent(tag));
+ tag = findSubTag(e, "show", &found);
+ if(found)
+ p.setShow(tagContent(tag));
+ tag = findSubTag(e, "priority", &found);
+ if(found)
+ p.setPriority(tagContent(tag).toInt());
+
+ for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+
+ if(i.tagName() == "x" && i.attribute("xmlns") == "jabber:x:delay") {
+ if(i.hasAttribute("stamp")) {
+ QDateTime dt;
+ if(stamp2TS(i.attribute("stamp"), &dt))
+ dt = dt.addSecs(client()->timeZoneOffset() * 3600);
+ p.setTimeStamp(dt);
+ }
+ }
+ else if(i.tagName() == "x" && i.attribute("xmlns") == "gabber:x:music:info") {
+ QDomElement t;
+ bool found;
+ QString title, state;
+
+ t = findSubTag(i, "title", &found);
+ if(found)
+ title = tagContent(t);
+ t = findSubTag(i, "state", &found);
+ if(found)
+ state = tagContent(t);
+
+ if(!title.isEmpty() && state == "playing")
+ p.setSongTitle(title);
+ }
+ else if(i.tagName() == "x" && i.attribute("xmlns") == "jabber:x:signed") {
+ p.setXSigned(tagContent(i));
+ }
+ else if(i.tagName() == "x" && i.attribute("xmlns") == "http://jabber.org/protocol/e2e") {
+ p.setKeyID(tagContent(i));
+ }
+ else if(i.tagName() == "c" && i.attribute("xmlns") == "http://jabber.org/protocol/caps") {
+ p.setCapsNode(i.attribute("node"));
+ p.setCapsVersion(i.attribute("ver"));
+ p.setCapsExt(i.attribute("ext"));
+ }
+ }
+
+ presence(j, p);
+
+ return true;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_Message
+//----------------------------------------------------------------------------
+static QDomElement oldStyleNS(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;
+
+ QDomElement i;
+ uint x;
+ //if(noShowNS)
+ i = e.ownerDocument().createElement(e.tagName());
+ //else
+ // i = e.ownerDocument().createElementNS(e.namespaceURI(), e.tagName());
+
+ // copy attributes
+ QDomNamedNodeMap al = e.attributes();
+ for(x = 0; x < al.count(); ++x)
+ i.setAttributeNode(al.item(x).cloneNode().toAttr());
+
+ if(!noShowNS)
+ i.setAttribute("xmlns", e.namespaceURI());
+
+ // copy children
+ QDomNodeList nl = e.childNodes();
+ for(x = 0; x < nl.count(); ++x) {
+ QDomNode n = nl.item(x);
+ if(n.isElement())
+ i.appendChild(oldStyleNS(n.toElement()));
+ else
+ i.appendChild(n.cloneNode());
+ }
+ return i;
+}
+
+JT_Message::JT_Message(Task *parent, const Message &msg)
+:Task(parent)
+{
+ m = msg;
+ m.setId(id());
+}
+
+JT_Message::~JT_Message()
+{
+}
+
+void JT_Message::onGo()
+{
+ Stanza s = m.toStanza(&(client()->stream()));
+ QDomElement e = oldStyleNS(s.element());
+ send(e);
+ setSuccess();
+}
+
+
+//----------------------------------------------------------------------------
+// JT_PushMessage
+//----------------------------------------------------------------------------
+static QDomElement addCorrectNS(const QDomElement &e)
+{
+ uint x;
+
+ // grab child nodes
+ /*QDomDocumentFragment frag = e.ownerDocument().createDocumentFragment();
+ QDomNodeList nl = e.childNodes();
+ for(x = 0; x < nl.count(); ++x)
+ frag.appendChild(nl.item(x).cloneNode());*/
+
+ // find closest xmlns
+ QDomNode n = e;
+ while(!n.isNull() && !n.toElement().hasAttribute("xmlns"))
+ n = n.parentNode();
+ QString ns;
+ if(n.isNull() || !n.toElement().hasAttribute("xmlns"))
+ ns = "jabber:client";
+ else
+ ns = n.toElement().attribute("xmlns");
+
+ // make a new node
+ QDomElement i = e.ownerDocument().createElementNS(ns, e.tagName());
+
+ // copy attributes
+ QDomNamedNodeMap al = e.attributes();
+ for(x = 0; x < al.count(); ++x) {
+ QDomAttr a = al.item(x).toAttr();
+ if(a.name() != "xmlns")
+ i.setAttributeNodeNS(al.item(x).cloneNode().toAttr());
+ }
+
+ // copy children
+ QDomNodeList nl = e.childNodes();
+ for(x = 0; x < nl.count(); ++x) {
+ QDomNode n = nl.item(x);
+ if(n.isElement())
+ i.appendChild(addCorrectNS(n.toElement()));
+ else
+ i.appendChild(n.cloneNode());
+ }
+
+ //i.appendChild(frag);
+ return i;
+}
+
+JT_PushMessage::JT_PushMessage(Task *parent)
+:Task(parent)
+{
+}
+
+JT_PushMessage::~JT_PushMessage()
+{
+}
+
+bool JT_PushMessage::take(const QDomElement &e)
+{
+ if(e.tagName() != "message")
+ return false;
+
+ Stanza s = client()->stream().createStanza(addCorrectNS(e));
+ if(s.isNull()) {
+ //printf("take: bad stanza??\n");
+ return false;
+ }
+
+ Message m;
+ if(!m.fromStanza(s, client()->timeZoneOffset())) {
+ //printf("bad message\n");
+ return false;
+ }
+
+ message(m);
+ return true;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_GetLastActivity
+//----------------------------------------------------------------------------
+class JT_GetLastActivity::Private
+{
+public:
+ Private() {}
+
+ int seconds;
+ QString message;
+};
+
+JT_GetLastActivity::JT_GetLastActivity(Task *parent)
+:Task(parent)
+{
+ d = new Private;
+}
+
+JT_GetLastActivity::~JT_GetLastActivity()
+{
+ delete d;
+}
+
+void JT_GetLastActivity::get(const Jid &j)
+{
+ jid = j;
+ iq = createIQ(doc(), "get", jid.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:last");
+ iq.appendChild(query);
+}
+
+int JT_GetLastActivity::seconds() const
+{
+ return d->seconds;
+}
+
+const QString &JT_GetLastActivity::message() const
+{
+ return d->message;
+}
+
+void JT_GetLastActivity::onGo()
+{
+ send(iq);
+}
+
+bool JT_GetLastActivity::take(const QDomElement &x)
+{
+ if(!iqVerify(x, jid, id()))
+ return false;
+
+ if(x.attribute("type") == "result") {
+ QDomElement q = queryTag(x);
+
+ d->message = q.text();
+ bool ok;
+ d->seconds = q.attribute("seconds").toInt(&ok);
+
+ setSuccess(ok);
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// JT_GetServices
+//----------------------------------------------------------------------------
+JT_GetServices::JT_GetServices(Task *parent)
+:Task(parent)
+{
+}
+
+void JT_GetServices::get(const Jid &j)
+{
+ agentList.clear();
+
+ jid = j;
+ iq = createIQ(doc(), "get", jid.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:agents");
+ iq.appendChild(query);
+}
+
+const AgentList & JT_GetServices::agents() const
+{
+ return agentList;
+}
+
+void JT_GetServices::onGo()
+{
+ send(iq);
+}
+
+bool JT_GetServices::take(const QDomElement &x)
+{
+ if(!iqVerify(x, jid, id()))
+ return false;
+
+ if(x.attribute("type") == "result") {
+ QDomElement q = queryTag(x);
+
+ // agents
+ for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+
+ if(i.tagName() == "agent") {
+ AgentItem a;
+
+ a.setJid(Jid(i.attribute("jid")));
+
+ QDomElement tag;
+ bool found;
+
+ tag = findSubTag(i, "name", &found);
+ if(found)
+ a.setName(tagContent(tag));
+
+ // determine which namespaces does item support
+ QStringList ns;
+
+ tag = findSubTag(i, "register", &found);
+ if(found)
+ ns << "jabber:iq:register";
+ tag = findSubTag(i, "search", &found);
+ if(found)
+ ns << "jabber:iq:search";
+ tag = findSubTag(i, "groupchat", &found);
+ if(found)
+ ns << "jabber:iq:conference";
+ tag = findSubTag(i, "transport", &found);
+ if(found)
+ ns << "jabber:iq:gateway";
+
+ a.setFeatures(ns);
+
+ agentList += a;
+ }
+ }
+
+ setSuccess(true);
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_VCard
+//----------------------------------------------------------------------------
+class JT_VCard::Private
+{
+public:
+ Private() {}
+
+ QDomElement iq;
+ Jid jid;
+ VCard vcard;
+};
+
+JT_VCard::JT_VCard(Task *parent)
+:Task(parent)
+{
+ type = -1;
+ d = new Private;
+}
+
+JT_VCard::~JT_VCard()
+{
+ delete d;
+}
+
+void JT_VCard::get(const Jid &_jid)
+{
+ type = 0;
+ d->jid = _jid;
+ d->iq = createIQ(doc(), "get", d->jid.full(), id());
+ QDomElement v = doc()->createElement("vCard");
+ v.setAttribute("xmlns", "vcard-temp");
+ v.setAttribute("version", "2.0");
+ v.setAttribute("prodid", "-//HandGen//NONSGML vGen v1.0//EN");
+ d->iq.appendChild(v);
+}
+
+const Jid & JT_VCard::jid() const
+{
+ return d->jid;
+}
+
+const VCard & JT_VCard::vcard() const
+{
+ return d->vcard;
+}
+
+void JT_VCard::set(const VCard &card)
+{
+ type = 1;
+ d->vcard = card;
+ d->jid = "";
+ d->iq = createIQ(doc(), "set", d->jid.full(), id());
+ d->iq.appendChild(card.toXml(doc()) );
+}
+
+void JT_VCard::onGo()
+{
+ send(d->iq);
+}
+
+bool JT_VCard::take(const QDomElement &x)
+{
+ Jid to = d->jid;
+ if (to.userHost() == client()->jid().userHost())
+ to = client()->host();
+ if(!iqVerify(x, to, id()))
+ return false;
+
+ if(x.attribute("type") == "result") {
+ if(type == 0) {
+ for(QDomNode n = x.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement q = n.toElement();
+ if(q.isNull())
+ continue;
+
+ if(q.tagName().upper() == "VCARD") {
+ if(d->vcard.fromXml(q)) {
+ setSuccess();
+ return true;
+ }
+ }
+ }
+
+ setError(ErrDisc + 1, tr("No VCard available"));
+ return true;
+ }
+ else {
+ setSuccess();
+ return true;
+ }
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_Search
+//----------------------------------------------------------------------------
+class JT_Search::Private
+{
+public:
+ Private() {}
+
+ Jid jid;
+ Form form;
+ QValueList<SearchResult> resultList;
+};
+
+JT_Search::JT_Search(Task *parent)
+:Task(parent)
+{
+ d = new Private;
+ type = -1;
+}
+
+JT_Search::~JT_Search()
+{
+ delete d;
+}
+
+void JT_Search::get(const Jid &jid)
+{
+ type = 0;
+ d->jid = jid;
+ iq = createIQ(doc(), "get", d->jid.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:search");
+ iq.appendChild(query);
+}
+
+void JT_Search::set(const Form &form)
+{
+ type = 1;
+ d->jid = form.jid();
+ iq = createIQ(doc(), "set", d->jid.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:search");
+ iq.appendChild(query);
+
+ // key?
+ if(!form.key().isEmpty())
+ query.appendChild(textTag(doc(), "key", form.key()));
+
+ // fields
+ for(Form::ConstIterator it = form.begin(); it != form.end(); ++it) {
+ const FormField &f = *it;
+ query.appendChild(textTag(doc(), f.realName(), f.value()));
+ }
+}
+
+const Form & JT_Search::form() const
+{
+ return d->form;
+}
+
+const QValueList<SearchResult> & JT_Search::results() const
+{
+ return d->resultList;
+}
+
+void JT_Search::onGo()
+{
+ send(iq);
+}
+
+bool JT_Search::take(const QDomElement &x)
+{
+ if(!iqVerify(x, d->jid, id()))
+ return false;
+
+ Jid from(x.attribute("from"));
+ if(x.attribute("type") == "result") {
+ if(type == 0) {
+ d->form.clear();
+ d->form.setJid(from);
+
+ QDomElement q = queryTag(x);
+ for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+
+ if(i.tagName() == "instructions")
+ d->form.setInstructions(tagContent(i));
+ else if(i.tagName() == "key")
+ d->form.setKey(tagContent(i));
+ else {
+ FormField f;
+ if(f.setType(i.tagName())) {
+ f.setValue(tagContent(i));
+ d->form += f;
+ }
+ }
+ }
+ }
+ else {
+ d->resultList.clear();
+
+ QDomElement q = queryTag(x);
+ for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+
+ if(i.tagName() == "item") {
+ SearchResult r(Jid(i.attribute("jid")));
+
+ QDomElement tag;
+ bool found;
+
+ tag = findSubTag(i, "nick", &found);
+ if(found)
+ r.setNick(tagContent(tag));
+ tag = findSubTag(i, "first", &found);
+ if(found)
+ r.setFirst(tagContent(tag));
+ tag = findSubTag(i, "last", &found);
+ if(found)
+ r.setLast(tagContent(tag));
+ tag = findSubTag(i, "email", &found);
+ if(found)
+ r.setEmail(tagContent(tag));
+
+ d->resultList += r;
+ }
+ }
+ }
+ setSuccess();
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_ClientVersion
+//----------------------------------------------------------------------------
+JT_ClientVersion::JT_ClientVersion(Task *parent)
+:Task(parent)
+{
+}
+
+void JT_ClientVersion::get(const Jid &jid)
+{
+ j = jid;
+ iq = createIQ(doc(), "get", j.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:version");
+ iq.appendChild(query);
+}
+
+void JT_ClientVersion::onGo()
+{
+ send(iq);
+}
+
+bool JT_ClientVersion::take(const QDomElement &x)
+{
+ if(!iqVerify(x, j, id()))
+ return false;
+
+ if(x.attribute("type") == "result") {
+ bool found;
+ QDomElement q = queryTag(x);
+ QDomElement tag;
+ tag = findSubTag(q, "name", &found);
+ if(found)
+ v_name = tagContent(tag);
+ tag = findSubTag(q, "version", &found);
+ if(found)
+ v_ver = tagContent(tag);
+ tag = findSubTag(q, "os", &found);
+ if(found)
+ v_os = tagContent(tag);
+
+ setSuccess();
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+}
+
+const Jid & JT_ClientVersion::jid() const
+{
+ return j;
+}
+
+const QString & JT_ClientVersion::name() const
+{
+ return v_name;
+}
+
+const QString & JT_ClientVersion::version() const
+{
+ return v_ver;
+}
+
+const QString & JT_ClientVersion::os() const
+{
+ return v_os;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_ClientTime
+//----------------------------------------------------------------------------
+/*JT_ClientTime::JT_ClientTime(Task *parent, const Jid &_j)
+:Task(parent)
+{
+ j = _j;
+ iq = createIQ("get", j.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:time");
+ iq.appendChild(query);
+}
+
+void JT_ClientTime::go()
+{
+ send(iq);
+}
+
+bool JT_ClientTime::take(const QDomElement &x)
+{
+ if(x.attribute("id") != id())
+ return FALSE;
+
+ if(x.attribute("type") == "result") {
+ bool found;
+ QDomElement q = queryTag(x);
+ QDomElement tag;
+ tag = findSubTag(q, "utc", &found);
+ if(found)
+ stamp2TS(tagContent(tag), &utc);
+ tag = findSubTag(q, "tz", &found);
+ if(found)
+ timezone = tagContent(tag);
+ tag = findSubTag(q, "display", &found);
+ if(found)
+ display = tagContent(tag);
+
+ setSuccess(TRUE);
+ }
+ else {
+ setError(getErrorString(x));
+ setSuccess(FALSE);
+ }
+
+ return TRUE;
+}
+*/
+
+
+//----------------------------------------------------------------------------
+// JT_ServInfo
+//----------------------------------------------------------------------------
+JT_ServInfo::JT_ServInfo(Task *parent)
+:Task(parent)
+{
+}
+
+JT_ServInfo::~JT_ServInfo()
+{
+}
+
+bool JT_ServInfo::take(const QDomElement &e)
+{
+ if(e.tagName() != "iq" || e.attribute("type") != "get")
+ return false;
+
+ QString ns = queryNS(e);
+ if(ns == "jabber:iq:version") {
+ QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id"));
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:version");
+ iq.appendChild(query);
+ query.appendChild(textTag(doc(), "name", client()->clientName()));
+ query.appendChild(textTag(doc(), "version", client()->clientVersion()));
+ query.appendChild(textTag(doc(), "os", client()->OSName()));
+ send(iq);
+ return true;
+ }
+ //else if(ns == "jabber:iq:time") {
+ // QDomElement iq = createIQ("result", e.attribute("from"), e.attribute("id"));
+ // QDomElement query = doc()->createElement("query");
+ // query.setAttribute("xmlns", "jabber:iq:time");
+ // iq.appendChild(query);
+ // QDateTime local = QDateTime::currentDateTime();
+ // QDateTime utc = local.addSecs(-getTZOffset() * 3600);
+ // QString str = getTZString();
+ // query.appendChild(textTag("utc", TS2stamp(utc)));
+ // query.appendChild(textTag("tz", str));
+ // query.appendChild(textTag("display", QString("%1 %2").arg(local.toString()).arg(str)));
+ // send(iq);
+ // return TRUE;
+ //}
+ else if(ns == "http://jabber.org/protocol/disco#info") {
+ // Find out the node
+ QString node;
+ bool found;
+ QDomElement q = findSubTag(e, "query", &found);
+ if(found) // NOTE: Should always be true, since a NS was found above
+ node = q.attribute("node");
+
+ QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id"));
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "http://jabber.org/protocol/disco#info");
+ if (!node.isEmpty())
+ query.setAttribute("node", node);
+ iq.appendChild(query);
+
+ // Identity
+ DiscoItem::Identity identity = client()->identity();
+ QDomElement id = doc()->createElement("identity");
+ if (!identity.category.isEmpty() && !identity.type.isEmpty()) {
+ id.setAttribute("category",identity.category);
+ id.setAttribute("type",identity.type);
+ if (!identity.name.isEmpty()) {
+ id.setAttribute("name",identity.name);
+ }
+ }
+ else {
+ // Default values
+ id.setAttribute("category","client");
+ id.setAttribute("type","pc");
+ }
+ query.appendChild(id);
+
+ QDomElement feature;
+ if (node.isEmpty() || node == client()->capsNode() + "#" + client()->capsVersion()) {
+ // Standard features
+ feature = doc()->createElement("feature");
+ feature.setAttribute("var", "http://jabber.org/protocol/bytestreams");
+ query.appendChild(feature);
+
+ feature = doc()->createElement("feature");
+ feature.setAttribute("var", "http://jabber.org/protocol/si");
+ query.appendChild(feature);
+
+ feature = doc()->createElement("feature");
+ feature.setAttribute("var", "http://jabber.org/protocol/si/profile/file-transfer");
+ query.appendChild(feature);
+
+ feature = doc()->createElement("feature");
+ feature.setAttribute("var", "http://jabber.org/protocol/xhtml-im");
+ query.appendChild(feature);
+
+ feature = doc()->createElement("feature");
+ feature.setAttribute("var", "http://jabber.org/protocol/disco#info");
+ query.appendChild(feature);
+
+ if (node.isEmpty()) {
+ // Extended features
+ QStringList exts = client()->extensions();
+ for (QStringList::ConstIterator i = exts.begin(); i != exts.end(); ++i) {
+ const QStringList& l = client()->extension(*i).list();
+ for ( QStringList::ConstIterator j = l.begin(); j != l.end(); ++j ) {
+ feature = doc()->createElement("feature");
+ feature.setAttribute("var", *j);
+ query.appendChild(feature);
+ }
+ }
+ }
+ }
+ else if (node.startsWith(client()->capsNode() + "#")) {
+ QString ext = node.right(node.length()-client()->capsNode().length()-1);
+ if (client()->extensions().contains(ext)) {
+ const QStringList& l = client()->extension(ext).list();
+ for ( QStringList::ConstIterator it = l.begin(); it != l.end(); ++it ) {
+ feature = doc()->createElement("feature");
+ feature.setAttribute("var", *it);
+ query.appendChild(feature);
+ }
+ }
+ else {
+ // TODO: ERROR
+ }
+ }
+ else {
+ // TODO: ERROR
+ }
+
+ send(iq);
+ return true;
+ }
+
+ return false;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_Gateway
+//----------------------------------------------------------------------------
+JT_Gateway::JT_Gateway(Task *parent)
+:Task(parent)
+{
+ type = -1;
+}
+
+void JT_Gateway::get(const Jid &jid)
+{
+ type = 0;
+ v_jid = jid;
+ iq = createIQ(doc(), "get", v_jid.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:gateway");
+ iq.appendChild(query);
+}
+
+void JT_Gateway::set(const Jid &jid, const QString &prompt)
+{
+ type = 1;
+ v_jid = jid;
+ v_prompt = prompt;
+ iq = createIQ(doc(), "set", v_jid.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:gateway");
+ iq.appendChild(query);
+ query.appendChild(textTag(doc(), "prompt", v_prompt));
+}
+
+void JT_Gateway::onGo()
+{
+ send(iq);
+}
+
+Jid JT_Gateway::jid() const
+{
+ return v_jid;
+}
+
+QString JT_Gateway::desc() const
+{
+ return v_desc;
+}
+
+QString JT_Gateway::prompt() const
+{
+ return v_prompt;
+}
+
+bool JT_Gateway::take(const QDomElement &x)
+{
+ if(!iqVerify(x, v_jid, id()))
+ return false;
+
+ if(x.attribute("type") == "result") {
+ if(type == 0) {
+ QDomElement query = queryTag(x);
+ bool found;
+ QDomElement tag;
+ tag = findSubTag(query, "desc", &found);
+ if(found)
+ v_desc = tagContent(tag);
+ tag = findSubTag(query, "prompt", &found);
+ if(found)
+ v_prompt = tagContent(tag);
+ }
+ else {
+ QDomElement query = queryTag(x);
+ bool found;
+ QDomElement tag;
+ tag = findSubTag(query, "prompt", &found);
+ if(found)
+ v_prompt = tagContent(tag);
+ }
+
+ setSuccess();
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// JT_Browse
+//----------------------------------------------------------------------------
+class JT_Browse::Private
+{
+public:
+ QDomElement iq;
+ Jid jid;
+ AgentList agentList;
+ AgentItem root;
+};
+
+JT_Browse::JT_Browse (Task *parent)
+:Task (parent)
+{
+ d = new Private;
+}
+
+JT_Browse::~JT_Browse ()
+{
+ delete d;
+}
+
+void JT_Browse::get (const Jid &j)
+{
+ d->agentList.clear();
+
+ d->jid = j;
+ d->iq = createIQ(doc(), "get", d->jid.full(), id());
+ QDomElement query = doc()->createElement("item");
+ query.setAttribute("xmlns", "jabber:iq:browse");
+ d->iq.appendChild(query);
+}
+
+const AgentList & JT_Browse::agents() const
+{
+ return d->agentList;
+}
+
+const AgentItem & JT_Browse::root() const
+{
+ return d->root;
+}
+
+void JT_Browse::onGo ()
+{
+ send(d->iq);
+}
+
+AgentItem JT_Browse::browseHelper (const QDomElement &i)
+{
+ AgentItem a;
+
+ if ( i.tagName() == "ns" )
+ return a;
+
+ a.setName ( i.attribute("name") );
+ a.setJid ( i.attribute("jid") );
+
+ // there are two types of category/type specification:
+ //
+ // 1. <item category="category_name" type="type_name" />
+ // 2. <category_name type="type_name" />
+
+ if ( i.tagName() == "item" || i.tagName() == "query" )
+ a.setCategory ( i.attribute("category") );
+ else
+ a.setCategory ( i.tagName() );
+
+ a.setType ( i.attribute("type") );
+
+ QStringList ns;
+ for(QDomNode n = i.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+
+ if ( i.tagName() == "ns" )
+ ns << i.text();
+ }
+
+ // For now, conference.jabber.org returns proper namespace only
+ // when browsing individual rooms. So it's a quick client-side fix.
+ if ( !a.features().canGroupchat() && a.category() == "conference" )
+ ns << "jabber:iq:conference";
+
+ a.setFeatures (ns);
+
+ return a;
+}
+
+bool JT_Browse::take(const QDomElement &x)
+{
+ if(!iqVerify(x, d->jid, id()))
+ return false;
+
+ if(x.attribute("type") == "result") {
+ for(QDomNode n = x.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+
+ d->root = browseHelper (i);
+
+ for(QDomNode nn = i.firstChild(); !nn.isNull(); nn = nn.nextSibling()) {
+ QDomElement e = nn.toElement();
+ if ( e.isNull() )
+ continue;
+ if ( e.tagName() == "ns" )
+ continue;
+
+ d->agentList += browseHelper (e);
+ }
+ }
+
+ setSuccess(true);
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// JT_DiscoItems
+//----------------------------------------------------------------------------
+class JT_DiscoItems::Private
+{
+public:
+ Private() { }
+
+ QDomElement iq;
+ Jid jid;
+ DiscoList items;
+};
+
+JT_DiscoItems::JT_DiscoItems(Task *parent)
+: Task(parent)
+{
+ d = new Private;
+}
+
+JT_DiscoItems::~JT_DiscoItems()
+{
+ delete d;
+}
+
+void JT_DiscoItems::get(const DiscoItem &item)
+{
+ get(item.jid(), item.node());
+}
+
+void JT_DiscoItems::get (const Jid &j, const QString &node)
+{
+ d->items.clear();
+
+ d->jid = j;
+ d->iq = createIQ(doc(), "get", d->jid.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "http://jabber.org/protocol/disco#items");
+
+ if ( !node.isEmpty() )
+ query.setAttribute("node", node);
+
+ d->iq.appendChild(query);
+}
+
+const DiscoList &JT_DiscoItems::items() const
+{
+ return d->items;
+}
+
+void JT_DiscoItems::onGo ()
+{
+ send(d->iq);
+}
+
+bool JT_DiscoItems::take(const QDomElement &x)
+{
+ if(!iqVerify(x, d->jid, id()))
+ return false;
+
+ if(x.attribute("type") == "result") {
+ QDomElement q = queryTag(x);
+
+ for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement e = n.toElement();
+ if( e.isNull() )
+ continue;
+
+ if ( e.tagName() == "item" ) {
+ DiscoItem item;
+
+ item.setJid ( e.attribute("jid") );
+ item.setName( e.attribute("name") );
+ item.setNode( e.attribute("node") );
+ item.setAction( DiscoItem::string2action(e.attribute("action")) );
+
+ d->items.append( item );
+ }
+ }
+
+ setSuccess(true);
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// JT_DiscoInfo
+//----------------------------------------------------------------------------
+class JT_DiscoInfo::Private
+{
+public:
+ Private() { }
+
+ QDomElement iq;
+ Jid jid;
+ QString node;
+ DiscoItem item;
+};
+
+JT_DiscoInfo::JT_DiscoInfo(Task *parent)
+: Task(parent)
+{
+ d = new Private;
+}
+
+JT_DiscoInfo::~JT_DiscoInfo()
+{
+ delete d;
+}
+
+void JT_DiscoInfo::get(const DiscoItem &item)
+{
+ DiscoItem::Identity id;
+ if ( item.identities().count() == 1 )
+ id = item.identities().first();
+ get(item.jid(), item.node(), id);
+}
+
+void JT_DiscoInfo::get (const Jid &j, const QString &node, DiscoItem::Identity ident)
+{
+ d->item = DiscoItem(); // clear item
+
+ d->jid = j;
+ d->node = node;
+ d->iq = createIQ(doc(), "get", d->jid.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "http://jabber.org/protocol/disco#info");
+
+ if ( !node.isEmpty() )
+ query.setAttribute("node", node);
+
+ if ( !ident.category.isEmpty() && !ident.type.isEmpty() ) {
+ QDomElement i = doc()->createElement("item");
+
+ i.setAttribute("category", ident.category);
+ i.setAttribute("type", ident.type);
+ if ( !ident.name.isEmpty() )
+ i.setAttribute("name", ident.name);
+
+ query.appendChild( i );
+
+ }
+
+ d->iq.appendChild(query);
+}
+
+
+/**
+ * Original requested jid.
+ * Is here because sometimes the responder does not include this information
+ * in the reply.
+ */
+const Jid& JT_DiscoInfo::jid() const
+{
+ return d->jid;
+}
+
+/**
+ * Original requested node.
+ * Is here because sometimes the responder does not include this information
+ * in the reply.
+ */
+const QString& JT_DiscoInfo::node() const
+{
+ return d->node;
+}
+
+
+
+const DiscoItem &JT_DiscoInfo::item() const
+{
+ return d->item;
+}
+
+void JT_DiscoInfo::onGo ()
+{
+ send(d->iq);
+}
+
+bool JT_DiscoInfo::take(const QDomElement &x)
+{
+ if(!iqVerify(x, d->jid, id()))
+ return false;
+
+ if(x.attribute("type") == "result") {
+ QDomElement q = queryTag(x);
+
+ DiscoItem item;
+
+ item.setJid( d->jid );
+ item.setNode( q.attribute("node") );
+
+ QStringList features;
+ DiscoItem::Identities identities;
+
+ for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement e = n.toElement();
+ if( e.isNull() )
+ continue;
+
+ if ( e.tagName() == "feature" ) {
+ features << e.attribute("var");
+ }
+ else if ( e.tagName() == "identity" ) {
+ DiscoItem::Identity id;
+
+ id.category = e.attribute("category");
+ id.name = e.attribute("name");
+ id.type = e.attribute("type");
+
+ identities.append( id );
+ }
+ }
+
+ item.setFeatures( features );
+ item.setIdentities( identities );
+
+ d->item = item;
+
+ setSuccess(true);
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// JT_DiscoPublish
+//----------------------------------------------------------------------------
+class JT_DiscoPublish::Private
+{
+public:
+ Private() { }
+
+ QDomElement iq;
+ Jid jid;
+ DiscoList list;
+};
+
+JT_DiscoPublish::JT_DiscoPublish(Task *parent)
+: Task(parent)
+{
+ d = new Private;
+}
+
+JT_DiscoPublish::~JT_DiscoPublish()
+{
+ delete d;
+}
+
+void JT_DiscoPublish::set(const Jid &j, const DiscoList &list)
+{
+ d->list = list;
+ d->jid = j;
+
+ d->iq = createIQ(doc(), "set", d->jid.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "http://jabber.org/protocol/disco#items");
+
+ // FIXME: unsure about this
+ //if ( !node.isEmpty() )
+ // query.setAttribute("node", node);
+
+ DiscoList::ConstIterator it = list.begin();
+ for ( ; it != list.end(); ++it) {
+ QDomElement w = doc()->createElement("item");
+
+ w.setAttribute("jid", (*it).jid().full());
+ if ( !(*it).name().isEmpty() )
+ w.setAttribute("name", (*it).name());
+ if ( !(*it).node().isEmpty() )
+ w.setAttribute("node", (*it).node());
+ w.setAttribute("action", DiscoItem::action2string((*it).action()));
+
+ query.appendChild( w );
+ }
+
+ d->iq.appendChild(query);
+}
+
+void JT_DiscoPublish::onGo ()
+{
+ send(d->iq);
+}
+
+bool JT_DiscoPublish::take(const QDomElement &x)
+{
+ if(!iqVerify(x, d->jid, id()))
+ return false;
+
+ if(x.attribute("type") == "result") {
+ setSuccess(true);
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// JT_MucPresence
+//----------------------------------------------------------------------------
+JT_MucPresence::JT_MucPresence(Task *parent)
+:Task(parent)
+{
+ type = -1;
+}
+
+JT_MucPresence::~JT_MucPresence()
+{
+}
+
+void JT_MucPresence::pres(const Status &s)
+{
+ type = 0;
+
+ tag = doc()->createElement("presence");
+ if(!s.isAvailable()) {
+ tag.setAttribute("type", "unavailable");
+ if(!s.status().isEmpty())
+ tag.appendChild(textTag(doc(), "status", s.status()));
+ }
+ else {
+ if(s.isInvisible())
+ tag.setAttribute("type", "invisible");
+
+ if(!s.show().isEmpty())
+ tag.appendChild(textTag(doc(), "show", s.show()));
+ if(!s.status().isEmpty())
+ tag.appendChild(textTag(doc(), "status", s.status()));
+
+ tag.appendChild( textTag(doc(), "priority", QString("%1").arg(s.priority()) ) );
+
+ if(!s.keyID().isEmpty()) {
+ QDomElement x = textTag(doc(), "x", s.keyID());
+ x.setAttribute("xmlns", "http://jabber.org/protocol/e2e");
+ tag.appendChild(x);
+ }
+ if(!s.xsigned().isEmpty()) {
+ QDomElement x = textTag(doc(), "x", s.xsigned());
+ x.setAttribute("xmlns", "jabber:x:signed");
+ tag.appendChild(x);
+ }
+
+ if(!s.capsNode().isEmpty() && !s.capsVersion().isEmpty()) {
+ QDomElement c = doc()->createElement("c");
+ c.setAttribute("xmlns","http://jabber.org/protocol/caps");
+ c.setAttribute("node",s.capsNode());
+ c.setAttribute("ver",s.capsVersion());
+ if (!s.capsExt().isEmpty())
+ c.setAttribute("ext",s.capsExt());
+ tag.appendChild(c);
+ }
+ }
+}
+
+void JT_MucPresence::pres(const Jid &to, const Status &s, const QString &password)
+{
+ pres(s);
+ tag.setAttribute("to", to.full());
+ QDomElement x = textTag(doc(), "x", s.xsigned());
+ x.setAttribute("xmlns", "http://jabber.org/protocol/muc");
+ x.appendChild( textTag(doc(), "password", password.latin1()) );
+ tag.appendChild(x);
+}
+
+void JT_MucPresence::onGo()
+{
+ send(tag);
+ setSuccess();
+}
+
+
+//----------------------------------------------------------------------------
+// JT_PrivateStorage
+//----------------------------------------------------------------------------
+class JT_PrivateStorage::Private
+{
+ public:
+ Private() : type(-1) {}
+
+ QDomElement iq;
+ QDomElement elem;
+ int type;
+};
+
+JT_PrivateStorage::JT_PrivateStorage(Task *parent)
+ :Task(parent)
+{
+ d = new Private;
+}
+
+JT_PrivateStorage::~JT_PrivateStorage()
+{
+ delete d;
+}
+
+void JT_PrivateStorage::get(const QString& tag, const QString& xmlns)
+{
+ d->type = 0;
+ d->iq = createIQ(doc(), "get" , QString() , id() );
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:private");
+ d->iq.appendChild(query);
+ QDomElement s = doc()->createElement(tag);
+ if(!xmlns.isEmpty())
+ s.setAttribute("xmlns", xmlns);
+ query.appendChild(s);
+}
+
+void JT_PrivateStorage::set(const QDomElement& element)
+{
+ d->type = 1;
+ d->elem=element;
+ QDomNode n=doc()->importNode(element,true);
+
+ d->iq = createIQ(doc(), "set" , QString() , id() );
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:private");
+ d->iq.appendChild(query);
+ query.appendChild(n);
+}
+
+void JT_PrivateStorage::onGo()
+{
+ send(d->iq);
+}
+
+bool JT_PrivateStorage::take(const QDomElement &x)
+{
+ QString to = client()->host();
+ if(!iqVerify(x, to, id()))
+ return false;
+
+ if(x.attribute("type") == "result") {
+ if(d->type == 0) {
+ QDomElement q = queryTag(x);
+ for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+ d->elem=i;
+ break;
+ }
+ }
+ setSuccess();
+ return true;
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+}
+
+
+QDomElement JT_PrivateStorage::element( )
+{
+ return d->elem;
+}
+
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_tasks.h b/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_tasks.h
new file mode 100644
index 00000000..ceb1e294
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_tasks.h
@@ -0,0 +1,485 @@
+/*
+ * tasks.h - basic tasks
+ * Copyright (C) 2001, 2002 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef JABBER_TASKS_H
+#define JABBER_TASKS_H
+
+#include<qstring.h>
+#include<qdom.h>
+
+#include"im.h"
+#include"xmpp_vcard.h"
+
+namespace XMPP
+{
+ class Roster;
+ class Status;
+
+ class JT_Register : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_Register(Task *parent);
+ ~JT_Register();
+
+ void reg(const QString &user, const QString &pass);
+ void changepw(const QString &pass);
+ void unreg(const Jid &j="");
+
+ const Form & form() const;
+ void getForm(const Jid &);
+ void setForm(const Form &);
+
+ void onGo();
+ bool take(const QDomElement &);
+
+ private:
+ QDomElement iq;
+ Jid to;
+
+ class Private;
+ Private *d;
+ };
+
+ class JT_UnRegister : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_UnRegister(Task *parent);
+ ~JT_UnRegister();
+
+ void unreg(const Jid &);
+
+ void onGo();
+
+ private slots:
+ void getFormFinished();
+ void unregFinished();
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ class JT_Roster : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_Roster(Task *parent);
+ ~JT_Roster();
+
+ void get();
+ void set(const Jid &, const QString &name, const QStringList &groups);
+ void remove(const Jid &);
+
+ const Roster & roster() const;
+
+ QString toString() const;
+ bool fromString(const QString &);
+
+ void onGo();
+ bool take(const QDomElement &x);
+
+ private:
+ int type;
+ QDomElement iq;
+ Jid to;
+
+ class Private;
+ Private *d;
+ };
+
+ class JT_PushRoster : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_PushRoster(Task *parent);
+ ~JT_PushRoster();
+
+ bool take(const QDomElement &);
+
+ signals:
+ void roster(const Roster &);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ class JT_Presence : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_Presence(Task *parent);
+ ~JT_Presence();
+
+ void pres(const Status &);
+ void pres(const Jid &, const Status &);
+ void sub(const Jid &, const QString &subType);
+
+ void onGo();
+
+ private:
+ QDomElement tag;
+ int type;
+
+ class Private;
+ Private *d;
+ };
+
+ class JT_PushPresence : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_PushPresence(Task *parent);
+ ~JT_PushPresence();
+
+ bool take(const QDomElement &);
+
+ signals:
+ void presence(const Jid &, const Status &);
+ void subscription(const Jid &, const QString &);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ class JT_Message : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_Message(Task *parent, const Message &);
+ ~JT_Message();
+
+ void onGo();
+
+ private:
+ Message m;
+
+ class Private;
+ Private *d;
+ };
+
+ class JT_PushMessage : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_PushMessage(Task *parent);
+ ~JT_PushMessage();
+
+ bool take(const QDomElement &);
+
+ signals:
+ void message(const Message &);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ class JT_GetLastActivity : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_GetLastActivity(Task *);
+ ~JT_GetLastActivity();
+
+ void get(const Jid &);
+
+ int seconds() const;
+ const QString &message() const;
+
+ void onGo();
+ bool take(const QDomElement &x);
+
+ private:
+ class Private;
+ Private *d;
+
+ QDomElement iq;
+ Jid jid;
+ };
+
+ class JT_GetServices : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_GetServices(Task *);
+
+ void get(const Jid &);
+
+ const AgentList & agents() const;
+
+ void onGo();
+ bool take(const QDomElement &x);
+
+ private:
+ class Private;
+ Private *d;
+
+ QDomElement iq;
+ Jid jid;
+ AgentList agentList;
+ };
+
+ class JT_VCard : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_VCard(Task *parent);
+ ~JT_VCard();
+
+ void get(const Jid &);
+ void set(const VCard &);
+
+ const Jid & jid() const;
+ const VCard & vcard() const;
+
+ void onGo();
+ bool take(const QDomElement &x);
+
+ private:
+ int type;
+
+ class Private;
+ Private *d;
+ };
+
+ class JT_Search : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_Search(Task *parent);
+ ~JT_Search();
+
+ const Form & form() const;
+ const QValueList<SearchResult> & results() const;
+
+ void get(const Jid &);
+ void set(const Form &);
+
+ void onGo();
+ bool take(const QDomElement &x);
+
+ private:
+ QDomElement iq;
+ int type;
+
+ class Private;
+ Private *d;
+ };
+
+ class JT_ClientVersion : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_ClientVersion(Task *);
+
+ void get(const Jid &);
+ void onGo();
+ bool take(const QDomElement &);
+
+ const Jid & jid() const;
+ const QString & name() const;
+ const QString & version() const;
+ const QString & os() const;
+
+ private:
+ QDomElement iq;
+
+ Jid j;
+ QString v_name, v_ver, v_os;
+ };
+/*
+ class JT_ClientTime : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_ClientTime(Task *, const Jid &);
+
+ void go();
+ bool take(const QDomElement &);
+
+ Jid j;
+ QDateTime utc;
+ QString timezone, display;
+
+ private:
+ QDomElement iq;
+ };
+*/
+ class JT_ServInfo : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_ServInfo(Task *);
+ ~JT_ServInfo();
+
+ bool take(const QDomElement &);
+ };
+
+ class JT_Gateway : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_Gateway(Task *);
+
+ void get(const Jid &);
+ void set(const Jid &, const QString &prompt);
+ void onGo();
+ bool take(const QDomElement &);
+
+ Jid jid() const;
+ QString desc() const;
+ QString prompt() const;
+
+ private:
+ QDomElement iq;
+
+ int type;
+ Jid v_jid;
+ QString v_prompt, v_desc;
+ };
+
+ class JT_Browse : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_Browse(Task *);
+ ~JT_Browse();
+
+ void get(const Jid &);
+
+ const AgentList & agents() const;
+ const AgentItem & root() const;
+
+ void onGo();
+ bool take(const QDomElement &);
+
+ private:
+ class Private;
+ Private *d;
+
+ AgentItem browseHelper (const QDomElement &i);
+ };
+
+ class JT_DiscoItems : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_DiscoItems(Task *);
+ ~JT_DiscoItems();
+
+ void get(const Jid &, const QString &node = QString::null);
+ void get(const DiscoItem &);
+
+ const DiscoList &items() const;
+
+ void onGo();
+ bool take(const QDomElement &);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ class JT_DiscoInfo : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_DiscoInfo(Task *);
+ ~JT_DiscoInfo();
+
+ void get(const Jid &, const QString &node = QString::null, const DiscoItem::Identity = DiscoItem::Identity());
+ void get(const DiscoItem &);
+
+ const DiscoItem &item() const;
+ const Jid& jid() const;
+ const QString& node() const;
+
+ void onGo();
+ bool take(const QDomElement &);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ class JT_DiscoPublish : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_DiscoPublish(Task *);
+ ~JT_DiscoPublish();
+
+ void set(const Jid &, const DiscoList &);
+
+ void onGo();
+ bool take(const QDomElement &);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ class JT_MucPresence : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_MucPresence(Task *parent);
+ ~JT_MucPresence();
+
+ void pres(const Status &);
+ void pres(const Jid &, const Status &, const QString &password);
+
+ void onGo();
+
+ private:
+ QDomElement tag;
+ int type;
+
+ class Private;
+ Private *d;
+ };
+
+ class JT_PrivateStorage : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_PrivateStorage(Task *parent);
+ ~JT_PrivateStorage();
+
+ void set(const QDomElement &);
+ void get(const QString &tag, const QString& xmlns);
+
+ QDomElement element();
+
+ void onGo();
+ bool take(const QDomElement &);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+}
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_vcard.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_vcard.cpp
new file mode 100644
index 00000000..296c53c6
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_vcard.cpp
@@ -0,0 +1,1241 @@
+/*
+ * xmpp_vcard.cpp - classes for handling vCards
+ * Copyright (C) 2003 Michail Pishchagin
+ *
+ * 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_vcard.h"
+
+#include "base64.h"
+
+#include <qdom.h>
+#include <qdatetime.h>
+
+#include <qimage.h> // needed for image format recognition
+#include <qbuffer.h>
+
+// Justin's XML helper functions
+
+static QDomElement textTag(QDomDocument *doc, const QString &name, const QString &content)
+{
+ QDomElement tag = doc->createElement(name);
+ QDomText text = doc->createTextNode(content);
+ tag.appendChild(text);
+
+ return tag;
+}
+
+static QDomElement findSubTag(const QDomElement &e, const QString &name, bool *found)
+{
+ if(found)
+ *found = FALSE;
+
+ for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+ if(i.tagName().upper() == name.upper()) { // mblsha: ignore case when searching
+ if(found)
+ *found = TRUE;
+ return i;
+ }
+ }
+
+ QDomElement tmp;
+ return tmp;
+}
+
+// mblsha's own functions
+
+static QDomElement emptyTag(QDomDocument *doc, const QString &name)
+{
+ QDomElement tag = doc->createElement(name);
+
+ return tag;
+}
+
+static bool hasSubTag(const QDomElement &e, const QString &name)
+{
+ bool found;
+ findSubTag(e, name, &found);
+ return found;
+}
+
+static QString subTagText(const QDomElement &e, const QString &name)
+{
+ bool found;
+ QDomElement i = findSubTag(e, name, &found);
+ if ( found )
+ return i.text().stripWhiteSpace();
+ return QString::null;
+}
+
+using namespace XMPP;
+
+//----------------------------------------------------------------------------
+// VCard
+//----------------------------------------------------------------------------
+static QString image2type(const QByteArray &ba)
+{
+ QBuffer buf(ba);
+ buf.open(IO_ReadOnly);
+ QString format = QImageIO::imageFormat( &buf );
+
+ // TODO: add more formats
+ if ( format == "PNG" || format == "PsiPNG" )
+ return "image/png";
+ if ( format == "MNG" )
+ return "video/x-mng";
+ if ( format == "GIF" )
+ return "image/gif";
+ if ( format == "BMP" )
+ return "image/bmp";
+ if ( format == "XPM" )
+ return "image/x-xpm";
+ if ( format == "SVG" )
+ return "image/svg+xml";
+ if ( format == "JPEG" )
+ return "image/jpeg";
+
+ qWarning("WARNING! VCard::image2type: unknown format = '%s'", format.latin1());
+
+ return "image/unknown";
+}
+
+// Long lines of encoded binary data SHOULD BE folded to 75 characters using the folding method defined in [MIME-DIR].
+static QString foldString(const QString &s)
+{
+ QString ret;
+
+ for (int i = 0; i < (int)s.length(); i++) {
+ if ( !(i % 75) )
+ ret += '\n';
+ ret += s[i];
+ }
+
+ return ret;
+}
+
+class VCard::Private
+{
+public:
+ Private();
+ ~Private();
+
+ QString version;
+ QString fullName;
+ QString familyName, givenName, middleName, prefixName, suffixName;
+ QString nickName;
+
+ QByteArray photo;
+ QString photoURI;
+
+ QString bday;
+ AddressList addressList;
+ LabelList labelList;
+ PhoneList phoneList;
+ EmailList emailList;
+ QString jid;
+ QString mailer;
+ QString timezone;
+ Geo geo;
+ QString title;
+ QString role;
+
+ QByteArray logo;
+ QString logoURI;
+
+ VCard *agent;
+ QString agentURI;
+
+ Org org;
+ QStringList categories;
+ QString note;
+ QString prodId;
+ QString rev;
+ QString sortString;
+
+ QByteArray sound;
+ QString soundURI, soundPhonetic;
+
+ QString uid;
+ QString url;
+ QString desc;
+ PrivacyClass privacyClass;
+ QByteArray key;
+
+ bool isEmpty();
+};
+
+VCard::Private::Private()
+{
+ privacyClass = pcNone;
+ agent = 0;
+}
+
+VCard::Private::~Private()
+{
+ delete agent;
+}
+
+bool VCard::Private::isEmpty()
+{
+ if ( !version.isEmpty() ||
+ !fullName.isEmpty() ||
+ !familyName.isEmpty() || !givenName.isEmpty() || !middleName.isEmpty() || !prefixName.isEmpty() || !suffixName.isEmpty() ||
+ !nickName.isEmpty() ||
+ !photo.isEmpty() || !photoURI.isEmpty() ||
+ !bday.isEmpty() ||
+ !addressList.isEmpty() ||
+ !labelList.isEmpty() ||
+ !phoneList.isEmpty() ||
+ !emailList.isEmpty() ||
+ !jid.isEmpty() ||
+ !mailer.isEmpty() ||
+ !timezone.isEmpty() ||
+ !geo.lat.isEmpty() || !geo.lon.isEmpty() ||
+ !title.isEmpty() ||
+ !role.isEmpty() ||
+ !logo.isEmpty() || !logoURI.isEmpty() ||
+ (agent && !agent->isEmpty()) || !agentURI.isEmpty() ||
+ !org.name.isEmpty() || !org.unit.isEmpty() ||
+ !categories.isEmpty() ||
+ !note.isEmpty() ||
+ !prodId.isEmpty() ||
+ !rev.isEmpty() ||
+ !sortString.isEmpty() ||
+ !sound.isEmpty() || !soundURI.isEmpty() || !soundPhonetic.isEmpty() ||
+ !uid.isEmpty() ||
+ !url.isEmpty() ||
+ !desc.isEmpty() ||
+ (privacyClass != pcNone) ||
+ !key.isEmpty() )
+ {
+ return false;
+ }
+ return true;
+}
+
+VCard::VCard()
+{
+ d = new Private;
+}
+
+VCard::VCard(const VCard &from)
+{
+ d = new Private;
+ *this = from;
+}
+
+VCard & VCard::operator=(const VCard &from)
+{
+ if(d->agent) {
+ delete d->agent;
+ d->agent = 0;
+ }
+
+ *d = *from.d;
+
+ if(from.d->agent) {
+ // dup the agent
+ d->agent = new VCard(*from.d->agent);
+ }
+
+ return *this;
+}
+
+VCard::~VCard()
+{
+ delete d;
+}
+
+QDomElement VCard::toXml(QDomDocument *doc) const
+{
+ QDomElement v = doc->createElement("vCard");
+ v.setAttribute("version", "2.0");
+ v.setAttribute("prodid", "-//HandGen//NONSGML vGen v1.0//EN");
+ v.setAttribute("xmlns", "vcard-temp");
+
+ if ( !d->version.isEmpty() )
+ v.appendChild( textTag(doc, "VERSION", d->version) );
+ if ( !d->fullName.isEmpty() )
+ v.appendChild( textTag(doc, "FN", d->fullName) );
+
+ if ( !d->familyName.isEmpty() || !d->givenName.isEmpty() || !d->middleName.isEmpty() ||
+ !d->prefixName.isEmpty() || !d->suffixName.isEmpty() ) {
+ QDomElement w = doc->createElement("N");
+
+ if ( !d->familyName.isEmpty() )
+ w.appendChild( textTag(doc, "FAMILY", d->familyName) );
+ if ( !d->givenName.isEmpty() )
+ w.appendChild( textTag(doc, "GIVEN", d->givenName) );
+ if ( !d->middleName.isEmpty() )
+ w.appendChild( textTag(doc, "MIDDLE", d->middleName) );
+ if ( !d->prefixName.isEmpty() )
+ w.appendChild( textTag(doc, "PREFIX", d->prefixName) );
+ if ( !d->suffixName.isEmpty() )
+ w.appendChild( textTag(doc, "SUFFIX", d->suffixName) );
+
+ v.appendChild(w);
+ }
+
+ if ( !d->nickName.isEmpty() )
+ v.appendChild( textTag(doc, "NICKNAME", d->nickName) );
+
+ if ( !d->photo.isEmpty() || !d->photoURI.isEmpty() ) {
+ QDomElement w = doc->createElement("PHOTO");
+
+ if ( !d->photo.isEmpty() ) {
+ w.appendChild( textTag(doc, "TYPE", image2type(d->photo)) );
+ w.appendChild( textTag(doc, "BINVAL", foldString( Base64::arrayToString(d->photo)) ) );
+ }
+ else if ( !d->photoURI.isEmpty() )
+ w.appendChild( textTag(doc, "EXTVAL", d->photoURI) );
+
+ v.appendChild(w);
+ }
+
+ if ( !d->bday.isEmpty() )
+ v.appendChild( textTag(doc, "BDAY", d->bday) );
+
+ if ( !d->addressList.isEmpty() ) {
+ AddressList::Iterator it = d->addressList.begin();
+ for ( ; it != d->addressList.end(); ++it ) {
+ QDomElement w = doc->createElement("ADR");
+ Address a = *it;
+
+ if ( a.home )
+ w.appendChild( emptyTag(doc, "HOME") );
+ if ( a.work )
+ w.appendChild( emptyTag(doc, "WORK") );
+ if ( a.postal )
+ w.appendChild( emptyTag(doc, "POSTAL") );
+ if ( a.parcel )
+ w.appendChild( emptyTag(doc, "PARCEL") );
+ if ( a.dom )
+ w.appendChild( emptyTag(doc, "DOM") );
+ if ( a.intl )
+ w.appendChild( emptyTag(doc, "INTL") );
+ if ( a.pref )
+ w.appendChild( emptyTag(doc, "PREF") );
+
+ if ( !a.pobox.isEmpty() )
+ w.appendChild( textTag(doc, "POBOX", a.pobox) );
+ if ( !a.extaddr.isEmpty() )
+ w.appendChild( textTag(doc, "EXTADR", a.extaddr) );
+ if ( !a.street.isEmpty() )
+ w.appendChild( textTag(doc, "STREET", a.street) );
+ if ( !a.locality.isEmpty() )
+ w.appendChild( textTag(doc, "LOCALITY", a.locality) );
+ if ( !a.region.isEmpty() )
+ w.appendChild( textTag(doc, "REGION", a.region) );
+ if ( !a.pcode.isEmpty() )
+ w.appendChild( textTag(doc, "PCODE", a.pcode) );
+ if ( !a.country.isEmpty() )
+ w.appendChild( textTag(doc, "CTRY", a.country) );
+
+ v.appendChild(w);
+ }
+ }
+
+ if ( !d->labelList.isEmpty() ) {
+ LabelList::Iterator it = d->labelList.begin();
+ for ( ; it != d->labelList.end(); ++it ) {
+ QDomElement w = doc->createElement("LABEL");
+ Label l = *it;
+
+ if ( l.home )
+ w.appendChild( emptyTag(doc, "HOME") );
+ if ( l.work )
+ w.appendChild( emptyTag(doc, "WORK") );
+ if ( l.postal )
+ w.appendChild( emptyTag(doc, "POSTAL") );
+ if ( l.parcel )
+ w.appendChild( emptyTag(doc, "PARCEL") );
+ if ( l.dom )
+ w.appendChild( emptyTag(doc, "DOM") );
+ if ( l.intl )
+ w.appendChild( emptyTag(doc, "INTL") );
+ if ( l.pref )
+ w.appendChild( emptyTag(doc, "PREF") );
+
+ if ( !l.lines.isEmpty() ) {
+ QStringList::Iterator it = l.lines.begin();
+ for ( ; it != l.lines.end(); ++it )
+ w.appendChild( textTag(doc, "LINE", *it) );
+ }
+
+ v.appendChild(w);
+ }
+ }
+
+ if ( !d->phoneList.isEmpty() ) {
+ PhoneList::Iterator it = d->phoneList.begin();
+ for ( ; it != d->phoneList.end(); ++it ) {
+ QDomElement w = doc->createElement("TEL");
+ Phone p = *it;
+
+ if ( p.home )
+ w.appendChild( emptyTag(doc, "HOME") );
+ if ( p.work )
+ w.appendChild( emptyTag(doc, "WORK") );
+ if ( p.voice )
+ w.appendChild( emptyTag(doc, "VOICE") );
+ if ( p.fax )
+ w.appendChild( emptyTag(doc, "FAX") );
+ if ( p.pager )
+ w.appendChild( emptyTag(doc, "PAGER") );
+ if ( p.msg )
+ w.appendChild( emptyTag(doc, "MSG") );
+ if ( p.cell )
+ w.appendChild( emptyTag(doc, "CELL") );
+ if ( p.video )
+ w.appendChild( emptyTag(doc, "VIDEO") );
+ if ( p.bbs )
+ w.appendChild( emptyTag(doc, "BBS") );
+ if ( p.modem )
+ w.appendChild( emptyTag(doc, "MODEM") );
+ if ( p.isdn )
+ w.appendChild( emptyTag(doc, "ISDN") );
+ if ( p.pcs )
+ w.appendChild( emptyTag(doc, "PCS") );
+ if ( p.pref )
+ w.appendChild( emptyTag(doc, "PREF") );
+
+ if ( !p.number.isEmpty() )
+ w.appendChild( textTag(doc, "NUMBER", p.number) );
+
+ v.appendChild(w);
+ }
+ }
+
+ if ( !d->emailList.isEmpty() ) {
+ EmailList::Iterator it = d->emailList.begin();
+ for ( ; it != d->emailList.end(); ++it ) {
+ QDomElement w = doc->createElement("EMAIL");
+ Email e = *it;
+
+ if ( e.home )
+ w.appendChild( emptyTag(doc, "HOME") );
+ if ( e.work )
+ w.appendChild( emptyTag(doc, "WORK") );
+ if ( e.internet )
+ w.appendChild( emptyTag(doc, "INTERNET") );
+ if ( e.x400 )
+ w.appendChild( emptyTag(doc, "X400") );
+
+ if ( !e.userid.isEmpty() )
+ w.appendChild( textTag(doc, "USERID", e.userid) );
+
+ v.appendChild(w);
+ }
+ }
+
+ if ( !d->jid.isEmpty() )
+ v.appendChild( textTag(doc, "JABBERID", d->jid) );
+ if ( !d->mailer.isEmpty() )
+ v.appendChild( textTag(doc, "MAILER", d->mailer) );
+ if ( !d->timezone.isEmpty() )
+ v.appendChild( textTag(doc, "TZ", d->timezone) );
+
+ if ( !d->geo.lat.isEmpty() || !d->geo.lon.isEmpty() ) {
+ QDomElement w = doc->createElement("GEO");
+
+ if ( !d->geo.lat.isEmpty() )
+ w.appendChild( textTag(doc, "LAT", d->geo.lat) );
+ if ( !d->geo.lon.isEmpty() )
+ w.appendChild( textTag(doc, "LON", d->geo.lon));
+
+ v.appendChild(w);
+ }
+
+ if ( !d->title.isEmpty() )
+ v.appendChild( textTag(doc, "TITLE", d->title) );
+ if ( !d->role.isEmpty() )
+ v.appendChild( textTag(doc, "ROLE", d->role) );
+
+ if ( !d->logo.isEmpty() || !d->logoURI.isEmpty() ) {
+ QDomElement w = doc->createElement("LOGO");
+
+ if ( !d->logo.isEmpty() ) {
+ w.appendChild( textTag(doc, "TYPE", image2type(d->logo)) );
+ w.appendChild( textTag(doc, "BINVAL", foldString( Base64::arrayToString(d->logo)) ) );
+ }
+ else if ( !d->logoURI.isEmpty() )
+ w.appendChild( textTag(doc, "EXTVAL", d->logoURI) );
+
+ v.appendChild(w);
+ }
+
+ if ( !d->agentURI.isEmpty() || (d->agent && d->agent->isEmpty()) ) {
+ QDomElement w = doc->createElement("AGENT");
+
+ if ( d->agent && !d->agent->isEmpty() )
+ w.appendChild( d->agent->toXml(doc) );
+ else if ( !d->agentURI.isEmpty() )
+ w.appendChild( textTag(doc, "EXTVAL", d->agentURI) );
+
+ v.appendChild(w);
+ }
+
+ if ( !d->org.name.isEmpty() || !d->org.unit.isEmpty() ) {
+ QDomElement w = doc->createElement("ORG");
+
+ if ( !d->org.name.isEmpty() )
+ w.appendChild( textTag(doc, "ORGNAME", d->org.name) );
+
+ if ( !d->org.unit.isEmpty() ) {
+ QStringList::Iterator it = d->org.unit.begin();
+ for ( ; it != d->org.unit.end(); ++it )
+ w.appendChild( textTag(doc, "ORGUNIT", *it) );
+ }
+
+ v.appendChild(w);
+ }
+
+ if ( !d->categories.isEmpty() ) {
+ QDomElement w = doc->createElement("CATEGORIES");
+
+ QStringList::Iterator it = d->categories.begin();
+ for ( ; it != d->categories.end(); ++it )
+ w.appendChild( textTag(doc, "KEYWORD", *it) );
+
+ v.appendChild(w);
+ }
+
+ if ( !d->note.isEmpty() )
+ v.appendChild( textTag(doc, "NOTE", d->note) );
+ if ( !d->prodId.isEmpty() )
+ v.appendChild( textTag(doc, "PRODID", d->prodId) );
+ if ( !d->rev.isEmpty() )
+ v.appendChild( textTag(doc, "REV", d->rev) );
+ if ( !d->sortString.isEmpty() )
+ v.appendChild( textTag(doc, "SORT-STRING", d->sortString) );
+
+ if ( !d->sound.isEmpty() || !d->soundURI.isEmpty() || !d->soundPhonetic.isEmpty() ) {
+ QDomElement w = doc->createElement("SOUND");
+
+ if ( !d->sound.isEmpty() )
+ w.appendChild( textTag(doc, "BINVAL", foldString( Base64::arrayToString(d->sound)) ) );
+ else if ( !d->soundURI.isEmpty() )
+ w.appendChild( textTag(doc, "EXTVAL", d->soundURI) );
+ else if ( !d->soundPhonetic.isEmpty() )
+ w.appendChild( textTag(doc, "PHONETIC", d->soundPhonetic) );
+
+ v.appendChild(w);
+ }
+
+ if ( !d->uid.isEmpty() )
+ v.appendChild( textTag(doc, "UID", d->uid) );
+ if ( !d->url.isEmpty() )
+ v.appendChild( textTag(doc, "URL", d->url) );
+ if ( !d->desc.isEmpty() )
+ v.appendChild( textTag(doc, "DESC", d->desc) );
+
+ if ( d->privacyClass != pcNone ) {
+ QDomElement w = doc->createElement("CLASS");
+
+ if ( d->privacyClass == pcPublic )
+ w.appendChild( emptyTag(doc, "PUBLIC") );
+ else if ( d->privacyClass == pcPrivate )
+ w.appendChild( emptyTag(doc, "PRIVATE") );
+ else if ( d->privacyClass == pcConfidential )
+ w.appendChild( emptyTag(doc, "CONFIDENTIAL") );
+
+ v.appendChild(w);
+ }
+
+ if ( !d->key.isEmpty() ) {
+ QDomElement w = doc->createElement("KEY");
+
+ // TODO: Justin, please check out this code
+ w.appendChild( textTag(doc, "TYPE", "text/plain")); // FIXME
+ w.appendChild( textTag(doc, "CRED", QString::fromUtf8(d->key)) ); // FIXME
+
+ v.appendChild(w);
+ }
+
+ return v;
+}
+
+bool VCard::fromXml(const QDomElement &q)
+{
+ if ( q.tagName().upper() != "VCARD" )
+ return false;
+
+ QDomNode n = q.firstChild();
+ for ( ; !n.isNull(); n = n.nextSibling() ) {
+ QDomElement i = n.toElement();
+ if ( i.isNull() )
+ continue;
+
+ QString tag = i.tagName().upper();
+
+ bool found;
+ QDomElement e;
+
+ if ( tag == "VERSION" )
+ d->version = i.text().stripWhiteSpace();
+ else if ( tag == "FN" )
+ d->fullName = i.text().stripWhiteSpace();
+ else if ( tag == "N" ) {
+ d->familyName = subTagText(i, "FAMILY");
+ d->givenName = subTagText(i, "GIVEN");
+ d->middleName = subTagText(i, "MIDDLE");
+ d->prefixName = subTagText(i, "PREFIX");
+ d->suffixName = subTagText(i, "SUFFIX");
+ }
+ else if ( tag == "NICKNAME" )
+ d->nickName = i.text().stripWhiteSpace();
+ else if ( tag == "PHOTO" ) {
+ d->photo = Base64::stringToArray( subTagText(i, "BINVAL") );
+ d->photoURI = subTagText(i, "EXTVAL");
+ }
+ else if ( tag == "BDAY" )
+ d->bday = i.text().stripWhiteSpace();
+ else if ( tag == "ADR" ) {
+ Address a;
+
+ a.home = hasSubTag(i, "HOME");
+ a.work = hasSubTag(i, "WORK");
+ a.postal = hasSubTag(i, "POSTAL");
+ a.parcel = hasSubTag(i, "PARCEL");
+ a.dom = hasSubTag(i, "DOM");
+ a.intl = hasSubTag(i, "INTL");
+ a.pref = hasSubTag(i, "PREF");
+
+ a.pobox = subTagText(i, "POBOX");
+ a.extaddr = subTagText(i, "EXTADR");
+ a.street = subTagText(i, "STREET");
+ a.locality = subTagText(i, "LOCALITY");
+ a.region = subTagText(i, "REGION");
+ a.pcode = subTagText(i, "PCODE");
+ a.country = subTagText(i, "CTRY");
+
+ if ( a.country.isEmpty() ) // FIXME: Workaround for Psi prior to 0.9
+ if ( hasSubTag(i, "COUNTRY") )
+ a.country = subTagText(i, "COUNTRY");
+
+ if ( a.extaddr.isEmpty() ) // FIXME: Workaround for Psi prior to 0.9
+ if ( hasSubTag(i, "EXTADD") )
+ a.extaddr = subTagText(i, "EXTADD");
+
+ d->addressList.append ( a );
+ }
+ else if ( tag == "LABEL" ) {
+ Label l;
+
+ l.home = hasSubTag(i, "HOME");
+ l.work = hasSubTag(i, "WORK");
+ l.postal = hasSubTag(i, "POSTAL");
+ l.parcel = hasSubTag(i, "PARCEL");
+ l.dom = hasSubTag(i, "DOM");
+ l.intl = hasSubTag(i, "INTL");
+ l.pref = hasSubTag(i, "PREF");
+
+ QDomNode nn = i.firstChild();
+ for ( ; !nn.isNull(); nn = nn.nextSibling() ) {
+ QDomElement ii = nn.toElement();
+ if ( ii.isNull() )
+ continue;
+
+ if ( ii.tagName().upper() == "LINE" )
+ l.lines.append ( ii.text().stripWhiteSpace() );
+ }
+
+ d->labelList.append ( l );
+ }
+ else if ( tag == "TEL" ) {
+ Phone p;
+
+ p.home = hasSubTag(i, "HOME");
+ p.work = hasSubTag(i, "WORK");
+ p.voice = hasSubTag(i, "VOICE");
+ p.fax = hasSubTag(i, "FAX");
+ p.pager = hasSubTag(i, "PAGER");
+ p.msg = hasSubTag(i, "MSG");
+ p.cell = hasSubTag(i, "CELL");
+ p.video = hasSubTag(i, "VIDEO");
+ p.bbs = hasSubTag(i, "BBS");
+ p.modem = hasSubTag(i, "MODEM");
+ p.isdn = hasSubTag(i, "ISDN");
+ p.pcs = hasSubTag(i, "PCS");
+ p.pref = hasSubTag(i, "PREF");
+
+ p.number = subTagText(i, "NUMBER");
+
+ if ( p.number.isEmpty() ) // FIXME: Workaround for Psi prior to 0.9
+ if ( hasSubTag(i, "VOICE") )
+ p.number = subTagText(i, "VOICE");
+
+ d->phoneList.append ( p );
+ }
+ else if ( tag == "EMAIL" ) {
+ Email m;
+
+ m.home = hasSubTag(i, "HOME");
+ m.work = hasSubTag(i, "WORK");
+ m.internet = hasSubTag(i, "INTERNET");
+ m.x400 = hasSubTag(i, "X400");
+
+ m.userid = subTagText(i, "USERID");
+
+ if ( m.userid.isEmpty() ) // FIXME: Workaround for Psi prior to 0.9
+ if ( !i.text().isEmpty() )
+ m.userid = i.text().stripWhiteSpace();
+
+ d->emailList.append ( m );
+ }
+ else if ( tag == "JABBERID" )
+ d->jid = i.text().stripWhiteSpace();
+ else if ( tag == "MAILER" )
+ d->mailer = i.text().stripWhiteSpace();
+ else if ( tag == "TZ" )
+ d->timezone = i.text().stripWhiteSpace();
+ else if ( tag == "GEO" ) {
+ d->geo.lat = subTagText(i, "LAT");
+ d->geo.lon = subTagText(i, "LON");
+ }
+ else if ( tag == "TITLE" )
+ d->title = i.text().stripWhiteSpace();
+ else if ( tag == "ROLE" )
+ d->role = i.text().stripWhiteSpace();
+ else if ( tag == "LOGO" ) {
+ d->logo = Base64::stringToArray( subTagText(i, "BINVAL") );
+ d->logoURI = subTagText(i, "EXTVAL");
+ }
+ else if ( tag == "AGENT" ) {
+ e = findSubTag(i, "VCARD", &found);
+ if ( found ) {
+ VCard a;
+ if ( a.fromXml(e) ) {
+ if ( !d->agent )
+ d->agent = new VCard;
+ *(d->agent) = a;
+ }
+ }
+
+ d->agentURI = subTagText(i, "EXTVAL");
+ }
+ else if ( tag == "ORG" ) {
+ d->org.name = subTagText(i, "ORGNAME");
+
+ QDomNode nn = i.firstChild();
+ for ( ; !nn.isNull(); nn = nn.nextSibling() ) {
+ QDomElement ii = nn.toElement();
+ if ( ii.isNull() )
+ continue;
+
+ if ( ii.tagName().upper() == "ORGUNIT" )
+ d->org.unit.append( ii.text().stripWhiteSpace() );
+ }
+ }
+ else if ( tag == "CATEGORIES") {
+ QDomNode nn = i.firstChild();
+ for ( ; !nn.isNull(); nn = nn.nextSibling() ) {
+ QDomElement ee = nn.toElement();
+ if ( ee.isNull() )
+ continue;
+
+ if ( ee.tagName().upper() == "KEYWORD" )
+ d->categories << ee.text().stripWhiteSpace();
+ }
+ }
+ else if ( tag == "NOTE" )
+ d->note = i.text().stripWhiteSpace();
+ else if ( tag == "PRODID" )
+ d->prodId = i.text().stripWhiteSpace();
+ else if ( tag == "REV" )
+ d->rev = i.text().stripWhiteSpace();
+ else if ( tag == "SORT-STRING" )
+ d->sortString = i.text().stripWhiteSpace();
+ else if ( tag == "SOUND" ) {
+ d->sound = Base64::stringToArray( subTagText(i, "BINVAL") );
+ d->soundURI = subTagText(i, "EXTVAL");
+ d->soundPhonetic = subTagText(i, "PHONETIC");
+ }
+ else if ( tag == "UID" )
+ d->uid = i.text().stripWhiteSpace();
+ else if ( tag == "URL")
+ d->url = i.text().stripWhiteSpace();
+ else if ( tag == "DESC" )
+ d->desc = i.text().stripWhiteSpace();
+ else if ( tag == "CLASS" ) {
+ if ( hasSubTag(i, "PUBLIC") )
+ d->privacyClass = pcPublic;
+ else if ( hasSubTag(i, "PRIVATE") )
+ d->privacyClass = pcPrivate;
+ else if ( hasSubTag(i, "CONFIDENTIAL") )
+ d->privacyClass = pcConfidential;
+ }
+ else if ( tag == "KEY" ) {
+ // TODO: Justin, please check out this code
+ e = findSubTag(i, "TYPE", &found);
+ QString type = "text/plain";
+ if ( found )
+ type = e.text().stripWhiteSpace();
+
+ e = findSubTag(i, "CRED", &found );
+ if ( !found )
+ e = findSubTag(i, "BINVAL", &found); // case for very clever clients ;-)
+
+ if ( found )
+ d->key = e.text().utf8(); // FIXME
+ }
+ }
+
+ return true;
+}
+
+bool VCard::isEmpty() const
+{
+ return d->isEmpty();
+}
+
+// Some constructors
+
+VCard::Address::Address()
+{
+ home = work = postal = parcel = dom = intl = pref = false;
+}
+
+VCard::Label::Label()
+{
+ home = work = postal = parcel = dom = intl = pref = false;
+}
+
+VCard::Phone::Phone()
+{
+ home = work = voice = fax = pager = msg = cell = video = bbs = modem = isdn = pcs = pref = false;
+}
+
+VCard::Email::Email()
+{
+ home = work = internet = x400 = false;
+}
+
+VCard::Geo::Geo()
+{
+}
+
+VCard::Org::Org()
+{
+}
+
+// vCard properties...
+
+const QString &VCard::version() const
+{
+ return d->version;
+}
+
+void VCard::setVersion(const QString &v)
+{
+ d->version = v;
+}
+
+const QString &VCard::fullName() const
+{
+ return d->fullName;
+}
+
+void VCard::setFullName(const QString &n)
+{
+ d->fullName = n;
+}
+
+const QString &VCard::familyName() const
+{
+ return d->familyName;
+}
+
+void VCard::setFamilyName(const QString &n)
+{
+ d->familyName = n;
+}
+
+const QString &VCard::givenName() const
+{
+ return d->givenName;
+}
+
+void VCard::setGivenName(const QString &n)
+{
+ d->givenName = n;
+}
+
+const QString &VCard::middleName() const
+{
+ return d->middleName;
+}
+
+void VCard::setMiddleName(const QString &n)
+{
+ d->middleName = n;
+}
+
+const QString &VCard::prefixName() const
+{
+ return d->prefixName;
+}
+
+void VCard::setPrefixName(const QString &p)
+{
+ d->prefixName = p;
+}
+
+const QString &VCard::suffixName() const
+{
+ return d->suffixName;
+}
+
+void VCard::setSuffixName(const QString &s)
+{
+ d->suffixName = s;
+}
+
+const QString &VCard::nickName() const
+{
+ return d->nickName;
+}
+
+void VCard::setNickName(const QString &n)
+{
+ d->nickName = n;
+}
+
+const QByteArray &VCard::photo() const
+{
+ return d->photo;
+}
+
+void VCard::setPhoto(const QByteArray &i)
+{
+ d->photo = i;
+}
+
+const QString &VCard::photoURI() const
+{
+ return d->photoURI;
+}
+
+void VCard::setPhotoURI(const QString &p)
+{
+ d->photoURI = p;
+}
+
+const QDate VCard::bday() const
+{
+ return QDate::fromString(d->bday);
+}
+
+void VCard::setBday(const QDate &date)
+{
+ d->bday = date.toString();
+}
+
+const QString &VCard::bdayStr() const
+{
+ return d->bday;
+}
+
+void VCard::setBdayStr(const QString &date)
+{
+ d->bday = date;
+}
+
+const VCard::AddressList &VCard::addressList() const
+{
+ return d->addressList;
+}
+
+void VCard::setAddressList(const VCard::AddressList &a)
+{
+ d->addressList = a;
+}
+
+const VCard::LabelList &VCard::labelList() const
+{
+ return d->labelList;
+}
+
+void VCard::setLabelList(const VCard::LabelList &l)
+{
+ d->labelList = l;
+}
+
+const VCard::PhoneList &VCard::phoneList() const
+{
+ return d->phoneList;
+}
+
+void VCard::setPhoneList(const VCard::PhoneList &p)
+{
+ d->phoneList = p;
+}
+
+const VCard::EmailList &VCard::emailList() const
+{
+ return d->emailList;
+}
+
+void VCard::setEmailList(const VCard::EmailList &e)
+{
+ d->emailList = e;
+}
+
+const QString &VCard::jid() const
+{
+ return d->jid;
+}
+
+void VCard::setJid(const QString &j)
+{
+ d->jid = j;
+}
+
+const QString &VCard::mailer() const
+{
+ return d->mailer;
+}
+
+void VCard::setMailer(const QString &m)
+{
+ d->mailer = m;
+}
+
+const QString &VCard::timezone() const
+{
+ return d->timezone;
+}
+
+void VCard::setTimezone(const QString &t)
+{
+ d->timezone = t;
+}
+
+const VCard::Geo &VCard::geo() const
+{
+ return d->geo;
+}
+
+void VCard::setGeo(const VCard::Geo &g)
+{
+ d->geo = g;
+}
+
+const QString &VCard::title() const
+{
+ return d->title;
+}
+
+void VCard::setTitle(const QString &t)
+{
+ d->title = t;
+}
+
+const QString &VCard::role() const
+{
+ return d->role;
+}
+
+void VCard::setRole(const QString &r)
+{
+ d->role = r;
+}
+
+const QByteArray &VCard::logo() const
+{
+ return d->logo;
+}
+
+void VCard::setLogo(const QByteArray &i)
+{
+ d->logo = i;
+}
+
+const QString &VCard::logoURI() const
+{
+ return d->logoURI;
+}
+
+void VCard::setLogoURI(const QString &l)
+{
+ d->logoURI = l;
+}
+
+const VCard *VCard::agent() const
+{
+ return d->agent;
+}
+
+void VCard::setAgent(const VCard &v)
+{
+ if ( !d->agent )
+ d->agent = new VCard;
+ *(d->agent) = v;
+}
+
+const QString VCard::agentURI() const
+{
+ return d->agentURI;
+}
+
+void VCard::setAgentURI(const QString &a)
+{
+ d->agentURI = a;
+}
+
+const VCard::Org &VCard::org() const
+{
+ return d->org;
+}
+
+void VCard::setOrg(const VCard::Org &o)
+{
+ d->org = o;
+}
+
+const QStringList &VCard::categories() const
+{
+ return d->categories;
+}
+
+void VCard::setCategories(const QStringList &c)
+{
+ d->categories = c;
+}
+
+const QString &VCard::note() const
+{
+ return d->note;
+}
+
+void VCard::setNote(const QString &n)
+{
+ d->note = n;
+}
+
+const QString &VCard::prodId() const
+{
+ return d->prodId;
+}
+
+void VCard::setProdId(const QString &p)
+{
+ d->prodId = p;
+}
+
+const QString &VCard::rev() const
+{
+ return d->rev;
+}
+
+void VCard::setRev(const QString &r)
+{
+ d->rev = r;
+}
+
+const QString &VCard::sortString() const
+{
+ return d->sortString;
+}
+
+void VCard::setSortString(const QString &s)
+{
+ d->sortString = s;
+}
+
+const QByteArray &VCard::sound() const
+{
+ return d->sound;
+}
+
+void VCard::setSound(const QByteArray &s)
+{
+ d->sound = s;
+}
+
+const QString &VCard::soundURI() const
+{
+ return d->soundURI;
+}
+
+void VCard::setSoundURI(const QString &s)
+{
+ d->soundURI = s;
+}
+
+const QString &VCard::soundPhonetic() const
+{
+ return d->soundPhonetic;
+}
+
+void VCard::setSoundPhonetic(const QString &s)
+{
+ d->soundPhonetic = s;
+}
+
+const QString &VCard::uid() const
+{
+ return d->uid;
+}
+
+void VCard::setUid(const QString &u)
+{
+ d->uid = u;
+}
+
+const QString &VCard::url() const
+{
+ return d->url;
+}
+
+void VCard::setUrl(const QString &u)
+{
+ d->url = u;
+}
+
+const QString &VCard::desc() const
+{
+ return d->desc;
+}
+
+void VCard::setDesc(const QString &desc)
+{
+ d->desc = desc;
+}
+
+const VCard::PrivacyClass &VCard::privacyClass() const
+{
+ return d->privacyClass;
+}
+
+void VCard::setPrivacyClass(const VCard::PrivacyClass &c)
+{
+ d->privacyClass = c;
+}
+
+const QByteArray &VCard::key() const
+{
+ return d->key;
+}
+
+void VCard::setKey(const QByteArray &k)
+{
+ d->key = k;
+}
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_vcard.h b/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_vcard.h
new file mode 100644
index 00000000..ae8cc873
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_vcard.h
@@ -0,0 +1,284 @@
+/*
+ * xmpp_vcard.h - classes for handling vCards
+ * Copyright (C) 2003 Michail Pishchagin
+ *
+ * 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 JABBER_VCARD_H
+#define JABBER_VCARD_H
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qcstring.h>
+
+#include <qvaluelist.h>
+#include <qdom.h>
+
+class QDate;
+
+namespace XMPP
+{
+ class VCard
+ {
+ public:
+ VCard();
+ VCard(const VCard &);
+ VCard & operator=(const VCard &);
+ ~VCard();
+
+ QDomElement toXml(QDomDocument *) const;
+ bool fromXml(const QDomElement &);
+ bool isEmpty() const;
+
+ const QString &version() const;
+ void setVersion(const QString &);
+
+ const QString &fullName() const;
+ void setFullName(const QString &);
+
+
+ const QString &familyName() const;
+ void setFamilyName(const QString &);
+
+ const QString &givenName() const;
+ void setGivenName(const QString &);
+
+ const QString &middleName() const;
+ void setMiddleName(const QString &);
+
+ const QString &prefixName() const;
+ void setPrefixName(const QString &);
+
+ const QString &suffixName() const;
+ void setSuffixName(const QString &);
+
+
+ const QString &nickName() const;
+ void setNickName(const QString &);
+
+
+ const QByteArray &photo() const;
+ void setPhoto(const QByteArray &);
+
+ const QString &photoURI() const;
+ void setPhotoURI(const QString &);
+
+
+ const QDate bday() const;
+ void setBday(const QDate &);
+
+ const QString &bdayStr() const;
+ void setBdayStr(const QString &);
+
+
+ class Address {
+ public:
+ Address();
+
+ bool home;
+ bool work;
+ bool postal;
+ bool parcel;
+
+ bool dom;
+ bool intl;
+
+ bool pref;
+
+ QString pobox;
+ QString extaddr;
+ QString street;
+ QString locality;
+ QString region;
+ QString pcode;
+ QString country;
+ };
+ typedef QValueList<Address> AddressList;
+ const AddressList &addressList() const;
+ void setAddressList(const AddressList &);
+
+ class Label {
+ public:
+ Label();
+
+ bool home;
+ bool work;
+ bool postal;
+ bool parcel;
+
+ bool dom;
+ bool intl;
+
+ bool pref;
+
+ QStringList lines;
+ };
+ typedef QValueList<Label> LabelList;
+ const LabelList &labelList() const;
+ void setLabelList(const LabelList &);
+
+
+ class Phone {
+ public:
+ Phone();
+
+ bool home;
+ bool work;
+ bool voice;
+ bool fax;
+ bool pager;
+ bool msg;
+ bool cell;
+ bool video;
+ bool bbs;
+ bool modem;
+ bool isdn;
+ bool pcs;
+ bool pref;
+
+ QString number;
+ };
+ typedef QValueList<Phone> PhoneList;
+ const PhoneList &phoneList() const;
+ void setPhoneList(const PhoneList &);
+
+
+ class Email {
+ public:
+ Email();
+
+ bool home;
+ bool work;
+ bool internet;
+ bool x400;
+
+ QString userid;
+ };
+ typedef QValueList<Email> EmailList;
+ const EmailList &emailList() const;
+ void setEmailList(const EmailList &);
+
+
+ const QString &jid() const;
+ void setJid(const QString &);
+
+ const QString &mailer() const;
+ void setMailer(const QString &);
+
+ const QString &timezone() const;
+ void setTimezone(const QString &);
+
+
+ class Geo {
+ public:
+ Geo();
+
+ QString lat;
+ QString lon;
+ };
+ const Geo &geo() const;
+ void setGeo(const Geo &);
+
+
+ const QString &title() const;
+ void setTitle(const QString &);
+
+ const QString &role() const;
+ void setRole(const QString &);
+
+
+ const QByteArray &logo() const;
+ void setLogo(const QByteArray &);
+
+ const QString &logoURI() const;
+ void setLogoURI(const QString &);
+
+
+ const VCard *agent() const;
+ void setAgent(const VCard &);
+
+ const QString agentURI() const;
+ void setAgentURI(const QString &);
+
+
+ class Org {
+ public:
+ Org();
+
+ QString name;
+ QStringList unit;
+ };
+ const Org &org() const;
+ void setOrg(const Org &);
+
+
+ const QStringList &categories() const;
+ void setCategories(const QStringList &);
+
+ const QString &note() const;
+ void setNote(const QString &);
+
+ const QString &prodId() const; // it must equal to "Psi" ;-)
+ void setProdId(const QString &);
+
+ const QString &rev() const;
+ void setRev(const QString &);
+
+ const QString &sortString() const;
+ void setSortString(const QString &);
+
+
+ const QByteArray &sound() const;
+ void setSound(const QByteArray &);
+
+ const QString &soundURI() const;
+ void setSoundURI(const QString &);
+
+ const QString &soundPhonetic() const;
+ void setSoundPhonetic(const QString &);
+
+
+ const QString &uid() const;
+ void setUid(const QString &);
+
+ const QString &url() const;
+ void setUrl(const QString &);
+
+ const QString &desc() const;
+ void setDesc(const QString &);
+
+
+ enum PrivacyClass {
+ pcNone = 0,
+ pcPublic = 1,
+ pcPrivate,
+ pcConfidential
+ };
+ const PrivacyClass &privacyClass() const;
+ void setPrivacyClass(const PrivacyClass &);
+
+
+ const QByteArray &key() const;
+ void setKey(const QByteArray &);
+
+ private:
+ class Private;
+ Private *d;
+ };
+}
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_xmlcommon.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_xmlcommon.cpp
new file mode 100644
index 00000000..2715faf8
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_xmlcommon.cpp
@@ -0,0 +1,386 @@
+/*
+ * xmlcommon.cpp - helper functions for dealing with XML
+ * Copyright (C) 2001, 2002 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include"xmpp_xmlcommon.h"
+
+#include <qstring.h>
+#include <qdom.h>
+#include <qdatetime.h>
+#include <qsize.h>
+#include <qrect.h>
+#include <qstringlist.h>
+#include <qcolor.h>
+
+#include"im.h"
+
+bool stamp2TS(const QString &ts, QDateTime *d)
+{
+ if(ts.length() != 17)
+ return false;
+
+ int year = ts.mid(0,4).toInt();
+ int month = ts.mid(4,2).toInt();
+ int day = ts.mid(6,2).toInt();
+
+ int hour = ts.mid(9,2).toInt();
+ int min = ts.mid(12,2).toInt();
+ int sec = ts.mid(15,2).toInt();
+
+ QDate xd;
+ xd.setYMD(year, month, day);
+ if(!xd.isValid())
+ return false;
+
+ QTime xt;
+ xt.setHMS(hour, min, sec);
+ if(!xt.isValid())
+ return false;
+
+ d->setDate(xd);
+ d->setTime(xt);
+
+ return true;
+}
+
+QString TS2stamp(const QDateTime &d)
+{
+ QString str;
+
+ str.sprintf("%04d%02d%02dT%02d:%02d:%02d",
+ d.date().year(),
+ d.date().month(),
+ d.date().day(),
+ d.time().hour(),
+ d.time().minute(),
+ d.time().second());
+
+ return str;
+}
+
+QDomElement textTag(QDomDocument *doc, const QString &name, const QString &content)
+{
+ QDomElement tag = doc->createElement(name);
+ QDomText text = doc->createTextNode(content);
+ tag.appendChild(text);
+
+ return tag;
+}
+
+QString tagContent(const QDomElement &e)
+{
+ // look for some tag content
+ for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomText i = n.toText();
+ if(i.isNull())
+ continue;
+ return i.data();
+ }
+
+ return "";
+}
+
+QDomElement findSubTag(const QDomElement &e, const QString &name, bool *found)
+{
+ if(found)
+ *found = false;
+
+ for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+ if(i.tagName() == name) {
+ if(found)
+ *found = true;
+ return i;
+ }
+ }
+
+ QDomElement tmp;
+ return tmp;
+}
+
+QDomElement createIQ(QDomDocument *doc, const QString &type, const QString &to, const QString &id)
+{
+ QDomElement iq = doc->createElement("iq");
+ if(!type.isEmpty())
+ iq.setAttribute("type", type);
+ if(!to.isEmpty())
+ iq.setAttribute("to", to);
+ if(!id.isEmpty())
+ iq.setAttribute("id", id);
+
+ return iq;
+}
+
+QDomElement queryTag(const QDomElement &e)
+{
+ bool found;
+ QDomElement q = findSubTag(e, "query", &found);
+ return q;
+}
+
+QString queryNS(const QDomElement &e)
+{
+ bool found;
+ QDomElement q = findSubTag(e, "query", &found);
+ if(found)
+ return q.attribute("xmlns");
+
+ return "";
+}
+
+void getErrorFromElement(const QDomElement &e, int *code, QString *str)
+{
+ bool found;
+ QDomElement tag = findSubTag(e, "error", &found);
+ if(!found)
+ return;
+
+ if(code)
+ *code = tag.attribute("code").toInt();
+ if(str)
+ *str = tagContent(tag);
+}
+
+//----------------------------------------------------------------------------
+// XMLHelper
+//----------------------------------------------------------------------------
+
+namespace XMLHelper {
+
+QDomElement emptyTag(QDomDocument *doc, const QString &name)
+{
+ QDomElement tag = doc->createElement(name);
+
+ return tag;
+}
+
+bool hasSubTag(const QDomElement &e, const QString &name)
+{
+ bool found;
+ findSubTag(e, name, &found);
+ return found;
+}
+
+QString subTagText(const QDomElement &e, const QString &name)
+{
+ bool found;
+ QDomElement i = findSubTag(e, name, &found);
+ if ( found )
+ return i.text();
+ return QString::null;
+}
+
+QDomElement textTag(QDomDocument &doc, const QString &name, const QString &content)
+{
+ QDomElement tag = doc.createElement(name);
+ QDomText text = doc.createTextNode(content);
+ tag.appendChild(text);
+
+ return tag;
+}
+
+QDomElement textTag(QDomDocument &doc, const QString &name, int content)
+{
+ QDomElement tag = doc.createElement(name);
+ QDomText text = doc.createTextNode(QString::number(content));
+ tag.appendChild(text);
+
+ return tag;
+}
+
+QDomElement textTag(QDomDocument &doc, const QString &name, bool content)
+{
+ QDomElement tag = doc.createElement(name);
+ QDomText text = doc.createTextNode(content ? "true" : "false");
+ tag.appendChild(text);
+
+ return tag;
+}
+
+QDomElement textTag(QDomDocument &doc, const QString &name, QSize &s)
+{
+ QString str;
+ str.sprintf("%d,%d", s.width(), s.height());
+
+ QDomElement tag = doc.createElement(name);
+ QDomText text = doc.createTextNode(str);
+ tag.appendChild(text);
+
+ return tag;
+}
+
+QDomElement textTag(QDomDocument &doc, const QString &name, QRect &r)
+{
+ QString str;
+ str.sprintf("%d,%d,%d,%d", r.x(), r.y(), r.width(), r.height());
+
+ QDomElement tag = doc.createElement(name);
+ QDomText text = doc.createTextNode(str);
+ tag.appendChild(text);
+
+ return tag;
+}
+
+QDomElement stringListToXml(QDomDocument &doc, const QString &name, const QStringList &l)
+{
+ QDomElement tag = doc.createElement(name);
+ for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it)
+ tag.appendChild(textTag(doc, "item", *it));
+
+ return tag;
+}
+
+/*QString tagContent(const QDomElement &e)
+{
+ // look for some tag content
+ for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomText i = n.toText();
+ if(i.isNull())
+ continue;
+ return i.data();
+ }
+
+ return "";
+}*/
+
+/*QDomElement findSubTag(const QDomElement &e, const QString &name, bool *found)
+{
+ if(found)
+ *found = FALSE;
+
+ for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+ if(i.tagName() == name) {
+ if(found)
+ *found = TRUE;
+ return i;
+ }
+ }
+
+ QDomElement tmp;
+ return tmp;
+}*/
+
+void readEntry(const QDomElement &e, const QString &name, QString *v)
+{
+ bool found = FALSE;
+ QDomElement tag = findSubTag(e, name, &found);
+ if(!found)
+ return;
+ *v = tagContent(tag);
+}
+
+void readNumEntry(const QDomElement &e, const QString &name, int *v)
+{
+ bool found = FALSE;
+ QDomElement tag = findSubTag(e, name, &found);
+ if(!found)
+ return;
+ *v = tagContent(tag).toInt();
+}
+
+void readBoolEntry(const QDomElement &e, const QString &name, bool *v)
+{
+ bool found = FALSE;
+ QDomElement tag = findSubTag(e, name, &found);
+ if(!found)
+ return;
+ *v = (tagContent(tag) == "true") ? TRUE: FALSE;
+}
+
+void readSizeEntry(const QDomElement &e, const QString &name, QSize *v)
+{
+ bool found = FALSE;
+ QDomElement tag = findSubTag(e, name, &found);
+ if(!found)
+ return;
+ QStringList list = QStringList::split(',', tagContent(tag));
+ if(list.count() != 2)
+ return;
+ QSize s;
+ s.setWidth(list[0].toInt());
+ s.setHeight(list[1].toInt());
+ *v = s;
+}
+
+void readRectEntry(const QDomElement &e, const QString &name, QRect *v)
+{
+ bool found = FALSE;
+ QDomElement tag = findSubTag(e, name, &found);
+ if(!found)
+ return;
+ QStringList list = QStringList::split(',', tagContent(tag));
+ if(list.count() != 4)
+ return;
+ QRect r;
+ r.setX(list[0].toInt());
+ r.setY(list[1].toInt());
+ r.setWidth(list[2].toInt());
+ r.setHeight(list[3].toInt());
+ *v = r;
+}
+
+void readColorEntry(const QDomElement &e, const QString &name, QColor *v)
+{
+ bool found = FALSE;
+ QDomElement tag = findSubTag(e, name, &found);
+ if(!found)
+ return;
+ QColor c;
+ c.setNamedColor(tagContent(tag));
+ if(c.isValid())
+ *v = c;
+}
+
+void xmlToStringList(const QDomElement &e, const QString &name, QStringList *v)
+{
+ bool found = false;
+ QDomElement tag = findSubTag(e, name, &found);
+ if(!found)
+ return;
+ QStringList list;
+ for(QDomNode n = tag.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+ if(i.tagName() == "item")
+ list += tagContent(i);
+ }
+ *v = list;
+}
+
+void setBoolAttribute(QDomElement e, const QString &name, bool b)
+{
+ e.setAttribute(name, b ? "true" : "false");
+}
+
+void readBoolAttribute(QDomElement e, const QString &name, bool *v)
+{
+ if(e.hasAttribute(name)) {
+ QString s = e.attribute(name);
+ *v = (s == "true") ? TRUE: FALSE;
+ }
+}
+
+}
+
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_xmlcommon.h b/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_xmlcommon.h
new file mode 100644
index 00000000..f0499c4b
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_xmlcommon.h
@@ -0,0 +1,71 @@
+/*
+ * xmlcommon.h - helper functions for dealing with XML
+ * Copyright (C) 2001, 2002 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef JABBER_XMLCOMMON_H
+#define JABBER_XMLCOMMON_H
+
+#include<qdom.h>
+
+class QDateTime;
+class QRect;
+class QSize;
+class QColor;
+class QStringList;
+
+bool stamp2TS(const QString &ts, QDateTime *d);
+QString TS2stamp(const QDateTime &d);
+QDomElement textTag(QDomDocument *doc, const QString &name, const QString &content);
+QString tagContent(const QDomElement &e);
+QDomElement findSubTag(const QDomElement &e, const QString &name, bool *found);
+QDomElement createIQ(QDomDocument *doc, const QString &type, const QString &to, const QString &id);
+QDomElement queryTag(const QDomElement &e);
+QString queryNS(const QDomElement &e);
+void getErrorFromElement(const QDomElement &e, int *code, QString *str);
+
+namespace XMLHelper {
+ //QDomElement findSubTag(const QDomElement &e, const QString &name, bool *found);
+ bool hasSubTag(const QDomElement &e, const QString &name);
+
+ QDomElement emptyTag(QDomDocument *doc, const QString &name);
+ QString subTagText(const QDomElement &e, const QString &name);
+
+ QDomElement textTag(QDomDocument &doc, const QString &name, const QString &content);
+ QDomElement textTag(QDomDocument &doc, const QString &name, int content);
+ QDomElement textTag(QDomDocument &doc, const QString &name, bool content);
+ QDomElement textTag(QDomDocument &doc, const QString &name, QSize &s);
+ QDomElement textTag(QDomDocument &doc, const QString &name, QRect &r);
+ QDomElement stringListToXml(QDomDocument &doc, const QString &name, const QStringList &l);
+
+ void readEntry(const QDomElement &e, const QString &name, QString *v);
+ void readNumEntry(const QDomElement &e, const QString &name, int *v);
+ void readBoolEntry(const QDomElement &e, const QString &name, bool *v);
+ void readSizeEntry(const QDomElement &e, const QString &name, QSize *v);
+ void readRectEntry(const QDomElement &e, const QString &name, QRect *v);
+ void readColorEntry(const QDomElement &e, const QString &name, QColor *v);
+
+ void xmlToStringList(const QDomElement &e, const QString &name, QStringList *v);
+
+ void setBoolAttribute(QDomElement e, const QString &name, bool b);
+ void readBoolAttribute(QDomElement e, const QString &name, bool *v);
+
+ //QString tagContent(const QDomElement &e); // obsolete;
+}
+
+#endif