From bcb704366cb5e333a626c18c308c7e0448a8e69f Mon Sep 17 00:00:00 2001 From: toma Date: Wed, 25 Nov 2009 17:56:58 +0000 Subject: 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 --- .../jabber/libiris/iris/xmpp-im/client.cpp | 1522 ++++++++++++++++++++ 1 file changed, 1522 insertions(+) create mode 100644 kopete/protocols/jabber/libiris/iris/xmpp-im/client.cpp (limited to 'kopete/protocols/jabber/libiris/iris/xmpp-im/client.cpp') 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 +#include +#include +#include +#include +#include"xmpp_tasks.h" +#include"xmpp_xmlcommon.h" +#include"s5b.h" +#include"xmpp_ibb.h" +#include"xmpp_jidlink.h" +#include"filetransfer.h" + +/*#include +#include +#include +#include +#include +#include +#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 extension_features; + int tzoffset; + bool active; + + LiveRoster roster; + ResourceList resourceList; + S5BManager *s5bman; + IBBManager *ibbman; + JidLinkManager *jlman; + FileTransferManager *ftman; + bool ftEnabled; + QValueList 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::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::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::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::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::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::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 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(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::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::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(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() +{ +} + +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; +} + +} -- cgit v1.2.1