/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 TQt. All Tasks have a tqparent, //! except for the root Task, and are considered TQObjects. By using TQt's RTTI //! facilities (TQObject::sender(), TQObject::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, TQT_SIGNAL(handshaken()), TQT_SLOT(clientHandshaken())); //! connect(client, TQT_SIGNAL(authFinished(bool, int, const TQString &)), TQT_SLOT(authFinished(bool, int, const TQString &))); //! client->connectToHost("jabber.org"); //! } //! //! void Session::clientHandshaken() //! { //! client->authDigest("jabtest", "12345", "Psi"); //! } //! //! void Session::authFinished(bool success, int, const TQString &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 TQ_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; TQDomDocument doc; int id_seed; Task *root; TQString host, user, pass, resource; TQString osname, tzname, clientName, clientVersion, capsNode, capsVersion, capsExt; DiscoItem::Identity identity; TQMap extension_features; int tzoffset; bool active; LiveRoster roster; ResourceList resourceList; S5BManager *s5bman; IBBManager *ibbman; JidLinkManager *jlman; FileTransferManager *ftman; bool ftEnabled; TQValueList groupChatList; }; Client::Client(TQObject *par) :TQObject(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, TQT_SIGNAL(incomingReady()), TQT_SLOT(s5b_incomingReady())); d->ibbman = new IBBManager(this); connect(d->ibbman, TQT_SIGNAL(incomingReady()), TQT_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, TQT_SIGNAL(connected()), TQT_SLOT(streamConnected())); //connect(d->stream, TQT_SIGNAL(handshaken()), TQT_SLOT(streamHandshaken())); connect(d->stream, TQT_SIGNAL(error(int)), TQT_SLOT(streamError(int))); //connect(d->stream, TQT_SIGNAL(sslCertificateReady(const TQSSLCert &)), TQT_SLOT(streamSSLCertificateReady(const TQSSLCert &))); connect(d->stream, TQT_SIGNAL(readyRead()), TQT_SLOT(streamReadyRead())); //connect(d->stream, TQT_SIGNAL(closeFinished()), TQT_SLOT(streamCloseFinished())); connect(d->stream, TQT_SIGNAL(incomingXml(const TQString &)), TQT_SLOT(streamIncomingXml(const TQString &))); connect(d->stream, TQT_SIGNAL(outgoingXml(const TQString &)), TQT_SLOT(streamOutgoingXml(const TQString &))); d->stream->connectToServer(j, auth); } void Client::start(const TQString &host, const TQString &user, const TQString &pass, const TQString &_resource) { // TODO d->host = host; d->user = user; d->pass = pass; d->resource = _resource; tqStatus stat; stat.setIsAvailable(false); d->resourceList += Resource(resource(), stat); JT_PushPresence *pp = new JT_PushPresence(rootTask()); connect(pp, TQT_SIGNAL(subscription(const Jid &, const TQString &)), TQT_SLOT(ppSubscription(const Jid &, const TQString &))); connect(pp, TQT_SIGNAL(presence(const Jid &, const tqStatus &)), TQT_SLOT(ppPresence(const Jid &, const tqStatus &))); JT_PushMessage *pm = new JT_PushMessage(rootTask()); connect(pm, TQT_SIGNAL(message(const Message &)), TQT_SLOT(pmMessage(const Message &))); JT_PushRoster *pr = new JT_PushRoster(rootTask()); connect(pr, TQT_SIGNAL(roster(const Roster &)), TQT_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 TQString &host, const TQString &room, const TQString &nick, const tqStatus &_s) { Jid jid(room + "@" + host + "/" + nick); for(TQValueList::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) { GroupChat &i = *it; if(i.j.compare(jid, false)) { i.j = jid; tqStatus s = _s; s.setIsAvailable(true); JT_Presence *j = new JT_Presence(rootTask()); j->pres(jid, s); j->go(true); break; } } } bool Client::groupChatJoin(const TQString &host, const TQString &room, const TQString &nick) { Jid jid(room + "@" + host + "/" + nick); for(TQValueList::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(TQString("Client: Joined: [%1]\n").tqarg(jid.full())); GroupChat i; i.j = jid; i.status = GroupChat::Connecting; d->groupChatList += i; JT_Presence *j = new JT_Presence(rootTask()); j->pres(jid, tqStatus()); j->go(true); return true; } bool Client::groupChatJoin(const TQString &host, const TQString &room, const TQString &nick, const TQString &password) { Jid jid(room + "@" + host + "/" + nick); for(TQValueList::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(TQString("Client: Joined: [%1]\n").tqarg(jid.full())); GroupChat i; i.j = jid; i.status = GroupChat::Connecting; d->groupChatList += i; JT_MucPresence *j = new JT_MucPresence(rootTask()); j->pres(jid, tqStatus(), password); j->go(true); return true; } void Client::groupChatSettqStatus(const TQString &host, const TQString &room, const tqStatus &_s) { Jid jid(room + "@" + host); bool found = false; for(TQValueList::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; tqStatus s = _s; s.setIsAvailable(true); JT_Presence *j = new JT_Presence(rootTask()); j->pres(jid, s); j->go(true); } void Client::groupChatLeave(const TQString &host, const TQString &room) { Jid jid(room + "@" + host); for(TQValueList::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(TQString("Client: Leaving: [%1]\n").tqarg(i.j.full())); JT_Presence *j = new JT_Presence(rootTask()); tqStatus 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(TQValueList::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) { GroupChat &i = *it; i.status = GroupChat::Closing; JT_Presence *j = new JT_Presence(rootTask()); tqStatus 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 TQSSLCert &cert) { sslCertReady(cert); } void Client::streamCloseFinished() { closeFinished(); }*/ static TQDomElement oldStyleNS(const TQDomElement &e) { // find closest tqparent with a namespace TQDomNode par = e.tqparentNode(); while(!par.isNull() && par.namespaceURI().isNull()) par = par.tqparentNode(); bool noShowNS = false; if(!par.isNull() && par.namespaceURI() == e.namespaceURI()) noShowNS = true; TQDomElement i; uint x; //if(noShowNS) i = e.ownerDocument().createElement(e.tagName()); //else // i = e.ownerDocument().createElementNS(e.namespaceURI(), e.tagName()); // copy attributes TQDomNamedNodeMap 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 tqchildren TQDomNodeList nl = e.childNodes(); for(x = 0; x < nl.count(); ++x) { TQDomNode 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 TQGuardedPtr pstream = d->stream; while(pstream && d->stream->stanzaAvailable()) { Stanza s = d->stream->read(); TQString out = s.toString(); debug(TQString("Client: incoming: [\n%1]\n").tqarg(out)); xmlIncoming(out); TQDomElement x = oldStyleNS(s.element()); distribute(x); } } void Client::streamIncomingXml(const TQString &s) { TQString str = s; if(str.at(str.length()-1) != '\n') str += '\n'; xmlIncoming(str); } void Client::streamOutgoingXml(const TQString &s) { TQString str = s; if(str.at(str.length()-1) != '\n') str += '\n'; xmlOutgoing(str); } void Client::debug(const TQString &str) { debugText(str); } TQString Client::genUniqueId() { TQString s; s.sprintf("a%x", d->id_seed); d->id_seed += 0x10; return s; } Task *Client::rootTask() { return d->root; } TQDomDocument *Client::doc() const { return &d->doc; } void Client::distribute(const TQDomElement &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 TQDomElement addCorrectNS(const TQDomElement &e) { uint x; // grab child nodes /*TQDomDocumentFragment frag = e.ownerDocument().createDocumentFragment(); TQDomNodeList nl = e.childNodes(); for(x = 0; x < nl.count(); ++x) frag.appendChild(nl.item(x).cloneNode());*/ // find closest xmlns TQDomNode n = e; while(!n.isNull() && !n.toElement().hasAttribute("xmlns")) n = n.tqparentNode(); TQString ns; if(n.isNull() || !n.toElement().hasAttribute("xmlns")) ns = "jabber:client"; else ns = n.toElement().attribute("xmlns"); // make a new node TQDomElement i = e.ownerDocument().createElementNS(ns, e.tagName()); // copy attributes TQDomNamedNodeMap al = e.attributes(); for(x = 0; x < al.count(); ++x) { TQDomAttr a = al.item(x).toAttr(); if(a.name() != "xmlns") i.setAttributeNodeNS(a.cloneNode().toAttr()); } // copy tqchildren TQDomNodeList nl = e.childNodes(); for(x = 0; x < nl.count(); ++x) { TQDomNode 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 TQDomElement &x) { if(!d->stream) return; //TQString out; //TQTextStream ts(&out, IO_WriteOnly); //x.save(ts, 0); //TQString out = Stream::xmlToString(x); //debug(TQString("Client: outgoing: [\n%1]\n").tqarg(out)); //xmlOutgoing(out); TQDomElement e = addCorrectNS(x); Stanza s = d->stream->createStanza(e); if(s.isNull()) { //printf("bad stanza??\n"); return; } TQString out = s.toString(); debug(TQString("Client: outgoing: [\n%1]\n").tqarg(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 TQString &str) { if(!d->stream) return; debug(TQString("Client: outgoing: [\n%1]\n").tqarg(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; } TQString Client::host() const { return d->host; } TQString Client::user() const { return d->user; } TQString Client::pass() const { return d->pass; } TQString Client::resource() const { return d->resource; } Jid Client::jid() const { TQString 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 TQString &s) { subscription(j, s); } void Client::ppPresence(const Jid &j, const tqStatus &s) { if(s.isAvailable()) debug(TQString("Client: %1 is available.\n").tqarg(j.full())); else debug(TQString("Client: %1 is unavailable.\n").tqarg(j.full())); for(TQValueList::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(TQString("for groupchat i=[%1] pres=[%2], [us=%3].\n").tqarg(i.j.full()).tqarg(j.full()).tqarg(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 tqStatus &s) { ResourceList::Iterator rit = d->resourceList.tqfind(j.resource()); bool found = (rit == d->resourceList.end()) ? false: true; // unavailable? remove the resource if(!s.isAvailable()) { if(found) { debug(TQString("Client: Removing self resource: name=[%1]\n").tqarg(j.resource())); (*rit).settqStatus(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(TQString("Client: Adding self resource: name=[%1]\n").tqarg(j.resource())); } else { (*rit).settqStatus(s); r = *rit; debug(TQString("Client: Updating self resource: name=[%1]\n").tqarg(j.resource())); } resourceAvailable(j, r); } } void Client::updatePresence(LiveRosterItem *i, const Jid &j, const tqStatus &s) { ResourceList::Iterator rit = i->resourceList().tqfind(j.resource()); bool found = (rit == i->resourceList().end()) ? false: true; // unavailable? remove the resource if(!s.isAvailable()) { if(found) { (*rit).settqStatus(s); debug(TQString("Client: Removing resource from [%1]: name=[%2]\n").tqarg(i->jid().full()).tqarg(j.resource())); resourceUnavailable(j, *rit); i->resourceList().remove(rit); i->setLastUnavailabletqStatus(s); } } // available? add/update the resource else { Resource r; if(!found) { r = Resource(j.resource(), s); i->resourceList() += r; debug(TQString("Client: Adding resource to [%1]: name=[%2]\n").tqarg(i->jid().full()).tqarg(j.resource())); } else { (*rit).settqStatus(s); r = *rit; debug(TQString("Client: Updating resource to [%1]: name=[%2]\n").tqarg(i->jid().full()).tqarg(j.resource())); } resourceAvailable(j, r); } } void Client::pmMessage(const Message &m) { debug(TQString("Client: Message from %1\n").tqarg(m.from().full())); if(m.type() == "groupchat") { for(TQValueList::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, TQT_SIGNAL(finished()), TQT_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) { TQString 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; } TQString dstr, str; str.sprintf(" %s %-32s", substr.latin1(), item.jid().full().latin1()); if(!item.name().isEmpty()) str += TQString(" [") + item.name() + "]"; str += '\n'; // Remove if(item.subscription().type() == Subscription::Remove) { LiveRoster::Iterator it = d->roster.tqfind(item.jid()); if(it != d->roster.end()) { rosterItemRemoved(*it); d->roster.remove(it); } dstr = "Client: (Removed) "; } // Add/Update else { LiveRoster::Iterator it = d->roster.tqfind(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 TQString &type) { JT_Presence *j = new JT_Presence(rootTask()); j->sub(jid, type); j->go(true); } void Client::setPresence(const tqStatus &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.tqfind(resource()); //Resource &r = *rit; //r.settqStatus(s); } TQString Client::OSName() const { return d->osname; } TQString Client::timeZone() const { return d->tzname; } int Client::timeZoneOffset() const { return d->tzoffset; } TQString Client::clientName() const { return d->clientName; } TQString Client::clientVersion() const { return d->clientVersion; } TQString Client::capsNode() const { return d->capsNode; } TQString Client::capsVersion() const { return d->capsVersion; } TQString Client::capsExt() const { return d->capsExt; } void Client::setOSName(const TQString &name) { d->osname = name; } void Client::setTimeZone(const TQString &name, int offset) { d->tzname = name; d->tzoffset = offset; } void Client::setClientName(const TQString &s) { d->clientName = s; } void Client::setClientVersion(const TQString &s) { d->clientVersion = s; } void Client::setCapsNode(const TQString &s) { d->capsNode = s; } void Client::setCapsVersion(const TQString &s) { d->capsVersion = s; } DiscoItem::Identity Client::identity() { return d->identity; } void Client::setIdentity(DiscoItem::Identity identity) { d->identity = identity; } void Client::addExtension(const TQString& ext, const Features& features) { if (!ext.isEmpty()) { d->extension_features[ext] = features; d->capsExt = extensions().join(" "); } } void Client::removeExtension(const TQString& ext) { if (d->extension_features.tqcontains(ext)) { d->extension_features.remove(ext); d->capsExt = extensions().join(" "); } } TQStringList Client::extensions() const { return d->extension_features.keys(); } const Features& Client::extension(const TQString& 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() {} TQString id; bool success; int statusCode; TQString statusString; Client *client; bool insig, deleteme, autoDelete; bool done; }; Task::Task(Task *tqparent) :TQObject(tqparent) { init(); d->client = tqparent->client(); d->id = client()->genUniqueId(); connect(d->client, TQT_SIGNAL(disconnected()), TQT_SLOT(clientDisconnected())); } Task::Task(Client *tqparent, bool) :TQObject(0) { init(); d->client = tqparent; connect(d->client, TQT_SIGNAL(disconnected()), TQT_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::tqparent() const { return (Task *)TQObject::tqparent(); } Client *Task::client() const { return d->client; } TQDomDocument *Task::doc() const { return client()->doc(); } TQString Task::id() const { return d->id; } bool Task::success() const { return d->success; } int Task::statusCode() const { return d->statusCode; } const TQString & Task::statusString() const { return d->statusString; } void Task::go(bool autoDelete) { d->autoDelete = autoDelete; onGo(); } bool Task::take(const TQDomElement &x) { const TQObjectList p = childrenListObject(); if(p.isEmpty()) return false; // pass along the xml TQObjectListIt it(p); Task *t; for(; it.current(); ++it) { TQObject *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 TQTimer::singleShot(0, this, TQT_SLOT(done())); } } void Task::send(const TQDomElement &x) { client()->send(x); } void Task::setSuccess(int code, const TQString &str) { if(!d->done) { d->success = true; d->statusCode = code; d->statusString = str; done(); } } void Task::setError(const TQDomElement &e) { if(!d->done) { d->success = false; getErrorFromElement(e, &d->statusCode, &d->statusString); done(); } } void Task::setError(int code, const TQString &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; TQString 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 = TQString(buf); delete [] buf; size *= 2; } while(r == -1); debug(str); } void Task::debug(const TQString &str) { client()->debug(TQString("%1: ").tqarg(className()) + str); } bool Task::iqVerify(const TQDomElement &x, const Jid &to, const TQString &id, const TQString &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 tqStatus & LiveRosterItem::lastUnavailabletqStatus() const { return v_lastUnavailabletqStatus; } bool LiveRosterItem::flagForDelete() const { return v_flagForDelete; } void LiveRosterItem::setLastUnavailabletqStatus(const tqStatus &s) { v_lastUnavailabletqStatus = s; } void LiveRosterItem::setFlagForDelete(bool b) { v_flagForDelete = b; } //--------------------------------------------------------------------------- // LiveRoster //--------------------------------------------------------------------------- LiveRoster::LiveRoster() :TQValueList() { } LiveRoster::~LiveRoster() { } void LiveRoster::flagAllForDelete() { for(Iterator it = begin(); it != end(); ++it) (*it).setFlagForDelete(true); } LiveRoster::Iterator LiveRoster::tqfind(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::tqfind(const Jid &j, bool compareRes) const { ConstIterator it; for(it = begin(); it != end(); ++it) { if((*it).jid().compare(j, compareRes)) break; } return it; } }