Switch to side-by-side view

--- a/libupnpp/discovery.cxx
+++ b/libupnpp/discovery.cxx
@@ -22,7 +22,9 @@
 
 #include <iostream>
 #include <map>
+#include <functional>
 using namespace std;
+using namespace std::placeholders;
 
 #include "upnpp_p.hxx"
 
@@ -33,36 +35,13 @@
 #include "expatmm.hxx"
 #include "upnpplib.hxx"
 #include "description.hxx"
-#include "cdirectory.hxx"
 #include "discovery.hxx"
 #include "log.hxx"
 
-#undef LOCAL_LOGINC
-#define LOCAL_LOGINC 0
-
-// The service type string for Content Directories:
-static const string
-ContentDirectorySType("urn:schemas-upnp-org:service:ContentDirectory:1");
-
-// We don't include a version in comparisons, as we are satisfied with
-// version 1
-static bool isCDService(const string& st)
-{
-    const string::size_type sz(ContentDirectorySType.size()-2);
-    return !ContentDirectorySType.compare(0, sz, st, 0, sz);
-}
-
-// The device type string for Media Servers
-static const string
-MediaServerDType("urn:schemas-upnp-org:device:MediaServer:1") ;
-
-#if 0
-static bool isMSDevice(const string& st)
-{
-    const string::size_type sz(MediaServerDType.size()-2);
-    return !MediaServerDType.compare(0, sz, st, 0, sz);
-}
-#endif
+namespace UPnPClient {
+
+//#undef LOCAL_LOGINC
+//#define LOCAL_LOGINC 3
 
 static string cluDiscoveryToStr(const struct Upnp_Discovery *disco)
 {
@@ -100,8 +79,60 @@
 };
 static WorkQueue<DiscoveredTask*> discoveredQueue("DiscoveredQueue");
 
-// Descriptor for one device having a Content Directory service found
-// on the network.
+// This gets called in a libupnp thread context for all asynchronous
+// events which we asked for.
+// Example: ContentDirectories appearing and disappearing from the network
+// We queue a task for our worker thread(s)
+// It seems that this can get called by several threads. We have a
+// mutex just for clarifying the message printing, the workqueue is
+// mt-safe of course.
+static PTMutexInit cblock;
+static int cluCallBack(Upnp_EventType et, void* evp, void*)
+{
+    PTMutexLocker lock(cblock);
+    LOGDEB1("discovery:cluCallBack: " << LibUPnP::evTypeAsString(et) << endl);
+
+    switch (et) {
+    case UPNP_DISCOVERY_SEARCH_RESULT:
+    case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
+    {
+        struct Upnp_Discovery *disco = (struct Upnp_Discovery *)evp;
+        // Devices send multiple messages for themselves, their subdevices and 
+        // services. AFAIK they all point to the same description.xml document,
+        // which has all the interesting data. So let's try to only process
+        // one message per device: the one which probably correspond to the 
+        // upnp "root device" message and has empty service and device types:
+        if (!disco->DeviceType[0] && !disco->ServiceType[0]) {
+            LOGDEB1("discovery:cllb:ALIVE: " << cluDiscoveryToStr(disco) 
+                    << endl);
+            DiscoveredTask *tp = new DiscoveredTask(1, disco);
+            if (discoveredQueue.put(tp)) {
+                return UPNP_E_FINISH;
+            }
+        }
+        break;
+    }
+    case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
+    {
+        struct Upnp_Discovery *disco = (struct Upnp_Discovery *)evp;
+        //LOGDEB("discovery:cllB:BYEBYE: " << cluDiscoveryToStr(disco) << endl);
+        DiscoveredTask *tp = new DiscoveredTask(0, disco);
+        if (discoveredQueue.put(tp)) {
+            return UPNP_E_FINISH;
+        }
+        break;
+    }
+    default:
+        // Ignore other events for now
+        LOGDEB("discovery:cluCallBack: unprocessed evt type: [" << 
+               LibUPnP::evTypeAsString(et) << "]"  << endl);
+        break;
+    }
+
+    return UPNP_E_SUCCESS;
+}
+
+// Descriptor for one device found on the network.
 class DeviceDescriptor {
 public:
     DeviceDescriptor(const string& url, const string& description,
@@ -139,8 +170,9 @@
             discoveredQueue.workerExit();
             return (void*)1;
         }
-        PLOGDEB("discoExplorer: alive %d deviceId [%s] URL [%s]\n",
-                tsk->alive, tsk->deviceId.c_str(), tsk->url.c_str());
+        LOGDEB1("discoExplorer: got task: alive " << tsk->alive << " deviceId ["
+                << tsk->deviceId << " URL [" << tsk->url << "]" << endl);
+
         if (!tsk->alive) {
             // Device signals it is going off.
             PTMutexLocker lock(o_pool.m_mutex);
@@ -165,8 +197,8 @@
             string sdesc(buf);
             free(buf);
                         
-            //LOGDEB("discoExplorer: downloaded description document of " <<
-            //   sdesc.size() << " bytes" << endl);
+            LOGDEB1("discoExplorer: downloaded description document of " <<
+                    sdesc.size() << " bytes" << endl);
 
             // Update or insert the device
             DeviceDescriptor d(tsk->url, sdesc, time(0), tsk->expires);
@@ -177,57 +209,12 @@
                 continue;
             }
             PTMutexLocker lock(o_pool.m_mutex);
-            //LOGDEB("discoExplorer: inserting device id "<< tsk->deviceId << 
-            //       " description: " << endl << d.device.dump() << endl);
+            //LOGDEB1("discoExplorer: inserting device id "<< tsk->deviceId << 
+            //        " description: " << endl << d.device.dump() << endl);
             o_pool.m_devices[tsk->deviceId] = d;
         }
         delete tsk;
     }
-}
-
-// This gets called in a libupnp thread context for all asynchronous
-// events which we asked for.
-// Example: ContentDirectories appearing and disappearing from the network
-// We queue a task for our worker thread(s)
-// It seems that this can get called by several threads. We have a
-// mutex just for clarifying the message printing, the workqueue is
-// mt-safe of course.
-static PTMutexInit cblock;
-static int cluCallBack(Upnp_EventType et, void* evp, void*)
-{
-    PTMutexLocker lock(cblock);
-    //LOGDEB("discovery:cluCallBack: " << LibUPnP::evTypeAsString(et) << endl);
-
-    switch (et) {
-    case UPNP_DISCOVERY_SEARCH_RESULT:
-    case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
-    {
-        struct Upnp_Discovery *disco = (struct Upnp_Discovery *)evp;
-        //LOGDEB("discovery:cllb:ALIVE: " << cluDiscoveryToStr(disco) << endl);
-        DiscoveredTask *tp = new DiscoveredTask(1, disco);
-        if (discoveredQueue.put(tp)) {
-            return UPNP_E_FINISH;
-        }
-        break;
-    }
-    case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
-    {
-        struct Upnp_Discovery *disco = (struct Upnp_Discovery *)evp;
-        //LOGDEB("discovery:cllB:BYEBYE: " << cluDiscoveryToStr(disco) << endl);
-        DiscoveredTask *tp = new DiscoveredTask(0, disco);
-        if (discoveredQueue.put(tp)) {
-            return UPNP_E_FINISH;
-        }
-        break;
-    }
-    default:
-        // Ignore other events for now
-        LOGDEB("discovery:cluCallBack: unprocessed evt type: [" << 
-               LibUPnP::evTypeAsString(et) << "]"  << endl);
-        break;
-    }
-
-    return UPNP_E_SUCCESS;
 }
 
 // Look at the devices and get rid of those which have not been seen
@@ -241,6 +228,8 @@
 
     for (DevPoolIt it = o_pool.m_devices.begin();
          it != o_pool.m_devices.end();) {
+        LOGDEB1("Dev in pool: type: " << it->second.device.deviceType <<
+                " friendlyName " << it->second.device.friendlyName << endl);
         if (now - it->second.last_seen > it->second.expires) {
             //LOGDEB("expireDevices: deleting " <<  it->first.c_str() << " " << 
             //   it->second.device.friendlyName.c_str() << endl);
@@ -266,6 +255,7 @@
         m_reason = "Discover work queue start failed";
         return;
     }
+    pthread_yield();
     LibUPnP *lib = LibUPnP::getLibUPnP();
     if (lib == 0) {
         m_reason = "Can't get lib";
@@ -282,7 +272,7 @@
 
 bool UPnPDeviceDirectory::search()
 {
-    PLOGDEB("UPnPDeviceDirectory::search\n");
+    LOGDEB1("UPnPDeviceDirectory::search" << endl);
     if (time(0) - m_lastSearch < 10)
         return true;
 
@@ -292,19 +282,15 @@
         return false;
     }
 
-    // We search both for device and service just in case.
-    int code1 = UpnpSearchAsync(lib->getclh(), m_searchTimeout,
-                                ContentDirectorySType.c_str(), lib);
+    LOGDEB1("UPnPDeviceDirectory::search: calling upnpsearchasync"<<endl);
+    //const char *cp = "ssdp:all";
+    const char *cp = "upnp:rootdevice";
+    int code1 = UpnpSearchAsync(lib->getclh(), m_searchTimeout, cp, lib);
     if (code1 != UPNP_E_SUCCESS) {
         m_reason = LibUPnP::errAsString("UpnpSearchAsync", code1);
-    }
-    int code2 = UpnpSearchAsync(lib->getclh(), m_searchTimeout,
-                                MediaServerDType.c_str(), lib);
-    if (code2 != UPNP_E_SUCCESS) {
-        m_reason = LibUPnP::errAsString("UpnpSearchAsync", code2);
-    }
-    if (code1 != UPNP_E_SUCCESS && code2 != UPNP_E_SUCCESS)
-        return false;
+        LOGERR("UPnPDeviceDirectory::search: UpnpSearchAsync failed: " <<
+               m_reason << endl);
+    }
     m_lastSearch = time(0);
     return true;
 }
@@ -327,14 +313,14 @@
 time_t UPnPDeviceDirectory::getRemainingDelay()
 {
     time_t now = time(0);
-    if (now - m_lastSearch >= m_searchTimeout)
+    if (now - m_lastSearch >= m_searchTimeout+1)
         return 0;
     return  m_searchTimeout - (now - m_lastSearch);
 }
 
-bool UPnPDeviceDirectory::getDirServices(vector<ContentDirectoryService>& out)
-{
-    //LOGDEB("UPnPDeviceDirectory::getDirServices" << endl);
+bool UPnPDeviceDirectory::traverse(UPnPDeviceDirectory::Visitor visit)
+{
+    //LOGDEB("UPnPDeviceDirectory::traverse" << endl);
     if (m_ok == false)
         return false;
 
@@ -346,38 +332,14 @@
 
     PTMutexLocker lock(o_pool.m_mutex);
 
-    for (DevPoolIt dit = o_pool.m_devices.begin();
-         dit != o_pool.m_devices.end(); dit++) {
-        for (DevServIt sit = dit->second.device.services.begin();
-             sit != dit->second.device.services.end(); sit++) {
-            if (isCDService(sit->serviceType)) {
-                out.push_back(ContentDirectoryService(dit->second.device,
-                                                      *sit));
-            }
-        }
-    }
-
+    for (auto& dde : o_pool.m_devices) {
+        for (auto& service : dde.second.device.services) {
+            if (!visit(dde.second.device, service))
+                return false;
+        }
+    }
     return true;
 }
 
-// Get server by friendly name. It's a bit wasteful to copy all
-// servers for this, we could directly walk the list. Otoh there isn't
-// going to be millions...
-bool UPnPDeviceDirectory::getServer(const string& friendlyName,
-                                    ContentDirectoryService& server)
-{
-    vector<ContentDirectoryService> ds;
-    if (!getDirServices(ds)) {
-        PLOGDEB("UPnPDeviceDirectory::getServer: no servers?\n");
-        return false;
-    }
-    for (vector<ContentDirectoryService>::const_iterator it = ds.begin();
-         it != ds.end(); it++) {
-        if (!friendlyName.compare(it->getFriendlyName())) {
-            server = *it;
-            return true;
-        }
-    }
-    return false;
-}
-
+} // namespace UPnPClient
+