You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1523 lines
31 KiB
1523 lines
31 KiB
/*
|
|
* 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<stdarg.h>
|
|
#include<tqmap.h>
|
|
#include<tqobjectlist.h>
|
|
#include<tqtimer.h>
|
|
#include<tqguardedptr.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<tqstring.h>
|
|
#include<tqdom.h>
|
|
#include<tqobjectlist.h>
|
|
#include<tqtimer.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 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<TQString,Features> extension_features;
|
|
int tzoffset;
|
|
bool active;
|
|
|
|
LiveRoster roster;
|
|
ResourceList resourceList;
|
|
S5BManager *s5bman;
|
|
IBBManager *ibbman;
|
|
JidLinkManager *jlman;
|
|
FileTransferManager *ftman;
|
|
bool ftEnabled;
|
|
TQValueList<GroupChat> 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<GroupChat>::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<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(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<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(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<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;
|
|
|
|
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<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(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<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());
|
|
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<ClientStream> 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<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;
|
|
}
|
|
|
|
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<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(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<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, 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<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
|
|
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<LiveRosterItem>()
|
|
{
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
}
|