diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | bcb704366cb5e333a626c18c308c7e0448a8e69f (patch) | |
tree | f0d6ab7d78ecdd9207cf46536376b44b91a1ca71 /kopete/protocols/jabber/libiris/iris/xmpp-im | |
download | tdenetwork-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/iris/xmpp-im')
9 files changed, 8004 insertions, 0 deletions
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 ¬e() 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 |