File indexing completed on 2024-04-21 04:04:43
0001 /* 0002 * jabberresourcepool.cpp 0003 * 0004 * Copyright (c) 2004 by Till Gerken <till@tantalo.net> 0005 * Copyright (c) 2006 by Michaƫl Larouche <larouche@kde.org> 0006 * 0007 * Kopete (c) by the Kopete developers <kopete-devel@kde.org> 0008 * 0009 * ************************************************************************* 0010 * * * 0011 * * This program is free software; you can redistribute it and/or modify * 0012 * * it under the terms of the GNU General Public License as published by * 0013 * * the Free Software Foundation; either either version 2 0014 of the License, or (at your option) any later version.of the License, or * 0015 * * (at your option) any later version. * 0016 * * * 0017 * ************************************************************************* 0018 */ 0019 0020 #include "jabber_protocol_debug.h" 0021 0022 #include "jabberresourcepool.h" 0023 #include "jabberresource.h" 0024 #include "jabbercontactpool.h" 0025 #include "jabberbasecontact.h" 0026 #include "jabberaccount.h" 0027 #include "jabberprotocol.h" 0028 #include "jabbercapabilitiesmanager.h" 0029 0030 /** 0031 * This resource will be returned if no other resource 0032 * for a given JID can be found. It's an empty offline 0033 * resource. 0034 */ 0035 XMPP::Resource JabberResourcePool::EmptyResource ( "", XMPP::Status ( "", "", 0, false ) ); 0036 0037 class JabberResourcePool::Private 0038 { 0039 public: 0040 Private(JabberAccount *pAccount) 0041 : account(pAccount) 0042 {} 0043 0044 QList<JabberResource*> pool; 0045 QList<JabberResource*> lockList; 0046 0047 /** 0048 * Pointer to the JabberAccount instance. 0049 */ 0050 JabberAccount *account; 0051 }; 0052 0053 JabberResourcePool::JabberResourcePool ( JabberAccount *account ) 0054 : d(new Private(account)) 0055 {} 0056 0057 JabberResourcePool::~JabberResourcePool () 0058 { 0059 // Delete all resources in the pool upon removal 0060 qDeleteAll(d->pool); 0061 delete d; 0062 } 0063 0064 void JabberResourcePool::slotResourceDestroyed (QObject *sender) 0065 { 0066 qCDebug(JABBER_PROTOCOL_LOG) << "Resource has been destroyed, collecting the pieces."; 0067 0068 JabberResource *oldResource = static_cast<JabberResource *>(sender); 0069 0070 // remove this resource from the lock list if it existed 0071 d->lockList.removeAll ( oldResource ); 0072 } 0073 0074 void JabberResourcePool::slotResourceUpdated ( JabberResource *resource ) 0075 { 0076 QList<JabberBaseContact*> list = d->account->contactPool()->findRelevantSources ( resource->jid () ); 0077 0078 foreach(JabberBaseContact *mContact, list) 0079 { 0080 mContact->updateResourceList (); 0081 } 0082 0083 // Update capabilities 0084 if( !resource->resource().status().capsNode().isEmpty() ) 0085 { 0086 qCDebug(JABBER_PROTOCOL_LOG) << "Updating capabilities for JID: " << resource->jid().full(); 0087 d->account->protocol()->capabilitiesManager()->updateCapabilities( d->account, resource->jid(), resource->resource().status() ); 0088 } 0089 } 0090 0091 void JabberResourcePool::notifyRelevantContacts ( const XMPP::Jid &jid ) 0092 { 0093 QList<JabberBaseContact*> list = d->account->contactPool()->findRelevantSources ( jid ); 0094 0095 foreach(JabberBaseContact *mContact, list) 0096 { 0097 mContact->reevaluateStatus (); 0098 } 0099 } 0100 0101 void JabberResourcePool::addResource ( const XMPP::Jid &jid, const XMPP::Resource &resource ) 0102 { 0103 // see if the resource already exists 0104 foreach(JabberResource *mResource, d->pool) 0105 { 0106 if ( (mResource->jid().userHost().toLower() == jid.userHost().toLower()) && (mResource->resource().name().toLower() == resource.name().toLower()) ) 0107 { 0108 qCDebug(JABBER_PROTOCOL_LOG) << "Updating existing resource " << resource.name() << " for " << jid.userHost(); 0109 0110 // It exists, update it. Don't do a "lazy" update by deleting 0111 // it here and readding it with new parameters later on, 0112 // any possible lockings to this resource will get lost. 0113 mResource->setResource ( resource ); 0114 0115 // we still need to notify the contact in case the status 0116 // of this resource changed 0117 notifyRelevantContacts ( jid ); 0118 0119 return; 0120 } 0121 } 0122 0123 qCDebug(JABBER_PROTOCOL_LOG) << "Adding new resource " << resource.name() << " for " << jid.userHost(); 0124 0125 // Update initial capabilities if available. 0126 // Called before creating JabberResource so JabberResource wouldn't ask for disco information. 0127 if( !resource.status().capsNode().isEmpty() ) 0128 { 0129 qCDebug(JABBER_PROTOCOL_LOG) << "Initial update of capabilities for JID: " << jid.full(); 0130 d->account->protocol()->capabilitiesManager()->updateCapabilities( d->account, jid, resource.status() ); 0131 } 0132 0133 // create new resource instance and add it to the dictionary 0134 JabberResource *newResource = new JabberResource(d->account, jid, resource); 0135 connect ( newResource, SIGNAL (destroyed(QObject*)), this, SLOT (slotResourceDestroyed(QObject*)) ); 0136 connect ( newResource, SIGNAL (updated(JabberResource*)), this, SLOT (slotResourceUpdated(JabberResource*)) ); 0137 d->pool.append ( newResource ); 0138 0139 // send notifications out to the relevant contacts that 0140 // a new resource is available for them 0141 notifyRelevantContacts ( jid ); 0142 } 0143 0144 void JabberResourcePool::removeResource ( const XMPP::Jid &jid, const XMPP::Resource &resource ) 0145 { 0146 qCDebug(JABBER_PROTOCOL_LOG) << "Removing resource " << resource.name() << " from " << jid.userHost(); 0147 0148 foreach(JabberResource *mResource, d->pool) 0149 { 0150 if ( (mResource->jid().userHost().toLower() == jid.userHost().toLower()) && (mResource->resource().name().toLower() == resource.name().toLower()) ) 0151 { 0152 JabberResource *deletedResource = d->pool.takeAt( d->pool.indexOf(mResource) ); 0153 delete deletedResource; 0154 0155 notifyRelevantContacts ( jid ); 0156 return; 0157 } 0158 } 0159 0160 qCDebug(JABBER_PROTOCOL_LOG) << "WARNING: No match found!"; 0161 } 0162 0163 void JabberResourcePool::removeAllResources ( const XMPP::Jid &jid ) 0164 { 0165 qCDebug(JABBER_PROTOCOL_LOG) << "Removing all resources for " << jid.userHost(); 0166 0167 foreach(JabberResource *mResource, d->pool) 0168 { 0169 if ( mResource->jid().userHost().toLower() == jid.userHost().toLower() ) 0170 { 0171 // only remove preselected resource in case there is one 0172 if ( jid.resource().isEmpty () || ( jid.resource().toLower () == mResource->resource().name().toLower () ) ) 0173 { 0174 qCDebug(JABBER_PROTOCOL_LOG) << "Removing resource " << jid.userHost() << "/" << mResource->resource().name (); 0175 JabberResource *deletedResource = d->pool.takeAt( d->pool.indexOf(mResource) ); 0176 delete deletedResource; 0177 } 0178 } 0179 } 0180 } 0181 0182 void JabberResourcePool::clear () 0183 { 0184 qCDebug(JABBER_PROTOCOL_LOG) << "Clearing the resource pool."; 0185 0186 /* 0187 * Since many contacts can have multiple resources, we can't simply delete 0188 * each resource and trigger a notification upon each deletion. This would 0189 * cause lots of status updates in the GUI and create unnecessary flicker 0190 * and API traffic. Instead, collect all JIDs, clear the dictionary 0191 * and then notify all JIDs after the resources have been deleted. 0192 */ 0193 0194 QStringList jidList; 0195 0196 foreach(JabberResource *mResource, d->pool) 0197 { 0198 jidList += mResource->jid().full (); 0199 } 0200 0201 /* 0202 * The lock list will be cleaned automatically. 0203 */ 0204 qDeleteAll(d->pool); 0205 d->pool.clear (); 0206 0207 /* 0208 * Now go through the list of JIDs and notify each contact 0209 * of its status change 0210 */ 0211 for ( QStringList::Iterator it = jidList.begin (); it != jidList.end (); ++it ) 0212 { 0213 notifyRelevantContacts ( XMPP::Jid ( *it ) ); 0214 } 0215 0216 } 0217 0218 void JabberResourcePool::lockToResource ( const XMPP::Jid &jid, const XMPP::Resource &resource ) 0219 { 0220 qCDebug(JABBER_PROTOCOL_LOG) << "Locking " << jid.full() << " to " << resource.name(); 0221 0222 // remove all existing locks first 0223 removeLock ( jid ); 0224 0225 // find the resource in our dictionary that matches 0226 foreach(JabberResource *mResource, d->pool) 0227 { 0228 if ( (mResource->jid().userHost().toLower() == jid.full().toLower()) && (mResource->resource().name().toLower() == resource.name().toLower()) ) 0229 { 0230 d->lockList.append ( mResource ); 0231 return; 0232 } 0233 } 0234 0235 qCDebug(JABBER_PROTOCOL_LOG) << "WARNING: No match found!"; 0236 } 0237 0238 void JabberResourcePool::removeLock ( const XMPP::Jid &jid ) 0239 { 0240 qCDebug(JABBER_PROTOCOL_LOG) << "Removing resource lock for " << jid.userHost(); 0241 0242 // find the resource in our dictionary that matches 0243 foreach(JabberResource *mResource, d->pool) 0244 { 0245 if ( (mResource->jid().userHost().toLower() == jid.userHost().toLower()) ) 0246 { 0247 d->lockList.removeAll (mResource); 0248 } 0249 } 0250 0251 qCDebug(JABBER_PROTOCOL_LOG) << "No locks found."; 0252 } 0253 0254 JabberResource *JabberResourcePool::lockedJabberResource( const XMPP::Jid &jid ) 0255 { 0256 // check if the JID already carries a resource, then we will have to use that one 0257 if ( !jid.resource().isEmpty () ) 0258 { 0259 // we are subscribed to a JID, find the according resource in the pool 0260 foreach(JabberResource *mResource, d->pool) 0261 { 0262 if ( ( mResource->jid().userHost().toLower () == jid.userHost().toLower () ) && ( mResource->resource().name () == jid.resource () ) ) 0263 { 0264 return mResource; 0265 } 0266 } 0267 0268 qCDebug(JABBER_PROTOCOL_LOG) << "WARNING: No resource found in pool, returning as offline."; 0269 0270 return 0L; 0271 } 0272 0273 // see if we have a locked resource 0274 foreach(JabberResource *mResource, d->lockList) 0275 { 0276 if ( mResource->jid().userHost().toLower() == jid.userHost().toLower() ) 0277 { 0278 qCDebug(JABBER_PROTOCOL_LOG) << "Current lock for " << jid.userHost () << " is '" << mResource->resource().name () << "'"; 0279 return mResource; 0280 } 0281 } 0282 0283 qCDebug(JABBER_PROTOCOL_LOG) << "No lock available for " << jid.userHost (); 0284 0285 // there's no locked resource, return an empty resource 0286 return 0L; 0287 } 0288 0289 const XMPP::Resource &JabberResourcePool::lockedResource ( const XMPP::Jid &jid ) 0290 { 0291 JabberResource *resource = lockedJabberResource( jid ); 0292 return (resource) ? resource->resource() : EmptyResource; 0293 } 0294 0295 JabberResource *JabberResourcePool::bestJabberResource( const XMPP::Jid &jid, bool honourLock ) 0296 { 0297 qCDebug(JABBER_PROTOCOL_LOG) << "Determining best resource for " << jid.full (); 0298 0299 if ( honourLock ) 0300 { 0301 // if we are locked to a certain resource, always return that one 0302 JabberResource *mResource = lockedJabberResource ( jid ); 0303 if ( mResource ) 0304 { 0305 qCDebug(JABBER_PROTOCOL_LOG) << "We have a locked resource '" << mResource->resource().name () << "' for " << jid.full (); 0306 return mResource; 0307 } 0308 } 0309 0310 JabberResource *bestResource = 0L; 0311 JabberResource *currentResource = 0L; 0312 0313 foreach(currentResource, d->pool) 0314 { 0315 // make sure we are only looking up resources for the specified JID 0316 if ( currentResource->jid().userHost().toLower() != jid.userHost().toLower() ) 0317 { 0318 continue; 0319 } 0320 0321 // take first resource if no resource has been chosen yet 0322 if(!bestResource) 0323 { 0324 qCDebug(JABBER_PROTOCOL_LOG) << "Taking '" << currentResource->resource().name () << "' as first available resource."; 0325 0326 bestResource = currentResource; 0327 continue; 0328 } 0329 0330 if(currentResource->resource().priority() > bestResource->resource().priority()) 0331 { 0332 qCDebug(JABBER_PROTOCOL_LOG) << "Using '" << currentResource->resource().name () << "' due to better priority."; 0333 0334 // got a better match by priority 0335 bestResource = currentResource; 0336 } 0337 else 0338 { 0339 if(currentResource->resource().priority() == bestResource->resource().priority()) 0340 { 0341 if(currentResource->resource().status().timeStamp() > bestResource->resource().status().timeStamp()) 0342 { 0343 qCDebug(JABBER_PROTOCOL_LOG) << "Using '" << currentResource->resource().name () << "' due to better timestamp."; 0344 0345 // got a better match by timestamp (priorities are equal) 0346 bestResource = currentResource; 0347 } 0348 } 0349 } 0350 } 0351 0352 return (bestResource) ? bestResource : 0L; 0353 } 0354 0355 const XMPP::Resource &JabberResourcePool::bestResource ( const XMPP::Jid &jid, bool honourLock ) 0356 { 0357 JabberResource *bestResource = bestJabberResource( jid, honourLock); 0358 return (bestResource) ? bestResource->resource() : EmptyResource; 0359 } 0360 0361 //TODO: Find Resources based on certain Features. 0362 void JabberResourcePool::findResources ( const XMPP::Jid &jid, JabberResourcePool::ResourceList &resourceList ) 0363 { 0364 foreach(JabberResource *mResource, d->pool) 0365 { 0366 if ( mResource->jid().userHost().toLower() == jid.userHost().toLower() ) 0367 { 0368 // we found a resource for the JID, let's see if the JID already contains a resource 0369 if ( !jid.resource().isEmpty() && ( jid.resource().toLower() != mResource->resource().name().toLower() ) ) 0370 // the JID contains a resource but it's not the one we have in the dictionary, 0371 // thus we have to ignore this resource 0372 continue; 0373 0374 resourceList.append ( mResource ); 0375 } 0376 } 0377 } 0378 0379 void JabberResourcePool::findResources ( const XMPP::Jid &jid, XMPP::ResourceList &resourceList ) 0380 { 0381 foreach(JabberResource *mResource, d->pool) 0382 { 0383 if ( mResource->jid().userHost().toLower() == jid.userHost().toLower() ) 0384 { 0385 // we found a resource for the JID, let's see if the JID already contains a resource 0386 if ( !jid.resource().isEmpty() && ( jid.resource().toLower() != mResource->resource().name().toLower() ) ) 0387 // the JID contains a resource but it's not the one we have in the dictionary, 0388 // thus we have to ignore this resource 0389 continue; 0390 0391 resourceList.append ( mResource->resource () ); 0392 } 0393 } 0394 } 0395 0396 #include "moc_jabberresourcepool.cpp"