summaryrefslogtreecommitdiffstats
path: root/kopete/protocols/jabber/libiris
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitbcb704366cb5e333a626c18c308c7e0448a8e69f (patch)
treef0d6ab7d78ecdd9207cf46536376b44b91a1ca71 /kopete/protocols/jabber/libiris
downloadtdenetwork-bcb704366cb5e333a626c18c308c7e0448a8e69f.tar.gz
tdenetwork-bcb704366cb5e333a626c18c308c7e0448a8e69f.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdenetwork@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kopete/protocols/jabber/libiris')
-rw-r--r--kopete/protocols/jabber/libiris/001_last_activity.patch113
-rw-r--r--kopete/protocols/jabber/libiris/002_offline_event.patch17
-rw-r--r--kopete/protocols/jabber/libiris/003_case_insensitive_jid.patch14
-rw-r--r--kopete/protocols/jabber/libiris/004_xhtml_im.patch266
-rw-r--r--kopete/protocols/jabber/libiris/005_join_muc_with_password.patch163
-rw-r--r--kopete/protocols/jabber/libiris/006_private_storage.patch130
-rw-r--r--kopete/protocols/jabber/libiris/007_chatstates.patch132
-rw-r--r--kopete/protocols/jabber/libiris/008_chatstatesfix.patch38
-rw-r--r--kopete/protocols/jabber/libiris/Makefile.am2
-rw-r--r--kopete/protocols/jabber/libiris/README_BEFORE_COMMITTING21
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/Makefile.am1
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/README13
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/TODO25
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/Makefile.am16
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/bsocket.cpp394
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/bsocket.h87
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.cpp369
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.h67
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/httppoll.cpp666
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/httppoll.h104
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/ndns.cpp378
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/ndns.h88
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/servsock.cpp112
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/servsock.h68
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/socks.cpp1223
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/socks.h160
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.cpp320
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.h65
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/Makefile.am12
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/TODO7
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/base64.cpp182
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/base64.h40
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/bytestream.cpp268
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/bytestream.h78
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/cipher.cpp357
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/cipher.h79
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/qrandom.cpp24
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/qrandom.h14
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/safedelete.cpp119
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/safedelete.h60
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/sha1.cpp196
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/sha1.h63
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/showtextdlg.cpp61
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/showtextdlg.h33
-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
-rw-r--r--kopete/protocols/jabber/libiris/jingle_iris.patch432
-rw-r--r--kopete/protocols/jabber/libiris/qca/COPYING504
-rw-r--r--kopete/protocols/jabber/libiris/qca/INSTALL12
-rw-r--r--kopete/protocols/jabber/libiris/qca/Makefile.am1
-rw-r--r--kopete/protocols/jabber/libiris/qca/README29
-rw-r--r--kopete/protocols/jabber/libiris/qca/TODO6
-rw-r--r--kopete/protocols/jabber/libiris/qca/src/Makefile.am7
-rw-r--r--kopete/protocols/jabber/libiris/qca/src/qca.cpp1481
-rw-r--r--kopete/protocols/jabber/libiris/qca/src/qca.h466
-rw-r--r--kopete/protocols/jabber/libiris/qca/src/qcaprovider.h191
98 files changed, 32803 insertions, 0 deletions
diff --git a/kopete/protocols/jabber/libiris/001_last_activity.patch b/kopete/protocols/jabber/libiris/001_last_activity.patch
new file mode 100644
index 00000000..24673e80
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/001_last_activity.patch
@@ -0,0 +1,113 @@
+Index: iris/xmpp-im/xmpp_tasks.h
+===================================================================
+--- iris/xmpp-im/xmpp_tasks.h (revision 419672)
++++ iris/xmpp-im/xmpp_tasks.h (working copy)
+@@ -195,6 +195,29 @@
+ 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
+Index: iris/xmpp-im/xmpp_tasks.cpp
+===================================================================
+--- iris/xmpp-im/xmpp_tasks.cpp (revision 419672)
++++ iris/xmpp-im/xmpp_tasks.cpp (working copy)
+@@ -773,6 +773,74 @@
+
+
+ //----------------------------------------------------------------------------
++// 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)
diff --git a/kopete/protocols/jabber/libiris/002_offline_event.patch b/kopete/protocols/jabber/libiris/002_offline_event.patch
new file mode 100644
index 00000000..dfaa1f8e
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/002_offline_event.patch
@@ -0,0 +1,17 @@
+? 002_offline_event.patch
+Index: iris/xmpp-im/types.cpp
+===================================================================
+RCS file: /home/kde/kdenetwork/kopete/protocols/jabber/libiris/iris/xmpp-im/types.cpp,v
+retrieving revision 1.3
+diff -u -p -r1.3 types.cpp
+--- iris/xmpp-im/types.cpp 21 May 2004 14:35:44 -0000 1.3
++++ iris/xmpp-im/types.cpp 5 Feb 2005 21:04:44 -0000
+@@ -639,6 +639,8 @@ bool Message::fromStanza(const Stanza &s
+ d->eventList += ComposingEvent;
+ else if (evtag == "delivered")
+ d->eventList += DeliveredEvent;
++ else if (evtag == "offline")
++ d->eventList += OfflineEvent;
+ }
+ if (d->eventList.isEmpty())
+ d->eventList += CancelEvent;
diff --git a/kopete/protocols/jabber/libiris/003_case_insensitive_jid.patch b/kopete/protocols/jabber/libiris/003_case_insensitive_jid.patch
new file mode 100644
index 00000000..d4b0e285
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/003_case_insensitive_jid.patch
@@ -0,0 +1,14 @@
+Index: iris/xmpp-core/jid.cpp
+===================================================================
+--- iris/xmpp-core/jid.cpp (revision 469141)
++++ iris/xmpp-core/jid.cpp (working copy)
+@@ -233,6 +233,9 @@
+ b = d;
+ else
+ b = n + '@' + d;
++
++ b=b.lower(); // JID are not case sensitive
++
+ if(r.isEmpty())
+ f = b;
+ else
diff --git a/kopete/protocols/jabber/libiris/004_xhtml_im.patch b/kopete/protocols/jabber/libiris/004_xhtml_im.patch
new file mode 100644
index 00000000..990ab4f7
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/004_xhtml_im.patch
@@ -0,0 +1,266 @@
+Index: iris/include/xmpp.h
+===================================================================
+--- iris/include/xmpp.h (revision 470311)
++++ iris/include/xmpp.h (working copy)
+@@ -318,8 +318,11 @@
+
+ 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;
+@@ -372,6 +375,8 @@
+
+ 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;
+@@ -479,6 +484,8 @@
+ // reimplemented
+ QDomDocument & doc() const;
+ QString baseNS() const;
++ QString xhtmlImNS() const;
++ QString xhtmlNS() const;
+ bool old() const;
+
+ void close();
+Index: iris/include/im.h
+===================================================================
+--- iris/include/im.h (revision 470311)
++++ iris/include/im.h (working copy)
+@@ -65,6 +65,7 @@
+ 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;
+
+@@ -75,6 +76,7 @@
+ 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);
+
+@@ -286,6 +288,7 @@
+ bool canSearch() const;
+ bool canGroupchat() const;
+ bool canDisco() const;
++ bool canXHTML() const;
+ bool isGateway() const;
+ bool haveVCard() const;
+
+@@ -298,6 +301,7 @@
+ FID_Disco,
+ FID_Gateway,
+ FID_VCard,
++ FID_Xhtml,
+
+ // private Psi actions
+ FID_Add
+Index: iris/xmpp-im/types.cpp
+===================================================================
+--- iris/xmpp-im/types.cpp (revision 470311)
++++ iris/xmpp-im/types.cpp (working copy)
+@@ -19,7 +19,7 @@
+ */
+
+ #include"im.h"
+-
++#include "protocol.h"
+ #include<qmap.h>
+ #include<qapplication.h>
+
+@@ -180,7 +180,8 @@
+ Jid to, from;
+ QString id, type, lang;
+
+- StringMap subject, body;
++ StringMap subject, body, xHTMLBody;
++
+ QString thread;
+ Stanza::Error error;
+
+@@ -279,6 +280,11 @@
+ return d->body[lang];
+ }
+
++QString Message::xHTMLBody(const QString &lang) const
++{
++ return d->xHTMLBody[lang];
++}
++
+ QString Message::thread() const
+ {
+ return d->thread;
+@@ -340,9 +346,16 @@
+ void Message::setBody(const QString &s, const QString &lang)
+ {
+ d->body[lang] = s;
+- //d->flag = false;
+ }
+
++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;
+@@ -489,7 +502,19 @@
+ 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);
+
+@@ -591,6 +616,21 @@
+ 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 {
+ //printf("extension element: [%s]\n", e.tagName().latin1());
+ }
+@@ -1418,6 +1458,16 @@
+ 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
+ {
+Index: iris/xmpp-im/xmpp_tasks.cpp
+===================================================================
+--- iris/xmpp-im/xmpp_tasks.cpp (revision 470311)
++++ iris/xmpp-im/xmpp_tasks.cpp (working copy)
+@@ -1348,6 +1348,10 @@
+ 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/si/profile/file-transfer");
+ query.appendChild(feature);
+
+Index: iris/xmpp-core/protocol.h
+===================================================================
+--- iris/xmpp-core/protocol.h (revision 470311)
++++ iris/xmpp-core/protocol.h (working copy)
+@@ -35,6 +35,8 @@
+ #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"
+
+ namespace XMPP
+ {
+Index: iris/xmpp-core/stream.cpp
+===================================================================
+--- iris/xmpp-core/stream.cpp (revision 470311)
++++ iris/xmpp-core/stream.cpp (working copy)
+@@ -293,6 +293,16 @@
+ 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);
+@@ -305,6 +315,16 @@
+ 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);
+@@ -861,6 +881,16 @@
+ 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;
diff --git a/kopete/protocols/jabber/libiris/005_join_muc_with_password.patch b/kopete/protocols/jabber/libiris/005_join_muc_with_password.patch
new file mode 100644
index 00000000..058825db
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/005_join_muc_with_password.patch
@@ -0,0 +1,163 @@
+Index: iris/include/im.h
+===================================================================
+--- iris/include/im.h (révision 498969)
++++ iris/include/im.h (copie de travail)
+@@ -607,6 +607,7 @@
+ 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);
+Index: iris/xmpp-im/client.cpp
+===================================================================
+--- iris/xmpp-im/client.cpp (révision 498969)
++++ iris/xmpp-im/client.cpp (copie de travail)
+@@ -315,6 +315,35 @@
+ 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);
+Index: iris/xmpp-im/xmpp_tasks.h
+===================================================================
+--- iris/xmpp-im/xmpp_tasks.h (révision 498969)
++++ iris/xmpp-im/xmpp_tasks.h (copie de travail)
+@@ -439,6 +439,26 @@
+ 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;
++ };
+ }
+
+ #endif
+Index: iris/xmpp-im/xmpp_tasks.cpp
+===================================================================
+--- iris/xmpp-im/xmpp_tasks.cpp (révision 498969)
++++ iris/xmpp-im/xmpp_tasks.cpp (copie de travail)
+@@ -1956,3 +1956,75 @@
+ 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();
++}
diff --git a/kopete/protocols/jabber/libiris/006_private_storage.patch b/kopete/protocols/jabber/libiris/006_private_storage.patch
new file mode 100644
index 00000000..288d24c5
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/006_private_storage.patch
@@ -0,0 +1,130 @@
+Index: iris/xmpp-im/xmpp_tasks.h
+===================================================================
+--- iris/xmpp-im/xmpp_tasks.h (revision 499691)
++++ iris/xmpp-im/xmpp_tasks.h (working copy)
+@@ -459,6 +459,27 @@
+ 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
+Index: iris/xmpp-im/xmpp_tasks.cpp
+===================================================================
+--- iris/xmpp-im/xmpp_tasks.cpp (revision 499691)
++++ iris/xmpp-im/xmpp_tasks.cpp (working copy)
+@@ -2028,3 +2028,93 @@
+ 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/007_chatstates.patch b/kopete/protocols/jabber/libiris/007_chatstates.patch
new file mode 100644
index 00000000..af32728c
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/007_chatstates.patch
@@ -0,0 +1,132 @@
+Index: iris/include/im.h
+===================================================================
+--- iris/include/im.h (revision 525193)
++++ iris/include/im.h (working copy)
+@@ -49,7 +49,7 @@
+ typedef QValueList<Url> UrlList;
+ typedef QMap<QString, QString> StringMap;
+ typedef enum { OfflineEvent, DeliveredEvent, DisplayedEvent,
+- ComposingEvent, CancelEvent } MsgEvent;
++ ComposingEvent, CancelEvent, InactiveEvent, GoneEvent } MsgEvent;
+
+ class Message
+ {
+Index: iris/xmpp-im/types.cpp
+===================================================================
+--- iris/xmpp-im/types.cpp (revision 525193)
++++ iris/xmpp-im/types.cpp (working copy)
+@@ -544,28 +544,49 @@
+ else
+ x.appendChild(s.createTextElement("jabber:x:event","id",d->eventId));
+ }
++ else
++ 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())
++ s.appendChild( s.createElement(NS_CHATSTATES , "composing" ) );
+ break;
+ case CancelEvent:
+- // Add nothing
++ need_x_event=true;
++ if (d->body.isEmpty())
++ s.appendChild( s.createElement(NS_CHATSTATES , "paused" ) );
+ break;
++ case InactiveEvent:
++ if (d->body.isEmpty())
++ s.appendChild( s.createElement(NS_CHATSTATES , "inactive" ) );
++ break;
++ case GoneEvent:
++ if (d->body.isEmpty())
++ s.appendChild( s.createElement(NS_CHATSTATES , "gone" ) );
++ break;
+ }
+ }
+- s.appendChild(x);
+- }
++ 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())
+@@ -595,6 +616,7 @@
+ d->subject.clear();
+ d->body.clear();
+ d->thread = QString();
++ d->eventList.clear();
+
+ QDomElement root = s.element();
+
+@@ -631,6 +653,33 @@
+ }
+ }
+ }
++ 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());
+ }
+@@ -664,7 +713,6 @@
+ }
+
+ // events
+- d->eventList.clear();
+ nl = root.elementsByTagNameNS("jabber:x:event", "x");
+ if (nl.count()) {
+ nl = nl.item(0).childNodes();
+Index: iris/xmpp-core/protocol.h
+===================================================================
+--- iris/xmpp-core/protocol.h (revision 525193)
++++ iris/xmpp-core/protocol.h (working copy)
+@@ -37,6 +37,7 @@
+ #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
+ {
diff --git a/kopete/protocols/jabber/libiris/008_chatstatesfix.patch b/kopete/protocols/jabber/libiris/008_chatstatesfix.patch
new file mode 100644
index 00000000..63a4f680
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/008_chatstatesfix.patch
@@ -0,0 +1,38 @@
+Index: iris/xmpp-im/types.cpp
+===================================================================
+--- iris/xmpp-im/types.cpp (revision 526236)
++++ iris/xmpp-im/types.cpp (working copy)
+@@ -544,7 +544,7 @@
+ else
+ x.appendChild(s.createTextElement("jabber:x:event","id",d->eventId));
+ }
+- else
++ else if (d->type=="chat" || d->type=="groupchat")
+ s.appendChild( s.createElement(NS_CHATSTATES , "active" ) );
+
+ bool need_x_event=false;
+@@ -565,20 +565,20 @@
+ case ComposingEvent:
+ x.appendChild(s.createElement("jabber:x:event", "composing"));
+ need_x_event=true;
+- if (d->body.isEmpty())
++ 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())
++ if (d->body.isEmpty() && (d->type=="chat" || d->type=="groupchat") )
+ s.appendChild( s.createElement(NS_CHATSTATES , "paused" ) );
+ break;
+ case InactiveEvent:
+- if (d->body.isEmpty())
++ if (d->body.isEmpty() && (d->type=="chat" || d->type=="groupchat") )
+ s.appendChild( s.createElement(NS_CHATSTATES , "inactive" ) );
+ break;
+ case GoneEvent:
+- if (d->body.isEmpty())
++ if (d->body.isEmpty() && (d->type=="chat" || d->type=="groupchat") )
+ s.appendChild( s.createElement(NS_CHATSTATES , "gone" ) );
+ break;
+ }
diff --git a/kopete/protocols/jabber/libiris/Makefile.am b/kopete/protocols/jabber/libiris/Makefile.am
new file mode 100644
index 00000000..a80d204c
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/Makefile.am
@@ -0,0 +1,2 @@
+SUBDIRS = iris qca cutestuff
+
diff --git a/kopete/protocols/jabber/libiris/README_BEFORE_COMMITTING b/kopete/protocols/jabber/libiris/README_BEFORE_COMMITTING
new file mode 100644
index 00000000..1fd42d3a
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/README_BEFORE_COMMITTING
@@ -0,0 +1,21 @@
+This library is the xmpp backend also used in Psi. (http://psi.affinix.com)
+The main author is Justin Karneges (infiniti@affinix.com) and other
+Psi developers, see the Psi homepage for details.
+
+Please DO NOT change the source unless really necessary. This is a
+third-party library and any change will make synching very hard in the
+future. It is best to send patches upstream so they'll end up in the
+main tree. We will benefit from them at the next synch point.
+
+If you really really need to make a change to one of the source files,
+please make sure to commit a diff to the original file in this directory in
+the form of 001_your_fix_name.patch. Always pick the next free number
+for your patch, the version found in this directory is meant to have
+all patches applied in order. When committing, CCMAIL kopete-devel@kde.org.
+
+Changes to the Makefile.am files are fine and require no diffs, since Psi
+uses qmake.
+
+This library depends on: libidn (compile time), qca-tls (runtime)
+
+27.02.2004, Till Gerken (till@tantalo.net)
diff --git a/kopete/protocols/jabber/libiris/cutestuff/Makefile.am b/kopete/protocols/jabber/libiris/cutestuff/Makefile.am
new file mode 100644
index 00000000..8f579310
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = network util
diff --git a/kopete/protocols/jabber/libiris/cutestuff/README b/kopete/protocols/jabber/libiris/cutestuff/README
new file mode 100644
index 00000000..c4509acc
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/README
@@ -0,0 +1,13 @@
+iconset - generic classes for handling iconsets / animations
+idle - detecting desktop idle
+input - making life easier with text input (including richtext)
+openpgp - pgp/gpg classes
+richtext - richtext parsing function, xhtml conversion
+ssl - SSL
+tray - desktop tray icon
+util - various things, see util/TODO
+globalaccel - global hotkeys
+network - sockets, servers, dns, and proxies
+sasl - SASL library
+xmlsec - XML Encryption
+crash - generates some (hopefully useful) feedback when program crashes
diff --git a/kopete/protocols/jabber/libiris/cutestuff/TODO b/kopete/protocols/jabber/libiris/cutestuff/TODO
new file mode 100644
index 00000000..e897c854
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/TODO
@@ -0,0 +1,25 @@
+test:
+ httppoll
+ sasl
+ xmlenc
+
+code:
+ bsocket: 'maintain' internal sockets even after destruct (till flush)
+ qssl: server support
+ securestream: wrap QSSLFilter as ByteStream
+ qrandom: better randomness (use /dev/urandom on unix, srand on windows)
+ floating TODOs in gnupg, gpgproc ?
+ import misha's code
+ finish globalaccel
+ finish dirwatch
+ trayicon?
+
+port:
+ win32: bconsole
+
+document:
+ sha1
+ servsock
+ srvresolver
+ bsocket
+
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/Makefile.am b/kopete/protocols/jabber/libiris/cutestuff/network/Makefile.am
new file mode 100644
index 00000000..5e370089
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/Makefile.am
@@ -0,0 +1,16 @@
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libcutestuff_network.la
+INCLUDES = -I$(srcdir)/../util -I$(srcdir)/../../qca/src $(all_includes)
+
+libcutestuff_network_la_SOURCES = \
+ bsocket.cpp \
+ httpconnect.cpp \
+ httppoll.cpp \
+ ndns.cpp \
+ servsock.cpp \
+ socks.cpp \
+ srvresolver.cpp
+
+KDE_OPTIONS = nofinal
+
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/bsocket.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/bsocket.cpp
new file mode 100644
index 00000000..57e5fe66
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/bsocket.cpp
@@ -0,0 +1,394 @@
+/*
+ * bsocket.cpp - QSocket wrapper based on Bytestream with SRV DNS support
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include"bsocket.h"
+
+#include<qcstring.h>
+#include<qsocket.h>
+#include<qdns.h>
+#include<qguardedptr.h>
+#include"safedelete.h"
+#ifndef NO_NDNS
+#include"ndns.h"
+#endif
+#include"srvresolver.h"
+
+#ifdef BS_DEBUG
+#include<stdio.h>
+#endif
+
+#define READBUFSIZE 65536
+
+// CS_NAMESPACE_BEGIN
+
+class BSocket::Private
+{
+public:
+ Private()
+ {
+ qsock = 0;
+ }
+
+ QSocket *qsock;
+ int state;
+
+#ifndef NO_NDNS
+ NDns ndns;
+#endif
+ SrvResolver srv;
+ QString host;
+ int port;
+ SafeDelete sd;
+};
+
+BSocket::BSocket(QObject *parent)
+:ByteStream(parent)
+{
+ d = new Private;
+#ifndef NO_NDNS
+ connect(&d->ndns, SIGNAL(resultsReady()), SLOT(ndns_done()));
+#endif
+ connect(&d->srv, SIGNAL(resultsReady()), SLOT(srv_done()));
+
+ reset();
+}
+
+BSocket::~BSocket()
+{
+ reset(true);
+ delete d;
+}
+
+void BSocket::reset(bool clear)
+{
+ if(d->qsock) {
+ d->qsock->disconnect(this);
+
+ if(!clear && d->qsock->isOpen()) {
+ // move remaining into the local queue
+ QByteArray block(d->qsock->bytesAvailable());
+ d->qsock->readBlock(block.data(), block.size());
+ appendRead(block);
+ }
+
+ d->sd.deleteLater(d->qsock);
+ d->qsock = 0;
+ }
+ else {
+ if(clear)
+ clearReadBuffer();
+ }
+
+ if(d->srv.isBusy())
+ d->srv.stop();
+#ifndef NO_NDNS
+ if(d->ndns.isBusy())
+ d->ndns.stop();
+#endif
+ d->state = Idle;
+}
+
+void BSocket::ensureSocket()
+{
+ if(!d->qsock) {
+ d->qsock = new QSocket;
+#if QT_VERSION >= 0x030200
+ d->qsock->setReadBufferSize(READBUFSIZE);
+#endif
+ connect(d->qsock, SIGNAL(hostFound()), SLOT(qs_hostFound()));
+ connect(d->qsock, SIGNAL(connected()), SLOT(qs_connected()));
+ connect(d->qsock, SIGNAL(connectionClosed()), SLOT(qs_connectionClosed()));
+ connect(d->qsock, SIGNAL(delayedCloseFinished()), SLOT(qs_delayedCloseFinished()));
+ connect(d->qsock, SIGNAL(readyRead()), SLOT(qs_readyRead()));
+ connect(d->qsock, SIGNAL(bytesWritten(int)), SLOT(qs_bytesWritten(int)));
+ connect(d->qsock, SIGNAL(error(int)), SLOT(qs_error(int)));
+ }
+}
+
+void BSocket::connectToHost(const QString &host, Q_UINT16 port)
+{
+ reset(true);
+ d->host = host;
+ d->port = port;
+#ifdef NO_NDNS
+ d->state = Connecting;
+ do_connect();
+#else
+ d->state = HostLookup;
+ d->ndns.resolve(d->host);
+#endif
+}
+
+void BSocket::connectToServer(const QString &srv, const QString &type)
+{
+ reset(true);
+ d->state = HostLookup;
+ d->srv.resolve(srv, type, "tcp");
+}
+
+int BSocket::socket() const
+{
+ if(d->qsock)
+ return d->qsock->socket();
+ else
+ return -1;
+}
+
+void BSocket::setSocket(int s)
+{
+ reset(true);
+ ensureSocket();
+ d->state = Connected;
+ d->qsock->setSocket(s);
+}
+
+int BSocket::state() const
+{
+ return d->state;
+}
+
+bool BSocket::isOpen() const
+{
+ if(d->state == Connected)
+ return true;
+ else
+ return false;
+}
+
+void BSocket::close()
+{
+ if(d->state == Idle)
+ return;
+
+ if(d->qsock) {
+ d->qsock->close();
+ d->state = Closing;
+ if(d->qsock->bytesToWrite() == 0)
+ reset();
+ }
+ else {
+ reset();
+ }
+}
+
+void BSocket::write(const QByteArray &a)
+{
+ if(d->state != Connected)
+ return;
+#ifdef BS_DEBUG
+ QCString cs;
+ cs.resize(a.size()+1);
+ memcpy(cs.data(), a.data(), a.size());
+ QString s = QString::fromUtf8(cs);
+ fprintf(stderr, "BSocket: writing [%d]: {%s}\n", a.size(), cs.data());
+#endif
+ d->qsock->writeBlock(a.data(), a.size());
+}
+
+QByteArray BSocket::read(int bytes)
+{
+ QByteArray block;
+ if(d->qsock) {
+ int max = bytesAvailable();
+ if(bytes <= 0 || bytes > max)
+ bytes = max;
+ block.resize(bytes);
+ d->qsock->readBlock(block.data(), block.size());
+ }
+ else
+ block = ByteStream::read(bytes);
+
+#ifdef BS_DEBUG
+ QCString cs;
+ cs.resize(block.size()+1);
+ memcpy(cs.data(), block.data(), block.size());
+ QString s = QString::fromUtf8(cs);
+ fprintf(stderr, "BSocket: read [%d]: {%s}\n", block.size(), s.latin1());
+#endif
+ return block;
+}
+
+int BSocket::bytesAvailable() const
+{
+ if(d->qsock)
+ return d->qsock->bytesAvailable();
+ else
+ return ByteStream::bytesAvailable();
+}
+
+int BSocket::bytesToWrite() const
+{
+ if(!d->qsock)
+ return 0;
+ return d->qsock->bytesToWrite();
+}
+
+QHostAddress BSocket::address() const
+{
+ if(d->qsock)
+ return d->qsock->address();
+ else
+ return QHostAddress();
+}
+
+Q_UINT16 BSocket::port() const
+{
+ if(d->qsock)
+ return d->qsock->port();
+ else
+ return 0;
+}
+
+QHostAddress BSocket::peerAddress() const
+{
+ if(d->qsock)
+ return d->qsock->peerAddress();
+ else
+ return QHostAddress();
+}
+
+Q_UINT16 BSocket::peerPort() const
+{
+ if(d->qsock)
+ return d->qsock->port();
+ else
+ return 0;
+}
+
+void BSocket::srv_done()
+{
+ if(d->srv.failed()) {
+#ifdef BS_DEBUG
+ fprintf(stderr, "BSocket: Error resolving hostname.\n");
+#endif
+ error(ErrHostNotFound);
+ return;
+ }
+
+ d->host = d->srv.resultAddress().toString();
+ d->port = d->srv.resultPort();
+ do_connect();
+ //QTimer::singleShot(0, this, SLOT(do_connect()));
+ //hostFound();
+}
+
+void BSocket::ndns_done()
+{
+#ifndef NO_NDNS
+ if(d->ndns.result()) {
+ d->host = d->ndns.resultString();
+ d->state = Connecting;
+ do_connect();
+ //QTimer::singleShot(0, this, SLOT(do_connect()));
+ //hostFound();
+ }
+ else {
+#ifdef BS_DEBUG
+ fprintf(stderr, "BSocket: Error resolving hostname.\n");
+#endif
+ error(ErrHostNotFound);
+ }
+#endif
+}
+
+void BSocket::do_connect()
+{
+#ifdef BS_DEBUG
+ fprintf(stderr, "BSocket: Connecting to %s:%d\n", d->host.latin1(), d->port);
+#endif
+ ensureSocket();
+ d->qsock->connectToHost(d->host, d->port);
+}
+
+void BSocket::qs_hostFound()
+{
+ //SafeDeleteLock s(&d->sd);
+}
+
+void BSocket::qs_connected()
+{
+ d->state = Connected;
+#ifdef BS_DEBUG
+ fprintf(stderr, "BSocket: Connected.\n");
+#endif
+ SafeDeleteLock s(&d->sd);
+ connected();
+}
+
+void BSocket::qs_connectionClosed()
+{
+#ifdef BS_DEBUG
+ fprintf(stderr, "BSocket: Connection Closed.\n");
+#endif
+ SafeDeleteLock s(&d->sd);
+ reset();
+ connectionClosed();
+}
+
+void BSocket::qs_delayedCloseFinished()
+{
+#ifdef BS_DEBUG
+ fprintf(stderr, "BSocket: Delayed Close Finished.\n");
+#endif
+ SafeDeleteLock s(&d->sd);
+ reset();
+ delayedCloseFinished();
+}
+
+void BSocket::qs_readyRead()
+{
+ SafeDeleteLock s(&d->sd);
+ readyRead();
+}
+
+void BSocket::qs_bytesWritten(int x)
+{
+#ifdef BS_DEBUG
+ fprintf(stderr, "BSocket: BytesWritten [%d].\n", x);
+#endif
+ SafeDeleteLock s(&d->sd);
+ bytesWritten(x);
+}
+
+void BSocket::qs_error(int x)
+{
+#ifdef BS_DEBUG
+ fprintf(stderr, "BSocket: Error.\n");
+#endif
+ SafeDeleteLock s(&d->sd);
+
+ // connection error during SRV host connect? try next
+ if(d->state == HostLookup && (x == QSocket::ErrConnectionRefused || x == QSocket::ErrHostNotFound)) {
+ d->srv.next();
+ return;
+ }
+
+ reset();
+ if(x == QSocket::ErrConnectionRefused)
+ error(ErrConnectionRefused);
+ else if(x == QSocket::ErrHostNotFound)
+ error(ErrHostNotFound);
+ else if(x == QSocket::ErrSocketRead)
+ error(ErrRead);
+}
+
+// CS_NAMESPACE_END
+
+#include "bsocket.moc"
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/bsocket.h b/kopete/protocols/jabber/libiris/cutestuff/network/bsocket.h
new file mode 100644
index 00000000..bedaa54e
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/bsocket.h
@@ -0,0 +1,87 @@
+/*
+ * bsocket.h - QSocket wrapper based on Bytestream with SRV DNS support
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef CS_BSOCKET_H
+#define CS_BSOCKET_H
+
+#include<qobject.h>
+#include<qhostaddress.h>
+#include"bytestream.h"
+
+// CS_NAMESPACE_BEGIN
+
+class BSocket : public ByteStream
+{
+ Q_OBJECT
+public:
+ enum Error { ErrConnectionRefused = ErrCustom, ErrHostNotFound };
+ enum State { Idle, HostLookup, Connecting, Connected, Closing };
+ BSocket(QObject *parent=0);
+ ~BSocket();
+
+ void connectToHost(const QString &host, Q_UINT16 port);
+ void connectToServer(const QString &srv, const QString &type);
+ int socket() const;
+ void setSocket(int);
+ int state() const;
+
+ // from ByteStream
+ bool isOpen() const;
+ void close();
+ void write(const QByteArray &);
+ QByteArray read(int bytes=0);
+ int bytesAvailable() const;
+ int bytesToWrite() const;
+
+ // local
+ QHostAddress address() const;
+ Q_UINT16 port() const;
+
+ // remote
+ QHostAddress peerAddress() const;
+ Q_UINT16 peerPort() const;
+
+signals:
+ void hostFound();
+ void connected();
+
+private slots:
+ void qs_hostFound();
+ void qs_connected();
+ void qs_connectionClosed();
+ void qs_delayedCloseFinished();
+ void qs_readyRead();
+ void qs_bytesWritten(int);
+ void qs_error(int);
+ void srv_done();
+ void ndns_done();
+ void do_connect();
+
+private:
+ class Private;
+ Private *d;
+
+ void reset(bool clear=false);
+ void ensureSocket();
+};
+
+// CS_NAMESPACE_END
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.cpp
new file mode 100644
index 00000000..c194324a
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.cpp
@@ -0,0 +1,369 @@
+/*
+ * httpconnect.cpp - HTTP "CONNECT" proxy
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include"httpconnect.h"
+
+#include<qstringlist.h>
+#include"bsocket.h"
+#include"base64.h"
+
+#ifdef PROX_DEBUG
+#include<stdio.h>
+#endif
+
+// CS_NAMESPACE_BEGIN
+
+static QString extractLine(QByteArray *buf, bool *found)
+{
+ // scan for newline
+ int n;
+ for(n = 0; n < (int)buf->size()-1; ++n) {
+ if(buf->at(n) == '\r' && buf->at(n+1) == '\n') {
+ QCString cstr;
+ cstr.resize(n+1);
+ memcpy(cstr.data(), buf->data(), n);
+ n += 2; // hack off CR/LF
+
+ memmove(buf->data(), buf->data() + n, buf->size() - n);
+ buf->resize(buf->size() - n);
+ QString s = QString::fromUtf8(cstr);
+
+ if(found)
+ *found = true;
+ return s;
+ }
+ }
+
+ if(found)
+ *found = false;
+ return "";
+}
+
+static bool extractMainHeader(const QString &line, QString *proto, int *code, QString *msg)
+{
+ int n = line.find(' ');
+ if(n == -1)
+ return false;
+ if(proto)
+ *proto = line.mid(0, n);
+ ++n;
+ int n2 = line.find(' ', n);
+ if(n2 == -1)
+ return false;
+ if(code)
+ *code = line.mid(n, n2-n).toInt();
+ n = n2+1;
+ if(msg)
+ *msg = line.mid(n);
+ return true;
+}
+
+class HttpConnect::Private
+{
+public:
+ Private() {}
+
+ BSocket sock;
+ QString host;
+ int port;
+ QString user, pass;
+ QString real_host;
+ int real_port;
+
+ QByteArray recvBuf;
+
+ bool inHeader;
+ QStringList headerLines;
+
+ int toWrite;
+ bool active;
+};
+
+HttpConnect::HttpConnect(QObject *parent)
+:ByteStream(parent)
+{
+ d = new Private;
+ connect(&d->sock, SIGNAL(connected()), SLOT(sock_connected()));
+ connect(&d->sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed()));
+ connect(&d->sock, SIGNAL(delayedCloseFinished()), SLOT(sock_delayedCloseFinished()));
+ connect(&d->sock, SIGNAL(readyRead()), SLOT(sock_readyRead()));
+ connect(&d->sock, SIGNAL(bytesWritten(int)), SLOT(sock_bytesWritten(int)));
+ connect(&d->sock, SIGNAL(error(int)), SLOT(sock_error(int)));
+
+ reset(true);
+}
+
+HttpConnect::~HttpConnect()
+{
+ reset(true);
+ delete d;
+}
+
+void HttpConnect::reset(bool clear)
+{
+ if(d->sock.state() != BSocket::Idle)
+ d->sock.close();
+ if(clear) {
+ clearReadBuffer();
+ d->recvBuf.resize(0);
+ }
+ d->active = false;
+}
+
+void HttpConnect::setAuth(const QString &user, const QString &pass)
+{
+ d->user = user;
+ d->pass = pass;
+}
+
+void HttpConnect::connectToHost(const QString &proxyHost, int proxyPort, const QString &host, int port)
+{
+ reset(true);
+
+ d->host = proxyHost;
+ d->port = proxyPort;
+ d->real_host = host;
+ d->real_port = port;
+
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpConnect: Connecting to %s:%d", proxyHost.latin1(), proxyPort);
+ if(d->user.isEmpty())
+ fprintf(stderr, "\n");
+ else
+ fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1());
+#endif
+ d->sock.connectToHost(d->host, d->port);
+}
+
+bool HttpConnect::isOpen() const
+{
+ return d->active;
+}
+
+void HttpConnect::close()
+{
+ d->sock.close();
+ if(d->sock.bytesToWrite() == 0)
+ reset();
+}
+
+void HttpConnect::write(const QByteArray &buf)
+{
+ if(d->active)
+ d->sock.write(buf);
+}
+
+QByteArray HttpConnect::read(int bytes)
+{
+ return ByteStream::read(bytes);
+}
+
+int HttpConnect::bytesAvailable() const
+{
+ return ByteStream::bytesAvailable();
+}
+
+int HttpConnect::bytesToWrite() const
+{
+ if(d->active)
+ return d->sock.bytesToWrite();
+ else
+ return 0;
+}
+
+void HttpConnect::sock_connected()
+{
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpConnect: Connected\n");
+#endif
+ d->inHeader = true;
+ d->headerLines.clear();
+
+ // connected, now send the request
+ QString s;
+ s += QString("CONNECT ") + d->real_host + ':' + QString::number(d->real_port) + " HTTP/1.0\r\n";
+ if(!d->user.isEmpty()) {
+ QString str = d->user + ':' + d->pass;
+ s += QString("Proxy-Authorization: Basic ") + Base64::encodeString(str) + "\r\n";
+ }
+ s += "Proxy-Connection: Keep-Alive\r\n";
+ s += "Pragma: no-cache\r\n";
+ s += "\r\n";
+
+ QCString cs = s.utf8();
+ QByteArray block(cs.length());
+ memcpy(block.data(), cs.data(), block.size());
+ d->toWrite = block.size();
+ d->sock.write(block);
+}
+
+void HttpConnect::sock_connectionClosed()
+{
+ if(d->active) {
+ reset();
+ connectionClosed();
+ }
+ else {
+ error(ErrProxyNeg);
+ }
+}
+
+void HttpConnect::sock_delayedCloseFinished()
+{
+ if(d->active) {
+ reset();
+ delayedCloseFinished();
+ }
+}
+
+void HttpConnect::sock_readyRead()
+{
+ QByteArray block = d->sock.read();
+
+ if(!d->active) {
+ ByteStream::appendArray(&d->recvBuf, block);
+
+ if(d->inHeader) {
+ // grab available lines
+ while(1) {
+ bool found;
+ QString line = extractLine(&d->recvBuf, &found);
+ if(!found)
+ break;
+ if(line.isEmpty()) {
+ d->inHeader = false;
+ break;
+ }
+ d->headerLines += line;
+ }
+
+ // done with grabbing the header?
+ if(!d->inHeader) {
+ QString str = d->headerLines.first();
+ d->headerLines.remove(d->headerLines.begin());
+
+ QString proto;
+ int code;
+ QString msg;
+ if(!extractMainHeader(str, &proto, &code, &msg)) {
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpConnect: invalid header!\n");
+#endif
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ else {
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpConnect: header proto=[%s] code=[%d] msg=[%s]\n", proto.latin1(), code, msg.latin1());
+ for(QStringList::ConstIterator it = d->headerLines.begin(); it != d->headerLines.end(); ++it)
+ fprintf(stderr, "HttpConnect: * [%s]\n", (*it).latin1());
+#endif
+ }
+
+ if(code == 200) { // OK
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpConnect: << Success >>\n");
+#endif
+ d->active = true;
+ connected();
+
+ if(!d->recvBuf.isEmpty()) {
+ appendRead(d->recvBuf);
+ d->recvBuf.resize(0);
+ readyRead();
+ return;
+ }
+ }
+ else {
+ int err;
+ QString errStr;
+ if(code == 407) { // Authentication failed
+ err = ErrProxyAuth;
+ errStr = tr("Authentication failed");
+ }
+ else if(code == 404) { // Host not found
+ err = ErrHostNotFound;
+ errStr = tr("Host not found");
+ }
+ else if(code == 403) { // Access denied
+ err = ErrProxyNeg;
+ errStr = tr("Access denied");
+ }
+ else if(code == 503) { // Connection refused
+ err = ErrConnectionRefused;
+ errStr = tr("Connection refused");
+ }
+ else { // invalid reply
+ err = ErrProxyNeg;
+ errStr = tr("Invalid reply");
+ }
+
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpConnect: << Error >> [%s]\n", errStr.latin1());
+#endif
+ reset(true);
+ error(err);
+ return;
+ }
+ }
+ }
+ }
+ else {
+ appendRead(block);
+ readyRead();
+ return;
+ }
+}
+
+void HttpConnect::sock_bytesWritten(int x)
+{
+ if(d->toWrite > 0) {
+ int size = x;
+ if(d->toWrite < x)
+ size = d->toWrite;
+ d->toWrite -= size;
+ x -= size;
+ }
+
+ if(d->active && x > 0)
+ bytesWritten(x);
+}
+
+void HttpConnect::sock_error(int x)
+{
+ if(d->active) {
+ reset();
+ error(ErrRead);
+ }
+ else {
+ reset(true);
+ if(x == BSocket::ErrHostNotFound)
+ error(ErrProxyConnect);
+ else if(x == BSocket::ErrConnectionRefused)
+ error(ErrProxyConnect);
+ else if(x == BSocket::ErrRead)
+ error(ErrProxyNeg);
+ }
+}
+
+// CS_NAMESPACE_END
+
+#include "httpconnect.moc"
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.h b/kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.h
new file mode 100644
index 00000000..38129c60
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.h
@@ -0,0 +1,67 @@
+/*
+ * httpconnect.h - HTTP "CONNECT" proxy
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef CS_HTTPCONNECT_H
+#define CS_HTTPCONNECT_H
+
+#include"bytestream.h"
+
+// CS_NAMESPACE_BEGIN
+
+class HttpConnect : public ByteStream
+{
+ Q_OBJECT
+public:
+ enum Error { ErrConnectionRefused = ErrCustom, ErrHostNotFound, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth };
+ HttpConnect(QObject *parent=0);
+ ~HttpConnect();
+
+ void setAuth(const QString &user, const QString &pass="");
+ void connectToHost(const QString &proxyHost, int proxyPort, const QString &host, int port);
+
+ // from ByteStream
+ bool isOpen() const;
+ void close();
+ void write(const QByteArray &);
+ QByteArray read(int bytes=0);
+ int bytesAvailable() const;
+ int bytesToWrite() const;
+
+signals:
+ void connected();
+
+private slots:
+ void sock_connected();
+ void sock_connectionClosed();
+ void sock_delayedCloseFinished();
+ void sock_readyRead();
+ void sock_bytesWritten(int);
+ void sock_error(int);
+
+private:
+ class Private;
+ Private *d;
+
+ void reset(bool clear=false);
+};
+
+// CS_NAMESPACE_END
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/httppoll.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/httppoll.cpp
new file mode 100644
index 00000000..4975d0e5
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/httppoll.cpp
@@ -0,0 +1,666 @@
+/*
+ * httppoll.cpp - HTTP polling proxy
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include"httppoll.h"
+
+#include<qstringlist.h>
+#include<qurl.h>
+#include<qtimer.h>
+#include<qguardedptr.h>
+#include<qca.h>
+#include<stdlib.h>
+#include"bsocket.h"
+#include"base64.h"
+
+#ifdef PROX_DEBUG
+#include<stdio.h>
+#endif
+
+#define POLL_KEYS 64
+
+// CS_NAMESPACE_BEGIN
+
+static QByteArray randomArray(int size)
+{
+ QByteArray a(size);
+ for(int n = 0; n < size; ++n)
+ a[n] = (char)(256.0*rand()/(RAND_MAX+1.0));
+ return a;
+}
+
+//----------------------------------------------------------------------------
+// HttpPoll
+//----------------------------------------------------------------------------
+static QString hpk(int n, const QString &s)
+{
+ if(n == 0)
+ return s;
+ else
+ return Base64::arrayToString( QCA::SHA1::hash( QCString(hpk(n - 1, s).latin1()) ) );
+}
+
+class HttpPoll::Private
+{
+public:
+ Private() {}
+
+ HttpProxyPost http;
+ QString host;
+ int port;
+ QString user, pass;
+ QString url;
+ bool use_proxy;
+
+ QByteArray out;
+
+ int state;
+ bool closing;
+ QString ident;
+
+ QTimer *t;
+
+ QString key[POLL_KEYS];
+ int key_n;
+
+ int polltime;
+};
+
+HttpPoll::HttpPoll(QObject *parent)
+:ByteStream(parent)
+{
+ d = new Private;
+
+ d->polltime = 30;
+ d->t = new QTimer;
+ connect(d->t, SIGNAL(timeout()), SLOT(do_sync()));
+
+ connect(&d->http, SIGNAL(result()), SLOT(http_result()));
+ connect(&d->http, SIGNAL(error(int)), SLOT(http_error(int)));
+
+ reset(true);
+}
+
+HttpPoll::~HttpPoll()
+{
+ reset(true);
+ delete d->t;
+ delete d;
+}
+
+void HttpPoll::reset(bool clear)
+{
+ if(d->http.isActive())
+ d->http.stop();
+ if(clear)
+ clearReadBuffer();
+ clearWriteBuffer();
+ d->out.resize(0);
+ d->state = 0;
+ d->closing = false;
+ d->t->stop();
+}
+
+void HttpPoll::setAuth(const QString &user, const QString &pass)
+{
+ d->user = user;
+ d->pass = pass;
+}
+
+void HttpPoll::connectToUrl(const QString &url)
+{
+ connectToHost("", 0, url);
+}
+
+void HttpPoll::connectToHost(const QString &proxyHost, int proxyPort, const QString &url)
+{
+ reset(true);
+
+ // using proxy?
+ if(!proxyHost.isEmpty()) {
+ d->host = proxyHost;
+ d->port = proxyPort;
+ d->url = url;
+ d->use_proxy = true;
+ }
+ else {
+ QUrl u = url;
+ d->host = u.host();
+ if(u.hasPort())
+ d->port = u.port();
+ else
+ d->port = 80;
+ d->url = u.encodedPathAndQuery();
+ d->use_proxy = false;
+ }
+
+ resetKey();
+ bool last;
+ QString key = getKey(&last);
+
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpPoll: Connecting to %s:%d [%s]", d->host.latin1(), d->port, d->url.latin1());
+ if(d->user.isEmpty())
+ fprintf(stderr, "\n");
+ else
+ fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1());
+#endif
+ QGuardedPtr<QObject> self = this;
+ syncStarted();
+ if(!self)
+ return;
+
+ d->state = 1;
+ d->http.setAuth(d->user, d->pass);
+ d->http.post(d->host, d->port, d->url, makePacket("0", key, "", QByteArray()), d->use_proxy);
+}
+
+QByteArray HttpPoll::makePacket(const QString &ident, const QString &key, const QString &newkey, const QByteArray &block)
+{
+ QString str = ident;
+ if(!key.isEmpty()) {
+ str += ';';
+ str += key;
+ }
+ if(!newkey.isEmpty()) {
+ str += ';';
+ str += newkey;
+ }
+ str += ',';
+ QCString cs = str.latin1();
+ int len = cs.length();
+
+ QByteArray a(len + block.size());
+ memcpy(a.data(), cs.data(), len);
+ memcpy(a.data() + len, block.data(), block.size());
+ return a;
+}
+
+int HttpPoll::pollInterval() const
+{
+ return d->polltime;
+}
+
+void HttpPoll::setPollInterval(int seconds)
+{
+ d->polltime = seconds;
+}
+
+bool HttpPoll::isOpen() const
+{
+ return (d->state == 2 ? true: false);
+}
+
+void HttpPoll::close()
+{
+ if(d->state == 0 || d->closing)
+ return;
+
+ if(bytesToWrite() == 0)
+ reset();
+ else
+ d->closing = true;
+}
+
+void HttpPoll::http_result()
+{
+ // check for death :)
+ QGuardedPtr<QObject> self = this;
+ syncFinished();
+ if(!self)
+ return;
+
+ // get id and packet
+ QString id;
+ QString cookie = d->http.getHeader("Set-Cookie");
+ int n = cookie.find("ID=");
+ if(n == -1) {
+ reset();
+ error(ErrRead);
+ return;
+ }
+ n += 3;
+ int n2 = cookie.find(';', n);
+ if(n2 != -1)
+ id = cookie.mid(n, n2-n);
+ else
+ id = cookie.mid(n);
+ QByteArray block = d->http.body();
+
+ // session error?
+ if(id.right(2) == ":0") {
+ if(id == "0:0" && d->state == 2) {
+ reset();
+ connectionClosed();
+ return;
+ }
+ else {
+ reset();
+ error(ErrRead);
+ return;
+ }
+ }
+
+ d->ident = id;
+ bool justNowConnected = false;
+ if(d->state == 1) {
+ d->state = 2;
+ justNowConnected = true;
+ }
+
+ // sync up again soon
+ if(bytesToWrite() > 0 || !d->closing)
+ d->t->start(d->polltime * 1000, true);
+
+ // connecting
+ if(justNowConnected) {
+ connected();
+ }
+ else {
+ if(!d->out.isEmpty()) {
+ int x = d->out.size();
+ d->out.resize(0);
+ takeWrite(x);
+ bytesWritten(x);
+ }
+ }
+
+ if(!self)
+ return;
+
+ if(!block.isEmpty()) {
+ appendRead(block);
+ readyRead();
+ }
+
+ if(!self)
+ return;
+
+ if(bytesToWrite() > 0) {
+ do_sync();
+ }
+ else {
+ if(d->closing) {
+ reset();
+ delayedCloseFinished();
+ return;
+ }
+ }
+}
+
+void HttpPoll::http_error(int x)
+{
+ reset();
+ if(x == HttpProxyPost::ErrConnectionRefused)
+ error(ErrConnectionRefused);
+ else if(x == HttpProxyPost::ErrHostNotFound)
+ error(ErrHostNotFound);
+ else if(x == HttpProxyPost::ErrSocket)
+ error(ErrRead);
+ else if(x == HttpProxyPost::ErrProxyConnect)
+ error(ErrProxyConnect);
+ else if(x == HttpProxyPost::ErrProxyNeg)
+ error(ErrProxyNeg);
+ else if(x == HttpProxyPost::ErrProxyAuth)
+ error(ErrProxyAuth);
+}
+
+int HttpPoll::tryWrite()
+{
+ if(!d->http.isActive())
+ do_sync();
+ return 0;
+}
+
+void HttpPoll::do_sync()
+{
+ if(d->http.isActive())
+ return;
+
+ d->t->stop();
+ d->out = takeWrite(0, false);
+
+ bool last;
+ QString key = getKey(&last);
+ QString newkey;
+ if(last) {
+ resetKey();
+ newkey = getKey(&last);
+ }
+
+ QGuardedPtr<QObject> self = this;
+ syncStarted();
+ if(!self)
+ return;
+
+ d->http.post(d->host, d->port, d->url, makePacket(d->ident, key, newkey, d->out), d->use_proxy);
+}
+
+void HttpPoll::resetKey()
+{
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpPoll: reset key!\n");
+#endif
+ QByteArray a = randomArray(64);
+ QString str = QString::fromLatin1(a.data(), a.size());
+
+ d->key_n = POLL_KEYS;
+ for(int n = 0; n < POLL_KEYS; ++n)
+ d->key[n] = hpk(n+1, str);
+}
+
+const QString & HttpPoll::getKey(bool *last)
+{
+ *last = false;
+ --(d->key_n);
+ if(d->key_n == 0)
+ *last = true;
+ return d->key[d->key_n];
+}
+
+
+//----------------------------------------------------------------------------
+// HttpProxyPost
+//----------------------------------------------------------------------------
+static QString extractLine(QByteArray *buf, bool *found)
+{
+ // scan for newline
+ int n;
+ for(n = 0; n < (int)buf->size()-1; ++n) {
+ if(buf->at(n) == '\r' && buf->at(n+1) == '\n') {
+ QCString cstr;
+ cstr.resize(n+1);
+ memcpy(cstr.data(), buf->data(), n);
+ n += 2; // hack off CR/LF
+
+ memmove(buf->data(), buf->data() + n, buf->size() - n);
+ buf->resize(buf->size() - n);
+ QString s = QString::fromUtf8(cstr);
+
+ if(found)
+ *found = true;
+ return s;
+ }
+ }
+
+ if(found)
+ *found = false;
+ return "";
+}
+
+static bool extractMainHeader(const QString &line, QString *proto, int *code, QString *msg)
+{
+ int n = line.find(' ');
+ if(n == -1)
+ return false;
+ if(proto)
+ *proto = line.mid(0, n);
+ ++n;
+ int n2 = line.find(' ', n);
+ if(n2 == -1)
+ return false;
+ if(code)
+ *code = line.mid(n, n2-n).toInt();
+ n = n2+1;
+ if(msg)
+ *msg = line.mid(n);
+ return true;
+}
+
+class HttpProxyPost::Private
+{
+public:
+ Private() {}
+
+ BSocket sock;
+ QByteArray postdata, recvBuf, body;
+ QString url;
+ QString user, pass;
+ bool inHeader;
+ QStringList headerLines;
+ bool asProxy;
+ QString host;
+};
+
+HttpProxyPost::HttpProxyPost(QObject *parent)
+:QObject(parent)
+{
+ d = new Private;
+ connect(&d->sock, SIGNAL(connected()), SLOT(sock_connected()));
+ connect(&d->sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed()));
+ connect(&d->sock, SIGNAL(readyRead()), SLOT(sock_readyRead()));
+ connect(&d->sock, SIGNAL(error(int)), SLOT(sock_error(int)));
+ reset(true);
+}
+
+HttpProxyPost::~HttpProxyPost()
+{
+ reset(true);
+ delete d;
+}
+
+void HttpProxyPost::reset(bool clear)
+{
+ if(d->sock.state() != BSocket::Idle)
+ d->sock.close();
+ d->recvBuf.resize(0);
+ if(clear)
+ d->body.resize(0);
+}
+
+void HttpProxyPost::setAuth(const QString &user, const QString &pass)
+{
+ d->user = user;
+ d->pass = pass;
+}
+
+bool HttpProxyPost::isActive() const
+{
+ return (d->sock.state() == BSocket::Idle ? false: true);
+}
+
+void HttpProxyPost::post(const QString &proxyHost, int proxyPort, const QString &url, const QByteArray &data, bool asProxy)
+{
+ reset(true);
+
+ d->host = proxyHost;
+ d->url = url;
+ d->postdata = data;
+ d->asProxy = asProxy;
+
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpProxyPost: Connecting to %s:%d", proxyHost.latin1(), proxyPort);
+ if(d->user.isEmpty())
+ fprintf(stderr, "\n");
+ else
+ fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1());
+#endif
+ d->sock.connectToHost(proxyHost, proxyPort);
+}
+
+void HttpProxyPost::stop()
+{
+ reset();
+}
+
+QByteArray HttpProxyPost::body() const
+{
+ return d->body;
+}
+
+QString HttpProxyPost::getHeader(const QString &var) const
+{
+ for(QStringList::ConstIterator it = d->headerLines.begin(); it != d->headerLines.end(); ++it) {
+ const QString &s = *it;
+ int n = s.find(": ");
+ if(n == -1)
+ continue;
+ QString v = s.mid(0, n);
+ if(v == var)
+ return s.mid(n+2);
+ }
+ return "";
+}
+
+void HttpProxyPost::sock_connected()
+{
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpProxyPost: Connected\n");
+#endif
+ d->inHeader = true;
+ d->headerLines.clear();
+
+ QUrl u = d->url;
+
+ // connected, now send the request
+ QString s;
+ s += QString("POST ") + d->url + " HTTP/1.0\r\n";
+ if(d->asProxy) {
+ if(!d->user.isEmpty()) {
+ QString str = d->user + ':' + d->pass;
+ s += QString("Proxy-Authorization: Basic ") + Base64::encodeString(str) + "\r\n";
+ }
+ s += "Proxy-Connection: Keep-Alive\r\n";
+ s += "Pragma: no-cache\r\n";
+ s += QString("Host: ") + u.host() + "\r\n";
+ }
+ else {
+ s += QString("Host: ") + d->host + "\r\n";
+ }
+ s += "Content-Type: application/x-www-form-urlencoded\r\n";
+ s += QString("Content-Length: ") + QString::number(d->postdata.size()) + "\r\n";
+ s += "\r\n";
+
+ // write request
+ QCString cs = s.utf8();
+ QByteArray block(cs.length());
+ memcpy(block.data(), cs.data(), block.size());
+ d->sock.write(block);
+
+ // write postdata
+ d->sock.write(d->postdata);
+}
+
+void HttpProxyPost::sock_connectionClosed()
+{
+ d->body = d->recvBuf.copy();
+ reset();
+ result();
+}
+
+void HttpProxyPost::sock_readyRead()
+{
+ QByteArray block = d->sock.read();
+ ByteStream::appendArray(&d->recvBuf, block);
+
+ if(d->inHeader) {
+ // grab available lines
+ while(1) {
+ bool found;
+ QString line = extractLine(&d->recvBuf, &found);
+ if(!found)
+ break;
+ if(line.isEmpty()) {
+ d->inHeader = false;
+ break;
+ }
+ d->headerLines += line;
+ }
+
+ // done with grabbing the header?
+ if(!d->inHeader) {
+ QString str = d->headerLines.first();
+ d->headerLines.remove(d->headerLines.begin());
+
+ QString proto;
+ int code;
+ QString msg;
+ if(!extractMainHeader(str, &proto, &code, &msg)) {
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpProxyPost: invalid header!\n");
+#endif
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ else {
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpProxyPost: header proto=[%s] code=[%d] msg=[%s]\n", proto.latin1(), code, msg.latin1());
+ for(QStringList::ConstIterator it = d->headerLines.begin(); it != d->headerLines.end(); ++it)
+ fprintf(stderr, "HttpProxyPost: * [%s]\n", (*it).latin1());
+#endif
+ }
+
+ if(code == 200) { // OK
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpProxyPost: << Success >>\n");
+#endif
+ }
+ else {
+ int err;
+ QString errStr;
+ if(code == 407) { // Authentication failed
+ err = ErrProxyAuth;
+ errStr = tr("Authentication failed");
+ }
+ else if(code == 404) { // Host not found
+ err = ErrHostNotFound;
+ errStr = tr("Host not found");
+ }
+ else if(code == 403) { // Access denied
+ err = ErrProxyNeg;
+ errStr = tr("Access denied");
+ }
+ else if(code == 503) { // Connection refused
+ err = ErrConnectionRefused;
+ errStr = tr("Connection refused");
+ }
+ else { // invalid reply
+ err = ErrProxyNeg;
+ errStr = tr("Invalid reply");
+ }
+
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpProxyPost: << Error >> [%s]\n", errStr.latin1());
+#endif
+ reset(true);
+ error(err);
+ return;
+ }
+ }
+ }
+}
+
+void HttpProxyPost::sock_error(int x)
+{
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpProxyPost: socket error: %d\n", x);
+#endif
+ reset(true);
+ if(x == BSocket::ErrHostNotFound)
+ error(ErrProxyConnect);
+ else if(x == BSocket::ErrConnectionRefused)
+ error(ErrProxyConnect);
+ else if(x == BSocket::ErrRead)
+ error(ErrProxyNeg);
+}
+
+// CS_NAMESPACE_END
+
+#include "httppoll.moc"
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/httppoll.h b/kopete/protocols/jabber/libiris/cutestuff/network/httppoll.h
new file mode 100644
index 00000000..8bbebee3
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/httppoll.h
@@ -0,0 +1,104 @@
+/*
+ * httppoll.h - HTTP polling proxy
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef CS_HTTPPOLL_H
+#define CS_HTTPPOLL_H
+
+#include"bytestream.h"
+
+// CS_NAMESPACE_BEGIN
+
+class HttpPoll : public ByteStream
+{
+ Q_OBJECT
+public:
+ enum Error { ErrConnectionRefused = ErrCustom, ErrHostNotFound, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth };
+ HttpPoll(QObject *parent=0);
+ ~HttpPoll();
+
+ void setAuth(const QString &user, const QString &pass="");
+ void connectToUrl(const QString &url);
+ void connectToHost(const QString &proxyHost, int proxyPort, const QString &url);
+
+ int pollInterval() const;
+ void setPollInterval(int seconds);
+
+ // from ByteStream
+ bool isOpen() const;
+ void close();
+
+signals:
+ void connected();
+ void syncStarted();
+ void syncFinished();
+
+protected:
+ int tryWrite();
+
+private slots:
+ void http_result();
+ void http_error(int);
+ void do_sync();
+
+private:
+ class Private;
+ Private *d;
+
+ void reset(bool clear=false);
+ QByteArray makePacket(const QString &ident, const QString &key, const QString &newkey, const QByteArray &block);
+ void resetKey();
+ const QString & getKey(bool *);
+};
+
+class HttpProxyPost : public QObject
+{
+ Q_OBJECT
+public:
+ enum Error { ErrConnectionRefused, ErrHostNotFound, ErrSocket, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth };
+ HttpProxyPost(QObject *parent=0);
+ ~HttpProxyPost();
+
+ void setAuth(const QString &user, const QString &pass="");
+ bool isActive() const;
+ void post(const QString &proxyHost, int proxyPort, const QString &url, const QByteArray &data, bool asProxy=true);
+ void stop();
+ QByteArray body() const;
+ QString getHeader(const QString &) const;
+
+signals:
+ void result();
+ void error(int);
+
+private slots:
+ void sock_connected();
+ void sock_connectionClosed();
+ void sock_readyRead();
+ void sock_error(int);
+
+private:
+ class Private;
+ Private *d;
+
+ void reset(bool clear=false);
+};
+
+// CS_NAMESPACE_END
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/ndns.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/ndns.cpp
new file mode 100644
index 00000000..7fe60973
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/ndns.cpp
@@ -0,0 +1,378 @@
+/*
+ * ndns.cpp - native DNS resolution
+ * Copyright (C) 2001, 2002 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+//! \class NDns ndns.h
+//! \brief Simple DNS resolution using native system calls
+//!
+//! This class is to be used when Qt's QDns is not good enough. Because QDns
+//! does not use threads, it cannot make a system call asyncronously. Thus,
+//! QDns tries to imitate the behavior of each platform's native behavior, and
+//! generally falls short.
+//!
+//! NDns uses a thread to make the system call happen in the background. This
+//! gives your program native DNS behavior, at the cost of requiring threads
+//! to build.
+//!
+//! \code
+//! #include "ndns.h"
+//!
+//! ...
+//!
+//! NDns dns;
+//! dns.resolve("psi.affinix.com");
+//!
+//! // The class will emit the resultsReady() signal when the resolution
+//! // is finished. You may then retrieve the results:
+//!
+//! uint ip_address = dns.result();
+//!
+//! // or if you want to get the IP address as a string:
+//!
+//! QString ip_address = dns.resultString();
+//! \endcode
+
+#include"ndns.h"
+
+#include<qapplication.h>
+#include<qsocketdevice.h>
+#include<qptrlist.h>
+#include<qeventloop.h>
+
+#ifdef Q_OS_UNIX
+#include<netdb.h>
+#include<sys/types.h>
+#include<netinet/in.h>
+#include<arpa/inet.h>
+#endif
+
+#ifdef Q_OS_WIN32
+#include<windows.h>
+#endif
+
+// CS_NAMESPACE_BEGIN
+
+//! \if _hide_doc_
+class NDnsWorkerEvent : public QCustomEvent
+{
+public:
+ enum Type { WorkerEvent = QEvent::User + 100 };
+ NDnsWorkerEvent(NDnsWorker *);
+
+ NDnsWorker *worker;
+};
+
+class NDnsWorker : public QThread
+{
+public:
+ NDnsWorker(QObject *, const QCString &);
+
+ bool success;
+ bool cancelled;
+ QHostAddress addr;
+
+protected:
+ void run();
+
+private:
+ QCString host;
+ QObject *par;
+};
+//! \endif
+
+//----------------------------------------------------------------------------
+// NDnsManager
+//----------------------------------------------------------------------------
+#ifndef HAVE_GETHOSTBYNAME_R
+static QMutex *workerMutex = 0;
+static QMutex *workerCancelled = 0;
+#endif
+static NDnsManager *man = 0;
+bool winsock_init = false;
+
+class NDnsManager::Item
+{
+public:
+ NDns *ndns;
+ NDnsWorker *worker;
+};
+
+class NDnsManager::Private
+{
+public:
+ Item *find(const NDns *n)
+ {
+ QPtrListIterator<Item> it(list);
+ for(Item *i; (i = it.current()); ++it) {
+ if(i->ndns == n)
+ return i;
+ }
+ return 0;
+ }
+
+ Item *find(const NDnsWorker *w)
+ {
+ QPtrListIterator<Item> it(list);
+ for(Item *i; (i = it.current()); ++it) {
+ if(i->worker == w)
+ return i;
+ }
+ return 0;
+ }
+
+ QPtrList<Item> list;
+};
+
+NDnsManager::NDnsManager()
+{
+#ifndef HAVE_GETHOSTBYNAME_R
+ workerMutex = new QMutex;
+ workerCancelled = new QMutex;
+#endif
+
+#ifdef Q_OS_WIN32
+ if(!winsock_init) {
+ winsock_init = true;
+ QSocketDevice *sd = new QSocketDevice;
+ delete sd;
+ }
+#endif
+
+ d = new Private;
+ d->list.setAutoDelete(true);
+
+ connect(qApp, SIGNAL(aboutToQuit()), SLOT(app_aboutToQuit()));
+}
+
+NDnsManager::~NDnsManager()
+{
+ delete d;
+
+#ifndef HAVE_GETHOSTBYNAME_R
+ delete workerMutex;
+ workerMutex = 0;
+ delete workerCancelled;
+ workerCancelled = 0;
+#endif
+}
+
+void NDnsManager::resolve(NDns *self, const QString &name)
+{
+ Item *i = new Item;
+ i->ndns = self;
+ i->worker = new NDnsWorker(this, name.utf8());
+ d->list.append(i);
+
+ i->worker->start();
+}
+
+void NDnsManager::stop(NDns *self)
+{
+ Item *i = d->find(self);
+ if(!i)
+ return;
+ // disassociate
+ i->ndns = 0;
+
+#ifndef HAVE_GETHOSTBYNAME_R
+ // cancel
+ workerCancelled->lock();
+ i->worker->cancelled = true;
+ workerCancelled->unlock();
+#endif
+}
+
+bool NDnsManager::isBusy(const NDns *self) const
+{
+ Item *i = d->find(self);
+ return (i ? true: false);
+}
+
+bool NDnsManager::event(QEvent *e)
+{
+ if((int)e->type() == (int)NDnsWorkerEvent::WorkerEvent) {
+ NDnsWorkerEvent *we = static_cast<NDnsWorkerEvent*>(e);
+ we->worker->wait(); // ensure that the thread is terminated
+
+ Item *i = d->find(we->worker);
+ if(!i) {
+ // should NOT happen
+ return true;
+ }
+ QHostAddress addr = i->worker->addr;
+ NDns *ndns = i->ndns;
+ delete i->worker;
+ d->list.removeRef(i);
+
+ // nuke manager if no longer needed (code that follows MUST BE SAFE!)
+ tryDestroy();
+
+ // requestor still around?
+ if(ndns)
+ ndns->finished(addr);
+ return true;
+ }
+ return false;
+}
+
+void NDnsManager::tryDestroy()
+{
+ if(d->list.isEmpty()) {
+ man = 0;
+ delete this;
+ }
+}
+
+void NDnsManager::app_aboutToQuit()
+{
+ while(man) {
+ QEventLoop *e = qApp->eventLoop();
+ e->processEvents(QEventLoop::WaitForMore);
+ }
+}
+
+
+//----------------------------------------------------------------------------
+// NDns
+//----------------------------------------------------------------------------
+
+//! \fn void NDns::resultsReady()
+//! This signal is emitted when the DNS resolution succeeds or fails.
+
+//!
+//! Constructs an NDns object with parent \a parent.
+NDns::NDns(QObject *parent)
+:QObject(parent)
+{
+}
+
+//!
+//! Destroys the object and frees allocated resources.
+NDns::~NDns()
+{
+ stop();
+}
+
+//!
+//! Resolves hostname \a host (eg. psi.affinix.com)
+void NDns::resolve(const QString &host)
+{
+ stop();
+ if(!man)
+ man = new NDnsManager;
+ man->resolve(this, host);
+}
+
+//!
+//! Cancels the lookup action.
+//! \note This will not stop the underlying system call, which must finish before the next lookup will proceed.
+void NDns::stop()
+{
+ if(man)
+ man->stop(this);
+}
+
+//!
+//! Returns the IP address as a 32-bit integer in host-byte-order. This will be 0 if the lookup failed.
+//! \sa resultsReady()
+uint NDns::result() const
+{
+ return addr.ip4Addr();
+}
+
+//!
+//! Returns the IP address as a string. This will be an empty string if the lookup failed.
+//! \sa resultsReady()
+QString NDns::resultString() const
+{
+ return addr.toString();
+}
+
+//!
+//! Returns TRUE if busy resolving a hostname.
+bool NDns::isBusy() const
+{
+ if(!man)
+ return false;
+ return man->isBusy(this);
+}
+
+void NDns::finished(const QHostAddress &a)
+{
+ addr = a;
+ resultsReady();
+}
+
+//----------------------------------------------------------------------------
+// NDnsWorkerEvent
+//----------------------------------------------------------------------------
+NDnsWorkerEvent::NDnsWorkerEvent(NDnsWorker *p)
+:QCustomEvent(WorkerEvent)
+{
+ worker = p;
+}
+
+//----------------------------------------------------------------------------
+// NDnsWorker
+//----------------------------------------------------------------------------
+NDnsWorker::NDnsWorker(QObject *_par, const QCString &_host)
+{
+ success = cancelled = false;
+ par = _par;
+ host = _host.copy(); // do we need this to avoid sharing across threads?
+}
+
+void NDnsWorker::run()
+{
+ hostent *h = 0;
+
+#ifdef HAVE_GETHOSTBYNAME_R
+ hostent buf;
+ char char_buf[1024];
+ int err;
+ gethostbyname_r(host.data(), &buf, char_buf, sizeof(char_buf), &h, &err);
+#else
+ // lock for gethostbyname
+ QMutexLocker locker(workerMutex);
+
+ // check for cancel
+ workerCancelled->lock();
+ bool cancel = cancelled;
+ workerCancelled->unlock();
+
+ if(!cancel)
+ h = gethostbyname(host.data());
+#endif
+
+ if(!h) {
+ success = false;
+ QApplication::postEvent(par, new NDnsWorkerEvent(this));
+ return;
+ }
+
+ in_addr a = *((struct in_addr *)h->h_addr_list[0]);
+ addr.setAddress(ntohl(a.s_addr));
+ success = true;
+
+ QApplication::postEvent(par, new NDnsWorkerEvent(this));
+}
+
+// CS_NAMESPACE_END
+
+#include "ndns.moc"
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/ndns.h b/kopete/protocols/jabber/libiris/cutestuff/network/ndns.h
new file mode 100644
index 00000000..c11d1a28
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/ndns.h
@@ -0,0 +1,88 @@
+/*
+ * ndns.h - native DNS resolution
+ * Copyright (C) 2001, 2002 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef CS_NDNS_H
+#define CS_NDNS_H
+
+#include<qobject.h>
+#include<qcstring.h>
+#include<qthread.h>
+#include<qmutex.h>
+#include<qhostaddress.h>
+
+// CS_NAMESPACE_BEGIN
+
+class NDnsWorker;
+class NDnsManager;
+
+class NDns : public QObject
+{
+ Q_OBJECT
+public:
+ NDns(QObject *parent=0);
+ ~NDns();
+
+ void resolve(const QString &);
+ void stop();
+ bool isBusy() const;
+
+ uint result() const;
+ QString resultString() const;
+
+signals:
+ void resultsReady();
+
+private:
+ QHostAddress addr;
+
+ friend class NDnsManager;
+ void finished(const QHostAddress &);
+};
+
+class NDnsManager : public QObject
+{
+ Q_OBJECT
+public:
+ ~NDnsManager();
+ class Item;
+
+//! \if _hide_doc_
+protected:
+ bool event(QEvent *);
+//! \endif
+
+private slots:
+ void app_aboutToQuit();
+
+private:
+ class Private;
+ Private *d;
+
+ friend class NDns;
+ NDnsManager();
+ void resolve(NDns *self, const QString &name);
+ void stop(NDns *self);
+ bool isBusy(const NDns *self) const;
+ void tryDestroy();
+};
+
+// CS_NAMESPACE_END
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/servsock.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/servsock.cpp
new file mode 100644
index 00000000..4aee36dc
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/servsock.cpp
@@ -0,0 +1,112 @@
+/*
+ * servsock.cpp - simple wrapper to QServerSocket
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include"servsock.h"
+
+// CS_NAMESPACE_BEGIN
+
+//----------------------------------------------------------------------------
+// ServSock
+//----------------------------------------------------------------------------
+class ServSock::Private
+{
+public:
+ Private() {}
+
+ ServSockSignal *serv;
+};
+
+ServSock::ServSock(QObject *parent)
+:QObject(parent)
+{
+ d = new Private;
+ d->serv = 0;
+}
+
+ServSock::~ServSock()
+{
+ stop();
+ delete d;
+}
+
+bool ServSock::isActive() const
+{
+ return (d->serv ? true: false);
+}
+
+bool ServSock::listen(Q_UINT16 port)
+{
+ stop();
+
+ d->serv = new ServSockSignal(port);
+ if(!d->serv->ok()) {
+ delete d->serv;
+ d->serv = 0;
+ return false;
+ }
+ connect(d->serv, SIGNAL(connectionReady(int)), SLOT(sss_connectionReady(int)));
+
+ return true;
+}
+
+void ServSock::stop()
+{
+ delete d->serv;
+ d->serv = 0;
+}
+
+int ServSock::port() const
+{
+ if(d->serv)
+ return d->serv->port();
+ else
+ return -1;
+}
+
+QHostAddress ServSock::address() const
+{
+ if(d->serv)
+ return d->serv->address();
+ else
+ return QHostAddress();
+}
+
+void ServSock::sss_connectionReady(int s)
+{
+ connectionReady(s);
+}
+
+
+//----------------------------------------------------------------------------
+// ServSockSignal
+//----------------------------------------------------------------------------
+ServSockSignal::ServSockSignal(int port)
+:QServerSocket(port, 16)
+{
+}
+
+void ServSockSignal::newConnection(int x)
+{
+ connectionReady(x);
+}
+
+// CS_NAMESPACE_END
+
+#include "servsock.moc"
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/servsock.h b/kopete/protocols/jabber/libiris/cutestuff/network/servsock.h
new file mode 100644
index 00000000..60a0c99d
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/servsock.h
@@ -0,0 +1,68 @@
+/*
+ * servsock.h - simple wrapper to QServerSocket
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef CS_SERVSOCK_H
+#define CS_SERVSOCK_H
+
+#include<qserversocket.h>
+
+// CS_NAMESPACE_BEGIN
+
+class ServSock : public QObject
+{
+ Q_OBJECT
+public:
+ ServSock(QObject *parent=0);
+ ~ServSock();
+
+ bool isActive() const;
+ bool listen(Q_UINT16 port);
+ void stop();
+ int port() const;
+ QHostAddress address() const;
+
+signals:
+ void connectionReady(int);
+
+private slots:
+ void sss_connectionReady(int);
+
+private:
+ class Private;
+ Private *d;
+};
+
+class ServSockSignal : public QServerSocket
+{
+ Q_OBJECT
+public:
+ ServSockSignal(int port);
+
+signals:
+ void connectionReady(int);
+
+protected:
+ // reimplemented
+ void newConnection(int);
+};
+
+// CS_NAMESPACE_END
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/socks.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/socks.cpp
new file mode 100644
index 00000000..bae374f5
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/socks.cpp
@@ -0,0 +1,1223 @@
+/*
+ * socks.cpp - SOCKS5 TCP proxy client/server
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include"socks.h"
+
+#include<qhostaddress.h>
+#include<qstringlist.h>
+#include<qptrlist.h>
+#include<qtimer.h>
+#include<qguardedptr.h>
+#include<qsocketdevice.h>
+#include<qsocketnotifier.h>
+
+#ifdef Q_OS_UNIX
+#include<sys/types.h>
+#include<netinet/in.h>
+#endif
+
+#ifdef Q_OS_WIN32
+#include<windows.h>
+#endif
+
+#include"servsock.h"
+#include"bsocket.h"
+
+#ifdef PROX_DEBUG
+#include<stdio.h>
+#endif
+
+// CS_NAMESPACE_BEGIN
+
+//----------------------------------------------------------------------------
+// SocksUDP
+//----------------------------------------------------------------------------
+static QByteArray sp_create_udp(const QString &host, Q_UINT16 port, const QByteArray &buf)
+{
+ // detect for IP addresses
+ //QHostAddress addr;
+ //if(addr.setAddress(host))
+ // return sp_set_request(addr, port, cmd1);
+
+ QCString h = host.utf8();
+ h.truncate(255);
+ h = QString::fromUtf8(h).utf8(); // delete any partial characters?
+ int hlen = h.length();
+
+ int at = 0;
+ QByteArray a(4);
+ a[at++] = 0x00; // reserved
+ a[at++] = 0x00; // reserved
+ a[at++] = 0x00; // frag
+ a[at++] = 0x03; // address type = domain
+
+ // host
+ a.resize(at+hlen+1);
+ a[at++] = hlen;
+ memcpy(a.data() + at, h.data(), hlen);
+ at += hlen;
+
+ // port
+ a.resize(at+2);
+ unsigned short p = htons(port);
+ memcpy(a.data() + at, &p, 2);
+ at += 2;
+
+ a.resize(at+buf.size());
+ memcpy(a.data() + at, buf.data(), buf.size());
+
+ return a;
+}
+
+struct SPS_UDP
+{
+ QString host;
+ Q_UINT16 port;
+ QByteArray data;
+};
+
+static int sp_read_udp(QByteArray *from, SPS_UDP *s)
+{
+ int full_len = 4;
+ if((int)from->size() < full_len)
+ return 0;
+
+ QString host;
+ QHostAddress addr;
+ unsigned char atype = from->at(3);
+
+ if(atype == 0x01) {
+ full_len += 4;
+ if((int)from->size() < full_len)
+ return 0;
+ Q_UINT32 ip4;
+ memcpy(&ip4, from->data() + 4, 4);
+ addr.setAddress(ntohl(ip4));
+ host = addr.toString();
+ }
+ else if(atype == 0x03) {
+ ++full_len;
+ if((int)from->size() < full_len)
+ return 0;
+ unsigned char host_len = from->at(4);
+ full_len += host_len;
+ if((int)from->size() < full_len)
+ return 0;
+ QCString cs(host_len+1);
+ memcpy(cs.data(), from->data() + 5, host_len);
+ host = QString::fromLatin1(cs);
+ }
+ else if(atype == 0x04) {
+ full_len += 16;
+ if((int)from->size() < full_len)
+ return 0;
+ Q_UINT8 a6[16];
+ memcpy(a6, from->data() + 4, 16);
+ addr.setAddress(a6);
+ host = addr.toString();
+ }
+
+ full_len += 2;
+ if((int)from->size() < full_len)
+ return 0;
+
+ Q_UINT16 p;
+ memcpy(&p, from->data() + full_len - 2, 2);
+
+ s->host = host;
+ s->port = ntohs(p);
+ s->data.resize(from->size() - full_len);
+ memcpy(s->data.data(), from->data() + full_len, s->data.size());
+
+ return 1;
+}
+
+class SocksUDP::Private
+{
+public:
+ QSocketDevice *sd;
+ QSocketNotifier *sn;
+ SocksClient *sc;
+ QHostAddress routeAddr;
+ int routePort;
+ QString host;
+ int port;
+};
+
+SocksUDP::SocksUDP(SocksClient *sc, const QString &host, int port, const QHostAddress &routeAddr, int routePort)
+:QObject(sc)
+{
+ d = new Private;
+ d->sc = sc;
+ d->sd = new QSocketDevice(QSocketDevice::Datagram);
+ d->sd->setBlocking(false);
+ d->sn = new QSocketNotifier(d->sd->socket(), QSocketNotifier::Read);
+ connect(d->sn, SIGNAL(activated(int)), SLOT(sn_activated(int)));
+ d->host = host;
+ d->port = port;
+ d->routeAddr = routeAddr;
+ d->routePort = routePort;
+}
+
+SocksUDP::~SocksUDP()
+{
+ delete d->sn;
+ delete d->sd;
+ delete d;
+}
+
+void SocksUDP::change(const QString &host, int port)
+{
+ d->host = host;
+ d->port = port;
+}
+
+void SocksUDP::write(const QByteArray &data)
+{
+ QByteArray buf = sp_create_udp(d->host, d->port, data);
+ d->sd->setBlocking(true);
+ d->sd->writeBlock(buf.data(), buf.size(), d->routeAddr, d->routePort);
+ d->sd->setBlocking(false);
+}
+
+void SocksUDP::sn_activated(int)
+{
+ QByteArray buf(8192);
+ int actual = d->sd->readBlock(buf.data(), buf.size());
+ buf.resize(actual);
+ packetReady(buf);
+}
+
+//----------------------------------------------------------------------------
+// SocksClient
+//----------------------------------------------------------------------------
+#define REQ_CONNECT 0x01
+#define REQ_BIND 0x02
+#define REQ_UDPASSOCIATE 0x03
+
+#define RET_SUCCESS 0x00
+#define RET_UNREACHABLE 0x04
+#define RET_CONNREFUSED 0x05
+
+// spc = socks packet client
+// sps = socks packet server
+// SPCS = socks packet client struct
+// SPSS = socks packet server struct
+
+// Version
+static QByteArray spc_set_version()
+{
+ QByteArray ver(4);
+ ver[0] = 0x05; // socks version 5
+ ver[1] = 0x02; // number of methods
+ ver[2] = 0x00; // no-auth
+ ver[3] = 0x02; // username
+ return ver;
+}
+
+static QByteArray sps_set_version(int method)
+{
+ QByteArray ver(2);
+ ver[0] = 0x05;
+ ver[1] = method;
+ return ver;
+}
+
+struct SPCS_VERSION
+{
+ unsigned char version;
+ QByteArray methodList;
+};
+
+static int spc_get_version(QByteArray *from, SPCS_VERSION *s)
+{
+ if(from->size() < 1)
+ return 0;
+ if(from->at(0) != 0x05) // only SOCKS5 supported
+ return -1;
+ if(from->size() < 2)
+ return 0;
+ uint num = from->at(1);
+ if(num > 16) // who the heck has over 16 auth methods??
+ return -1;
+ if(from->size() < 2 + num)
+ return 0;
+ QByteArray a = ByteStream::takeArray(from, 2+num);
+ s->version = a[0];
+ s->methodList.resize(num);
+ memcpy(s->methodList.data(), a.data() + 2, num);
+ return 1;
+}
+
+struct SPSS_VERSION
+{
+ unsigned char version;
+ unsigned char method;
+};
+
+static int sps_get_version(QByteArray *from, SPSS_VERSION *s)
+{
+ if(from->size() < 2)
+ return 0;
+ QByteArray a = ByteStream::takeArray(from, 2);
+ s->version = a[0];
+ s->method = a[1];
+ return 1;
+}
+
+// authUsername
+static QByteArray spc_set_authUsername(const QCString &user, const QCString &pass)
+{
+ int len1 = user.length();
+ int len2 = pass.length();
+ if(len1 > 255)
+ len1 = 255;
+ if(len2 > 255)
+ len2 = 255;
+ QByteArray a(1+1+len1+1+len2);
+ a[0] = 0x01; // username auth version 1
+ a[1] = len1;
+ memcpy(a.data() + 2, user.data(), len1);
+ a[2+len1] = len2;
+ memcpy(a.data() + 3 + len1, pass.data(), len2);
+ return a;
+}
+
+static QByteArray sps_set_authUsername(bool success)
+{
+ QByteArray a(2);
+ a[0] = 0x01;
+ a[1] = success ? 0x00 : 0xff;
+ return a;
+}
+
+struct SPCS_AUTHUSERNAME
+{
+ QString user, pass;
+};
+
+static int spc_get_authUsername(QByteArray *from, SPCS_AUTHUSERNAME *s)
+{
+ if(from->size() < 1)
+ return 0;
+ unsigned char ver = from->at(0);
+ if(ver != 0x01)
+ return -1;
+ if(from->size() < 2)
+ return 0;
+ unsigned char ulen = from->at(1);
+ if((int)from->size() < ulen + 3)
+ return 0;
+ unsigned char plen = from->at(ulen+2);
+ if((int)from->size() < ulen + plen + 3)
+ return 0;
+ QByteArray a = ByteStream::takeArray(from, ulen + plen + 3);
+
+ QCString user, pass;
+ user.resize(ulen+1);
+ pass.resize(plen+1);
+ memcpy(user.data(), a.data()+2, ulen);
+ memcpy(pass.data(), a.data()+ulen+3, plen);
+ s->user = QString::fromUtf8(user);
+ s->pass = QString::fromUtf8(pass);
+ return 1;
+}
+
+struct SPSS_AUTHUSERNAME
+{
+ unsigned char version;
+ bool success;
+};
+
+static int sps_get_authUsername(QByteArray *from, SPSS_AUTHUSERNAME *s)
+{
+ if(from->size() < 2)
+ return 0;
+ QByteArray a = ByteStream::takeArray(from, 2);
+ s->version = a[0];
+ s->success = a[1] == 0 ? true: false;
+ return 1;
+}
+
+// connectRequest
+static QByteArray sp_set_request(const QHostAddress &addr, unsigned short port, unsigned char cmd1)
+{
+ int at = 0;
+ QByteArray a(4);
+ a[at++] = 0x05; // socks version 5
+ a[at++] = cmd1;
+ a[at++] = 0x00; // reserved
+ if(addr.isIp4Addr()) {
+ a[at++] = 0x01; // address type = ipv4
+ Q_UINT32 ip4 = htonl(addr.ip4Addr());
+ a.resize(at+4);
+ memcpy(a.data() + at, &ip4, 4);
+ at += 4;
+ }
+ else {
+ a[at++] = 0x04;
+ Q_UINT8 a6[16];
+ QStringList s6 = QStringList::split(':', addr.toString(), true);
+ int at = 0;
+ Q_UINT16 c;
+ bool ok;
+ for(QStringList::ConstIterator it = s6.begin(); it != s6.end(); ++it) {
+ c = (*it).toInt(&ok, 16);
+ a6[at++] = (c >> 8);
+ a6[at++] = c & 0xff;
+ }
+ a.resize(at+16);
+ memcpy(a.data() + at, a6, 16);
+ at += 16;
+ }
+
+ // port
+ a.resize(at+2);
+ unsigned short p = htons(port);
+ memcpy(a.data() + at, &p, 2);
+
+ return a;
+}
+
+static QByteArray sp_set_request(const QString &host, Q_UINT16 port, unsigned char cmd1)
+{
+ // detect for IP addresses
+ QHostAddress addr;
+ if(addr.setAddress(host))
+ return sp_set_request(addr, port, cmd1);
+
+ QCString h = host.utf8();
+ h.truncate(255);
+ h = QString::fromUtf8(h).utf8(); // delete any partial characters?
+ int hlen = h.length();
+
+ int at = 0;
+ QByteArray a(4);
+ a[at++] = 0x05; // socks version 5
+ a[at++] = cmd1;
+ a[at++] = 0x00; // reserved
+ a[at++] = 0x03; // address type = domain
+
+ // host
+ a.resize(at+hlen+1);
+ a[at++] = hlen;
+ memcpy(a.data() + at, h.data(), hlen);
+ at += hlen;
+
+ // port
+ a.resize(at+2);
+ unsigned short p = htons(port);
+ memcpy(a.data() + at, &p, 2);
+
+ return a;
+}
+
+struct SPS_CONNREQ
+{
+ unsigned char version;
+ unsigned char cmd;
+ int address_type;
+ QString host;
+ QHostAddress addr;
+ Q_UINT16 port;
+};
+
+static int sp_get_request(QByteArray *from, SPS_CONNREQ *s)
+{
+ int full_len = 4;
+ if((int)from->size() < full_len)
+ return 0;
+
+ QString host;
+ QHostAddress addr;
+ unsigned char atype = from->at(3);
+
+ if(atype == 0x01) {
+ full_len += 4;
+ if((int)from->size() < full_len)
+ return 0;
+ Q_UINT32 ip4;
+ memcpy(&ip4, from->data() + 4, 4);
+ addr.setAddress(ntohl(ip4));
+ }
+ else if(atype == 0x03) {
+ ++full_len;
+ if((int)from->size() < full_len)
+ return 0;
+ unsigned char host_len = from->at(4);
+ full_len += host_len;
+ if((int)from->size() < full_len)
+ return 0;
+ QCString cs(host_len+1);
+ memcpy(cs.data(), from->data() + 5, host_len);
+ host = QString::fromLatin1(cs);
+ }
+ else if(atype == 0x04) {
+ full_len += 16;
+ if((int)from->size() < full_len)
+ return 0;
+ Q_UINT8 a6[16];
+ memcpy(a6, from->data() + 4, 16);
+ addr.setAddress(a6);
+ }
+
+ full_len += 2;
+ if((int)from->size() < full_len)
+ return 0;
+
+ QByteArray a = ByteStream::takeArray(from, full_len);
+
+ Q_UINT16 p;
+ memcpy(&p, a.data() + full_len - 2, 2);
+
+ s->version = a[0];
+ s->cmd = a[1];
+ s->address_type = atype;
+ s->host = host;
+ s->addr = addr;
+ s->port = ntohs(p);
+
+ return 1;
+}
+
+enum { StepVersion, StepAuth, StepRequest };
+
+class SocksClient::Private
+{
+public:
+ Private() {}
+
+ BSocket sock;
+ QString host;
+ int port;
+ QString user, pass;
+ QString real_host;
+ int real_port;
+
+ QByteArray recvBuf;
+ bool active;
+ int step;
+ int authMethod;
+ bool incoming, waiting;
+
+ QString rhost;
+ int rport;
+
+ int pending;
+
+ bool udp;
+ QString udpAddr;
+ int udpPort;
+};
+
+SocksClient::SocksClient(QObject *parent)
+:ByteStream(parent)
+{
+ init();
+
+ d->incoming = false;
+}
+
+SocksClient::SocksClient(int s, QObject *parent)
+:ByteStream(parent)
+{
+ init();
+
+ d->incoming = true;
+ d->waiting = true;
+ d->sock.setSocket(s);
+}
+
+void SocksClient::init()
+{
+ d = new Private;
+ connect(&d->sock, SIGNAL(connected()), SLOT(sock_connected()));
+ connect(&d->sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed()));
+ connect(&d->sock, SIGNAL(delayedCloseFinished()), SLOT(sock_delayedCloseFinished()));
+ connect(&d->sock, SIGNAL(readyRead()), SLOT(sock_readyRead()));
+ connect(&d->sock, SIGNAL(bytesWritten(int)), SLOT(sock_bytesWritten(int)));
+ connect(&d->sock, SIGNAL(error(int)), SLOT(sock_error(int)));
+
+ reset(true);
+}
+
+SocksClient::~SocksClient()
+{
+ reset(true);
+ delete d;
+}
+
+void SocksClient::reset(bool clear)
+{
+ if(d->sock.state() != BSocket::Idle)
+ d->sock.close();
+ if(clear)
+ clearReadBuffer();
+ d->recvBuf.resize(0);
+ d->active = false;
+ d->waiting = false;
+ d->udp = false;
+ d->pending = 0;
+}
+
+bool SocksClient::isIncoming() const
+{
+ return d->incoming;
+}
+
+void SocksClient::setAuth(const QString &user, const QString &pass)
+{
+ d->user = user;
+ d->pass = pass;
+}
+
+void SocksClient::connectToHost(const QString &proxyHost, int proxyPort, const QString &host, int port, bool udpMode)
+{
+ reset(true);
+
+ d->host = proxyHost;
+ d->port = proxyPort;
+ d->real_host = host;
+ d->real_port = port;
+ d->udp = udpMode;
+
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: Connecting to %s:%d", proxyHost.latin1(), proxyPort);
+ if(d->user.isEmpty())
+ fprintf(stderr, "\n");
+ else
+ fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1());
+#endif
+ d->sock.connectToHost(d->host, d->port);
+}
+
+bool SocksClient::isOpen() const
+{
+ return d->active;
+}
+
+void SocksClient::close()
+{
+ d->sock.close();
+ if(d->sock.bytesToWrite() == 0)
+ reset();
+}
+
+void SocksClient::writeData(const QByteArray &buf)
+{
+ d->pending += buf.size();
+ d->sock.write(buf);
+}
+
+void SocksClient::write(const QByteArray &buf)
+{
+ if(d->active && !d->udp)
+ d->sock.write(buf);
+}
+
+QByteArray SocksClient::read(int bytes)
+{
+ return ByteStream::read(bytes);
+}
+
+int SocksClient::bytesAvailable() const
+{
+ return ByteStream::bytesAvailable();
+}
+
+int SocksClient::bytesToWrite() const
+{
+ if(d->active)
+ return d->sock.bytesToWrite();
+ else
+ return 0;
+}
+
+void SocksClient::sock_connected()
+{
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: Connected\n");
+#endif
+
+ d->step = StepVersion;
+ writeData(spc_set_version());
+}
+
+void SocksClient::sock_connectionClosed()
+{
+ if(d->active) {
+ reset();
+ connectionClosed();
+ }
+ else {
+ error(ErrProxyNeg);
+ }
+}
+
+void SocksClient::sock_delayedCloseFinished()
+{
+ if(d->active) {
+ reset();
+ delayedCloseFinished();
+ }
+}
+
+void SocksClient::sock_readyRead()
+{
+ QByteArray block = d->sock.read();
+
+ if(!d->active) {
+ if(d->incoming)
+ processIncoming(block);
+ else
+ processOutgoing(block);
+ }
+ else {
+ if(!d->udp) {
+ appendRead(block);
+ readyRead();
+ }
+ }
+}
+
+void SocksClient::processOutgoing(const QByteArray &block)
+{
+#ifdef PROX_DEBUG
+ // show hex
+ fprintf(stderr, "SocksClient: client recv { ");
+ for(int n = 0; n < (int)block.size(); ++n)
+ fprintf(stderr, "%02X ", (unsigned char)block[n]);
+ fprintf(stderr, " } \n");
+#endif
+ ByteStream::appendArray(&d->recvBuf, block);
+
+ if(d->step == StepVersion) {
+ SPSS_VERSION s;
+ int r = sps_get_version(&d->recvBuf, &s);
+ if(r == -1) {
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ else if(r == 1) {
+ if(s.version != 0x05 || s.method == 0xff) {
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: Method selection failed\n");
+#endif
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+
+ QString str;
+ if(s.method == 0x00) {
+ str = "None";
+ d->authMethod = AuthNone;
+ }
+ else if(s.method == 0x02) {
+ str = "Username/Password";
+ d->authMethod = AuthUsername;
+ }
+ else {
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: Server wants to use unknown method '%02x'\n", s.method);
+#endif
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+
+ if(d->authMethod == AuthNone) {
+ // no auth, go straight to the request
+ do_request();
+ }
+ else if(d->authMethod == AuthUsername) {
+ d->step = StepAuth;
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: Authenticating [Username] ...\n");
+#endif
+ writeData(spc_set_authUsername(d->user.latin1(), d->pass.latin1()));
+ }
+ }
+ }
+ if(d->step == StepAuth) {
+ if(d->authMethod == AuthUsername) {
+ SPSS_AUTHUSERNAME s;
+ int r = sps_get_authUsername(&d->recvBuf, &s);
+ if(r == -1) {
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ else if(r == 1) {
+ if(s.version != 0x01) {
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ if(!s.success) {
+ reset(true);
+ error(ErrProxyAuth);
+ return;
+ }
+
+ do_request();
+ }
+ }
+ }
+ else if(d->step == StepRequest) {
+ SPS_CONNREQ s;
+ int r = sp_get_request(&d->recvBuf, &s);
+ if(r == -1) {
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ else if(r == 1) {
+ if(s.cmd != RET_SUCCESS) {
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: client << Error >> [%02x]\n", s.cmd);
+#endif
+ reset(true);
+ if(s.cmd == RET_UNREACHABLE)
+ error(ErrHostNotFound);
+ else if(s.cmd == RET_CONNREFUSED)
+ error(ErrConnectionRefused);
+ else
+ error(ErrProxyNeg);
+ return;
+ }
+
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: client << Success >>\n");
+#endif
+ if(d->udp) {
+ if(s.address_type == 0x03)
+ d->udpAddr = s.host;
+ else
+ d->udpAddr = s.addr.toString();
+ d->udpPort = s.port;
+ }
+
+ d->active = true;
+
+ QGuardedPtr<QObject> self = this;
+ connected();
+ if(!self)
+ return;
+
+ if(!d->recvBuf.isEmpty()) {
+ appendRead(d->recvBuf);
+ d->recvBuf.resize(0);
+ readyRead();
+ }
+ }
+ }
+}
+
+void SocksClient::do_request()
+{
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: Requesting ...\n");
+#endif
+ d->step = StepRequest;
+ int cmd = d->udp ? REQ_UDPASSOCIATE : REQ_CONNECT;
+ QByteArray buf;
+ if(!d->real_host.isEmpty())
+ buf = sp_set_request(d->real_host, d->real_port, cmd);
+ else
+ buf = sp_set_request(QHostAddress(), 0, cmd);
+ writeData(buf);
+}
+
+void SocksClient::sock_bytesWritten(int x)
+{
+ int bytes = x;
+ if(d->pending >= bytes) {
+ d->pending -= bytes;
+ bytes = 0;
+ }
+ else {
+ bytes -= d->pending;
+ d->pending = 0;
+ }
+ if(bytes > 0)
+ bytesWritten(bytes);
+}
+
+void SocksClient::sock_error(int x)
+{
+ if(d->active) {
+ reset();
+ error(ErrRead);
+ }
+ else {
+ reset(true);
+ if(x == BSocket::ErrHostNotFound)
+ error(ErrProxyConnect);
+ else if(x == BSocket::ErrConnectionRefused)
+ error(ErrProxyConnect);
+ else if(x == BSocket::ErrRead)
+ error(ErrProxyNeg);
+ }
+}
+
+void SocksClient::serve()
+{
+ d->waiting = false;
+ d->step = StepVersion;
+ continueIncoming();
+}
+
+void SocksClient::processIncoming(const QByteArray &block)
+{
+#ifdef PROX_DEBUG
+ // show hex
+ fprintf(stderr, "SocksClient: server recv { ");
+ for(int n = 0; n < (int)block.size(); ++n)
+ fprintf(stderr, "%02X ", (unsigned char)block[n]);
+ fprintf(stderr, " } \n");
+#endif
+ ByteStream::appendArray(&d->recvBuf, block);
+
+ if(!d->waiting)
+ continueIncoming();
+}
+
+void SocksClient::continueIncoming()
+{
+ if(d->recvBuf.isEmpty())
+ return;
+
+ if(d->step == StepVersion) {
+ SPCS_VERSION s;
+ int r = spc_get_version(&d->recvBuf, &s);
+ if(r == -1) {
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ else if(r == 1) {
+ if(s.version != 0x05) {
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+
+ int methods = 0;
+ for(int n = 0; n < (int)s.methodList.size(); ++n) {
+ unsigned char c = s.methodList[n];
+ if(c == 0x00)
+ methods |= AuthNone;
+ else if(c == 0x02)
+ methods |= AuthUsername;
+ }
+ d->waiting = true;
+ incomingMethods(methods);
+ }
+ }
+ else if(d->step == StepAuth) {
+ SPCS_AUTHUSERNAME s;
+ int r = spc_get_authUsername(&d->recvBuf, &s);
+ if(r == -1) {
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ else if(r == 1) {
+ d->waiting = true;
+ incomingAuth(s.user, s.pass);
+ }
+ }
+ else if(d->step == StepRequest) {
+ SPS_CONNREQ s;
+ int r = sp_get_request(&d->recvBuf, &s);
+ if(r == -1) {
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ else if(r == 1) {
+ d->waiting = true;
+ if(s.cmd == REQ_CONNECT) {
+ if(!s.host.isEmpty())
+ d->rhost = s.host;
+ else
+ d->rhost = s.addr.toString();
+ d->rport = s.port;
+ incomingConnectRequest(d->rhost, d->rport);
+ }
+ else if(s.cmd == REQ_UDPASSOCIATE) {
+ incomingUDPAssociateRequest();
+ }
+ else {
+ requestDeny();
+ return;
+ }
+ }
+ }
+}
+
+void SocksClient::chooseMethod(int method)
+{
+ if(d->step != StepVersion || !d->waiting)
+ return;
+
+ unsigned char c;
+ if(method == AuthNone) {
+ d->step = StepRequest;
+ c = 0x00;
+ }
+ else {
+ d->step = StepAuth;
+ c = 0x02;
+ }
+
+ // version response
+ d->waiting = false;
+ writeData(sps_set_version(c));
+ continueIncoming();
+}
+
+void SocksClient::authGrant(bool b)
+{
+ if(d->step != StepAuth || !d->waiting)
+ return;
+
+ if(b)
+ d->step = StepRequest;
+
+ // auth response
+ d->waiting = false;
+ writeData(sps_set_authUsername(b));
+ if(!b) {
+ reset(true);
+ return;
+ }
+ continueIncoming();
+}
+
+void SocksClient::requestDeny()
+{
+ if(d->step != StepRequest || !d->waiting)
+ return;
+
+ // response
+ d->waiting = false;
+ writeData(sp_set_request(d->rhost, d->rport, RET_UNREACHABLE));
+ reset(true);
+}
+
+void SocksClient::grantConnect()
+{
+ if(d->step != StepRequest || !d->waiting)
+ return;
+
+ // response
+ d->waiting = false;
+ writeData(sp_set_request(d->rhost, d->rport, RET_SUCCESS));
+ d->active = true;
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: server << Success >>\n");
+#endif
+
+ if(!d->recvBuf.isEmpty()) {
+ appendRead(d->recvBuf);
+ d->recvBuf.resize(0);
+ readyRead();
+ }
+}
+
+void SocksClient::grantUDPAssociate(const QString &relayHost, int relayPort)
+{
+ if(d->step != StepRequest || !d->waiting)
+ return;
+
+ // response
+ d->waiting = false;
+ writeData(sp_set_request(relayHost, relayPort, RET_SUCCESS));
+ d->udp = true;
+ d->active = true;
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: server << Success >>\n");
+#endif
+
+ if(!d->recvBuf.isEmpty())
+ d->recvBuf.resize(0);
+}
+
+QHostAddress SocksClient::peerAddress() const
+{
+ return d->sock.peerAddress();
+}
+
+Q_UINT16 SocksClient::peerPort() const
+{
+ return d->sock.peerPort();
+}
+
+QString SocksClient::udpAddress() const
+{
+ return d->udpAddr;
+}
+
+Q_UINT16 SocksClient::udpPort() const
+{
+ return d->udpPort;
+}
+
+SocksUDP *SocksClient::createUDP(const QString &host, int port, const QHostAddress &routeAddr, int routePort)
+{
+ return new SocksUDP(this, host, port, routeAddr, routePort);
+}
+
+//----------------------------------------------------------------------------
+// SocksServer
+//----------------------------------------------------------------------------
+class SocksServer::Private
+{
+public:
+ Private() {}
+
+ ServSock serv;
+ QPtrList<SocksClient> incomingConns;
+ QSocketDevice *sd;
+ QSocketNotifier *sn;
+};
+
+SocksServer::SocksServer(QObject *parent)
+:QObject(parent)
+{
+ d = new Private;
+ d->sd = 0;
+ d->sn = 0;
+ connect(&d->serv, SIGNAL(connectionReady(int)), SLOT(connectionReady(int)));
+}
+
+SocksServer::~SocksServer()
+{
+ stop();
+ d->incomingConns.setAutoDelete(true);
+ d->incomingConns.clear();
+ delete d;
+}
+
+bool SocksServer::isActive() const
+{
+ return d->serv.isActive();
+}
+
+bool SocksServer::listen(Q_UINT16 port, bool udp)
+{
+ stop();
+ if(!d->serv.listen(port))
+ return false;
+ if(udp) {
+ d->sd = new QSocketDevice(QSocketDevice::Datagram);
+ d->sd->setBlocking(false);
+ if(!d->sd->bind(QHostAddress(), port)) {
+ delete d->sd;
+ d->sd = 0;
+ d->serv.stop();
+ return false;
+ }
+ d->sn = new QSocketNotifier(d->sd->socket(), QSocketNotifier::Read);
+ connect(d->sn, SIGNAL(activated(int)), SLOT(sn_activated(int)));
+ }
+ return true;
+}
+
+void SocksServer::stop()
+{
+ delete d->sn;
+ d->sn = 0;
+ delete d->sd;
+ d->sd = 0;
+ d->serv.stop();
+}
+
+int SocksServer::port() const
+{
+ return d->serv.port();
+}
+
+QHostAddress SocksServer::address() const
+{
+ return d->serv.address();
+}
+
+SocksClient *SocksServer::takeIncoming()
+{
+ if(d->incomingConns.isEmpty())
+ return 0;
+
+ SocksClient *c = d->incomingConns.getFirst();
+ d->incomingConns.removeRef(c);
+
+ // we don't care about errors anymore
+ disconnect(c, SIGNAL(error(int)), this, SLOT(connectionError()));
+
+ // don't serve the connection until the event loop, to give the caller a chance to map signals
+ QTimer::singleShot(0, c, SLOT(serve()));
+
+ return c;
+}
+
+void SocksServer::writeUDP(const QHostAddress &addr, int port, const QByteArray &data)
+{
+ if(d->sd) {
+ d->sd->setBlocking(true);
+ d->sd->writeBlock(data.data(), data.size(), addr, port);
+ d->sd->setBlocking(false);
+ }
+}
+
+void SocksServer::connectionReady(int s)
+{
+ SocksClient *c = new SocksClient(s, this);
+ connect(c, SIGNAL(error(int)), this, SLOT(connectionError()));
+ d->incomingConns.append(c);
+ incomingReady();
+}
+
+void SocksServer::connectionError()
+{
+ SocksClient *c = (SocksClient *)sender();
+ d->incomingConns.removeRef(c);
+ c->deleteLater();
+}
+
+void SocksServer::sn_activated(int)
+{
+ QByteArray buf(8192);
+ int actual = d->sd->readBlock(buf.data(), buf.size());
+ buf.resize(actual);
+ QHostAddress pa = d->sd->peerAddress();
+ int pp = d->sd->peerPort();
+ SPS_UDP s;
+ int r = sp_read_udp(&buf, &s);
+ if(r != 1)
+ return;
+ incomingUDP(s.host, s.port, pa, pp, s.data);
+}
+
+// CS_NAMESPACE_END
+
+#include "socks.moc"
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/socks.h b/kopete/protocols/jabber/libiris/cutestuff/network/socks.h
new file mode 100644
index 00000000..8f1e4ddc
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/socks.h
@@ -0,0 +1,160 @@
+/*
+ * socks.h - SOCKS5 TCP proxy client/server
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef CS_SOCKS_H
+#define CS_SOCKS_H
+
+#include"bytestream.h"
+
+// CS_NAMESPACE_BEGIN
+
+class QHostAddress;
+class SocksClient;
+class SocksServer;
+
+class SocksUDP : public QObject
+{
+ Q_OBJECT
+public:
+ ~SocksUDP();
+
+ void change(const QString &host, int port);
+ void write(const QByteArray &data);
+
+signals:
+ void packetReady(const QByteArray &data);
+
+private slots:
+ void sn_activated(int);
+
+private:
+ class Private;
+ Private *d;
+
+ friend class SocksClient;
+ SocksUDP(SocksClient *sc, const QString &host, int port, const QHostAddress &routeAddr, int routePort);
+};
+
+class SocksClient : public ByteStream
+{
+ Q_OBJECT
+public:
+ enum Error { ErrConnectionRefused = ErrCustom, ErrHostNotFound, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth };
+ enum Method { AuthNone=0x0001, AuthUsername=0x0002 };
+ enum Request { ReqConnect, ReqUDPAssociate };
+ SocksClient(QObject *parent=0);
+ SocksClient(int, QObject *parent=0);
+ ~SocksClient();
+
+ bool isIncoming() const;
+
+ // outgoing
+ void setAuth(const QString &user, const QString &pass="");
+ void connectToHost(const QString &proxyHost, int proxyPort, const QString &host, int port, bool udpMode=false);
+
+ // incoming
+ void chooseMethod(int);
+ void authGrant(bool);
+ void requestDeny();
+ void grantConnect();
+ void grantUDPAssociate(const QString &relayHost, int relayPort);
+
+ // from ByteStream
+ bool isOpen() const;
+ void close();
+ void write(const QByteArray &);
+ QByteArray read(int bytes=0);
+ int bytesAvailable() const;
+ int bytesToWrite() const;
+
+ // remote address
+ QHostAddress peerAddress() const;
+ Q_UINT16 peerPort() const;
+
+ // udp
+ QString udpAddress() const;
+ Q_UINT16 udpPort() const;
+ SocksUDP *createUDP(const QString &host, int port, const QHostAddress &routeAddr, int routePort);
+
+signals:
+ // outgoing
+ void connected();
+
+ // incoming
+ void incomingMethods(int);
+ void incomingAuth(const QString &user, const QString &pass);
+ void incomingConnectRequest(const QString &host, int port);
+ void incomingUDPAssociateRequest();
+
+private slots:
+ void sock_connected();
+ void sock_connectionClosed();
+ void sock_delayedCloseFinished();
+ void sock_readyRead();
+ void sock_bytesWritten(int);
+ void sock_error(int);
+ void serve();
+
+private:
+ class Private;
+ Private *d;
+
+ void init();
+ void reset(bool clear=false);
+ void do_request();
+ void processOutgoing(const QByteArray &);
+ void processIncoming(const QByteArray &);
+ void continueIncoming();
+ void writeData(const QByteArray &a);
+};
+
+class SocksServer : public QObject
+{
+ Q_OBJECT
+public:
+ SocksServer(QObject *parent=0);
+ ~SocksServer();
+
+ bool isActive() const;
+ bool listen(Q_UINT16 port, bool udp=false);
+ void stop();
+ int port() const;
+ QHostAddress address() const;
+ SocksClient *takeIncoming();
+
+ void writeUDP(const QHostAddress &addr, int port, const QByteArray &data);
+
+signals:
+ void incomingReady();
+ void incomingUDP(const QString &host, int port, const QHostAddress &addr, int sourcePort, const QByteArray &data);
+
+private slots:
+ void connectionReady(int);
+ void connectionError();
+ void sn_activated(int);
+
+private:
+ class Private;
+ Private *d;
+};
+
+// CS_NAMESPACE_END
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.cpp
new file mode 100644
index 00000000..0c454c49
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.cpp
@@ -0,0 +1,320 @@
+/*
+ * srvresolver.cpp - class to simplify SRV lookups
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include"srvresolver.h"
+
+#include<qcstring.h>
+#include<qtimer.h>
+#include<qdns.h>
+#include"safedelete.h"
+
+#ifndef NO_NDNS
+#include"ndns.h"
+#endif
+
+// CS_NAMESPACE_BEGIN
+
+static void sortSRVList(QValueList<QDns::Server> &list)
+{
+ QValueList<QDns::Server> tmp = list;
+ list.clear();
+
+ while(!tmp.isEmpty()) {
+ QValueList<QDns::Server>::Iterator p = tmp.end();
+ for(QValueList<QDns::Server>::Iterator it = tmp.begin(); it != tmp.end(); ++it) {
+ if(p == tmp.end())
+ p = it;
+ else {
+ int a = (*it).priority;
+ int b = (*p).priority;
+ int j = (*it).weight;
+ int k = (*p).weight;
+ if(a < b || (a == b && j < k))
+ p = it;
+ }
+ }
+ list.append(*p);
+ tmp.remove(p);
+ }
+}
+
+class SrvResolver::Private
+{
+public:
+ Private() {}
+
+ QDns *qdns;
+#ifndef NO_NDNS
+ NDns ndns;
+#endif
+
+ bool failed;
+ QHostAddress resultAddress;
+ Q_UINT16 resultPort;
+
+ bool srvonly;
+ QString srv;
+ QValueList<QDns::Server> servers;
+ bool aaaa;
+
+ QTimer t;
+ SafeDelete sd;
+};
+
+SrvResolver::SrvResolver(QObject *parent)
+:QObject(parent)
+{
+ d = new Private;
+ d->qdns = 0;
+
+#ifndef NO_NDNS
+ connect(&d->ndns, SIGNAL(resultsReady()), SLOT(ndns_done()));
+#endif
+ connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout()));
+ stop();
+}
+
+SrvResolver::~SrvResolver()
+{
+ stop();
+ delete d;
+}
+
+void SrvResolver::resolve(const QString &server, const QString &type, const QString &proto)
+{
+ stop();
+
+ d->failed = false;
+ d->srvonly = false;
+ d->srv = QString("_") + type + "._" + proto + '.' + server;
+ d->t.start(15000, true);
+ d->qdns = new QDns;
+ connect(d->qdns, SIGNAL(resultsReady()), SLOT(qdns_done()));
+ d->qdns->setRecordType(QDns::Srv);
+ d->qdns->setLabel(d->srv);
+}
+
+void SrvResolver::resolveSrvOnly(const QString &server, const QString &type, const QString &proto)
+{
+ stop();
+
+ d->failed = false;
+ d->srvonly = true;
+ d->srv = QString("_") + type + "._" + proto + '.' + server;
+ d->t.start(15000, true);
+ d->qdns = new QDns;
+ connect(d->qdns, SIGNAL(resultsReady()), SLOT(qdns_done()));
+ d->qdns->setRecordType(QDns::Srv);
+ d->qdns->setLabel(d->srv);
+}
+
+void SrvResolver::next()
+{
+ if(d->servers.isEmpty())
+ return;
+
+ tryNext();
+}
+
+void SrvResolver::stop()
+{
+ if(d->t.isActive())
+ d->t.stop();
+ if(d->qdns) {
+ d->qdns->disconnect(this);
+ d->sd.deleteLater(d->qdns);
+ d->qdns = 0;
+ }
+#ifndef NO_NDNS
+ if(d->ndns.isBusy())
+ d->ndns.stop();
+#endif
+ d->resultAddress = QHostAddress();
+ d->resultPort = 0;
+ d->servers.clear();
+ d->srv = "";
+ d->failed = true;
+}
+
+bool SrvResolver::isBusy() const
+{
+#ifndef NO_NDNS
+ if(d->qdns || d->ndns.isBusy())
+#else
+ if(d->qdns)
+#endif
+ return true;
+ else
+ return false;
+}
+
+QValueList<QDns::Server> SrvResolver::servers() const
+{
+ return d->servers;
+}
+
+bool SrvResolver::failed() const
+{
+ return d->failed;
+}
+
+QHostAddress SrvResolver::resultAddress() const
+{
+ return d->resultAddress;
+}
+
+Q_UINT16 SrvResolver::resultPort() const
+{
+ return d->resultPort;
+}
+
+void SrvResolver::tryNext()
+{
+#ifndef NO_NDNS
+ d->ndns.resolve(d->servers.first().name);
+#else
+ d->qdns = new QDns;
+ connect(d->qdns, SIGNAL(resultsReady()), SLOT(ndns_done()));
+ if(d->aaaa)
+ d->qdns->setRecordType(QDns::Aaaa); // IPv6
+ else
+ d->qdns->setRecordType(QDns::A); // IPv4
+ d->qdns->setLabel(d->servers.first().name);
+#endif
+}
+
+void SrvResolver::qdns_done()
+{
+ if(!d->qdns)
+ return;
+
+ // apparently we sometimes get this signal even though the results aren't ready
+ if(d->qdns->isWorking())
+ return;
+ d->t.stop();
+
+ SafeDeleteLock s(&d->sd);
+
+ // grab the server list and destroy the qdns object
+ QValueList<QDns::Server> list;
+ if(d->qdns->recordType() == QDns::Srv)
+ list = d->qdns->servers();
+ d->qdns->disconnect(this);
+ d->sd.deleteLater(d->qdns);
+ d->qdns = 0;
+
+ if(list.isEmpty()) {
+ stop();
+ resultsReady();
+ return;
+ }
+ sortSRVList(list);
+ d->servers = list;
+
+ if(d->srvonly)
+ resultsReady();
+ else {
+ // kick it off
+ d->aaaa = true;
+ tryNext();
+ }
+}
+
+void SrvResolver::ndns_done()
+{
+#ifndef NO_NDNS
+ SafeDeleteLock s(&d->sd);
+
+ uint r = d->ndns.result();
+ int port = d->servers.first().port;
+ d->servers.remove(d->servers.begin());
+
+ if(r) {
+ d->resultAddress = QHostAddress(d->ndns.result());
+ d->resultPort = port;
+ resultsReady();
+ }
+ else {
+ // failed? bail if last one
+ if(d->servers.isEmpty()) {
+ stop();
+ resultsReady();
+ return;
+ }
+
+ // otherwise try the next
+ tryNext();
+ }
+#else
+ if(!d->qdns)
+ return;
+
+ // apparently we sometimes get this signal even though the results aren't ready
+ if(d->qdns->isWorking())
+ return;
+
+ SafeDeleteLock s(&d->sd);
+
+ // grab the address list and destroy the qdns object
+ QValueList<QHostAddress> list;
+ if(d->qdns->recordType() == QDns::A || d->qdns->recordType() == QDns::Aaaa)
+ list = d->qdns->addresses();
+ d->qdns->disconnect(this);
+ d->sd.deleteLater(d->qdns);
+ d->qdns = 0;
+
+ if(!list.isEmpty()) {
+ int port = d->servers.first().port;
+ d->servers.remove(d->servers.begin());
+ d->aaaa = true;
+
+ d->resultAddress = list.first();
+ d->resultPort = port;
+ resultsReady();
+ }
+ else {
+ if(!d->aaaa)
+ d->servers.remove(d->servers.begin());
+ d->aaaa = !d->aaaa;
+
+ // failed? bail if last one
+ if(d->servers.isEmpty()) {
+ stop();
+ resultsReady();
+ return;
+ }
+
+ // otherwise try the next
+ tryNext();
+ }
+#endif
+}
+
+void SrvResolver::t_timeout()
+{
+ SafeDeleteLock s(&d->sd);
+
+ stop();
+ resultsReady();
+}
+
+// CS_NAMESPACE_END
+
+#include "srvresolver.moc"
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.h b/kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.h
new file mode 100644
index 00000000..6c9ac4f3
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.h
@@ -0,0 +1,65 @@
+/*
+ * srvresolver.h - class to simplify SRV lookups
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef CS_SRVRESOLVER_H
+#define CS_SRVRESOLVER_H
+
+#include<qvaluelist.h>
+#include<qdns.h>
+
+// CS_NAMESPACE_BEGIN
+
+class SrvResolver : public QObject
+{
+ Q_OBJECT
+public:
+ SrvResolver(QObject *parent=0);
+ ~SrvResolver();
+
+ void resolve(const QString &server, const QString &type, const QString &proto);
+ void resolveSrvOnly(const QString &server, const QString &type, const QString &proto);
+ void next();
+ void stop();
+ bool isBusy() const;
+
+ QValueList<QDns::Server> servers() const;
+
+ bool failed() const;
+ QHostAddress resultAddress() const;
+ Q_UINT16 resultPort() const;
+
+signals:
+ void resultsReady();
+
+private slots:
+ void qdns_done();
+ void ndns_done();
+ void t_timeout();
+
+private:
+ class Private;
+ Private *d;
+
+ void tryNext();
+};
+
+// CS_NAMESPACE_END
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/Makefile.am b/kopete/protocols/jabber/libiris/cutestuff/util/Makefile.am
new file mode 100644
index 00000000..649c0fcf
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/Makefile.am
@@ -0,0 +1,12 @@
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libcutestuff_util.la
+INCLUDES = $(all_includes)
+
+libcutestuff_util_la_SOURCES = \
+ base64.cpp \
+ bytestream.cpp \
+ qrandom.cpp \
+ safedelete.cpp \
+ sha1.cpp \
+ showtextdlg.cpp
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/TODO b/kopete/protocols/jabber/libiris/cutestuff/util/TODO
new file mode 100644
index 00000000..42d94b7d
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/TODO
@@ -0,0 +1,7 @@
+varlist
+common (opening urls)
+zip
+showtext
+format parsing
+xml handling, elem2string, etc
+
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/base64.cpp b/kopete/protocols/jabber/libiris/cutestuff/util/base64.cpp
new file mode 100644
index 00000000..a17ac335
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/base64.cpp
@@ -0,0 +1,182 @@
+/*
+ * base64.cpp - Base64 converting functions
+ * 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"base64.h"
+
+// CS_NAMESPACE_BEGIN
+
+//! \class Base64 base64.h
+//! \brief Base64 conversion functions.
+//!
+//! Converts Base64 data between arrays and strings.
+//!
+//! \code
+//! #include "base64.h"
+//!
+//! ...
+//!
+//! // encode a block of data into base64
+//! QByteArray block(1024);
+//! QByteArray enc = Base64::encode(block);
+//!
+//! \endcode
+
+//!
+//! Encodes array \a s and returns the result.
+QByteArray Base64::encode(const QByteArray &s)
+{
+ int i;
+ int len = s.size();
+ char tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+ int a, b, c;
+
+ QByteArray p((len+2)/3*4);
+ int at = 0;
+ for( i = 0; i < len; i += 3 ) {
+ a = ((unsigned char)s[i] & 3) << 4;
+ if(i + 1 < len) {
+ a += (unsigned char)s[i + 1] >> 4;
+ b = ((unsigned char)s[i + 1] & 0xF) << 2;
+ if(i + 2 < len) {
+ b += (unsigned char)s[i + 2] >> 6;
+ c = (unsigned char)s[i + 2] & 0x3F;
+ }
+ else
+ c = 64;
+ }
+ else
+ b = c = 64;
+
+ p[at++] = tbl[(unsigned char)s[i] >> 2];
+ p[at++] = tbl[a];
+ p[at++] = tbl[b];
+ p[at++] = tbl[c];
+ }
+ return p;
+}
+
+//!
+//! Decodes array \a s and returns the result.
+QByteArray Base64::decode(const QByteArray &s)
+{
+ // return value
+ QByteArray p;
+
+ // -1 specifies invalid
+ // 64 specifies eof
+ // everything else specifies data
+
+ char tbl[] = {
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,
+ 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,64,-1,-1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
+ 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
+ -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
+ 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ };
+
+ // this should be a multiple of 4
+ int len = s.size();
+
+ if(len % 4)
+ return p;
+
+ p.resize(len / 4 * 3);
+
+ int i;
+ int at = 0;
+
+ int a, b, c, d;
+ c = d = 0;
+
+ for( i = 0; i < len; i += 4 ) {
+ a = tbl[(int)s[i]];
+ b = tbl[(int)s[i + 1]];
+ c = tbl[(int)s[i + 2]];
+ d = tbl[(int)s[i + 3]];
+ if((a == 64 || b == 64) || (a < 0 || b < 0 || c < 0 || d < 0)) {
+ p.resize(0);
+ return p;
+ }
+ p[at++] = ((a & 0x3F) << 2) | ((b >> 4) & 0x03);
+ p[at++] = ((b & 0x0F) << 4) | ((c >> 2) & 0x0F);
+ p[at++] = ((c & 0x03) << 6) | ((d >> 0) & 0x3F);
+ }
+
+ if(c & 64)
+ p.resize(at - 2);
+ else if(d & 64)
+ p.resize(at - 1);
+
+ return p;
+}
+
+//!
+//! Encodes array \a a and returns the result as a string.
+QString Base64::arrayToString(const QByteArray &a)
+{
+ QByteArray b = encode(a);
+ QCString c;
+ c.resize(b.size()+1);
+ memcpy(c.data(), b.data(), b.size());
+ return QString::fromLatin1(c);
+}
+
+//!
+//! Decodes string \a s and returns the result as an array.
+QByteArray Base64::stringToArray(const QString &s)
+{
+ if(s.isEmpty())
+ return QByteArray();
+
+ // Unfold data
+ QString us(s);
+ us.remove('\n');
+
+ const char *c = us.latin1();
+ int len = strlen(c);
+ QByteArray b(len);
+ memcpy(b.data(), c, len);
+ QByteArray a = decode(b);
+ return a;
+}
+
+//!
+//! Encodes string \a s and returns the result as a string.
+QString Base64::encodeString(const QString &s)
+{
+ QCString c = s.utf8();
+ int len = c.length();
+ QByteArray b(len);
+ memcpy(b.data(), c.data(), len);
+ return arrayToString(b);
+}
+
+// CS_NAMESPACE_END
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/base64.h b/kopete/protocols/jabber/libiris/cutestuff/util/base64.h
new file mode 100644
index 00000000..128472c1
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/base64.h
@@ -0,0 +1,40 @@
+/*
+ * base64.h - Base64 converting functions
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef CS_BASE64_H
+#define CS_BASE64_H
+
+#include<qstring.h>
+
+// CS_NAMESPACE_BEGIN
+
+class Base64
+{
+public:
+ static QByteArray encode(const QByteArray &);
+ static QByteArray decode(const QByteArray &);
+ static QString arrayToString(const QByteArray &);
+ static QByteArray stringToArray(const QString &);
+ static QString encodeString(const QString &);
+};
+
+// CS_NAMESPACE_END
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/bytestream.cpp b/kopete/protocols/jabber/libiris/cutestuff/util/bytestream.cpp
new file mode 100644
index 00000000..1eccb284
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/bytestream.cpp
@@ -0,0 +1,268 @@
+/*
+ * bytestream.cpp - base class for bytestreams
+ * 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"bytestream.h"
+
+// CS_NAMESPACE_BEGIN
+
+//! \class ByteStream bytestream.h
+//! \brief Base class for "bytestreams"
+//!
+//! This class provides a basic framework for a "bytestream", here defined
+//! as a bi-directional, asynchronous pipe of data. It can be used to create
+//! several different kinds of bytestream-applications, such as a console or
+//! TCP connection, or something more abstract like a security layer or tunnel,
+//! all with the same interface. The provided functions make creating such
+//! classes simpler. ByteStream is a pure-virtual class, so you do not use it
+//! on its own, but instead through a subclass such as \a BSocket.
+//!
+//! The signals connectionClosed(), delayedCloseFinished(), readyRead(),
+//! bytesWritten(), and error() serve the exact same function as those from
+//! <A HREF="http://doc.trolltech.com/3.1/qsocket.html">QSocket</A>.
+//!
+//! The simplest way to create a ByteStream is to reimplement isOpen(), close(),
+//! and tryWrite(). Call appendRead() whenever you want to make data available for
+//! reading. ByteStream will take care of the buffers with regards to the caller,
+//! and will call tryWrite() when the write buffer gains data. It will be your
+//! job to call tryWrite() whenever it is acceptable to write more data to
+//! the underlying system.
+//!
+//! If you need more advanced control, reimplement read(), write(), bytesAvailable(),
+//! and/or bytesToWrite() as necessary.
+//!
+//! Use appendRead(), appendWrite(), takeRead(), and takeWrite() to modify the
+//! buffers. If you have more advanced requirements, the buffers can be accessed
+//! directly with readBuf() and writeBuf().
+//!
+//! Also available are the static convenience functions ByteStream::appendArray()
+//! and ByteStream::takeArray(), which make dealing with byte queues very easy.
+
+class ByteStream::Private
+{
+public:
+ Private() {}
+
+ QByteArray readBuf, writeBuf;
+};
+
+//!
+//! Constructs a ByteStream object with parent \a parent.
+ByteStream::ByteStream(QObject *parent)
+:QObject(parent)
+{
+ d = new Private;
+}
+
+//!
+//! Destroys the object and frees allocated resources.
+ByteStream::~ByteStream()
+{
+ delete d;
+}
+
+//!
+//! Returns TRUE if the stream is open, meaning that you can write to it.
+bool ByteStream::isOpen() const
+{
+ return false;
+}
+
+//!
+//! Closes the stream. If there is data in the write buffer then it will be
+//! written before actually closing the stream. Once all data has been written,
+//! the delayedCloseFinished() signal will be emitted.
+//! \sa delayedCloseFinished()
+void ByteStream::close()
+{
+}
+
+//!
+//! Writes array \a a to the stream.
+void ByteStream::write(const QByteArray &a)
+{
+ if(!isOpen())
+ return;
+
+ bool doWrite = bytesToWrite() == 0 ? true: false;
+ appendWrite(a);
+ if(doWrite)
+ tryWrite();
+}
+
+//!
+//! Reads bytes \a bytes of data from the stream and returns them as an array. If \a bytes is 0, then
+//! \a read will return all available data.
+QByteArray ByteStream::read(int bytes)
+{
+ return takeRead(bytes);
+}
+
+//!
+//! Returns the number of bytes available for reading.
+int ByteStream::bytesAvailable() const
+{
+ return d->readBuf.size();
+}
+
+//!
+//! Returns the number of bytes that are waiting to be written.
+int ByteStream::bytesToWrite() const
+{
+ return d->writeBuf.size();
+}
+
+//!
+//! Writes string \a cs to the stream.
+void ByteStream::write(const QCString &cs)
+{
+ QByteArray block(cs.length());
+ memcpy(block.data(), cs.data(), block.size());
+ write(block);
+}
+
+//!
+//! Clears the read buffer.
+void ByteStream::clearReadBuffer()
+{
+ d->readBuf.resize(0);
+}
+
+//!
+//! Clears the write buffer.
+void ByteStream::clearWriteBuffer()
+{
+ d->writeBuf.resize(0);
+}
+
+//!
+//! Appends \a block to the end of the read buffer.
+void ByteStream::appendRead(const QByteArray &block)
+{
+ appendArray(&d->readBuf, block);
+}
+
+//!
+//! Appends \a block to the end of the write buffer.
+void ByteStream::appendWrite(const QByteArray &block)
+{
+ appendArray(&d->writeBuf, block);
+}
+
+//!
+//! Returns \a size bytes from the start of the read buffer.
+//! If \a size is 0, then all available data will be returned.
+//! If \a del is TRUE, then the bytes are also removed.
+QByteArray ByteStream::takeRead(int size, bool del)
+{
+ return takeArray(&d->readBuf, size, del);
+}
+
+//!
+//! Returns \a size bytes from the start of the write buffer.
+//! If \a size is 0, then all available data will be returned.
+//! If \a del is TRUE, then the bytes are also removed.
+QByteArray ByteStream::takeWrite(int size, bool del)
+{
+ return takeArray(&d->writeBuf, size, del);
+}
+
+//!
+//! Returns a reference to the read buffer.
+QByteArray & ByteStream::readBuf()
+{
+ return d->readBuf;
+}
+
+//!
+//! Returns a reference to the write buffer.
+QByteArray & ByteStream::writeBuf()
+{
+ return d->writeBuf;
+}
+
+//!
+//! Attempts to try and write some bytes from the write buffer, and returns the number
+//! successfully written or -1 on error. The default implementation returns -1.
+int ByteStream::tryWrite()
+{
+ return -1;
+}
+
+//!
+//! Append array \a b to the end of the array pointed to by \a a.
+void ByteStream::appendArray(QByteArray *a, const QByteArray &b)
+{
+ int oldsize = a->size();
+ a->resize(oldsize + b.size());
+ memcpy(a->data() + oldsize, b.data(), b.size());
+}
+
+//!
+//! Returns \a size bytes from the start of the array pointed to by \a from.
+//! If \a size is 0, then all available data will be returned.
+//! If \a del is TRUE, then the bytes are also removed.
+QByteArray ByteStream::takeArray(QByteArray *from, int size, bool del)
+{
+ QByteArray a;
+ if(size == 0) {
+ a = from->copy();
+ if(del)
+ from->resize(0);
+ }
+ else {
+ if(size > (int)from->size())
+ size = from->size();
+ a.resize(size);
+ char *r = from->data();
+ memcpy(a.data(), r, size);
+ if(del) {
+ int newsize = from->size()-size;
+ memmove(r, r+size, newsize);
+ from->resize(newsize);
+ }
+ }
+ return a;
+}
+ void connectionClosed();
+ void delayedCloseFinished();
+ void readyRead();
+ void bytesWritten(int);
+ void error(int);
+
+//! \fn void ByteStream::connectionClosed()
+//! This signal is emitted when the remote end of the stream closes.
+
+//! \fn void ByteStream::delayedCloseFinished()
+//! This signal is emitted when all pending data has been written to the stream
+//! after an attempt to close.
+
+//! \fn void ByteStream::readyRead()
+//! This signal is emitted when data is available to be read.
+
+//! \fn void ByteStream::bytesWritten(int x);
+//! This signal is emitted when data has been successfully written to the stream.
+//! \a x is the number of bytes written.
+
+//! \fn void ByteStream::error(int code)
+//! This signal is emitted when an error occurs in the stream. The reason for
+//! error is indicated by \a code.
+
+// CS_NAMESPACE_END
+#include "bytestream.moc"
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/bytestream.h b/kopete/protocols/jabber/libiris/cutestuff/util/bytestream.h
new file mode 100644
index 00000000..c33b3976
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/bytestream.h
@@ -0,0 +1,78 @@
+/*
+ * bytestream.h - base class for bytestreams
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef CS_BYTESTREAM_H
+#define CS_BYTESTREAM_H
+
+#include<qobject.h>
+#include<qcstring.h>
+
+// CS_NAMESPACE_BEGIN
+
+// CS_EXPORT_BEGIN
+class ByteStream : public QObject
+{
+ Q_OBJECT
+public:
+ enum Error { ErrRead, ErrWrite, ErrCustom = 10 };
+ ByteStream(QObject *parent=0);
+ virtual ~ByteStream()=0;
+
+ virtual bool isOpen() const;
+ virtual void close();
+ virtual void write(const QByteArray &);
+ virtual QByteArray read(int bytes=0);
+ virtual int bytesAvailable() const;
+ virtual int bytesToWrite() const;
+
+ void write(const QCString &);
+
+ static void appendArray(QByteArray *a, const QByteArray &b);
+ static QByteArray takeArray(QByteArray *from, int size=0, bool del=true);
+
+signals:
+ void connectionClosed();
+ void delayedCloseFinished();
+ void readyRead();
+ void bytesWritten(int);
+ void error(int);
+
+protected:
+ void clearReadBuffer();
+ void clearWriteBuffer();
+ void appendRead(const QByteArray &);
+ void appendWrite(const QByteArray &);
+ QByteArray takeRead(int size=0, bool del=true);
+ QByteArray takeWrite(int size=0, bool del=true);
+ QByteArray & readBuf();
+ QByteArray & writeBuf();
+ virtual int tryWrite();
+
+private:
+//! \if _hide_doc_
+ class Private;
+ Private *d;
+//! \endif
+};
+// CS_EXPORT_END
+
+// CS_NAMESPACE_END
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/cipher.cpp b/kopete/protocols/jabber/libiris/cutestuff/util/cipher.cpp
new file mode 100644
index 00000000..3e2f3a15
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/cipher.cpp
@@ -0,0 +1,357 @@
+/*
+ * cipher.cpp - Simple wrapper to 3DES,AES128/256 CBC ciphers
+ * 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"cipher.h"
+
+#include<openssl/evp.h>
+#include<openssl/rsa.h>
+#include"bytestream.h"
+#include"qrandom.h"
+
+static bool lib_encryptArray(const EVP_CIPHER *type, const QByteArray &buf, const QByteArray &key, const QByteArray &iv, bool pad, QByteArray *out)
+{
+ QByteArray result(buf.size()+type->block_size);
+ int len;
+ EVP_CIPHER_CTX c;
+
+ unsigned char *ivp = NULL;
+ if(!iv.isEmpty())
+ ivp = (unsigned char *)iv.data();
+ EVP_CIPHER_CTX_init(&c);
+ //EVP_CIPHER_CTX_set_padding(&c, pad ? 1: 0);
+ if(!EVP_EncryptInit_ex(&c, type, NULL, (unsigned char *)key.data(), ivp))
+ return false;
+ if(!EVP_EncryptUpdate(&c, (unsigned char *)result.data(), &len, (unsigned char *)buf.data(), buf.size()))
+ return false;
+ result.resize(len);
+ if(pad) {
+ QByteArray last(type->block_size);
+ if(!EVP_EncryptFinal_ex(&c, (unsigned char *)last.data(), &len))
+ return false;
+ last.resize(len);
+ ByteStream::appendArray(&result, last);
+ }
+
+ memset(&c, 0, sizeof(EVP_CIPHER_CTX));
+ *out = result;
+ return true;
+}
+
+static bool lib_decryptArray(const EVP_CIPHER *type, const QByteArray &buf, const QByteArray &key, const QByteArray &iv, bool pad, QByteArray *out)
+{
+ QByteArray result(buf.size()+type->block_size);
+ int len;
+ EVP_CIPHER_CTX c;
+
+ unsigned char *ivp = NULL;
+ if(!iv.isEmpty())
+ ivp = (unsigned char *)iv.data();
+ EVP_CIPHER_CTX_init(&c);
+ //EVP_CIPHER_CTX_set_padding(&c, pad ? 1: 0);
+ if(!EVP_DecryptInit_ex(&c, type, NULL, (unsigned char *)key.data(), ivp))
+ return false;
+ if(!pad) {
+ if(!EVP_EncryptUpdate(&c, (unsigned char *)result.data(), &len, (unsigned char *)buf.data(), buf.size()))
+ return false;
+ }
+ else {
+ if(!EVP_DecryptUpdate(&c, (unsigned char *)result.data(), &len, (unsigned char *)buf.data(), buf.size()))
+ return false;
+ }
+ result.resize(len);
+ if(pad) {
+ QByteArray last(type->block_size);
+ if(!EVP_DecryptFinal_ex(&c, (unsigned char *)last.data(), &len))
+ return false;
+ last.resize(len);
+ ByteStream::appendArray(&result, last);
+ }
+
+ memset(&c, 0, sizeof(EVP_CIPHER_CTX));
+ *out = result;
+ return true;
+}
+
+static bool lib_generateKeyIV(const EVP_CIPHER *type, const QByteArray &data, const QByteArray &salt, QByteArray *key, QByteArray *iv)
+{
+ QByteArray k, i;
+ unsigned char *kp = 0;
+ unsigned char *ip = 0;
+ if(key) {
+ k.resize(type->key_len);
+ kp = (unsigned char *)k.data();
+ }
+ if(iv) {
+ i.resize(type->iv_len);
+ ip = (unsigned char *)i.data();
+ }
+ if(!EVP_BytesToKey(type, EVP_sha1(), (unsigned char *)salt.data(), (unsigned char *)data.data(), data.size(), 1, kp, ip))
+ return false;
+ if(key)
+ *key = k;
+ if(iv)
+ *iv = i;
+ return true;
+}
+
+static const EVP_CIPHER * typeToCIPHER(Cipher::Type t)
+{
+ if(t == Cipher::TripleDES)
+ return EVP_des_ede3_cbc();
+ else if(t == Cipher::AES_128)
+ return EVP_aes_128_cbc();
+ else if(t == Cipher::AES_256)
+ return EVP_aes_256_cbc();
+ else
+ return 0;
+}
+
+Cipher::Key Cipher::generateKey(Type t)
+{
+ Key k;
+ const EVP_CIPHER *type = typeToCIPHER(t);
+ if(!type)
+ return k;
+ QByteArray out;
+ if(!lib_generateKeyIV(type, QRandom::randomArray(128), QRandom::randomArray(2), &out, 0))
+ return k;
+ k.setType(t);
+ k.setData(out);
+ return k;
+}
+
+QByteArray Cipher::generateIV(Type t)
+{
+ const EVP_CIPHER *type = typeToCIPHER(t);
+ if(!type)
+ return QByteArray();
+ QByteArray out;
+ if(!lib_generateKeyIV(type, QCString("Get this man an iv!"), QByteArray(), 0, &out))
+ return QByteArray();
+ return out;
+}
+
+int Cipher::ivSize(Type t)
+{
+ const EVP_CIPHER *type = typeToCIPHER(t);
+ if(!type)
+ return -1;
+ return type->iv_len;
+}
+
+QByteArray Cipher::encrypt(const QByteArray &buf, const Key &key, const QByteArray &iv, bool pad, bool *ok)
+{
+ if(ok)
+ *ok = false;
+ const EVP_CIPHER *type = typeToCIPHER(key.type());
+ if(!type)
+ return QByteArray();
+ QByteArray out;
+ if(!lib_encryptArray(type, buf, key.data(), iv, pad, &out))
+ return QByteArray();
+
+ if(ok)
+ *ok = true;
+ return out;
+}
+
+QByteArray Cipher::decrypt(const QByteArray &buf, const Key &key, const QByteArray &iv, bool pad, bool *ok)
+{
+ if(ok)
+ *ok = false;
+ const EVP_CIPHER *type = typeToCIPHER(key.type());
+ if(!type)
+ return QByteArray();
+ QByteArray out;
+ if(!lib_decryptArray(type, buf, key.data(), iv, pad, &out))
+ return QByteArray();
+
+ if(ok)
+ *ok = true;
+ return out;
+}
+
+
+class RSAKey::Private
+{
+public:
+ Private() {}
+
+ RSA *rsa;
+ int ref;
+};
+
+RSAKey::RSAKey()
+{
+ d = 0;
+}
+
+RSAKey::RSAKey(const RSAKey &from)
+{
+ d = 0;
+ *this = from;
+}
+
+RSAKey & RSAKey::operator=(const RSAKey &from)
+{
+ free();
+
+ if(from.d) {
+ d = from.d;
+ ++d->ref;
+ }
+
+ return *this;
+}
+
+RSAKey::~RSAKey()
+{
+ free();
+}
+
+bool RSAKey::isNull() const
+{
+ return d ? false: true;
+}
+
+void * RSAKey::data() const
+{
+ if(d)
+ return (void *)d->rsa;
+ else
+ return 0;
+}
+
+void RSAKey::setData(void *p)
+{
+ free();
+
+ if(p) {
+ d = new Private;
+ d->ref = 1;
+ d->rsa = (RSA *)p;
+ }
+}
+
+void RSAKey::free()
+{
+ if(!d)
+ return;
+
+ --d->ref;
+ if(d->ref <= 0) {
+ RSA_free(d->rsa);
+ delete d;
+ }
+ d = 0;
+}
+
+RSAKey generateRSAKey()
+{
+ RSA *rsa = RSA_generate_key(1024, RSA_F4, NULL, NULL);
+ RSAKey key;
+ if(rsa)
+ key.setData(rsa);
+ return key;
+}
+
+QByteArray encryptRSA(const QByteArray &buf, const RSAKey &key, bool *ok)
+{
+ if(ok)
+ *ok = false;
+
+ int size = RSA_size((RSA *)key.data());
+ int flen = buf.size();
+ if(flen >= size - 11)
+ flen = size - 11;
+ QByteArray result(size);
+ unsigned char *from = (unsigned char *)buf.data();
+ unsigned char *to = (unsigned char *)result.data();
+ int r = RSA_public_encrypt(flen, from, to, (RSA *)key.data(), RSA_PKCS1_PADDING);
+ if(r == -1)
+ return QByteArray();
+ result.resize(r);
+
+ if(ok)
+ *ok = true;
+ return result;
+}
+
+QByteArray decryptRSA(const QByteArray &buf, const RSAKey &key, bool *ok)
+{
+ if(ok)
+ *ok = false;
+
+ int size = RSA_size((RSA *)key.data());
+ int flen = buf.size();
+ QByteArray result(size);
+ unsigned char *from = (unsigned char *)buf.data();
+ unsigned char *to = (unsigned char *)result.data();
+ int r = RSA_private_decrypt(flen, from, to, (RSA *)key.data(), RSA_PKCS1_PADDING);
+ if(r == -1)
+ return QByteArray();
+ result.resize(r);
+
+ if(ok)
+ *ok = true;
+ return result;
+}
+
+QByteArray encryptRSA2(const QByteArray &buf, const RSAKey &key, bool *ok)
+{
+ if(ok)
+ *ok = false;
+
+ int size = RSA_size((RSA *)key.data());
+ int flen = buf.size();
+ if(flen >= size - 41)
+ flen = size - 41;
+ QByteArray result(size);
+ unsigned char *from = (unsigned char *)buf.data();
+ unsigned char *to = (unsigned char *)result.data();
+ int r = RSA_public_encrypt(flen, from, to, (RSA *)key.data(), RSA_PKCS1_OAEP_PADDING);
+ if(r == -1)
+ return QByteArray();
+ result.resize(r);
+
+ if(ok)
+ *ok = true;
+ return result;
+}
+
+QByteArray decryptRSA2(const QByteArray &buf, const RSAKey &key, bool *ok)
+{
+ if(ok)
+ *ok = false;
+
+ int size = RSA_size((RSA *)key.data());
+ int flen = buf.size();
+ QByteArray result(size);
+ unsigned char *from = (unsigned char *)buf.data();
+ unsigned char *to = (unsigned char *)result.data();
+ int r = RSA_private_decrypt(flen, from, to, (RSA *)key.data(), RSA_PKCS1_OAEP_PADDING);
+ if(r == -1)
+ return QByteArray();
+ result.resize(r);
+
+ if(ok)
+ *ok = true;
+ return result;
+}
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/cipher.h b/kopete/protocols/jabber/libiris/cutestuff/util/cipher.h
new file mode 100644
index 00000000..f162f16a
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/cipher.h
@@ -0,0 +1,79 @@
+/*
+ * cipher.h - Simple wrapper to 3DES,AES128/256 CBC ciphers
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef CS_CIPHER_H
+#define CS_CIPHER_H
+
+#include<qstring.h>
+#include<qcstring.h>
+
+namespace Cipher
+{
+ enum Type { None, TripleDES, AES_128, AES_256 };
+
+ class Key
+ {
+ public:
+ Key() { v_type = None; }
+
+ bool isValid() const { return (v_type == None ? false: true); }
+ void setType(Type x) { v_type = x; }
+ Type type() const { return v_type; }
+ void setData(const QByteArray &d) { v_data = d; }
+ const QByteArray & data() const { return v_data; }
+
+ private:
+ Type v_type;
+ QByteArray v_data;
+ };
+
+ Key generateKey(Type);
+ QByteArray generateIV(Type);
+ int ivSize(Type);
+ QByteArray encrypt(const QByteArray &, const Key &, const QByteArray &iv, bool pad, bool *ok=0);
+ QByteArray decrypt(const QByteArray &, const Key &, const QByteArray &iv, bool pad, bool *ok=0);
+}
+
+class RSAKey
+{
+public:
+ RSAKey();
+ RSAKey(const RSAKey &);
+ RSAKey & operator=(const RSAKey &);
+ ~RSAKey();
+
+ bool isNull() const;
+ void *data() const;
+ void setData(void *);
+
+private:
+ class Private;
+ Private *d;
+
+ void free();
+};
+
+RSAKey generateRSAKey();
+QByteArray encryptRSA(const QByteArray &buf, const RSAKey &key, bool *ok=0);
+QByteArray decryptRSA(const QByteArray &buf, const RSAKey &key, bool *ok=0);
+QByteArray encryptRSA2(const QByteArray &buf, const RSAKey &key, bool *ok=0);
+QByteArray decryptRSA2(const QByteArray &buf, const RSAKey &key, bool *ok=0);
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/qrandom.cpp b/kopete/protocols/jabber/libiris/cutestuff/util/qrandom.cpp
new file mode 100644
index 00000000..3becd7c5
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/qrandom.cpp
@@ -0,0 +1,24 @@
+#include"qrandom.h"
+
+#include<stdlib.h>
+
+uchar QRandom::randomChar()
+{
+ return rand();
+}
+
+uint QRandom::randomInt()
+{
+ QByteArray a = randomArray(sizeof(uint));
+ uint x;
+ memcpy(&x, a.data(), a.size());
+ return x;
+}
+
+QByteArray QRandom::randomArray(uint size)
+{
+ QByteArray a(size);
+ for(uint n = 0; n < size; ++n)
+ a[n] = randomChar();
+ return a;
+}
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/qrandom.h b/kopete/protocols/jabber/libiris/cutestuff/util/qrandom.h
new file mode 100644
index 00000000..92339fb0
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/qrandom.h
@@ -0,0 +1,14 @@
+#ifndef CS_QRANDOM_H
+#define CS_QRANDOM_H
+
+#include<qcstring.h>
+
+class QRandom
+{
+public:
+ static uchar randomChar();
+ static uint randomInt();
+ static QByteArray randomArray(uint size);
+};
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/safedelete.cpp b/kopete/protocols/jabber/libiris/cutestuff/util/safedelete.cpp
new file mode 100644
index 00000000..6bd012e9
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/safedelete.cpp
@@ -0,0 +1,119 @@
+#include"safedelete.h"
+
+#include<qtimer.h>
+
+//----------------------------------------------------------------------------
+// SafeDelete
+//----------------------------------------------------------------------------
+SafeDelete::SafeDelete()
+{
+ lock = 0;
+}
+
+SafeDelete::~SafeDelete()
+{
+ if(lock)
+ lock->dying();
+}
+
+void SafeDelete::deleteLater(QObject *o)
+{
+ if(!lock)
+ deleteSingle(o);
+ else
+ list.append(o);
+}
+
+void SafeDelete::unlock()
+{
+ lock = 0;
+ deleteAll();
+}
+
+void SafeDelete::deleteAll()
+{
+ if(list.isEmpty())
+ return;
+
+ QObjectListIt it(list);
+ for(QObject *o; (o = it.current()); ++it)
+ deleteSingle(o);
+ list.clear();
+}
+
+void SafeDelete::deleteSingle(QObject *o)
+{
+#if QT_VERSION < 0x030000
+ // roll our own QObject::deleteLater()
+ SafeDeleteLater *sdl = SafeDeleteLater::ensureExists();
+ sdl->deleteItLater(o);
+#else
+ o->deleteLater();
+#endif
+}
+
+//----------------------------------------------------------------------------
+// SafeDeleteLock
+//----------------------------------------------------------------------------
+SafeDeleteLock::SafeDeleteLock(SafeDelete *sd)
+{
+ own = false;
+ if(!sd->lock) {
+ _sd = sd;
+ _sd->lock = this;
+ }
+ else
+ _sd = 0;
+}
+
+SafeDeleteLock::~SafeDeleteLock()
+{
+ if(_sd) {
+ _sd->unlock();
+ if(own)
+ delete _sd;
+ }
+}
+
+void SafeDeleteLock::dying()
+{
+ _sd = new SafeDelete(*_sd);
+ own = true;
+}
+
+//----------------------------------------------------------------------------
+// SafeDeleteLater
+//----------------------------------------------------------------------------
+SafeDeleteLater *SafeDeleteLater::self = 0;
+
+SafeDeleteLater *SafeDeleteLater::ensureExists()
+{
+ if(!self)
+ new SafeDeleteLater();
+ return self;
+}
+
+SafeDeleteLater::SafeDeleteLater()
+{
+ list.setAutoDelete(true);
+ self = this;
+ QTimer::singleShot(0, this, SLOT(explode()));
+}
+
+SafeDeleteLater::~SafeDeleteLater()
+{
+ list.clear();
+ self = 0;
+}
+
+void SafeDeleteLater::deleteItLater(QObject *o)
+{
+ list.append(o);
+}
+
+void SafeDeleteLater::explode()
+{
+ delete this;
+}
+
+#include "safedelete.moc"
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/safedelete.h b/kopete/protocols/jabber/libiris/cutestuff/util/safedelete.h
new file mode 100644
index 00000000..078d36cd
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/safedelete.h
@@ -0,0 +1,60 @@
+#ifndef SAFEDELETE_H
+#define SAFEDELETE_H
+
+#include<qobject.h>
+#include<qobjectlist.h>
+
+class SafeDelete;
+class SafeDeleteLock
+{
+public:
+ SafeDeleteLock(SafeDelete *sd);
+ ~SafeDeleteLock();
+
+private:
+ SafeDelete *_sd;
+ bool own;
+ friend class SafeDelete;
+ void dying();
+};
+
+class SafeDelete
+{
+public:
+ SafeDelete();
+ ~SafeDelete();
+
+ void deleteLater(QObject *o);
+
+ // same as QObject::deleteLater()
+ static void deleteSingle(QObject *o);
+
+private:
+ QObjectList list;
+ void deleteAll();
+
+ friend class SafeDeleteLock;
+ SafeDeleteLock *lock;
+ void unlock();
+};
+
+class SafeDeleteLater : public QObject
+{
+ Q_OBJECT
+public:
+ static SafeDeleteLater *ensureExists();
+ void deleteItLater(QObject *o);
+
+private slots:
+ void explode();
+
+private:
+ SafeDeleteLater();
+ ~SafeDeleteLater();
+
+ QObjectList list;
+ friend class SafeDelete;
+ static SafeDeleteLater *self;
+};
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/sha1.cpp b/kopete/protocols/jabber/libiris/cutestuff/util/sha1.cpp
new file mode 100644
index 00000000..3e3eb07c
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/sha1.cpp
@@ -0,0 +1,196 @@
+/*
+ * sha1.cpp - Secure Hash Algorithm 1
+ * 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"sha1.h"
+
+// CS_NAMESPACE_BEGIN
+
+/****************************************************************************
+ 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);
+
+
+SHA1::SHA1()
+{
+ int wordSize;
+
+ qSysInfo(&wordSize, &bigEndian);
+}
+
+unsigned long SHA1::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 SHA1::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
+ }
+ update(context, (unsigned char *)"\200", 1);
+ while ((context->count[0] & 504) != 448) {
+ update(context, (unsigned char *)"\0", 1);
+ }
+ 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);
+}
+
+QByteArray SHA1::hash(const QByteArray &a)
+{
+ SHA1_CONTEXT context;
+ QByteArray b(20);
+
+ SHA1 s;
+ s.init(&context);
+ s.update(&context, (unsigned char *)a.data(), (unsigned int)a.size());
+ s.final((unsigned char *)b.data(), &context);
+ return b;
+}
+
+QByteArray SHA1::hashString(const QCString &cs)
+{
+ QByteArray a(cs.length());
+ memcpy(a.data(), cs.data(), a.size());
+ return SHA1::hash(a);
+}
+
+QString SHA1::digest(const QString &in)
+{
+ QByteArray a = SHA1::hashString(in.utf8());
+ QString out;
+ for(int n = 0; n < (int)a.size(); ++n) {
+ QString str;
+ str.sprintf("%02x", (uchar)a[n]);
+ out.append(str);
+ }
+
+ return out;
+}
+
+// CS_NAMESPACE_END
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/sha1.h b/kopete/protocols/jabber/libiris/cutestuff/util/sha1.h
new file mode 100644
index 00000000..6b0453b4
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/sha1.h
@@ -0,0 +1,63 @@
+/*
+ * sha1.h - Secure Hash Algorithm 1
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef CS_SHA1_H
+#define CS_SHA1_H
+
+#include<qstring.h>
+
+// CS_NAMESPACE_BEGIN
+
+class SHA1
+{
+public:
+ static QByteArray hash(const QByteArray &);
+ static QByteArray hashString(const QCString &);
+ static QString digest(const QString &);
+
+private:
+ SHA1();
+
+ 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;
+
+ void transform(Q_UINT32 state[5], unsigned char buffer[64]);
+ void init(SHA1_CONTEXT* context);
+ void update(SHA1_CONTEXT* context, unsigned char* data, Q_UINT32 len);
+ void final(unsigned char digest[20], SHA1_CONTEXT* context);
+
+ unsigned long blk0(Q_UINT32 i);
+ bool bigEndian;
+
+ CHAR64LONG16* block;
+};
+
+// CS_NAMESPACE_END
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/showtextdlg.cpp b/kopete/protocols/jabber/libiris/cutestuff/util/showtextdlg.cpp
new file mode 100644
index 00000000..0b02df60
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/showtextdlg.cpp
@@ -0,0 +1,61 @@
+/*
+ * showtextdlg.cpp - dialog for displaying a text file
+ * 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"showtextdlg.h"
+
+#include<qlayout.h>
+#include<qtextedit.h>
+#include<qpushbutton.h>
+#include<qfile.h>
+#include<qtextstream.h>
+
+
+ShowTextDlg::ShowTextDlg(const QString &fname, bool rich, QWidget *parent, const char *name)
+:QDialog(parent, name, FALSE, WDestructiveClose)
+{
+ QString text;
+
+ QFile f(fname);
+ if(f.open(IO_ReadOnly)) {
+ QTextStream t(&f);
+ while(!t.eof())
+ text += t.readLine() + '\n';
+ f.close();
+ }
+
+ QVBoxLayout *vb1 = new QVBoxLayout(this, 8);
+ QTextEdit *te = new QTextEdit(this);
+ te->setReadOnly(TRUE);
+ te->setTextFormat(rich ? QTextEdit::RichText : QTextEdit::PlainText);
+ te->setText(text);
+
+ vb1->addWidget(te);
+
+ QHBoxLayout *hb1 = new QHBoxLayout(vb1);
+ hb1->addStretch(1);
+ QPushButton *pb = new QPushButton(tr("&OK"), this);
+ connect(pb, SIGNAL(clicked()), SLOT(accept()));
+ hb1->addWidget(pb);
+ hb1->addStretch(1);
+
+ resize(560, 384);
+}
+
+#include "showtextdlg.moc"
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/showtextdlg.h b/kopete/protocols/jabber/libiris/cutestuff/util/showtextdlg.h
new file mode 100644
index 00000000..f59ae32c
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/showtextdlg.h
@@ -0,0 +1,33 @@
+/*
+ * showtextdlg.h - dialog for displaying a text file
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef CS_SHOWTEXTDLG_H
+#define CS_SHOWTEXTDLG_H
+
+#include<qdialog.h>
+
+class ShowTextDlg : public QDialog
+{
+ Q_OBJECT
+public:
+ ShowTextDlg(const QString &fname, bool rich=FALSE, QWidget *parent=0, const char *name=0);
+};
+
+#endif
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
diff --git a/kopete/protocols/jabber/libiris/jingle_iris.patch b/kopete/protocols/jabber/libiris/jingle_iris.patch
new file mode 100644
index 00000000..41acad0e
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/jingle_iris.patch
@@ -0,0 +1,432 @@
+diff -ur psi/iris/include/im.h psi-jingle/iris/include/im.h
+--- psi/iris/include/im.h 2005-12-27 15:12:42.000000000 +0100
++++ psi-jingle/iris/include/im.h 2005-12-27 11:05:53.000000000 +0100
+@@ -22,6 +22,7 @@
+ #define XMPP_IM_H
+
+ #include<qdatetime.h>
++#include<qvaluelist.h>
+ #include"xmpp.h"
+
+ namespace XMPP
+@@ -153,6 +154,9 @@
+
+ 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 &);
+@@ -162,6 +166,9 @@
+ 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 &);
+@@ -176,6 +183,7 @@
+ QString v_xsigned;
+ // gabber song extension
+ QString v_songTitle;
++ QString v_capsNode, v_capsVersion, v_capsExt;
+
+ int ecode;
+ QString estr;
+@@ -285,6 +293,7 @@
+ bool canRegister() const;
+ bool canSearch() const;
+ bool canGroupchat() const;
++ bool canVoice() const;
+ bool canDisco() const;
+ bool isGateway() const;
+ bool haveVCard() const;
+@@ -567,12 +576,25 @@
+ 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;
+diff -ur psi/iris/xmpp-im/client.cpp psi-jingle/iris/xmpp-im/client.cpp
+--- psi/iris/xmpp-im/client.cpp 2005-12-27 15:12:44.000000000 +0100
++++ psi-jingle/iris/xmpp-im/client.cpp 2005-12-27 11:05:53.000000000 +0100
+@@ -70,6 +70,7 @@
+ //! \endcode
+
+ #include<stdarg.h>
++#include<qmap.h>
+ #include<qobjectlist.h>
+ #include<qtimer.h>
+ #include<qguardedptr.h>
+@@ -125,7 +126,9 @@
+ int id_seed;
+ Task *root;
+ QString host, user, pass, resource;
+- QString osname, tzname, clientName, clientVersion;
++ QString osname, tzname, clientName, clientVersion, capsNode, capsVersion, capsExt;
++ DiscoItem::Identity identity;
++ QMap<QString,Features> extension_features;
+ int tzoffset;
+ bool active;
+
+@@ -149,6 +152,9 @@
+ 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);
+@@ -996,6 +1002,21 @@
+ 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;
+@@ -1017,6 +1038,52 @@
+ 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();
+diff -ur psi/iris/xmpp-im/types.cpp psi-jingle/iris/xmpp-im/types.cpp
+--- psi/iris/xmpp-im/types.cpp 2005-12-27 15:12:55.000000000 +0100
++++ psi-jingle/iris/xmpp-im/types.cpp 2005-12-27 11:05:53.000000000 +0100
+@@ -784,6 +784,21 @@
+ 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;
+@@ -836,6 +851,21 @@
+ 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;
+@@ -1427,6 +1457,15 @@
+ 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
+ {
+diff -ur psi/iris/xmpp-im/xmpp_tasks.cpp psi-jingle/iris/xmpp-im/xmpp_tasks.cpp
+--- psi/iris/xmpp-im/xmpp_tasks.cpp 2005-12-27 15:12:45.000000000 +0100
++++ psi-jingle/iris/xmpp-im/xmpp_tasks.cpp 2005-12-27 11:05:53.000000000 +0100
+@@ -516,6 +516,16 @@
+ 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);
++ }
+ }
+ }
+
+@@ -625,6 +635,11 @@
+ 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);
+@@ -1265,23 +1280,86 @@
+ // 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);
+- QDomElement feature;
+
+- 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);
++ // 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/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;
+@@ -1599,6 +1677,7 @@
+
+ QDomElement iq;
+ Jid jid;
++ QString node;
+ DiscoItem item;
+ };
+
+@@ -1626,6 +1705,7 @@
+ 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");
+@@ -1648,6 +1728,29 @@
+ 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;
+diff -ur psi/iris/xmpp-im/xmpp_tasks.h psi-jingle/iris/xmpp-im/xmpp_tasks.h
+--- psi/iris/xmpp-im/xmpp_tasks.h 2005-12-27 15:12:45.000000000 +0100
++++ psi-jingle/iris/xmpp-im/xmpp_tasks.h 2005-12-27 11:05:53.000000000 +0100
+@@ -389,6 +389,8 @@
+ void get(const DiscoItem &);
+
+ const DiscoItem &item() const;
++ const Jid& jid() const;
++ const QString& node() const;
+
+ void onGo();
+ bool take(const QDomElement &);
diff --git a/kopete/protocols/jabber/libiris/qca/COPYING b/kopete/protocols/jabber/libiris/qca/COPYING
new file mode 100644
index 00000000..b1e3f5a2
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/qca/COPYING
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/kopete/protocols/jabber/libiris/qca/INSTALL b/kopete/protocols/jabber/libiris/qca/INSTALL
new file mode 100644
index 00000000..8dd34099
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/qca/INSTALL
@@ -0,0 +1,12 @@
+Installing QCA
+--------------
+
+Installation should be straightforward:
+
+ ./configure
+ make
+ make install
+
+NOTE: You may also need to run '/sbin/ldconfig' or a similar tool to
+ get the new library files recognized by the system. If you are
+ using Linux, just run it for good measure.
diff --git a/kopete/protocols/jabber/libiris/qca/Makefile.am b/kopete/protocols/jabber/libiris/qca/Makefile.am
new file mode 100644
index 00000000..af437a64
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/qca/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = src
diff --git a/kopete/protocols/jabber/libiris/qca/README b/kopete/protocols/jabber/libiris/qca/README
new file mode 100644
index 00000000..0641713a
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/qca/README
@@ -0,0 +1,29 @@
+Qt Cryptographic Architecture
+-----------------------------
+Version: API v1.0, Plugin v1
+Author: Justin Karneges <justin@affinix.com>
+Date: September 10th 2003
+
+This library provides an easy API for the following features:
+
+ SSL/TLS
+ X509
+ SASL
+ RSA
+ Hashing (SHA1, MD5)
+ Ciphers (BlowFish, 3DES, AES)
+
+Functionality is supplied via plugins. This is useful for avoiding
+dependence on a particular crypto library and makes upgrading easier,
+as there is no need to recompile your application when adding or
+upgrading a crypto plugin. Also, by pushing crypto functionality into
+plugins, your application is free of legal issues, such as export
+regulation.
+
+And of course, you get a very simple crypto API for Qt, where you can
+do things like:
+
+ QString hash = QCA::SHA1::hashToString(blockOfData);
+
+Have fun!
+
diff --git a/kopete/protocols/jabber/libiris/qca/TODO b/kopete/protocols/jabber/libiris/qca/TODO
new file mode 100644
index 00000000..bc8247e0
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/qca/TODO
@@ -0,0 +1,6 @@
+* plugins: thread safety ?
+
+* dsa
+* diffie-hellman
+* entropy
+
diff --git a/kopete/protocols/jabber/libiris/qca/src/Makefile.am b/kopete/protocols/jabber/libiris/qca/src/Makefile.am
new file mode 100644
index 00000000..b43d303e
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/qca/src/Makefile.am
@@ -0,0 +1,7 @@
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libqca.la
+INCLUDES = $(all_includes)
+
+libqca_la_SOURCES = \
+ qca.cpp
diff --git a/kopete/protocols/jabber/libiris/qca/src/qca.cpp b/kopete/protocols/jabber/libiris/qca/src/qca.cpp
new file mode 100644
index 00000000..5b67e6e3
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/qca/src/qca.cpp
@@ -0,0 +1,1481 @@
+/*
+ * qca.cpp - Qt Cryptographic Architecture
+ * 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"qca.h"
+
+#include<qptrlist.h>
+#include<qdir.h>
+#include<qfileinfo.h>
+#include<qstringlist.h>
+#include<qlibrary.h>
+#include<qtimer.h>
+#include<qhostaddress.h>
+#include<qapplication.h>
+#include<qguardedptr.h>
+#include<stdlib.h>
+#include"qcaprovider.h"
+
+#if defined(Q_OS_WIN32)
+#define PLUGIN_EXT "dll"
+#elif defined(Q_OS_MAC)
+#define PLUGIN_EXT "dylib"
+#else
+#define PLUGIN_EXT "so"
+#endif
+
+using namespace QCA;
+
+class ProviderItem
+{
+public:
+ QCAProvider *p;
+ QString fname;
+
+ static ProviderItem *load(const QString &fname)
+ {
+ QLibrary *lib = new QLibrary(fname);
+ if(!lib->load()) {
+ delete lib;
+ return 0;
+ }
+ void *s = lib->resolve("createProvider");
+ if(!s) {
+ delete lib;
+ return 0;
+ }
+ QCAProvider *(*createProvider)() = (QCAProvider *(*)())s;
+ QCAProvider *p = createProvider();
+ if(!p) {
+ delete lib;
+ return 0;
+ }
+ ProviderItem *i = new ProviderItem(lib, p);
+ i->fname = fname;
+ return i;
+ }
+
+ static ProviderItem *fromClass(QCAProvider *p)
+ {
+ ProviderItem *i = new ProviderItem(0, p);
+ return i;
+ }
+
+ ~ProviderItem()
+ {
+ delete p;
+ delete lib;
+ }
+
+ void ensureInit()
+ {
+ if(init_done)
+ return;
+ init_done = true;
+ p->init();
+ }
+
+private:
+ QLibrary *lib;
+ bool init_done;
+
+ ProviderItem(QLibrary *_lib, QCAProvider *_p)
+ {
+ lib = _lib;
+ p = _p;
+ init_done = false;
+ }
+};
+
+static QPtrList<ProviderItem> providerList;
+static bool qca_init = false;
+
+static bool plugin_have(const QString &fname)
+{
+ QPtrListIterator<ProviderItem> it(providerList);
+ for(ProviderItem *i; (i = it.current()); ++it) {
+ if(i->fname == fname)
+ return true;
+ }
+ return false;
+}
+
+static void plugin_scan()
+{
+ QStringList dirs = QApplication::libraryPaths();
+ for(QStringList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it) {
+ QDir libpath(*it);
+ QDir dir(libpath.filePath("crypto"));
+ if(!dir.exists())
+ continue;
+
+ QStringList list = dir.entryList();
+ for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
+ QFileInfo fi(dir.filePath(*it));
+ if(fi.isDir())
+ continue;
+ if(fi.extension() != PLUGIN_EXT)
+ continue;
+ QString fname = fi.filePath();
+
+ // don't load the same plugin again!
+ if(plugin_have(fname))
+ continue;
+ //printf("f=[%s]\n", fname.latin1());
+
+ ProviderItem *i = ProviderItem::load(fname);
+ if(!i)
+ continue;
+ if(i->p->qcaVersion() != QCA_PLUGIN_VERSION) {
+ delete i;
+ continue;
+ }
+
+ providerList.append(i);
+ }
+ }
+}
+
+static void plugin_addClass(QCAProvider *p)
+{
+ ProviderItem *i = ProviderItem::fromClass(p);
+ providerList.prepend(i);
+}
+
+static void plugin_unloadall()
+{
+ providerList.clear();
+}
+
+static int plugin_caps()
+{
+ int caps = 0;
+ QPtrListIterator<ProviderItem> it(providerList);
+ for(ProviderItem *i; (i = it.current()); ++it)
+ caps |= i->p->capabilities();
+ return caps;
+}
+
+QString QCA::arrayToHex(const QByteArray &a)
+{
+ QString out;
+ for(int n = 0; n < (int)a.size(); ++n) {
+ QString str;
+ str.sprintf("%02x", (uchar)a[n]);
+ out.append(str);
+ }
+ return out;
+}
+
+QByteArray QCA::hexToArray(const QString &str)
+{
+ QByteArray out(str.length() / 2);
+ int at = 0;
+ for(int n = 0; n + 1 < (int)str.length(); n += 2) {
+ uchar a = str[n];
+ uchar b = str[n+1];
+ uchar c = ((a & 0x0f) << 4) + (b & 0x0f);
+ out[at++] = c;
+ }
+ return out;
+}
+
+void QCA::init()
+{
+ if(qca_init)
+ return;
+ qca_init = true;
+ providerList.setAutoDelete(true);
+}
+
+bool QCA::isSupported(int capabilities)
+{
+ init();
+
+ int caps = plugin_caps();
+ if(caps & capabilities)
+ return true;
+
+ // ok, try scanning for new stuff
+ plugin_scan();
+ caps = plugin_caps();
+ if(caps & capabilities)
+ return true;
+
+ return false;
+}
+
+void QCA::insertProvider(QCAProvider *p)
+{
+ plugin_addClass(p);
+}
+
+void QCA::unloadAllPlugins()
+{
+ plugin_unloadall();
+}
+
+static void *getContext(int cap)
+{
+ init();
+
+ // this call will also trip a scan for new plugins if needed
+ if(!QCA::isSupported(cap))
+ return 0;
+
+ QPtrListIterator<ProviderItem> it(providerList);
+ for(ProviderItem *i; (i = it.current()); ++it) {
+ if(i->p->capabilities() & cap) {
+ i->ensureInit();
+ return i->p->context(cap);
+ }
+ }
+ return 0;
+}
+
+
+//----------------------------------------------------------------------------
+// Hash
+//----------------------------------------------------------------------------
+class Hash::Private
+{
+public:
+ Private()
+ {
+ c = 0;
+ }
+
+ ~Private()
+ {
+ delete c;
+ }
+
+ void reset()
+ {
+ c->reset();
+ }
+
+ QCA_HashContext *c;
+};
+
+Hash::Hash(QCA_HashContext *c)
+{
+ d = new Private;
+ d->c = c;
+}
+
+Hash::Hash(const Hash &from)
+{
+ d = new Private;
+ *this = from;
+}
+
+Hash & Hash::operator=(const Hash &from)
+{
+ delete d->c;
+ d->c = from.d->c->clone();
+ return *this;
+}
+
+Hash::~Hash()
+{
+ delete d;
+}
+
+void Hash::clear()
+{
+ d->reset();
+}
+
+void Hash::update(const QByteArray &a)
+{
+ d->c->update(a.data(), a.size());
+}
+
+QByteArray Hash::final()
+{
+ QByteArray buf;
+ d->c->final(&buf);
+ return buf;
+}
+
+
+//----------------------------------------------------------------------------
+// Cipher
+//----------------------------------------------------------------------------
+class Cipher::Private
+{
+public:
+ Private()
+ {
+ c = 0;
+ }
+
+ ~Private()
+ {
+ delete c;
+ }
+
+ void reset()
+ {
+ dir = Encrypt;
+ key.resize(0);
+ iv.resize(0);
+ err = false;
+ }
+
+ QCA_CipherContext *c;
+ int dir;
+ int mode;
+ QByteArray key, iv;
+ bool err;
+};
+
+Cipher::Cipher(QCA_CipherContext *c, int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad)
+{
+ d = new Private;
+ d->c = c;
+ reset(dir, mode, key, iv, pad);
+}
+
+Cipher::Cipher(const Cipher &from)
+{
+ d = new Private;
+ *this = from;
+}
+
+Cipher & Cipher::operator=(const Cipher &from)
+{
+ delete d->c;
+ d->c = from.d->c->clone();
+ d->dir = from.d->dir;
+ d->mode = from.d->mode;
+ d->key = from.d->key.copy();
+ d->iv = from.d->iv.copy();
+ d->err = from.d->err;
+ return *this;
+}
+
+Cipher::~Cipher()
+{
+ delete d;
+}
+
+QByteArray Cipher::dyn_generateKey(int size) const
+{
+ QByteArray buf;
+ if(size != -1)
+ buf.resize(size);
+ else
+ buf.resize(d->c->keySize());
+ if(!d->c->generateKey(buf.data(), size))
+ return QByteArray();
+ return buf;
+}
+
+QByteArray Cipher::dyn_generateIV() const
+{
+ QByteArray buf(d->c->blockSize());
+ if(!d->c->generateIV(buf.data()))
+ return QByteArray();
+ return buf;
+}
+
+void Cipher::reset(int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad)
+{
+ d->reset();
+
+ d->dir = dir;
+ d->mode = mode;
+ d->key = key.copy();
+ d->iv = iv.copy();
+ if(!d->c->setup(d->dir, d->mode, d->key.isEmpty() ? 0: d->key.data(), d->key.size(), d->iv.isEmpty() ? 0 : d->iv.data(), pad)) {
+ d->err = true;
+ return;
+ }
+}
+
+bool Cipher::update(const QByteArray &a)
+{
+ if(d->err)
+ return false;
+
+ if(!a.isEmpty()) {
+ if(!d->c->update(a.data(), a.size())) {
+ d->err = true;
+ return false;
+ }
+ }
+ return true;
+}
+
+QByteArray Cipher::final(bool *ok)
+{
+ if(ok)
+ *ok = false;
+ if(d->err)
+ return QByteArray();
+
+ QByteArray out;
+ if(!d->c->final(&out)) {
+ d->err = true;
+ return QByteArray();
+ }
+ if(ok)
+ *ok = true;
+ return out;
+}
+
+
+//----------------------------------------------------------------------------
+// SHA1
+//----------------------------------------------------------------------------
+SHA1::SHA1()
+:Hash((QCA_HashContext *)getContext(CAP_SHA1))
+{
+}
+
+
+//----------------------------------------------------------------------------
+// SHA256
+//----------------------------------------------------------------------------
+SHA256::SHA256()
+:Hash((QCA_HashContext *)getContext(CAP_SHA256))
+{
+}
+
+
+//----------------------------------------------------------------------------
+// MD5
+//----------------------------------------------------------------------------
+MD5::MD5()
+:Hash((QCA_HashContext *)getContext(CAP_MD5))
+{
+}
+
+
+//----------------------------------------------------------------------------
+// BlowFish
+//----------------------------------------------------------------------------
+BlowFish::BlowFish(int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad)
+:Cipher((QCA_CipherContext *)getContext(CAP_BlowFish), dir, mode, key, iv, pad)
+{
+}
+
+
+//----------------------------------------------------------------------------
+// TripleDES
+//----------------------------------------------------------------------------
+TripleDES::TripleDES(int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad)
+:Cipher((QCA_CipherContext *)getContext(CAP_TripleDES), dir, mode, key, iv, pad)
+{
+}
+
+
+//----------------------------------------------------------------------------
+// AES128
+//----------------------------------------------------------------------------
+AES128::AES128(int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad)
+:Cipher((QCA_CipherContext *)getContext(CAP_AES128), dir, mode, key, iv, pad)
+{
+}
+
+
+//----------------------------------------------------------------------------
+// AES256
+//----------------------------------------------------------------------------
+AES256::AES256(int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad)
+:Cipher((QCA_CipherContext *)getContext(CAP_AES256), dir, mode, key, iv, pad)
+{
+}
+
+
+//----------------------------------------------------------------------------
+// RSAKey
+//----------------------------------------------------------------------------
+class RSAKey::Private
+{
+public:
+ Private()
+ {
+ c = 0;
+ }
+
+ ~Private()
+ {
+ delete c;
+ }
+
+ QCA_RSAKeyContext *c;
+};
+
+RSAKey::RSAKey()
+{
+ d = new Private;
+ d->c = (QCA_RSAKeyContext *)getContext(CAP_RSA);
+}
+
+RSAKey::RSAKey(const RSAKey &from)
+{
+ d = new Private;
+ *this = from;
+}
+
+RSAKey & RSAKey::operator=(const RSAKey &from)
+{
+ delete d->c;
+ d->c = from.d->c->clone();
+ return *this;
+}
+
+RSAKey::~RSAKey()
+{
+ delete d;
+}
+
+bool RSAKey::isNull() const
+{
+ return d->c->isNull();
+}
+
+bool RSAKey::havePublic() const
+{
+ return d->c->havePublic();
+}
+
+bool RSAKey::havePrivate() const
+{
+ return d->c->havePrivate();
+}
+
+QByteArray RSAKey::toDER(bool publicOnly) const
+{
+ QByteArray out;
+ if(!d->c->toDER(&out, publicOnly))
+ return QByteArray();
+ return out;
+}
+
+bool RSAKey::fromDER(const QByteArray &a)
+{
+ return d->c->createFromDER(a.data(), a.size());
+}
+
+QString RSAKey::toPEM(bool publicOnly) const
+{
+ QByteArray out;
+ if(!d->c->toPEM(&out, publicOnly))
+ return QByteArray();
+
+ QCString cs;
+ cs.resize(out.size()+1);
+ memcpy(cs.data(), out.data(), out.size());
+ return QString::fromLatin1(cs);
+}
+
+bool RSAKey::fromPEM(const QString &str)
+{
+ QCString cs = str.latin1();
+ QByteArray a(cs.length());
+ memcpy(a.data(), cs.data(), a.size());
+ return d->c->createFromPEM(a.data(), a.size());
+}
+
+bool RSAKey::fromNative(void *p)
+{
+ return d->c->createFromNative(p);
+}
+
+bool RSAKey::encrypt(const QByteArray &a, QByteArray *b, bool oaep) const
+{
+ QByteArray out;
+ if(!d->c->encrypt(a, &out, oaep))
+ return false;
+ *b = out;
+ return true;
+}
+
+bool RSAKey::decrypt(const QByteArray &a, QByteArray *b, bool oaep) const
+{
+ QByteArray out;
+ if(!d->c->decrypt(a, &out, oaep))
+ return false;
+ *b = out;
+ return true;
+}
+
+bool RSAKey::generate(unsigned int bits)
+{
+ return d->c->generate(bits);
+}
+
+
+//----------------------------------------------------------------------------
+// RSA
+//----------------------------------------------------------------------------
+RSA::RSA()
+{
+}
+
+RSA::~RSA()
+{
+}
+
+RSAKey RSA::key() const
+{
+ return v_key;
+}
+
+void RSA::setKey(const RSAKey &k)
+{
+ v_key = k;
+}
+
+bool RSA::encrypt(const QByteArray &a, QByteArray *b, bool oaep) const
+{
+ if(v_key.isNull())
+ return false;
+ return v_key.encrypt(a, b, oaep);
+}
+
+bool RSA::decrypt(const QByteArray &a, QByteArray *b, bool oaep) const
+{
+ if(v_key.isNull())
+ return false;
+ return v_key.decrypt(a, b, oaep);
+}
+
+RSAKey RSA::generateKey(unsigned int bits)
+{
+ RSAKey k;
+ k.generate(bits);
+ return k;
+}
+
+
+//----------------------------------------------------------------------------
+// Cert
+//----------------------------------------------------------------------------
+class Cert::Private
+{
+public:
+ Private()
+ {
+ c = 0;
+ }
+
+ ~Private()
+ {
+ delete c;
+ }
+
+ QCA_CertContext *c;
+};
+
+Cert::Cert()
+{
+ d = new Private;
+ d->c = (QCA_CertContext *)getContext(CAP_X509);
+}
+
+Cert::Cert(const Cert &from)
+{
+ d = new Private;
+ *this = from;
+}
+
+Cert & Cert::operator=(const Cert &from)
+{
+ delete d->c;
+ d->c = from.d->c->clone();
+ return *this;
+}
+
+Cert::~Cert()
+{
+ delete d;
+}
+
+void Cert::fromContext(QCA_CertContext *ctx)
+{
+ delete d->c;
+ d->c = ctx;
+}
+
+bool Cert::isNull() const
+{
+ return d->c->isNull();
+}
+
+QString Cert::commonName() const
+{
+ CertProperties props = subject();
+ return props["CN"];
+}
+
+QString Cert::serialNumber() const
+{
+ return d->c->serialNumber();
+}
+
+QString Cert::subjectString() const
+{
+ return d->c->subjectString();
+}
+
+QString Cert::issuerString() const
+{
+ return d->c->issuerString();
+}
+
+CertProperties Cert::subject() const
+{
+ QValueList<QCA_CertProperty> list = d->c->subject();
+ CertProperties props;
+ for(QValueList<QCA_CertProperty>::ConstIterator it = list.begin(); it != list.end(); ++it)
+ props[(*it).var] = (*it).val;
+ return props;
+}
+
+CertProperties Cert::issuer() const
+{
+ QValueList<QCA_CertProperty> list = d->c->issuer();
+ CertProperties props;
+ for(QValueList<QCA_CertProperty>::ConstIterator it = list.begin(); it != list.end(); ++it)
+ props[(*it).var] = (*it).val;
+ return props;
+}
+
+QDateTime Cert::notBefore() const
+{
+ return d->c->notBefore();
+}
+
+QDateTime Cert::notAfter() const
+{
+ return d->c->notAfter();
+}
+
+QByteArray Cert::toDER() const
+{
+ QByteArray out;
+ if(!d->c->toDER(&out))
+ return QByteArray();
+ return out;
+}
+
+bool Cert::fromDER(const QByteArray &a)
+{
+ return d->c->createFromDER(a.data(), a.size());
+}
+
+QString Cert::toPEM() const
+{
+ QByteArray out;
+ if(!d->c->toPEM(&out))
+ return QByteArray();
+
+ QCString cs;
+ cs.resize(out.size()+1);
+ memcpy(cs.data(), out.data(), out.size());
+ return QString::fromLatin1(cs);
+}
+
+bool Cert::fromPEM(const QString &str)
+{
+ QCString cs = str.latin1();
+ QByteArray a(cs.length());
+ memcpy(a.data(), cs.data(), a.size());
+ return d->c->createFromPEM(a.data(), a.size());
+}
+
+
+//----------------------------------------------------------------------------
+// TLS
+//----------------------------------------------------------------------------
+class TLS::Private
+{
+public:
+ Private()
+ {
+ c = (QCA_TLSContext *)getContext(CAP_TLS);
+ }
+
+ ~Private()
+ {
+ delete c;
+ }
+
+ void reset()
+ {
+ handshaken = false;
+ closing = false;
+ in.resize(0);
+ out.resize(0);
+ from_net.resize(0);
+ to_net.resize(0);
+ host = "";
+ hostMismatch = false;
+ cert = Cert();
+ bytesEncoded = 0;
+ tryMore = false;
+ }
+
+ void appendArray(QByteArray *a, const QByteArray &b)
+ {
+ int oldsize = a->size();
+ a->resize(oldsize + b.size());
+ memcpy(a->data() + oldsize, b.data(), b.size());
+ }
+
+ Cert cert;
+ QCA_TLSContext *c;
+ QByteArray in, out, to_net, from_net;
+ int bytesEncoded;
+ bool tryMore;
+ bool handshaken;
+ QString host;
+ bool hostMismatch;
+ bool closing;
+
+ Cert ourCert;
+ RSAKey ourKey;
+ QPtrList<QCA_CertContext> store;
+};
+
+TLS::TLS(QObject *parent)
+:QObject(parent)
+{
+ d = new Private;
+}
+
+TLS::~TLS()
+{
+ delete d;
+}
+
+void TLS::setCertificate(const Cert &cert, const RSAKey &key)
+{
+ d->ourCert = cert;
+ d->ourKey = key;
+}
+
+void TLS::setCertificateStore(const QPtrList<Cert> &store)
+{
+ // convert the cert list into a context list
+ d->store.clear();
+ QPtrListIterator<Cert> it(store);
+ for(Cert *cert; (cert = it.current()); ++it)
+ d->store.append(cert->d->c);
+}
+
+void TLS::reset()
+{
+ d->reset();
+}
+
+bool TLS::startClient(const QString &host)
+{
+ d->reset();
+ d->host = host;
+
+ if(!d->c->startClient(d->store, *d->ourCert.d->c, *d->ourKey.d->c))
+ return false;
+ QTimer::singleShot(0, this, SLOT(update()));
+ return true;
+}
+
+bool TLS::startServer()
+{
+ d->reset();
+
+ if(!d->c->startServer(d->store, *d->ourCert.d->c, *d->ourKey.d->c))
+ return false;
+ QTimer::singleShot(0, this, SLOT(update()));
+ return true;
+}
+
+void TLS::close()
+{
+ if(!d->handshaken || d->closing)
+ return;
+
+ d->closing = true;
+ QTimer::singleShot(0, this, SLOT(update()));
+}
+
+bool TLS::isHandshaken() const
+{
+ return d->handshaken;
+}
+
+void TLS::write(const QByteArray &a)
+{
+ d->appendArray(&d->out, a);
+ update();
+}
+
+QByteArray TLS::read()
+{
+ QByteArray a = d->in.copy();
+ d->in.resize(0);
+ return a;
+}
+
+void TLS::writeIncoming(const QByteArray &a)
+{
+ d->appendArray(&d->from_net, a);
+ update();
+}
+
+QByteArray TLS::readOutgoing()
+{
+ QByteArray a = d->to_net.copy();
+ d->to_net.resize(0);
+ return a;
+}
+
+QByteArray TLS::readUnprocessed()
+{
+ QByteArray a = d->from_net.copy();
+ d->from_net.resize(0);
+ return a;
+}
+
+const Cert & TLS::peerCertificate() const
+{
+ return d->cert;
+}
+
+int TLS::certificateValidityResult() const
+{
+ if(d->hostMismatch)
+ return QCA::TLS::HostMismatch;
+ else
+ return d->c->validityResult();
+}
+
+void TLS::update()
+{
+ bool force_read = false;
+ bool eof = false;
+ bool done = false;
+ QGuardedPtr<TLS> self = this;
+
+ if(d->closing) {
+ QByteArray a;
+ int r = d->c->shutdown(d->from_net, &a);
+ d->from_net.resize(0);
+ if(r == QCA_TLSContext::Error) {
+ reset();
+ error(ErrHandshake);
+ return;
+ }
+ if(r == QCA_TLSContext::Success) {
+ d->from_net = d->c->unprocessed().copy();
+ done = true;
+ }
+ d->appendArray(&d->to_net, a);
+ }
+ else {
+ if(!d->handshaken) {
+ QByteArray a;
+ int r = d->c->handshake(d->from_net, &a);
+ d->from_net.resize(0);
+ if(r == QCA_TLSContext::Error) {
+ reset();
+ error(ErrHandshake);
+ return;
+ }
+ d->appendArray(&d->to_net, a);
+ if(r == QCA_TLSContext::Success) {
+ QCA_CertContext *cc = d->c->peerCertificate();
+ if(cc && !d->host.isEmpty() && d->c->validityResult() == QCA::TLS::Valid) {
+ if(!cc->matchesAddress(d->host))
+ d->hostMismatch = true;
+ }
+ d->cert.fromContext(cc);
+ d->handshaken = true;
+ handshaken();
+ if(!self)
+ return;
+
+ // there is a teeny tiny possibility that incoming data awaits. let us get it.
+ force_read = true;
+ }
+ }
+
+ if(d->handshaken) {
+ if(!d->out.isEmpty() || d->tryMore) {
+ d->tryMore = false;
+ QByteArray a;
+ int enc;
+ bool more = false;
+ bool ok = d->c->encode(d->out, &a, &enc);
+ eof = d->c->eof();
+ if(ok && enc < (int)d->out.size())
+ more = true;
+ d->out.resize(0);
+ if(!eof) {
+ if(!ok) {
+ reset();
+ error(ErrCrypt);
+ return;
+ }
+ d->bytesEncoded += enc;
+ if(more)
+ d->tryMore = true;
+ d->appendArray(&d->to_net, a);
+ }
+ }
+ if(!d->from_net.isEmpty() || force_read) {
+ QByteArray a, b;
+ bool ok = d->c->decode(d->from_net, &a, &b);
+ eof = d->c->eof();
+ d->from_net.resize(0);
+ if(!ok) {
+ reset();
+ error(ErrCrypt);
+ return;
+ }
+ d->appendArray(&d->in, a);
+ d->appendArray(&d->to_net, b);
+ }
+
+ if(!d->in.isEmpty()) {
+ readyRead();
+ if(!self)
+ return;
+ }
+ }
+ }
+
+ if(!d->to_net.isEmpty()) {
+ int bytes = d->bytesEncoded;
+ d->bytesEncoded = 0;
+ readyReadOutgoing(bytes);
+ if(!self)
+ return;
+ }
+
+ if(eof) {
+ close();
+ if(!self)
+ return;
+ return;
+ }
+
+ if(d->closing && done) {
+ reset();
+ closed();
+ }
+}
+
+
+//----------------------------------------------------------------------------
+// SASL
+//----------------------------------------------------------------------------
+QString saslappname = "qca";
+class SASL::Private
+{
+public:
+ Private()
+ {
+ c = (QCA_SASLContext *)getContext(CAP_SASL);
+ }
+
+ ~Private()
+ {
+ delete c;
+ }
+
+ void setSecurityProps()
+ {
+ c->setSecurityProps(noPlain, noActive, noDict, noAnon, reqForward, reqCreds, reqMutual, ssfmin, ssfmax, ext_authid, ext_ssf);
+ }
+
+ // security opts
+ bool noPlain, noActive, noDict, noAnon, reqForward, reqCreds, reqMutual;
+ int ssfmin, ssfmax;
+ QString ext_authid;
+ int ext_ssf;
+
+ bool tried;
+ QCA_SASLContext *c;
+ QHostAddress localAddr, remoteAddr;
+ int localPort, remotePort;
+ QByteArray stepData;
+ bool allowCSF;
+ bool first, server;
+
+ QByteArray inbuf, outbuf;
+};
+
+SASL::SASL(QObject *parent)
+:QObject(parent)
+{
+ d = new Private;
+ reset();
+}
+
+SASL::~SASL()
+{
+ delete d;
+}
+
+void SASL::setAppName(const QString &name)
+{
+ saslappname = name;
+}
+
+void SASL::reset()
+{
+ d->localPort = -1;
+ d->remotePort = -1;
+
+ d->noPlain = false;
+ d->noActive = false;
+ d->noDict = false;
+ d->noAnon = false;
+ d->reqForward = false;
+ d->reqCreds = false;
+ d->reqMutual = false;
+ d->ssfmin = 0;
+ d->ssfmax = 0;
+ d->ext_authid = "";
+ d->ext_ssf = 0;
+
+ d->inbuf.resize(0);
+ d->outbuf.resize(0);
+
+ d->c->reset();
+}
+
+int SASL::errorCondition() const
+{
+ return d->c->errorCond();
+}
+
+void SASL::setAllowPlain(bool b)
+{
+ d->noPlain = !b;
+}
+
+void SASL::setAllowAnonymous(bool b)
+{
+ d->noAnon = !b;
+}
+
+void SASL::setAllowActiveVulnerable(bool b)
+{
+ d->noActive = !b;
+}
+
+void SASL::setAllowDictionaryVulnerable(bool b)
+{
+ d->noDict = !b;
+}
+
+void SASL::setRequireForwardSecrecy(bool b)
+{
+ d->reqForward = b;
+}
+
+void SASL::setRequirePassCredentials(bool b)
+{
+ d->reqCreds = b;
+}
+
+void SASL::setRequireMutualAuth(bool b)
+{
+ d->reqMutual = b;
+}
+
+void SASL::setMinimumSSF(int x)
+{
+ d->ssfmin = x;
+}
+
+void SASL::setMaximumSSF(int x)
+{
+ d->ssfmax = x;
+}
+
+void SASL::setExternalAuthID(const QString &authid)
+{
+ d->ext_authid = authid;
+}
+
+void SASL::setExternalSSF(int x)
+{
+ d->ext_ssf = x;
+}
+
+void SASL::setLocalAddr(const QHostAddress &addr, Q_UINT16 port)
+{
+ d->localAddr = addr;
+ d->localPort = port;
+}
+
+void SASL::setRemoteAddr(const QHostAddress &addr, Q_UINT16 port)
+{
+ d->remoteAddr = addr;
+ d->remotePort = port;
+}
+
+bool SASL::startClient(const QString &service, const QString &host, const QStringList &mechlist, bool allowClientSendFirst)
+{
+ QCA_SASLHostPort la, ra;
+ if(d->localPort != -1) {
+ la.addr = d->localAddr;
+ la.port = d->localPort;
+ }
+ if(d->remotePort != -1) {
+ ra.addr = d->remoteAddr;
+ ra.port = d->remotePort;
+ }
+
+ d->allowCSF = allowClientSendFirst;
+ d->c->setCoreProps(service, host, d->localPort != -1 ? &la : 0, d->remotePort != -1 ? &ra : 0);
+ d->setSecurityProps();
+
+ if(!d->c->clientStart(mechlist))
+ return false;
+ d->first = true;
+ d->server = false;
+ d->tried = false;
+ QTimer::singleShot(0, this, SLOT(tryAgain()));
+ return true;
+}
+
+bool SASL::startServer(const QString &service, const QString &host, const QString &realm, QStringList *mechlist)
+{
+ QCA_SASLHostPort la, ra;
+ if(d->localPort != -1) {
+ la.addr = d->localAddr;
+ la.port = d->localPort;
+ }
+ if(d->remotePort != -1) {
+ ra.addr = d->remoteAddr;
+ ra.port = d->remotePort;
+ }
+
+ d->c->setCoreProps(service, host, d->localPort != -1 ? &la : 0, d->remotePort != -1 ? &ra : 0);
+ d->setSecurityProps();
+
+ if(!d->c->serverStart(realm, mechlist, saslappname))
+ return false;
+ d->first = true;
+ d->server = true;
+ d->tried = false;
+ return true;
+}
+
+void SASL::putServerFirstStep(const QString &mech)
+{
+ int r = d->c->serverFirstStep(mech, 0);
+ handleServerFirstStep(r);
+}
+
+void SASL::putServerFirstStep(const QString &mech, const QByteArray &clientInit)
+{
+ int r = d->c->serverFirstStep(mech, &clientInit);
+ handleServerFirstStep(r);
+}
+
+void SASL::handleServerFirstStep(int r)
+{
+ if(r == QCA_SASLContext::Success)
+ authenticated();
+ else if(r == QCA_SASLContext::Continue)
+ nextStep(d->c->result());
+ else if(r == QCA_SASLContext::AuthCheck)
+ tryAgain();
+ else
+ error(ErrAuth);
+}
+
+void SASL::putStep(const QByteArray &stepData)
+{
+ d->stepData = stepData.copy();
+ tryAgain();
+}
+
+void SASL::setUsername(const QString &user)
+{
+ d->c->setClientParams(&user, 0, 0, 0);
+}
+
+void SASL::setAuthzid(const QString &authzid)
+{
+ d->c->setClientParams(0, &authzid, 0, 0);
+}
+
+void SASL::setPassword(const QString &pass)
+{
+ d->c->setClientParams(0, 0, &pass, 0);
+}
+
+void SASL::setRealm(const QString &realm)
+{
+ d->c->setClientParams(0, 0, 0, &realm);
+}
+
+void SASL::continueAfterParams()
+{
+ tryAgain();
+}
+
+void SASL::continueAfterAuthCheck()
+{
+ tryAgain();
+}
+
+void SASL::tryAgain()
+{
+ int r;
+
+ if(d->server) {
+ if(!d->tried) {
+ r = d->c->nextStep(d->stepData);
+ d->tried = true;
+ }
+ else {
+ r = d->c->tryAgain();
+ }
+
+ if(r == QCA_SASLContext::Error) {
+ error(ErrAuth);
+ return;
+ }
+ else if(r == QCA_SASLContext::Continue) {
+ d->tried = false;
+ nextStep(d->c->result());
+ return;
+ }
+ else if(r == QCA_SASLContext::AuthCheck) {
+ authCheck(d->c->username(), d->c->authzid());
+ return;
+ }
+ }
+ else {
+ if(d->first) {
+ if(!d->tried) {
+ r = d->c->clientFirstStep(d->allowCSF);
+ d->tried = true;
+ }
+ else
+ r = d->c->tryAgain();
+
+ if(r == QCA_SASLContext::Error) {
+ error(ErrAuth);
+ return;
+ }
+ else if(r == QCA_SASLContext::NeedParams) {
+ //d->tried = false;
+ QCA_SASLNeedParams np = d->c->clientParamsNeeded();
+ needParams(np.user, np.authzid, np.pass, np.realm);
+ return;
+ }
+
+ QString mech = d->c->mech();
+ const QByteArray *clientInit = d->c->clientInit();
+
+ d->first = false;
+ d->tried = false;
+ clientFirstStep(mech, clientInit);
+ }
+ else {
+ if(!d->tried) {
+ r = d->c->nextStep(d->stepData);
+ d->tried = true;
+ }
+ else
+ r = d->c->tryAgain();
+
+ if(r == QCA_SASLContext::Error) {
+ error(ErrAuth);
+ return;
+ }
+ else if(r == QCA_SASLContext::NeedParams) {
+ //d->tried = false;
+ QCA_SASLNeedParams np = d->c->clientParamsNeeded();
+ needParams(np.user, np.authzid, np.pass, np.realm);
+ return;
+ }
+ d->tried = false;
+ //else if(r == QCA_SASLContext::Continue) {
+ nextStep(d->c->result());
+ // return;
+ //}
+ }
+ }
+
+ if(r == QCA_SASLContext::Success)
+ authenticated();
+ else if(r == QCA_SASLContext::Error)
+ error(ErrAuth);
+}
+
+int SASL::ssf() const
+{
+ return d->c->security();
+}
+
+void SASL::write(const QByteArray &a)
+{
+ QByteArray b;
+ if(!d->c->encode(a, &b)) {
+ error(ErrCrypt);
+ return;
+ }
+ int oldsize = d->outbuf.size();
+ d->outbuf.resize(oldsize + b.size());
+ memcpy(d->outbuf.data() + oldsize, b.data(), b.size());
+ readyReadOutgoing(a.size());
+}
+
+QByteArray SASL::read()
+{
+ QByteArray a = d->inbuf.copy();
+ d->inbuf.resize(0);
+ return a;
+}
+
+void SASL::writeIncoming(const QByteArray &a)
+{
+ QByteArray b;
+ if(!d->c->decode(a, &b)) {
+ error(ErrCrypt);
+ return;
+ }
+ int oldsize = d->inbuf.size();
+ d->inbuf.resize(oldsize + b.size());
+ memcpy(d->inbuf.data() + oldsize, b.data(), b.size());
+ readyRead();
+}
+
+QByteArray SASL::readOutgoing()
+{
+ QByteArray a = d->outbuf.copy();
+ d->outbuf.resize(0);
+ return a;
+}
+
+#include "qca.moc"
diff --git a/kopete/protocols/jabber/libiris/qca/src/qca.h b/kopete/protocols/jabber/libiris/qca/src/qca.h
new file mode 100644
index 00000000..e7cd1609
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/qca/src/qca.h
@@ -0,0 +1,466 @@
+/*
+ * qca.h - Qt Cryptographic Architecture
+ * 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 QCA_H
+#define QCA_H
+
+#include<qstring.h>
+#include<qcstring.h>
+#include<qdatetime.h>
+#include<qmap.h>
+#include<qptrlist.h>
+#include<qobject.h>
+
+#ifdef Q_OS_WIN32
+# ifndef QCA_STATIC
+# ifdef QCA_MAKEDLL
+# define QCA_EXPORT __declspec(dllexport)
+# else
+# define QCA_EXPORT __declspec(dllimport)
+# endif
+# endif
+#endif
+#ifndef QCA_EXPORT
+#define QCA_EXPORT
+#endif
+
+#ifdef Q_OS_WIN32
+# ifdef QCA_PLUGIN_DLL
+# define QCA_PLUGIN_EXPORT extern "C" __declspec(dllexport)
+# else
+# define QCA_PLUGIN_EXPORT extern "C" __declspec(dllimport)
+# endif
+#endif
+#ifndef QCA_PLUGIN_EXPORT
+#define QCA_PLUGIN_EXPORT extern "C"
+#endif
+
+class QHostAddress;
+class QStringList;
+
+class QCAProvider;
+class QCA_HashContext;
+class QCA_CipherContext;
+class QCA_CertContext;
+
+namespace QCA
+{
+ enum {
+ CAP_SHA1 = 0x0001,
+ CAP_SHA256 = 0x0002,
+ CAP_MD5 = 0x0004,
+ CAP_BlowFish = 0x0008,
+ CAP_TripleDES = 0x0010,
+ CAP_AES128 = 0x0020,
+ CAP_AES256 = 0x0040,
+ CAP_RSA = 0x0080,
+ CAP_X509 = 0x0100,
+ CAP_TLS = 0x0200,
+ CAP_SASL = 0x0400
+ };
+
+ enum {
+ CBC = 0x0001,
+ CFB = 0x0002
+ };
+
+ enum {
+ Encrypt = 0x0001,
+ Decrypt = 0x0002
+ };
+
+ QCA_EXPORT void init();
+ QCA_EXPORT bool isSupported(int capabilities);
+ QCA_EXPORT void insertProvider(QCAProvider *);
+ QCA_EXPORT void unloadAllPlugins();
+
+ QCA_EXPORT QString arrayToHex(const QByteArray &);
+ QCA_EXPORT QByteArray hexToArray(const QString &);
+
+ class QCA_EXPORT Hash
+ {
+ public:
+ Hash(const Hash &);
+ Hash & operator=(const Hash &);
+ ~Hash();
+
+ void clear();
+ void update(const QByteArray &a);
+ QByteArray final();
+
+ protected:
+ Hash(QCA_HashContext *);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ template <class T>
+ class QCA_EXPORT HashStatic
+ {
+ public:
+ HashStatic<T>() {}
+
+ static QByteArray hash(const QByteArray &a)
+ {
+ T obj;
+ obj.update(a);
+ return obj.final();
+ }
+
+ static QByteArray hash(const QCString &cs)
+ {
+ QByteArray a(cs.length());
+ memcpy(a.data(), cs.data(), a.size());
+ return hash(a);
+ }
+
+ static QString hashToString(const QByteArray &a)
+ {
+ return arrayToHex(hash(a));
+ }
+
+ static QString hashToString(const QCString &cs)
+ {
+ return arrayToHex(hash(cs));
+ }
+ };
+
+ class QCA_EXPORT Cipher
+ {
+ public:
+ Cipher(const Cipher &);
+ Cipher & operator=(const Cipher &);
+ ~Cipher();
+
+ QByteArray dyn_generateKey(int size=-1) const;
+ QByteArray dyn_generateIV() const;
+ void reset(int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad=true);
+ bool update(const QByteArray &a);
+ QByteArray final(bool *ok=0);
+
+ protected:
+ Cipher(QCA_CipherContext *, int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ template <class T>
+ class QCA_EXPORT CipherStatic
+ {
+ public:
+ CipherStatic<T>() {}
+
+ static QByteArray generateKey(int size=-1)
+ {
+ T obj;
+ return obj.dyn_generateKey(size);
+ }
+
+ static QByteArray generateIV()
+ {
+ T obj;
+ return obj.dyn_generateIV();
+ }
+ };
+
+ class QCA_EXPORT SHA1 : public Hash, public HashStatic<SHA1>
+ {
+ public:
+ SHA1();
+ };
+
+ class QCA_EXPORT SHA256 : public Hash, public HashStatic<SHA256>
+ {
+ public:
+ SHA256();
+ };
+
+ class QCA_EXPORT MD5 : public Hash, public HashStatic<MD5>
+ {
+ public:
+ MD5();
+ };
+
+ class QCA_EXPORT BlowFish : public Cipher, public CipherStatic<BlowFish>
+ {
+ public:
+ BlowFish(int dir=Encrypt, int mode=CBC, const QByteArray &key=QByteArray(), const QByteArray &iv=QByteArray(), bool pad=true);
+ };
+
+ class QCA_EXPORT TripleDES : public Cipher, public CipherStatic<TripleDES>
+ {
+ public:
+ TripleDES(int dir=Encrypt, int mode=CBC, const QByteArray &key=QByteArray(), const QByteArray &iv=QByteArray(), bool pad=true);
+ };
+
+ class QCA_EXPORT AES128 : public Cipher, public CipherStatic<AES128>
+ {
+ public:
+ AES128(int dir=Encrypt, int mode=CBC, const QByteArray &key=QByteArray(), const QByteArray &iv=QByteArray(), bool pad=true);
+ };
+
+ class QCA_EXPORT AES256 : public Cipher, public CipherStatic<AES256>
+ {
+ public:
+ AES256(int dir=Encrypt, int mode=CBC, const QByteArray &key=QByteArray(), const QByteArray &iv=QByteArray(), bool pad=true);
+ };
+
+ class RSA;
+ class QCA_EXPORT RSAKey
+ {
+ public:
+ RSAKey();
+ RSAKey(const RSAKey &from);
+ RSAKey & operator=(const RSAKey &from);
+ ~RSAKey();
+
+ bool isNull() const;
+ bool havePublic() const;
+ bool havePrivate() const;
+
+ QByteArray toDER(bool publicOnly=false) const;
+ bool fromDER(const QByteArray &a);
+
+ QString toPEM(bool publicOnly=false) const;
+ bool fromPEM(const QString &);
+
+ // only call if you know what you are doing
+ bool fromNative(void *);
+
+ private:
+ class Private;
+ Private *d;
+
+ friend class RSA;
+ friend class TLS;
+ bool encrypt(const QByteArray &a, QByteArray *out, bool oaep) const;
+ bool decrypt(const QByteArray &a, QByteArray *out, bool oaep) const;
+ bool generate(unsigned int bits);
+ };
+
+ class QCA_EXPORT RSA
+ {
+ public:
+ RSA();
+ ~RSA();
+
+ RSAKey key() const;
+ void setKey(const RSAKey &);
+
+ bool encrypt(const QByteArray &a, QByteArray *out, bool oaep=false) const;
+ bool decrypt(const QByteArray &a, QByteArray *out, bool oaep=false) const;
+
+ static RSAKey generateKey(unsigned int bits);
+
+ private:
+ RSAKey v_key;
+ };
+
+ typedef QMap<QString, QString> CertProperties;
+ class QCA_EXPORT Cert
+ {
+ public:
+ Cert();
+ Cert(const Cert &);
+ Cert & operator=(const Cert &);
+ ~Cert();
+
+ bool isNull() const;
+
+ QString commonName() const;
+ QString serialNumber() const;
+ QString subjectString() const;
+ QString issuerString() const;
+ CertProperties subject() const;
+ CertProperties issuer() const;
+ QDateTime notBefore() const;
+ QDateTime notAfter() const;
+
+ QByteArray toDER() const;
+ bool fromDER(const QByteArray &a);
+
+ QString toPEM() const;
+ bool fromPEM(const QString &);
+
+ private:
+ class Private;
+ Private *d;
+
+ friend class TLS;
+ void fromContext(QCA_CertContext *);
+ };
+
+ class QCA_EXPORT TLS : public QObject
+ {
+ Q_OBJECT
+ public:
+ enum Validity {
+ NoCert,
+ Valid,
+ HostMismatch,
+ Rejected,
+ Untrusted,
+ SignatureFailed,
+ InvalidCA,
+ InvalidPurpose,
+ SelfSigned,
+ Revoked,
+ PathLengthExceeded,
+ Expired,
+ Unknown
+ };
+ enum Error { ErrHandshake, ErrCrypt };
+
+ TLS(QObject *parent=0);
+ ~TLS();
+
+ void setCertificate(const Cert &cert, const RSAKey &key);
+ void setCertificateStore(const QPtrList<Cert> &store); // note: store must persist
+
+ void reset();
+ bool startClient(const QString &host="");
+ bool startServer();
+ void close();
+ bool isHandshaken() const;
+
+ // plain (application side)
+ void write(const QByteArray &a);
+ QByteArray read();
+
+ // encoded (socket side)
+ void writeIncoming(const QByteArray &a);
+ QByteArray readOutgoing();
+ QByteArray readUnprocessed();
+
+ // cert related
+ const Cert & peerCertificate() const;
+ int certificateValidityResult() const;
+
+ signals:
+ void handshaken();
+ void readyRead();
+ void readyReadOutgoing(int plainBytes);
+ void closed();
+ void error(int);
+
+ private slots:
+ void update();
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ class QCA_EXPORT SASL : public QObject
+ {
+ Q_OBJECT
+ public:
+ enum Error { ErrAuth, ErrCrypt };
+ enum ErrorCond {
+ NoMech,
+ BadProto,
+ BadServ,
+ BadAuth,
+ NoAuthzid,
+ TooWeak,
+ NeedEncrypt,
+ Expired,
+ Disabled,
+ NoUser,
+ RemoteUnavail
+ };
+ SASL(QObject *parent=0);
+ ~SASL();
+
+ static void setAppName(const QString &name);
+
+ void reset();
+ int errorCondition() const;
+
+ // options
+ void setAllowPlain(bool);
+ void setAllowAnonymous(bool);
+ void setAllowActiveVulnerable(bool);
+ void setAllowDictionaryVulnerable(bool);
+ void setRequireForwardSecrecy(bool);
+ void setRequirePassCredentials(bool);
+ void setRequireMutualAuth(bool);
+
+ void setMinimumSSF(int);
+ void setMaximumSSF(int);
+ void setExternalAuthID(const QString &authid);
+ void setExternalSSF(int);
+
+ void setLocalAddr(const QHostAddress &addr, Q_UINT16 port);
+ void setRemoteAddr(const QHostAddress &addr, Q_UINT16 port);
+
+ // initialize
+ bool startClient(const QString &service, const QString &host, const QStringList &mechlist, bool allowClientSendFirst=true);
+ bool startServer(const QString &service, const QString &host, const QString &realm, QStringList *mechlist);
+
+ // authentication
+ void putStep(const QByteArray &stepData);
+ void putServerFirstStep(const QString &mech);
+ void putServerFirstStep(const QString &mech, const QByteArray &clientInit);
+ void setUsername(const QString &user);
+ void setAuthzid(const QString &auth);
+ void setPassword(const QString &pass);
+ void setRealm(const QString &realm);
+ void continueAfterParams();
+ void continueAfterAuthCheck();
+
+ // security layer
+ int ssf() const;
+ void write(const QByteArray &a);
+ QByteArray read();
+ void writeIncoming(const QByteArray &a);
+ QByteArray readOutgoing();
+
+ signals:
+ // for authentication
+ void clientFirstStep(const QString &mech, const QByteArray *clientInit);
+ void nextStep(const QByteArray &stepData);
+ void needParams(bool user, bool authzid, bool pass, bool realm);
+ void authCheck(const QString &user, const QString &authzid);
+ void authenticated();
+
+ // for security layer
+ void readyRead();
+ void readyReadOutgoing(int plainBytes);
+
+ // error
+ void error(int);
+
+ private slots:
+ void tryAgain();
+
+ private:
+ class Private;
+ Private *d;
+
+ void handleServerFirstStep(int r);
+ };
+}
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/qca/src/qcaprovider.h b/kopete/protocols/jabber/libiris/qca/src/qcaprovider.h
new file mode 100644
index 00000000..a7f1805b
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/qca/src/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