Switch to unified view

a/libupnpp/discovery.cxx b/libupnpp/discovery.cxx
1
/* Copyright (C) 2013 J.F.Dockes
1
/* Copyright (C) 2013 J.F.Dockes
2
 *     This program is free software; you can redistribute it and/or modify
2
 *       This program is free software; you can redistribute it and/or modify
3
 *     it under the terms of the GNU General Public License as published by
3
 *       it under the terms of the GNU General Public License as published by
4
 *     the Free Software Foundation; either version 2 of the License, or
4
 *       the Free Software Foundation; either version 2 of the License, or
5
 *     (at your option) any later version.
5
 *       (at your option) any later version.
6
 *
6
 *
7
 *     This program is distributed in the hope that it will be useful,
7
 *       This program is distributed in the hope that it will be useful,
8
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
8
 *       but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9
 *       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 *     GNU General Public License for more details.
10
 *       GNU General Public License for more details.
11
 *
11
 *
12
 *     You should have received a copy of the GNU General Public License
12
 *       You should have received a copy of the GNU General Public License
13
 *     along with this program; if not, write to the
13
 *       along with this program; if not, write to the
14
 *     Free Software Foundation, Inc.,
14
 *       Free Software Foundation, Inc.,
15
 *     59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
15
 *       59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
16
 */
16
 */
17
#include "config.h"
17
#include "config.h"
18
18
19
#include <stdlib.h>
19
#include <stdlib.h>
20
#include <errno.h>
20
#include <errno.h>
...
...
35
#include "description.hxx"
35
#include "description.hxx"
36
#include "cdirectory.hxx"
36
#include "cdirectory.hxx"
37
#include "discovery.hxx"
37
#include "discovery.hxx"
38
#include "log.hxx"
38
#include "log.hxx"
39
39
40
// The service type string we are looking for.
40
#undef LOCAL_LOGINC
41
#define LOCAL_LOGINC 0
42
43
// The service type string for Content Directories:
41
static const string
44
static const string
42
ContentDirectorySType("urn:schemas-upnp-org:service:ContentDirectory:1");
45
ContentDirectorySType("urn:schemas-upnp-org:service:ContentDirectory:1");
46
43
// We don't include a version in comparisons, as we are satisfied with
47
// We don't include a version in comparisons, as we are satisfied with
44
// version 1
48
// version 1
45
static bool isCDService(const string& st)
49
static bool isCDService(const string& st)
46
{
50
{
47
  const string::size_type sz(ContentDirectorySType.size()-2);
51
    const string::size_type sz(ContentDirectorySType.size()-2);
48
  return !ContentDirectorySType.compare(0, sz, st, 0, sz);
52
    return !ContentDirectorySType.compare(0, sz, st, 0, sz);
49
}
53
}
50
54
51
// The type of device we're asking for in search
55
// The device type string for Media Servers
52
static const string
56
static const string
53
MediaServerDType("urn:schemas-upnp-org:device:MediaServer:1") ;
57
MediaServerDType("urn:schemas-upnp-org:device:MediaServer:1") ;
58
59
#if 0
54
static bool isMSDevice(const string& st)
60
static bool isMSDevice(const string& st)
55
{
61
{
56
  const string::size_type sz(MediaServerDType.size()-2);
62
    const string::size_type sz(MediaServerDType.size()-2);
57
  return !MediaServerDType.compare(0, sz, st, 0, sz);
63
    return !MediaServerDType.compare(0, sz, st, 0, sz);
58
}
64
}
65
#endif
59
66
60
#if defined(HAVE_UPNPSETLOGLEVEL)
61
static string cluDiscoveryToStr(const struct Upnp_Discovery *disco)
67
static string cluDiscoveryToStr(const struct Upnp_Discovery *disco)
62
{
68
{
63
  stringstream ss;
69
    stringstream ss;
64
  ss << "ErrCode: " << disco->ErrCode << endl;
70
    ss << "ErrCode: " << disco->ErrCode << endl;
65
  ss << "Expires: " << disco->Expires << endl;
71
    ss << "Expires: " << disco->Expires << endl;
66
  ss << "DeviceId: " << disco->DeviceId << endl;
72
    ss << "DeviceId: " << disco->DeviceId << endl;
67
  ss << "DeviceType: " << disco->DeviceType << endl;
73
    ss << "DeviceType: " << disco->DeviceType << endl;
68
  ss << "ServiceType: " << disco->ServiceType << endl;
74
    ss << "ServiceType: " << disco->ServiceType << endl;
69
  ss << "ServiceVer: " << disco->ServiceVer     << endl;
75
    ss << "ServiceVer: " << disco->ServiceVer    << endl;
70
  ss << "Location: " << disco->Location << endl;
76
    ss << "Location: " << disco->Location << endl;
71
  ss << "Os: " << disco->Os << endl;
77
    ss << "Os: " << disco->Os << endl;
72
  ss << "Date: " << disco->Date << endl;
78
    ss << "Date: " << disco->Date << endl;
73
  ss << "Ext: " << disco->Ext << endl;
79
    ss << "Ext: " << disco->Ext << endl;
74
80
75
  /** The host address of the device responding to the search. */
81
    /** The host address of the device responding to the search. */
76
  // struct sockaddr_storage DestAddr;
82
    // struct sockaddr_storage DestAddr;
77
  return ss.str();
83
    return ss.str();
78
}
84
}
79
#endif // DEBUG_DISCOVERY
80
85
81
// Each appropriate discovery event (executing in a libupnp thread
86
// Each appropriate discovery event (executing in a libupnp thread
82
// context) queues the following task object for processing by the
87
// context) queues the following task object for processing by the
83
// discovery thread.
88
// discovery thread.
84
class DiscoveredTask {
89
class DiscoveredTask {
85
public:
90
public:
86
  DiscoveredTask(bool _alive, const struct Upnp_Discovery *disco)
91
    DiscoveredTask(bool _alive, const struct Upnp_Discovery *disco)
87
      : alive(_alive), url(disco->Location), deviceId(disco->DeviceId),
92
        : alive(_alive), url(disco->Location), deviceId(disco->DeviceId),
88
        expires(disco->Expires)
93
          expires(disco->Expires)
89
      {}
94
        {}
90
95
91
  bool alive;
96
    bool alive;
92
  string url;
97
    string url;
93
  string deviceId;
98
    string deviceId;
94
  int expires; // Seconds valid
99
    int expires; // Seconds valid
95
};
100
};
96
static WorkQueue<DiscoveredTask*> discoveredQueue("DiscoveredQueue");
101
static WorkQueue<DiscoveredTask*> discoveredQueue("DiscoveredQueue");
97
102
98
// Descriptor for one device having a Content Directory service found
103
// Descriptor for one device having a Content Directory service found
99
// on the network.
104
// on the network.
100
class ContentDirectoryDescriptor {
105
class DeviceDescriptor {
101
public:
106
public:
102
  ContentDirectoryDescriptor(const string& url, const string& description,
107
    DeviceDescriptor(const string& url, const string& description,
103
                             time_t last, int exp)
108
                     time_t last, int exp)
104
      : device(url, description), last_seen(last), expires(exp+20)
109
        : device(url, description), last_seen(last), expires(exp+20)
105
      {}
110
        {}
106
  ContentDirectoryDescriptor()
111
    DeviceDescriptor()
107
      {}
112
        {}
108
  UPnPDevice device;
113
    UPnPDeviceDesc device;
109
  time_t last_seen;
114
    time_t last_seen;
110
  int expires; // seconds valid
115
    int expires; // seconds valid
111
};
116
};
112
117
113
// A ContentDirectoryPool holds the characteristics of the servers
118
// A DevicePool holds the characteristics of the devices
114
// currently on the network.
119
// currently on the network.
115
// The map is referenced by deviceId (==UDN)
120
// The map is referenced by deviceId (==UDN)
116
// The class is instanciated as a static (unenforced) singleton.
121
// The class is instanciated as a static (unenforced) singleton.
117
class ContentDirectoryPool {
122
class DevicePool {
118
public:
123
public:
119
  PTMutexInit m_mutex;
124
    PTMutexInit m_mutex;
120
  map<string, ContentDirectoryDescriptor> m_directories;
125
    map<string, DeviceDescriptor> m_devices;
121
};
126
};
122
static ContentDirectoryPool contentDirectories;
127
static DevicePool o_pool;
123
typedef map<string, ContentDirectoryDescriptor>::iterator DirPoolIt;
128
typedef map<string, DeviceDescriptor>::iterator DevPoolIt;
124
129
125
// Worker routine for the discovery queue. Get messages about devices
130
// Worker routine for the discovery queue. Get messages about devices
126
// appearing and disappearing, and update the directory pool
131
// appearing and disappearing, and update the directory pool
127
// accordingly.
132
// accordingly.
128
static void *discoExplorer(void *)
133
static void *discoExplorer(void *)
129
{
134
{
130
  for (;;) {
135
    for (;;) {
131
      DiscoveredTask *tsk = 0;
136
        DiscoveredTask *tsk = 0;
132
      size_t qsz;
137
        size_t qsz;
133
      if (!discoveredQueue.take(&tsk, &qsz)) {
138
        if (!discoveredQueue.take(&tsk, &qsz)) {
134
          discoveredQueue.workerExit();
139
            discoveredQueue.workerExit();
135
          return (void*)1;
140
            return (void*)1;
136
      }
141
        }
137
      PLOGDEB("discoExplorer: alive %d deviceId [%s] URL [%s]\n",
142
        PLOGDEB("discoExplorer: alive %d deviceId [%s] URL [%s]\n",
138
              tsk->alive, tsk->deviceId.c_str(), tsk->url.c_str());
143
                tsk->alive, tsk->deviceId.c_str(), tsk->url.c_str());
139
      if (!tsk->alive) {
144
        if (!tsk->alive) {
140
          // Device signals it is going off.
145
            // Device signals it is going off.
141
          PTMutexLocker lock(contentDirectories.m_mutex);
146
            PTMutexLocker lock(o_pool.m_mutex);
142
          DirPoolIt it = contentDirectories.m_directories.find(tsk->deviceId);
147
            DevPoolIt it = o_pool.m_devices.find(tsk->deviceId);
143
          if (it != contentDirectories.m_directories.end()) {
148
            if (it != o_pool.m_devices.end()) {
144
              contentDirectories.m_directories.erase(it);
149
                o_pool.m_devices.erase(it);
145
              //LOGDEB("discoExplorer: delete " << tsk->deviceId.c_str() << 
150
                //LOGDEB("discoExplorer: delete " << tsk->deviceId.c_str() << 
146
              // endl);
151
                // endl);
147
          }
152
            }
148
      } else {
153
        } else {
149
          // Device signals its existence and well-being. Perform the
154
            // Device signals its existence and well-being. Perform the
150
          // UPnP "description" phase by downloading and decoding the
155
            // UPnP "description" phase by downloading and decoding the
151
          // description document.
156
            // description document.
152
          char *buf = 0;
157
            char *buf = 0;
153
          // LINE_SIZE is defined by libupnp's upnp.h...
158
            // LINE_SIZE is defined by libupnp's upnp.h...
154
          char contentType[LINE_SIZE];
159
            char contentType[LINE_SIZE];
155
          int code = UpnpDownloadUrlItem(tsk->url.c_str(), &buf, contentType);
160
            int code = UpnpDownloadUrlItem(tsk->url.c_str(), &buf, contentType);
156
          if (code != UPNP_E_SUCCESS) {
161
            if (code != UPNP_E_SUCCESS) {
157
              LOGERR(LibUPnP::errAsString("discoExplorer", code) << endl);
162
                LOGERR(LibUPnP::errAsString("discoExplorer", code) << endl);
158
              continue;
163
                continue;
159
          }
164
            }
160
          string sdesc(buf);
165
            string sdesc(buf);
161
          free(buf);
166
            free(buf);
162
          
167
                        
163
          //LOGDEB("discoExplorer: downloaded description document of " <<
168
            //LOGDEB("discoExplorer: downloaded description document of " <<
164
          //   sdesc.size() << " bytes" << endl);
169
            //   sdesc.size() << " bytes" << endl);
165
170
166
          // Update or insert the device
171
            // Update or insert the device
167
          ContentDirectoryDescriptor d(tsk->url, sdesc,
172
            DeviceDescriptor d(tsk->url, sdesc, time(0), tsk->expires);
168
                                       time(0), tsk->expires);
173
            if (!d.device.ok) {
169
          if (!d.device.ok) {
170
              LOGERR("discoExplorer: description parse failed for " << 
174
                LOGERR("discoExplorer: description parse failed for " << 
171
                     tsk->deviceId << endl);
175
                       tsk->deviceId << endl);
172
              delete tsk;
176
                delete tsk;
173
              continue;
177
                continue;
174
          }
178
            }
175
          PTMutexLocker lock(contentDirectories.m_mutex);
179
            PTMutexLocker lock(o_pool.m_mutex);
176
          //LOGDEB("discoExplorer: inserting id "<< tsk->deviceId.c_str() << 
180
            //LOGDEB("discoExplorer: inserting device id "<< tsk->deviceId << 
177
          //   endl);
181
            //       " description: " << endl << d.device.dump() << endl);
178
          contentDirectories.m_directories[tsk->deviceId] = d;
182
            o_pool.m_devices[tsk->deviceId] = d;
179
      }
183
        }
180
      delete tsk;
184
        delete tsk;
181
  }
185
    }
182
}
186
}
183
187
184
// This gets called in a libupnp thread context for all asynchronous
188
// This gets called in a libupnp thread context for all asynchronous
185
// events which we asked for.
189
// events which we asked for.
186
// Example: ContentDirectories appearing and disappearing from the network
190
// Example: ContentDirectories appearing and disappearing from the network
...
...
189
// mutex just for clarifying the message printing, the workqueue is
193
// mutex just for clarifying the message printing, the workqueue is
190
// mt-safe of course.
194
// mt-safe of course.
191
static PTMutexInit cblock;
195
static PTMutexInit cblock;
192
static int cluCallBack(Upnp_EventType et, void* evp, void*)
196
static int cluCallBack(Upnp_EventType et, void* evp, void*)
193
{
197
{
194
  PTMutexLocker lock(cblock);
198
    PTMutexLocker lock(cblock);
195
  PLOGDEB("cluCallBack: evt type: [%s]\n",
199
    //LOGDEB("discovery:cluCallBack: " << LibUPnP::evTypeAsString(et) << endl);
196
          LibUPnP::evTypeAsString(et).c_str());
197
200
198
  switch (et) {
201
    switch (et) {
199
  case UPNP_DISCOVERY_SEARCH_RESULT:
202
    case UPNP_DISCOVERY_SEARCH_RESULT:
200
  case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
203
    case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
201
  {
204
    {
202
      struct Upnp_Discovery *disco = (struct Upnp_Discovery *)evp;
205
        struct Upnp_Discovery *disco = (struct Upnp_Discovery *)evp;
203
      if (isMSDevice(disco->DeviceType) || isCDService(disco->ServiceType)) {
206
        //LOGDEB("discovery:cllb:ALIVE: " << cluDiscoveryToStr(disco) << endl);
204
          PLOGDEB("ALIVE : %s\n", cluDiscoveryToStr(disco).c_str());
205
          DiscoveredTask *tp = new DiscoveredTask(1, disco);
207
        DiscoveredTask *tp = new DiscoveredTask(1, disco);
206
          if (discoveredQueue.put(tp)) {
208
        if (discoveredQueue.put(tp)) {
207
              return UPNP_E_FINISH;
209
            return UPNP_E_FINISH;
208
          }
210
        }
209
      }
211
        break;
210
      break;
212
    }
211
  }
212
  case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
213
    case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
213
  {
214
    {
214
      struct Upnp_Discovery *disco = (struct Upnp_Discovery *)evp;
215
        struct Upnp_Discovery *disco = (struct Upnp_Discovery *)evp;
215
216
        //LOGDEB("discovery:cllB:BYEBYE: " << cluDiscoveryToStr(disco) << endl);
216
      PLOGDEB("BYEBYE: %s\n", cluDiscoveryToStr(disco).c_str());
217
      DiscoveredTask *tp = new DiscoveredTask(0, disco);
217
        DiscoveredTask *tp = new DiscoveredTask(0, disco);
218
      if (discoveredQueue.put(tp)) {
218
        if (discoveredQueue.put(tp)) {
219
          return UPNP_E_FINISH;
219
            return UPNP_E_FINISH;
220
      }
220
        }
221
      break;
221
        break;
222
  }
222
    }
223
  default:
223
    default:
224
      // Ignore other events for now
224
        // Ignore other events for now
225
      break;
225
        LOGDEB("discovery:cluCallBack: unprocessed evt type: [" << 
226
  }
226
               LibUPnP::evTypeAsString(et) << "]"  << endl);
227
        break;
228
    }
227
229
228
  return UPNP_E_SUCCESS;
230
    return UPNP_E_SUCCESS;
229
}
231
}
230
232
231
// Look at the devices and get rid of those which have not been seen
233
// Look at the devices and get rid of those which have not been seen
232
// for too long. We do this when listing the top directory
234
// for too long. We do this when listing the top directory
233
void UPnPDeviceDirectory::expireDevices()
235
void UPnPDeviceDirectory::expireDevices()
234
{
236
{
235
  PLOGDEB("expireDevices:\n");
237
    LOGDEB1("discovery: expireDevices:" << endl);
236
  PTMutexLocker lock(contentDirectories.m_mutex);
238
    PTMutexLocker lock(o_pool.m_mutex);
237
  time_t now = time(0);
239
    time_t now = time(0);
238
  bool didsomething = false;
240
    bool didsomething = false;
239
241
240
  for (DirPoolIt it = contentDirectories.m_directories.begin();
242
    for (DevPoolIt it = o_pool.m_devices.begin();
241
       it != contentDirectories.m_directories.end();) {
243
         it != o_pool.m_devices.end();) {
242
      if (now - it->second.last_seen > it->second.expires) {
244
        if (now - it->second.last_seen > it->second.expires) {
243
          //LOGDEB("expireDevices: deleting " <<  it->first.c_str() << " " << 
245
            //LOGDEB("expireDevices: deleting " <<  it->first.c_str() << " " << 
244
          //   it->second.device.friendlyName.c_str() << endl);
246
            //   it->second.device.friendlyName.c_str() << endl);
245
          contentDirectories.m_directories.erase(it++);
247
            o_pool.m_devices.erase(it++);
246
          didsomething = true;
248
            didsomething = true;
247
      } else {
249
        } else {
248
          it++;
250
            it++;
249
      }
251
        }
250
  }
252
    }
251
  if (didsomething)
253
    if (didsomething)
252
      search();
254
        search();
253
}
255
}
254
256
255
// m_searchTimeout is the UPnP device search timeout, which should
257
// m_searchTimeout is the UPnP device search timeout, which should
256
// actually be called delay because it's the base of a random delay
258
// actually be called delay because it's the base of a random delay
257
// that the devices apply to avoid responding all at the same time.
259
// that the devices apply to avoid responding all at the same time.
258
// This means that you have to wait for the specified period before
260
// This means that you have to wait for the specified period before
259
// the results are complete.
261
// the results are complete.
260
UPnPDeviceDirectory::UPnPDeviceDirectory(time_t search_window)
262
UPnPDeviceDirectory::UPnPDeviceDirectory(time_t search_window)
261
  : m_ok(false), m_searchTimeout(search_window), m_lastSearch(0)
263
    : m_ok(false), m_searchTimeout(search_window), m_lastSearch(0)
262
{
264
{
263
  if (!discoveredQueue.start(1, discoExplorer, 0)) {
265
    if (!discoveredQueue.start(1, discoExplorer, 0)) {
264
      m_reason = "Discover work queue start failed";
266
        m_reason = "Discover work queue start failed";
265
      return;
267
        return;
266
  }
268
    }
267
  LibUPnP *lib = LibUPnP::getLibUPnP();
269
    LibUPnP *lib = LibUPnP::getLibUPnP();
268
  if (lib == 0) {
270
    if (lib == 0) {
269
      m_reason = "Can't get lib";
271
        m_reason = "Can't get lib";
270
      return;
272
        return;
271
  }
273
    }
272
  lib->registerHandler(UPNP_DISCOVERY_SEARCH_RESULT, cluCallBack, this);
274
    lib->registerHandler(UPNP_DISCOVERY_SEARCH_RESULT, cluCallBack, this);
273
  lib->registerHandler(UPNP_DISCOVERY_ADVERTISEMENT_ALIVE,
275
    lib->registerHandler(UPNP_DISCOVERY_ADVERTISEMENT_ALIVE,
274
                       cluCallBack, this);
276
                         cluCallBack, this);
275
  lib->registerHandler(UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE,
277
    lib->registerHandler(UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE,
276
                       cluCallBack, this);
278
                         cluCallBack, this);
277
279
278
  m_ok = search();
280
    m_ok = search();
279
}
281
}
280
282
281
bool UPnPDeviceDirectory::search()
283
bool UPnPDeviceDirectory::search()
282
{
284
{
283
  PLOGDEB("UPnPDeviceDirectory::search\n");
285
    PLOGDEB("UPnPDeviceDirectory::search\n");
284
  if (time(0) - m_lastSearch < 10)
286
    if (time(0) - m_lastSearch < 10)
285
      return true;
287
        return true;
286
288
287
  LibUPnP *lib = LibUPnP::getLibUPnP();
289
    LibUPnP *lib = LibUPnP::getLibUPnP();
288
  if (lib == 0) {
290
    if (lib == 0) {
289
      m_reason = "Can't get lib";
291
        m_reason = "Can't get lib";
290
      return false;
292
        return false;
291
  }
293
    }
292
294
293
  // We search both for device and service just in case.
295
    // We search both for device and service just in case.
294
  int code1 = UpnpSearchAsync(lib->getclh(), m_searchTimeout,
296
    int code1 = UpnpSearchAsync(lib->getclh(), m_searchTimeout,
295
                              ContentDirectorySType.c_str(), lib);
297
                                ContentDirectorySType.c_str(), lib);
296
  if (code1 != UPNP_E_SUCCESS) {
298
    if (code1 != UPNP_E_SUCCESS) {
297
      m_reason = LibUPnP::errAsString("UpnpSearchAsync", code1);
299
        m_reason = LibUPnP::errAsString("UpnpSearchAsync", code1);
298
  }
300
    }
299
  int code2 = UpnpSearchAsync(lib->getclh(), m_searchTimeout,
301
    int code2 = UpnpSearchAsync(lib->getclh(), m_searchTimeout,
300
                              MediaServerDType.c_str(), lib);
302
                                MediaServerDType.c_str(), lib);
301
  if (code2 != UPNP_E_SUCCESS) {
303
    if (code2 != UPNP_E_SUCCESS) {
302
      m_reason = LibUPnP::errAsString("UpnpSearchAsync", code2);
304
        m_reason = LibUPnP::errAsString("UpnpSearchAsync", code2);
303
  }
305
    }
304
  if (code1 != UPNP_E_SUCCESS && code2 != UPNP_E_SUCCESS)
306
    if (code1 != UPNP_E_SUCCESS && code2 != UPNP_E_SUCCESS)
305
      return false;
307
        return false;
306
  m_lastSearch = time(0);
308
    m_lastSearch = time(0);
307
  return true;
309
    return true;
308
}
310
}
309
311
310
static UPnPDeviceDirectory *theDevDir;
312
static UPnPDeviceDirectory *theDevDir;
311
UPnPDeviceDirectory *UPnPDeviceDirectory::getTheDir(time_t search_window)
313
UPnPDeviceDirectory *UPnPDeviceDirectory::getTheDir(time_t search_window)
312
{
314
{
313
  if (theDevDir == 0)
315
    if (theDevDir == 0)
314
      theDevDir = new UPnPDeviceDirectory(search_window);
316
        theDevDir = new UPnPDeviceDirectory(search_window);
315
  if (theDevDir && !theDevDir->ok())
317
    if (theDevDir && !theDevDir->ok())
316
      return 0;
318
        return 0;
317
  return theDevDir;
319
    return theDevDir;
318
}
320
}
319
321
320
void UPnPDeviceDirectory::terminate()
322
void UPnPDeviceDirectory::terminate()
321
{
323
{
322
  discoveredQueue.setTerminateAndWait();
324
    discoveredQueue.setTerminateAndWait();
323
}
325
}
324
326
325
time_t UPnPDeviceDirectory::getRemainingDelay()
327
time_t UPnPDeviceDirectory::getRemainingDelay()
326
{
328
{
327
  time_t now = time(0);
329
    time_t now = time(0);
328
  if (now - m_lastSearch >= m_searchTimeout)
330
    if (now - m_lastSearch >= m_searchTimeout)
329
      return 0;
331
        return 0;
330
  return  m_searchTimeout - (now - m_lastSearch);
332
    return  m_searchTimeout - (now - m_lastSearch);
331
}
333
}
332
334
333
bool UPnPDeviceDirectory::getDirServices(vector<ContentDirectoryService>& out)
335
bool UPnPDeviceDirectory::getDirServices(vector<ContentDirectoryService>& out)
334
{
336
{
335
  //LOGDEB("UPnPDeviceDirectory::getDirServices" << endl);
337
    //LOGDEB("UPnPDeviceDirectory::getDirServices" << endl);
336
  if (m_ok == false)
338
    if (m_ok == false)
337
      return false;
339
        return false;
338
340
339
  if (getRemainingDelay() > 0)
341
    if (getRemainingDelay() > 0)
340
      sleep(getRemainingDelay());
342
        sleep(getRemainingDelay());
341
343
342
  // Has locking, do it before our own lock
344
    // Has locking, do it before our own lock
343
  expireDevices();
345
    expireDevices();
344
346
345
  PTMutexLocker lock(contentDirectories.m_mutex);
347
    PTMutexLocker lock(o_pool.m_mutex);
346
348
347
  for (DirPoolIt dit = contentDirectories.m_directories.begin();
349
    for (DevPoolIt dit = o_pool.m_devices.begin();
348
       dit != contentDirectories.m_directories.end(); dit++) {
350
         dit != o_pool.m_devices.end(); dit++) {
349
      for (DevServIt sit = dit->second.device.services.begin();
351
        for (DevServIt sit = dit->second.device.services.begin();
350
           sit != dit->second.device.services.end(); sit++) {
352
             sit != dit->second.device.services.end(); sit++) {
351
          if (isCDService(sit->serviceType)) {
353
            if (isCDService(sit->serviceType)) {
352
              out.push_back(ContentDirectoryService(dit->second.device,
354
                out.push_back(ContentDirectoryService(dit->second.device,
353
                                                    *sit));
355
                                                      *sit));
354
          }
356
            }
355
      }
357
        }
356
  }
358
    }
357
359
358
  return true;
360
    return true;
359
}
361
}
360
362
361
// Get server by friendly name. It's a bit wasteful to copy all
363
// Get server by friendly name. It's a bit wasteful to copy all
362
// servers for this, we could directly walk the list. Otoh there isn't
364
// servers for this, we could directly walk the list. Otoh there isn't
363
// going to be millions...
365
// going to be millions...
364
bool UPnPDeviceDirectory::getServer(const string& friendlyName,
366
bool UPnPDeviceDirectory::getServer(const string& friendlyName,
365
                                  ContentDirectoryService& server)
367
                                    ContentDirectoryService& server)
366
{
368
{
367
  vector<ContentDirectoryService> ds;
369
    vector<ContentDirectoryService> ds;
368
  if (!getDirServices(ds)) {
370
    if (!getDirServices(ds)) {
369
      PLOGDEB("UPnPDeviceDirectory::getServer: no servers?\n");
371
        PLOGDEB("UPnPDeviceDirectory::getServer: no servers?\n");
370
      return false;
372
        return false;
371
  }
373
    }
372
  for (vector<ContentDirectoryService>::const_iterator it = ds.begin();
374
    for (vector<ContentDirectoryService>::const_iterator it = ds.begin();
373
       it != ds.end(); it++) {
375
         it != ds.end(); it++) {
374
      if (!friendlyName.compare(it->getFriendlyName())) {
376
        if (!friendlyName.compare(it->getFriendlyName())) {
375
          server = *it;
377
            server = *it;
376
          return true;
378
            return true;
377
      }
379
        }
378
  }
380
    }
379
  return false;
381
    return false;
380
}
382
}
381
383
382
/* Local Variables: */
383
/* mode: c++ */
384
/* c-basic-offset: 4 */
385
/* tab-width: 4 */
386
/* indent-tabs-mode: t */
387
/* End: */