~ubuntu-branches/ubuntu/saucy/kopete/saucy-proposed

« back to all changes in this revision

Viewing changes to protocols/jabber/jabberresourcepool.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2013-06-21 02:22:39 UTC
  • Revision ID: package-import@ubuntu.com-20130621022239-63l3zc8p0nf26pt6
Tags: upstream-4.10.80
ImportĀ upstreamĀ versionĀ 4.10.80

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 /*
 
2
  * jabberresourcepool.cpp
 
3
  *
 
4
  * Copyright (c) 2004 by Till Gerken <till@tantalo.net>
 
5
  * Copyright (c) 2006 by MichaĆ«l Larouche <larouche@kde.org>
 
6
  *
 
7
  * Kopete    (c) by the Kopete developers  <kopete-devel@kde.org>
 
8
  *
 
9
  * *************************************************************************
 
10
  * *                                                                       *
 
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.                                   *
 
15
  * *                                                                       *
 
16
  * *************************************************************************
 
17
  */
 
18
 
 
19
#include "jabberresourcepool.h"
 
20
 
 
21
#include <kdebug.h>
 
22
 
 
23
#include "jabberresource.h"
 
24
#include "jabbercontactpool.h"
 
25
#include "jabberbasecontact.h"
 
26
#include "jabberaccount.h"
 
27
#include "jabberprotocol.h"
 
28
#include "jabbercapabilitiesmanager.h"
 
29
 
 
30
/**
 
31
 * This resource will be returned if no other resource
 
32
 * for a given JID can be found. It's an empty offline
 
33
 * resource.
 
34
 */
 
35
XMPP::Resource JabberResourcePool::EmptyResource ( "", XMPP::Status ( "", "", 0, false ) );
 
36
 
 
37
class JabberResourcePool::Private
 
38
{
 
39
public:
 
40
        Private(JabberAccount *pAccount)
 
41
         : account(pAccount)
 
42
        {}
 
43
        
 
44
        QList<JabberResource*> pool;
 
45
        QList<JabberResource*> lockList;
 
46
 
 
47
        /**
 
48
         * Pointer to the JabberAccount instance.
 
49
         */
 
50
        JabberAccount *account;
 
51
};
 
52
 
 
53
JabberResourcePool::JabberResourcePool ( JabberAccount *account )
 
54
        : d(new Private(account))
 
55
{}
 
56
 
 
57
JabberResourcePool::~JabberResourcePool ()
 
58
{
 
59
        // Delete all resources in the pool upon removal
 
60
        qDeleteAll(d->pool);
 
61
        delete d;
 
62
}
 
63
 
 
64
void JabberResourcePool::slotResourceDestroyed (QObject *sender)
 
65
{
 
66
        kDebug(JABBER_DEBUG_GLOBAL) << "Resource has been destroyed, collecting the pieces.";
 
67
 
 
68
        JabberResource *oldResource = static_cast<JabberResource *>(sender);
 
69
 
 
70
        // remove this resource from the lock list if it existed
 
71
        d->lockList.removeAll ( oldResource );
 
72
}
 
73
 
 
74
void JabberResourcePool::slotResourceUpdated ( JabberResource *resource )
 
75
{
 
76
        QList<JabberBaseContact*> list = d->account->contactPool()->findRelevantSources ( resource->jid () );
 
77
 
 
78
        foreach(JabberBaseContact *mContact, list)
 
79
        {
 
80
                mContact->updateResourceList ();
 
81
        }
 
82
 
 
83
        // Update capabilities
 
84
        if( !resource->resource().status().capsNode().isEmpty() )
 
85
        {
 
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() );
 
88
        }
 
89
}
 
90
 
 
91
void JabberResourcePool::notifyRelevantContacts ( const XMPP::Jid &jid, bool removed )
 
92
{
 
93
        QList<JabberBaseContact*> list = d->account->contactPool()->findRelevantSources ( jid );
 
94
 
 
95
        foreach(JabberBaseContact *mContact, list)
 
96
        {
 
97
                if ( removed )
 
98
                        mContact->setSendsDeliveredEvent ( false );
 
99
 
 
100
                mContact->reevaluateStatus ();
 
101
        }
 
102
}
 
103
 
 
104
void JabberResourcePool::addResource ( const XMPP::Jid &jid, const XMPP::Resource &resource )
 
105
{
 
106
        // see if the resource already exists
 
107
        foreach(JabberResource *mResource, d->pool)
 
108
        {
 
109
                if ( (mResource->jid().bare().toLower() == jid.bare().toLower()) && (mResource->resource().name().toLower() == resource.name().toLower()) )
 
110
                {
 
111
                        kDebug(JABBER_DEBUG_GLOBAL) << "Updating existing resource " << resource.name() << " for " << jid.bare();
 
112
 
 
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 );
 
117
 
 
118
                        // we still need to notify the contact in case the status
 
119
                        // of this resource changed
 
120
                        notifyRelevantContacts ( jid );
 
121
 
 
122
                        return;
 
123
                }
 
124
        }
 
125
 
 
126
        kDebug(JABBER_DEBUG_GLOBAL) << "Adding new resource " << resource.name() << " for " << jid.bare();
 
127
 
 
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() )
 
131
        {
 
132
                kDebug(JABBER_DEBUG_GLOBAL) << "Initial update of capabilities for JID: " << jid.full();
 
133
                d->account->protocol()->capabilitiesManager()->updateCapabilities( d->account, jid, resource.status() );
 
134
        }
 
135
 
 
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 );
 
141
 
 
142
        // send notifications out to the relevant contacts that
 
143
        // a new resource is available for them
 
144
        notifyRelevantContacts ( jid );
 
145
}
 
146
 
 
147
void JabberResourcePool::removeResource ( const XMPP::Jid &jid, const XMPP::Resource &resource )
 
148
{
 
149
        kDebug(JABBER_DEBUG_GLOBAL) << "Removing resource " << resource.name() << " from " << jid.bare();
 
150
 
 
151
        foreach(JabberResource *mResource, d->pool)
 
152
        {
 
153
                if ( (mResource->jid().bare().toLower() == jid.bare().toLower()) && (mResource->resource().name().toLower() == resource.name().toLower()) )
 
154
                {
 
155
                        JabberResource *deletedResource = d->pool.takeAt( d->pool.indexOf(mResource) );
 
156
                        delete deletedResource;
 
157
 
 
158
                        notifyRelevantContacts ( jid, true );
 
159
                        return;
 
160
                }
 
161
        }
 
162
 
 
163
        kDebug(JABBER_DEBUG_GLOBAL) << "WARNING: No match found!";
 
164
}
 
165
 
 
166
void JabberResourcePool::removeAllResources ( const XMPP::Jid &jid )
 
167
{
 
168
        kDebug(JABBER_DEBUG_GLOBAL) << "Removing all resources for " << jid.bare();
 
169
 
 
170
        foreach(JabberResource *mResource, d->pool)
 
171
        {
 
172
                if ( mResource->jid().bare().toLower() == jid.bare().toLower() )
 
173
                {
 
174
                        // only remove preselected resource in case there is one
 
175
                        if ( jid.resource().isEmpty () || ( jid.resource().toLower () == mResource->resource().name().toLower () ) )
 
176
                        {
 
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;
 
180
                        }
 
181
                }
 
182
        }
 
183
}
 
184
 
 
185
void JabberResourcePool::clear ()
 
186
{
 
187
        kDebug(JABBER_DEBUG_GLOBAL) << "Clearing the resource pool.";
 
188
 
 
189
        /*
 
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.
 
195
         */
 
196
 
 
197
        QStringList jidList;
 
198
 
 
199
        foreach(JabberResource *mResource, d->pool)
 
200
        {
 
201
                jidList += mResource->jid().full ();
 
202
        }
 
203
 
 
204
        /*
 
205
         * The lock list will be cleaned automatically.
 
206
         */
 
207
        qDeleteAll(d->pool);
 
208
        d->pool.clear ();
 
209
 
 
210
        /*
 
211
         * Now go through the list of JIDs and notify each contact
 
212
         * of its status change
 
213
         */
 
214
        for ( QStringList::Iterator it = jidList.begin (); it != jidList.end (); ++it )
 
215
        {
 
216
                notifyRelevantContacts ( XMPP::Jid ( *it ), true );
 
217
        }
 
218
 
 
219
}
 
220
 
 
221
void JabberResourcePool::lockToResource ( const XMPP::Jid &jid, const XMPP::Resource &resource )
 
222
{
 
223
        kDebug(JABBER_DEBUG_GLOBAL) << "Locking " << jid.full() << " to " << resource.name();
 
224
 
 
225
        // remove all existing locks first
 
226
        removeLock ( jid );
 
227
 
 
228
        // find the resource in our dictionary that matches
 
229
        foreach(JabberResource *mResource, d->pool)
 
230
        {
 
231
                if ( (mResource->jid().bare().toLower() == jid.full().toLower()) && (mResource->resource().name().toLower() == resource.name().toLower()) )
 
232
                {
 
233
                        d->lockList.append ( mResource );
 
234
                        return;
 
235
                }
 
236
        }
 
237
 
 
238
        kDebug(JABBER_DEBUG_GLOBAL) << "WARNING: No match found!";
 
239
}
 
240
 
 
241
void JabberResourcePool::removeLock ( const XMPP::Jid &jid )
 
242
{
 
243
        kDebug(JABBER_DEBUG_GLOBAL) << "Removing resource lock for " << jid.bare();
 
244
 
 
245
        // find the resource in our dictionary that matches
 
246
        foreach(JabberResource *mResource, d->pool)
 
247
        {
 
248
                if ( (mResource->jid().bare().toLower() == jid.bare().toLower()) )
 
249
                {
 
250
                        d->lockList.removeAll (mResource);
 
251
                }
 
252
        }
 
253
 
 
254
        kDebug(JABBER_DEBUG_GLOBAL) << "No locks found.";
 
255
}
 
256
 
 
257
JabberResource *JabberResourcePool::lockedJabberResource( const XMPP::Jid &jid )
 
258
{
 
259
        // check if the JID already carries a resource, then we will have to use that one
 
260
        if ( !jid.resource().isEmpty () )
 
261
        {
 
262
                // we are subscribed to a JID, find the according resource in the pool
 
263
                foreach(JabberResource *mResource, d->pool)
 
264
                {
 
265
                        if ( ( mResource->jid().bare().toLower () == jid.bare().toLower () ) && ( mResource->resource().name () == jid.resource () ) )
 
266
                        {
 
267
                                return mResource;
 
268
                        }
 
269
                }
 
270
 
 
271
                kDebug ( JABBER_DEBUG_GLOBAL ) << "WARNING: No resource found in pool, returning as offline.";
 
272
 
 
273
                return 0L;
 
274
        }
 
275
 
 
276
        // see if we have a locked resource
 
277
        foreach(JabberResource *mResource, d->lockList)
 
278
        {
 
279
                if ( mResource->jid().bare().toLower() == jid.bare().toLower() )
 
280
                {
 
281
                        kDebug (JABBER_DEBUG_GLOBAL) << "Current lock for " << jid.bare() << " is '" << mResource->resource().name () << "'";
 
282
                        return mResource;
 
283
                }
 
284
        }
 
285
 
 
286
        kDebug (JABBER_DEBUG_GLOBAL) << "No lock available for " << jid.bare();
 
287
 
 
288
        // there's no locked resource, return an empty resource
 
289
        return 0L;
 
290
}
 
291
 
 
292
const XMPP::Resource &JabberResourcePool::lockedResource ( const XMPP::Jid &jid )
 
293
{
 
294
        JabberResource *resource = lockedJabberResource( jid );
 
295
        return (resource) ? resource->resource() : EmptyResource;
 
296
}
 
297
 
 
298
JabberResource *JabberResourcePool::bestJabberResource( const XMPP::Jid &jid, bool honourLock )
 
299
{
 
300
        kDebug(JABBER_DEBUG_GLOBAL) << "Determining best resource for " << jid.full ();
 
301
 
 
302
        if ( honourLock )
 
303
        {
 
304
                // if we are locked to a certain resource, always return that one
 
305
                JabberResource *mResource = lockedJabberResource ( jid );
 
306
                if ( mResource )
 
307
                {
 
308
                        kDebug(JABBER_DEBUG_GLOBAL) << "We have a locked resource '" << mResource->resource().name () << "' for " << jid.full ();
 
309
                        return mResource;
 
310
                }
 
311
        }
 
312
 
 
313
        JabberResource *bestResource = 0L;
 
314
        JabberResource *currentResource = 0L;
 
315
 
 
316
        foreach(currentResource, d->pool)
 
317
        {
 
318
                // make sure we are only looking up resources for the specified JID
 
319
                if ( currentResource->jid().bare().toLower() != jid.bare().toLower() )
 
320
                {
 
321
                        continue;
 
322
                }
 
323
 
 
324
                // take first resource if no resource has been chosen yet
 
325
                if(!bestResource)
 
326
                {
 
327
                        kDebug(JABBER_DEBUG_GLOBAL) << "Taking '" << currentResource->resource().name () << "' as first available resource.";
 
328
 
 
329
                        bestResource = currentResource;
 
330
                        continue;
 
331
                }
 
332
 
 
333
                if(currentResource->resource().priority() > bestResource->resource().priority())
 
334
                {
 
335
                        kDebug(JABBER_DEBUG_GLOBAL) << "Using '" << currentResource->resource().name () << "' due to better priority.";
 
336
 
 
337
                        // got a better match by priority
 
338
                        bestResource = currentResource;
 
339
                }
 
340
                else
 
341
                {
 
342
                        if(currentResource->resource().priority() == bestResource->resource().priority())
 
343
                        {
 
344
                                if(currentResource->resource().status().timeStamp() > bestResource->resource().status().timeStamp())
 
345
                                {
 
346
                                        kDebug(JABBER_DEBUG_GLOBAL) << "Using '" << currentResource->resource().name () << "' due to better timestamp.";
 
347
 
 
348
                                        // got a better match by timestamp (priorities are equal)
 
349
                                        bestResource = currentResource;
 
350
                                }
 
351
                        }
 
352
                }
 
353
        }
 
354
 
 
355
        return (bestResource) ? bestResource : 0L;
 
356
}
 
357
 
 
358
const XMPP::Resource &JabberResourcePool::bestResource ( const XMPP::Jid &jid, bool honourLock )
 
359
{
 
360
        JabberResource *bestResource = bestJabberResource( jid, honourLock);
 
361
        return (bestResource) ? bestResource->resource() : EmptyResource;
 
362
}
 
363
 
 
364
//TODO: Find Resources based on certain Features.
 
365
void JabberResourcePool::findResources ( const XMPP::Jid &jid, JabberResourcePool::ResourceList &resourceList )
 
366
{
 
367
        foreach(JabberResource *mResource, d->pool)
 
368
        {
 
369
                if ( mResource->jid().bare().toLower() == jid.bare().toLower() )
 
370
                {
 
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
 
375
                                continue;
 
376
 
 
377
                        resourceList.append ( mResource );
 
378
                }
 
379
        }
 
380
}
 
381
 
 
382
void JabberResourcePool::findResources ( const XMPP::Jid &jid, XMPP::ResourceList &resourceList )
 
383
{
 
384
        foreach(JabberResource *mResource, d->pool)
 
385
        {
 
386
                if ( mResource->jid().bare().toLower() == jid.bare().toLower() )
 
387
                {
 
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
 
392
                                continue;
 
393
 
 
394
                        resourceList.append ( mResource->resource () );
 
395
                }
 
396
        }
 
397
}
 
398
 
 
399
JabberResource *JabberResourcePool::getJabberResource ( const XMPP::Jid &jid, const QString &resource )
 
400
{
 
401
        if ( resource.isEmpty() )
 
402
                return bestJabberResource(jid);
 
403
 
 
404
        foreach(JabberResource *mResource, d->pool)
 
405
        {
 
406
                if ( mResource->jid().bare().toLower() == jid.bare().toLower() && jid.resource().toLower() == resource )
 
407
                {
 
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
 
412
                                continue;
 
413
 
 
414
                        return mResource;
 
415
                }
 
416
        }
 
417
 
 
418
        return bestJabberResource(jid);
 
419
}
 
420
 
 
421
#include "jabberresourcepool.moc"