00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00012
00013 #include "upnp.h"
00014 #include "mythevent.h"
00015 #include "mythlogging.h"
00016 #include "upnptaskcache.h"
00017
00018 SSDPCache* SSDPCache::g_pSSDPCache = NULL;
00019
00020 int SSDPCacheEntries::g_nAllocated = 0;
00021
00024
00025
00026
00029
00030 SSDPCacheEntries::SSDPCacheEntries()
00031 {
00032 g_nAllocated++;
00033 }
00034
00035 SSDPCacheEntries::~SSDPCacheEntries()
00036 {
00037 Clear();
00038 g_nAllocated--;
00039 }
00040
00042 void SSDPCacheEntries::Clear(void)
00043 {
00044 QMutexLocker locker(&m_mutex);
00045
00046 EntryMap::iterator it = m_mapEntries.begin();
00047 for (; it != m_mapEntries.end(); ++it)
00048 {
00049 if ((*it))
00050 (*it)->Release();
00051 }
00052
00053 m_mapEntries.clear();
00054 }
00055
00058 DeviceLocation *SSDPCacheEntries::Find(const QString &sUSN)
00059 {
00060 QMutexLocker locker(&m_mutex);
00061
00062 EntryMap::iterator it = m_mapEntries.find(GetNormalizedUSN(sUSN));
00063 DeviceLocation *pEntry = (it != m_mapEntries.end()) ? *it : NULL;
00064 if (pEntry)
00065 pEntry->AddRef();
00066
00067 return pEntry;
00068 }
00069
00072 DeviceLocation *SSDPCacheEntries::GetFirst(void)
00073 {
00074 QMutexLocker locker(&m_mutex);
00075 if (m_mapEntries.empty())
00076 return NULL;
00077 DeviceLocation *loc = *m_mapEntries.begin();
00078 loc->AddRef();
00079 return loc;
00080 }
00081
00084 void SSDPCacheEntries::GetEntryMap(EntryMap &map)
00085 {
00086 QMutexLocker locker(&m_mutex);
00087
00088 EntryMap::const_iterator it = m_mapEntries.begin();
00089 for (; it != m_mapEntries.end(); ++it)
00090 {
00091 (*it)->AddRef();
00092 map.insert(it.key(), *it);
00093 }
00094 }
00095
00097 void SSDPCacheEntries::Insert(const QString &sUSN, DeviceLocation *pEntry)
00098 {
00099 QMutexLocker locker(&m_mutex);
00100
00101 pEntry->AddRef();
00102
00103
00104
00105
00106
00107 QString usn = GetNormalizedUSN(sUSN);
00108
00109 EntryMap::iterator it = m_mapEntries.find(usn);
00110 if ((it != m_mapEntries.end()) && (*it != NULL))
00111 (*it)->Release();
00112
00113 m_mapEntries[usn] = pEntry;
00114
00115 LOG(VB_UPNP, LOG_INFO, QString("SSDP Cache adding USN: %1 Location %2")
00116 .arg(pEntry->m_sUSN).arg(pEntry->m_sLocation));
00117 }
00118
00120 void SSDPCacheEntries::Remove( const QString &sUSN )
00121 {
00122 QMutexLocker locker(&m_mutex);
00123
00124 QString usn = GetNormalizedUSN(sUSN);
00125 EntryMap::iterator it = m_mapEntries.find(usn);
00126 if (it != m_mapEntries.end())
00127 {
00128 if (*it)
00129 {
00130 LOG(VB_UPNP, LOG_INFO,
00131 QString("SSDP Cache removing USN: %1 Location %2")
00132 .arg((*it)->m_sUSN).arg((*it)->m_sLocation));
00133 (*it)->Release();
00134 }
00135
00136
00137
00138 m_mapEntries.erase(it);
00139 }
00140 }
00141
00143 uint SSDPCacheEntries::RemoveStale(const TaskTime &ttNow)
00144 {
00145 QMutexLocker locker(&m_mutex);
00146 uint nCount = 0;
00147
00148 EntryMap::iterator it = m_mapEntries.begin();
00149 while (it != m_mapEntries.end())
00150 {
00151 if (*it == NULL)
00152 {
00153 it = m_mapEntries.erase(it);
00154 }
00155 else if ((*it)->m_ttExpires < ttNow)
00156 {
00157
00158
00159 (*it)->Release();
00160
00161
00162
00163 it = m_mapEntries.erase(it);
00164 nCount++;
00165 }
00166 else
00167 {
00168 ++it;
00169 }
00170 }
00171
00172 return nCount;
00173 }
00174
00176 QTextStream &SSDPCacheEntries::OutputXML(
00177 QTextStream &os, uint *pnEntryCount) const
00178 {
00179 QMutexLocker locker(&m_mutex);
00180
00181 EntryMap::const_iterator it = m_mapEntries.begin();
00182 for (; it != m_mapEntries.end(); ++it)
00183 {
00184 if (*it == NULL)
00185 continue;
00186
00187
00188
00189 os << "<Service usn='" << (*it)->m_sUSN
00190 << "' expiresInSecs='" << (*it)->ExpiresInSecs()
00191 << "' url='" << (*it)->m_sLocation << "' />" << endl;
00192
00193 if (pnEntryCount != NULL)
00194 (*pnEntryCount)++;
00195 }
00196
00197 return os;
00198 }
00199
00201 void SSDPCacheEntries::Dump(uint &nEntryCount) const
00202 {
00203 QMutexLocker locker(&m_mutex);
00204
00205 EntryMap::const_iterator it = m_mapEntries.begin();
00206 for (; it != m_mapEntries.end(); ++it)
00207 {
00208 if (*it == NULL)
00209 continue;
00210
00211
00212
00213 LOG(VB_UPNP, LOG_DEBUG, QString(" * \t\t%1\t | %2\t | %3 ")
00214 .arg((*it)->m_sUSN) .arg((*it)->ExpiresInSecs())
00215 .arg((*it)->m_sLocation));
00216
00217 nEntryCount++;
00218 }
00219 }
00220
00223 QString SSDPCacheEntries::GetNormalizedUSN(const QString &sUSN)
00224 {
00225 int uuid_end_loc = sUSN.indexOf(":",5);
00226 if (uuid_end_loc > 0)
00227 return sUSN.left(uuid_end_loc).toLower() + sUSN.mid(uuid_end_loc);
00228 return sUSN;
00229 }
00230
00233
00234
00235
00238
00239 SSDPCache* SSDPCache::Instance()
00240 {
00241 return g_pSSDPCache ? g_pSSDPCache : (g_pSSDPCache = new SSDPCache());
00242
00243 }
00244
00246
00248
00249 SSDPCache::SSDPCache()
00250 {
00251 LOG(VB_UPNP, LOG_DEBUG, "SSDPCache - Constructor");
00252
00253
00254
00255
00256
00257 TaskQueue::Instance()->AddTask( new SSDPCacheTask() );
00258
00259 }
00260
00262
00264
00265 SSDPCache::~SSDPCache()
00266 {
00267
00268 #if 0
00269 LOG(VB_UPNP, LOG_DEBUG, "SSDPCache - Destructor");
00270 #endif
00271
00272 Clear();
00273 }
00274
00276
00278
00279 void SSDPCache::Clear(void)
00280 {
00281 QMutexLocker locker(&m_mutex);
00282
00283 SSDPCacheEntriesMap::iterator it = m_cache.begin();
00284 for (; it != m_cache.end(); ++it)
00285 {
00286 if (*it)
00287 (*it)->Release();
00288 }
00289
00290 m_cache.clear();
00291 }
00292
00295 SSDPCacheEntries *SSDPCache::Find(const QString &sURI)
00296 {
00297 QMutexLocker locker(&m_mutex);
00298
00299 SSDPCacheEntriesMap::iterator it = m_cache.find(sURI);
00300 if (it != m_cache.end() && (*it != NULL))
00301 (*it)->AddRef();
00302
00303 return (it != m_cache.end()) ? *it : NULL;
00304 }
00305
00308 DeviceLocation *SSDPCache::Find(const QString &sURI, const QString &sUSN)
00309 {
00310 DeviceLocation *pEntry = NULL;
00311 SSDPCacheEntries *pEntries = Find(sURI);
00312
00313 if (pEntries != NULL)
00314 {
00315 pEntry = pEntries->Find(sUSN);
00316 pEntries->Release();
00317 }
00318
00319 return pEntry;
00320 }
00321
00323
00325
00326 void SSDPCache::Add( const QString &sURI,
00327 const QString &sUSN,
00328 const QString &sLocation,
00329 long sExpiresInSecs )
00330 {
00331
00332
00333
00334
00335 TaskTime ttExpires;
00336 gettimeofday ( (&ttExpires), NULL );
00337 AddSecondsToTaskTime( ttExpires, sExpiresInSecs );
00338
00339
00340
00341
00342
00343 SSDPCacheEntries *pEntries = NULL;
00344 {
00345 QMutexLocker locker(&m_mutex);
00346 SSDPCacheEntriesMap::iterator it = m_cache.find(sURI);
00347 if (it == m_cache.end() || (*it == NULL))
00348 {
00349 pEntries = new SSDPCacheEntries();
00350 pEntries->AddRef();
00351 it = m_cache.insert(sURI, pEntries);
00352 }
00353 pEntries = *it;
00354 pEntries->AddRef();
00355 }
00356
00357
00358
00359
00360
00361 DeviceLocation *pEntry = pEntries->Find(sUSN);
00362 if (pEntry == NULL)
00363 {
00364 pEntry = new DeviceLocation(sURI, sUSN, sLocation, ttExpires);
00365 pEntries->Insert(sUSN, pEntry);
00366 NotifyAdd(sURI, sUSN, sLocation);
00367 }
00368 else
00369 {
00370 pEntry->m_sLocation = sLocation;
00371 pEntry->m_ttExpires = ttExpires;
00372 pEntry->Release();
00373 }
00374
00375 pEntries->Release();
00376 }
00377
00379
00381
00382 void SSDPCache::Remove( const QString &sURI, const QString &sUSN )
00383 {
00384 Lock();
00385
00386
00387
00388
00389
00390 SSDPCacheEntriesMap::Iterator it = m_cache.find( sURI );
00391
00392 if (it != m_cache.end())
00393 {
00394 SSDPCacheEntries *pEntries = *it;
00395
00396 if (pEntries != NULL)
00397 {
00398 pEntries->AddRef();
00399
00400 pEntries->Remove( sUSN );
00401
00402 if (pEntries->Count() == 0)
00403 {
00404 pEntries->Release();
00405 m_cache.erase(it);
00406 }
00407
00408 pEntries->Release();
00409 }
00410 }
00411
00412 Unlock();
00413
00414
00415
00416
00417 NotifyRemove( sURI, sUSN );
00418 }
00419
00421
00423
00424 int SSDPCache::RemoveStale()
00425 {
00426 int nCount = 0;
00427 TaskTime ttNow;
00428 QStringList lstKeys;
00429
00430 gettimeofday( (&ttNow), NULL );
00431
00432 Lock();
00433
00434
00435
00436
00437
00438 for (SSDPCacheEntriesMap::Iterator it = m_cache.begin();
00439 it != m_cache.end();
00440 ++it )
00441 {
00442 SSDPCacheEntries *pEntries = *it;
00443
00444 if (pEntries != NULL)
00445 {
00446 pEntries->AddRef();
00447
00448 nCount += pEntries->RemoveStale( ttNow );
00449
00450 if (pEntries->Count() == 0)
00451 lstKeys.append( it.key() );
00452
00453 pEntries->Release();
00454 }
00455 }
00456
00457 Unlock();
00458
00459 nCount = lstKeys.count();
00460
00461
00462
00463
00464
00465
00466 for ( QStringList::Iterator itKey = lstKeys.begin();
00467 itKey != lstKeys.end();
00468 ++itKey )
00469 {
00470 SSDPCacheEntriesMap::iterator it = m_cache.find( *itKey );
00471 if (it == m_cache.end())
00472 continue;
00473
00474 if (*it)
00475 {
00476 (*it)->Release();
00477 m_cache.erase(it);
00478 }
00479 }
00480
00481 return nCount;
00482 }
00483
00485
00487
00488 void SSDPCache::NotifyAdd( const QString &sURI,
00489 const QString &sUSN,
00490 const QString &sLocation )
00491 {
00492 QStringList values;
00493
00494 values.append( sURI );
00495 values.append( sUSN );
00496 values.append( sLocation );
00497
00498 MythEvent me( "SSDP_ADD", values );
00499
00500 dispatch( me );
00501 }
00502
00504
00506
00507 void SSDPCache::NotifyRemove( const QString &sURI, const QString &sUSN )
00508 {
00509 QStringList values;
00510
00511 values.append( sURI );
00512 values.append( sUSN );
00513
00514 MythEvent me( "SSDP_REMOVE", values );
00515
00516 dispatch( me );
00517 }
00518
00520 QTextStream &SSDPCache::OutputXML(
00521 QTextStream &os, uint *pnDevCount, uint *pnEntryCount) const
00522 {
00523 QMutexLocker locker(&m_mutex);
00524
00525 if (pnDevCount != NULL)
00526 *pnDevCount = 0;
00527 if (pnEntryCount != NULL)
00528 *pnEntryCount = 0;
00529
00530 SSDPCacheEntriesMap::const_iterator it = m_cache.begin();
00531 for (; it != m_cache.end(); ++it)
00532 {
00533 if (*it != NULL)
00534 {
00535 os << "<Device uri='" << it.key() << "'>" << endl;
00536
00537 uint tmp = 0;
00538
00539 (*it)->OutputXML(os, &tmp);
00540
00541 if (pnEntryCount != NULL)
00542 *pnEntryCount += tmp;
00543
00544 os << "</Device>" << endl;
00545
00546 if (pnDevCount != NULL)
00547 (*pnDevCount)++;
00548 }
00549 }
00550 os << flush;
00551
00552 return os;
00553 }
00554
00556 void SSDPCache::Dump(void)
00557 {
00558 if (!VERBOSE_LEVEL_CHECK(VB_UPNP, LOG_DEBUG))
00559 return;
00560
00561 QMutexLocker locker(&m_mutex);
00562
00563 LOG(VB_UPNP, LOG_DEBUG, "========================================"
00564 "=======================================");
00565 LOG(VB_UPNP, LOG_DEBUG, QString(" URI (type) - Found: %1 Entries - "
00566 "%2 have been Allocated. ")
00567 .arg(m_cache.count()).arg(SSDPCacheEntries::g_nAllocated));
00568 LOG(VB_UPNP, LOG_DEBUG, " \t\tUSN (unique id)\t\t | Expires"
00569 "\t | Location");
00570 LOG(VB_UPNP, LOG_DEBUG, "----------------------------------------"
00571 "---------------------------------------");
00572
00573 uint nCount = 0;
00574 SSDPCacheEntriesMap::const_iterator it = m_cache.begin();
00575 for (; it != m_cache.end(); ++it)
00576 {
00577 if (*it != NULL)
00578 {
00579 LOG(VB_UPNP, LOG_DEBUG, it.key());
00580 (*it)->Dump(nCount);
00581 LOG(VB_UPNP, LOG_DEBUG, " ");
00582 }
00583 }
00584
00585 LOG(VB_UPNP, LOG_DEBUG, "----------------------------------------"
00586 "---------------------------------------");
00587 LOG(VB_UPNP, LOG_DEBUG,
00588 QString(" Found: %1 Entries - %2 have been Allocated. ")
00589 .arg(nCount) .arg(DeviceLocation::g_nAllocated));
00590 LOG(VB_UPNP, LOG_DEBUG, "========================================"
00591 "=======================================" );
00592 }