2
* jabberresourcepool.cpp
4
* Copyright (c) 2004 by Till Gerken <till@tantalo.net>
5
* Copyright (c) 2006 by Michaƫl Larouche <larouche@kde.org>
7
* Kopete (c) by the Kopete developers <kopete-devel@kde.org>
9
* *************************************************************************
11
* * This program is free software; you can redistribute it and/or modify *
12
* * it under the terms of the GNU General Public License as published by *
13
* * the Free Software Foundation; either version 2 of the License, or *
14
* * (at your option) any later version. *
16
* *************************************************************************
19
#include "jabberresourcepool.h"
23
#include "jabberresource.h"
24
#include "jabbercontactpool.h"
25
#include "jabberbasecontact.h"
26
#include "jabberaccount.h"
27
#include "jabberprotocol.h"
28
#include "jabbercapabilitiesmanager.h"
31
* This resource will be returned if no other resource
32
* for a given JID can be found. It's an empty offline
35
XMPP::Resource JabberResourcePool::EmptyResource ( "", XMPP::Status ( "", "", 0, false ) );
37
class JabberResourcePool::Private
40
Private(JabberAccount *pAccount)
44
QList<JabberResource*> pool;
45
QList<JabberResource*> lockList;
48
* Pointer to the JabberAccount instance.
50
JabberAccount *account;
53
JabberResourcePool::JabberResourcePool ( JabberAccount *account )
54
: d(new Private(account))
57
JabberResourcePool::~JabberResourcePool ()
59
// Delete all resources in the pool upon removal
64
void JabberResourcePool::slotResourceDestroyed (QObject *sender)
66
kDebug(JABBER_DEBUG_GLOBAL) << "Resource has been destroyed, collecting the pieces.";
68
JabberResource *oldResource = static_cast<JabberResource *>(sender);
70
// remove this resource from the lock list if it existed
71
d->lockList.removeAll ( oldResource );
74
void JabberResourcePool::slotResourceUpdated ( JabberResource *resource )
76
QList<JabberBaseContact*> list = d->account->contactPool()->findRelevantSources ( resource->jid () );
78
foreach(JabberBaseContact *mContact, list)
80
mContact->updateResourceList ();
83
// Update capabilities
84
if( !resource->resource().status().capsNode().isEmpty() )
86
kDebug(JABBER_DEBUG_GLOBAL) << "Updating capabilities for JID: " << resource->jid().full();
87
d->account->protocol()->capabilitiesManager()->updateCapabilities( d->account, resource->jid(), resource->resource().status() );
91
void JabberResourcePool::notifyRelevantContacts ( const XMPP::Jid &jid, bool removed )
93
QList<JabberBaseContact*> list = d->account->contactPool()->findRelevantSources ( jid );
95
foreach(JabberBaseContact *mContact, list)
98
mContact->setSendsDeliveredEvent ( false );
100
mContact->reevaluateStatus ();
104
void JabberResourcePool::addResource ( const XMPP::Jid &jid, const XMPP::Resource &resource )
106
// see if the resource already exists
107
foreach(JabberResource *mResource, d->pool)
109
if ( (mResource->jid().bare().toLower() == jid.bare().toLower()) && (mResource->resource().name().toLower() == resource.name().toLower()) )
111
kDebug(JABBER_DEBUG_GLOBAL) << "Updating existing resource " << resource.name() << " for " << jid.bare();
113
// It exists, update it. Don't do a "lazy" update by deleting
114
// it here and readding it with new parameters later on,
115
// any possible lockings to this resource will get lost.
116
mResource->setResource ( resource );
118
// we still need to notify the contact in case the status
119
// of this resource changed
120
notifyRelevantContacts ( jid );
126
kDebug(JABBER_DEBUG_GLOBAL) << "Adding new resource " << resource.name() << " for " << jid.bare();
128
// Update initial capabilities if available.
129
// Called before creating JabberResource so JabberResource wouldn't ask for disco information.
130
if( !resource.status().capsNode().isEmpty() )
132
kDebug(JABBER_DEBUG_GLOBAL) << "Initial update of capabilities for JID: " << jid.full();
133
d->account->protocol()->capabilitiesManager()->updateCapabilities( d->account, jid, resource.status() );
136
// create new resource instance and add it to the dictionary
137
JabberResource *newResource = new JabberResource(d->account, jid, resource);
138
connect ( newResource, SIGNAL (destroyed(QObject*)), this, SLOT (slotResourceDestroyed(QObject*)) );
139
connect ( newResource, SIGNAL (updated(JabberResource*)), this, SLOT (slotResourceUpdated(JabberResource*)) );
140
d->pool.append ( newResource );
142
// send notifications out to the relevant contacts that
143
// a new resource is available for them
144
notifyRelevantContacts ( jid );
147
void JabberResourcePool::removeResource ( const XMPP::Jid &jid, const XMPP::Resource &resource )
149
kDebug(JABBER_DEBUG_GLOBAL) << "Removing resource " << resource.name() << " from " << jid.bare();
151
foreach(JabberResource *mResource, d->pool)
153
if ( (mResource->jid().bare().toLower() == jid.bare().toLower()) && (mResource->resource().name().toLower() == resource.name().toLower()) )
155
JabberResource *deletedResource = d->pool.takeAt( d->pool.indexOf(mResource) );
156
delete deletedResource;
158
notifyRelevantContacts ( jid, true );
163
kDebug(JABBER_DEBUG_GLOBAL) << "WARNING: No match found!";
166
void JabberResourcePool::removeAllResources ( const XMPP::Jid &jid )
168
kDebug(JABBER_DEBUG_GLOBAL) << "Removing all resources for " << jid.bare();
170
foreach(JabberResource *mResource, d->pool)
172
if ( mResource->jid().bare().toLower() == jid.bare().toLower() )
174
// only remove preselected resource in case there is one
175
if ( jid.resource().isEmpty () || ( jid.resource().toLower () == mResource->resource().name().toLower () ) )
177
kDebug(JABBER_DEBUG_GLOBAL) << "Removing resource " << jid.bare() << "/" << mResource->resource().name ();
178
JabberResource *deletedResource = d->pool.takeAt( d->pool.indexOf(mResource) );
179
delete deletedResource;
185
void JabberResourcePool::clear ()
187
kDebug(JABBER_DEBUG_GLOBAL) << "Clearing the resource pool.";
190
* Since many contacts can have multiple resources, we can't simply delete
191
* each resource and trigger a notification upon each deletion. This would
192
* cause lots of status updates in the GUI and create unnecessary flicker
193
* and API traffic. Instead, collect all JIDs, clear the dictionary
194
* and then notify all JIDs after the resources have been deleted.
199
foreach(JabberResource *mResource, d->pool)
201
jidList += mResource->jid().full ();
205
* The lock list will be cleaned automatically.
211
* Now go through the list of JIDs and notify each contact
212
* of its status change
214
for ( QStringList::Iterator it = jidList.begin (); it != jidList.end (); ++it )
216
notifyRelevantContacts ( XMPP::Jid ( *it ), true );
221
void JabberResourcePool::lockToResource ( const XMPP::Jid &jid, const XMPP::Resource &resource )
223
kDebug(JABBER_DEBUG_GLOBAL) << "Locking " << jid.full() << " to " << resource.name();
225
// remove all existing locks first
228
// find the resource in our dictionary that matches
229
foreach(JabberResource *mResource, d->pool)
231
if ( (mResource->jid().bare().toLower() == jid.full().toLower()) && (mResource->resource().name().toLower() == resource.name().toLower()) )
233
d->lockList.append ( mResource );
238
kDebug(JABBER_DEBUG_GLOBAL) << "WARNING: No match found!";
241
void JabberResourcePool::removeLock ( const XMPP::Jid &jid )
243
kDebug(JABBER_DEBUG_GLOBAL) << "Removing resource lock for " << jid.bare();
245
// find the resource in our dictionary that matches
246
foreach(JabberResource *mResource, d->pool)
248
if ( (mResource->jid().bare().toLower() == jid.bare().toLower()) )
250
d->lockList.removeAll (mResource);
254
kDebug(JABBER_DEBUG_GLOBAL) << "No locks found.";
257
JabberResource *JabberResourcePool::lockedJabberResource( const XMPP::Jid &jid )
259
// check if the JID already carries a resource, then we will have to use that one
260
if ( !jid.resource().isEmpty () )
262
// we are subscribed to a JID, find the according resource in the pool
263
foreach(JabberResource *mResource, d->pool)
265
if ( ( mResource->jid().bare().toLower () == jid.bare().toLower () ) && ( mResource->resource().name () == jid.resource () ) )
271
kDebug ( JABBER_DEBUG_GLOBAL ) << "WARNING: No resource found in pool, returning as offline.";
276
// see if we have a locked resource
277
foreach(JabberResource *mResource, d->lockList)
279
if ( mResource->jid().bare().toLower() == jid.bare().toLower() )
281
kDebug (JABBER_DEBUG_GLOBAL) << "Current lock for " << jid.bare() << " is '" << mResource->resource().name () << "'";
286
kDebug (JABBER_DEBUG_GLOBAL) << "No lock available for " << jid.bare();
288
// there's no locked resource, return an empty resource
292
const XMPP::Resource &JabberResourcePool::lockedResource ( const XMPP::Jid &jid )
294
JabberResource *resource = lockedJabberResource( jid );
295
return (resource) ? resource->resource() : EmptyResource;
298
JabberResource *JabberResourcePool::bestJabberResource( const XMPP::Jid &jid, bool honourLock )
300
kDebug(JABBER_DEBUG_GLOBAL) << "Determining best resource for " << jid.full ();
304
// if we are locked to a certain resource, always return that one
305
JabberResource *mResource = lockedJabberResource ( jid );
308
kDebug(JABBER_DEBUG_GLOBAL) << "We have a locked resource '" << mResource->resource().name () << "' for " << jid.full ();
313
JabberResource *bestResource = 0L;
314
JabberResource *currentResource = 0L;
316
foreach(currentResource, d->pool)
318
// make sure we are only looking up resources for the specified JID
319
if ( currentResource->jid().bare().toLower() != jid.bare().toLower() )
324
// take first resource if no resource has been chosen yet
327
kDebug(JABBER_DEBUG_GLOBAL) << "Taking '" << currentResource->resource().name () << "' as first available resource.";
329
bestResource = currentResource;
333
if(currentResource->resource().priority() > bestResource->resource().priority())
335
kDebug(JABBER_DEBUG_GLOBAL) << "Using '" << currentResource->resource().name () << "' due to better priority.";
337
// got a better match by priority
338
bestResource = currentResource;
342
if(currentResource->resource().priority() == bestResource->resource().priority())
344
if(currentResource->resource().status().timeStamp() > bestResource->resource().status().timeStamp())
346
kDebug(JABBER_DEBUG_GLOBAL) << "Using '" << currentResource->resource().name () << "' due to better timestamp.";
348
// got a better match by timestamp (priorities are equal)
349
bestResource = currentResource;
355
return (bestResource) ? bestResource : 0L;
358
const XMPP::Resource &JabberResourcePool::bestResource ( const XMPP::Jid &jid, bool honourLock )
360
JabberResource *bestResource = bestJabberResource( jid, honourLock);
361
return (bestResource) ? bestResource->resource() : EmptyResource;
364
//TODO: Find Resources based on certain Features.
365
void JabberResourcePool::findResources ( const XMPP::Jid &jid, JabberResourcePool::ResourceList &resourceList )
367
foreach(JabberResource *mResource, d->pool)
369
if ( mResource->jid().bare().toLower() == jid.bare().toLower() )
371
// we found a resource for the JID, let's see if the JID already contains a resource
372
if ( !jid.resource().isEmpty() && ( jid.resource().toLower() != mResource->resource().name().toLower() ) )
373
// the JID contains a resource but it's not the one we have in the dictionary,
374
// thus we have to ignore this resource
377
resourceList.append ( mResource );
382
void JabberResourcePool::findResources ( const XMPP::Jid &jid, XMPP::ResourceList &resourceList )
384
foreach(JabberResource *mResource, d->pool)
386
if ( mResource->jid().bare().toLower() == jid.bare().toLower() )
388
// we found a resource for the JID, let's see if the JID already contains a resource
389
if ( !jid.resource().isEmpty() && ( jid.resource().toLower() != mResource->resource().name().toLower() ) )
390
// the JID contains a resource but it's not the one we have in the dictionary,
391
// thus we have to ignore this resource
394
resourceList.append ( mResource->resource () );
399
JabberResource *JabberResourcePool::getJabberResource ( const XMPP::Jid &jid, const QString &resource )
401
if ( resource.isEmpty() )
402
return bestJabberResource(jid);
404
foreach(JabberResource *mResource, d->pool)
406
if ( mResource->jid().bare().toLower() == jid.bare().toLower() && jid.resource().toLower() == resource )
408
// we found a resource for the JID, let's see if the JID already contains a resource
409
if ( !jid.resource().isEmpty() && ( jid.resource().toLower() != mResource->resource().name().toLower() ) )
410
// the JID contains a resource but it's not the one we have in the dictionary,
411
// thus we have to ignore this resource
418
return bestJabberResource(jid);
421
#include "jabberresourcepool.moc"