2013-11-01 19:26:01 +01:00
|
|
|
/*
|
2017-01-03 20:48:59 +01:00
|
|
|
* Copyright 2003-2017 The Music Player Daemon Project
|
2013-11-01 19:26:01 +01:00
|
|
|
* http://www.musicpd.org
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef _UPNPPDISC_H_X_INCLUDED_
|
|
|
|
#define _UPNPPDISC_H_X_INCLUDED_
|
|
|
|
|
2017-11-16 11:39:11 +01:00
|
|
|
#include "Compat.hxx"
|
2014-01-26 15:04:56 +01:00
|
|
|
#include "Callback.hxx"
|
2014-01-16 12:50:59 +01:00
|
|
|
#include "Device.hxx"
|
2017-01-26 09:34:53 +01:00
|
|
|
#include "lib/curl/Init.hxx"
|
|
|
|
#include "lib/curl/Handler.hxx"
|
|
|
|
#include "lib/curl/Request.hxx"
|
2014-01-16 12:50:59 +01:00
|
|
|
#include "thread/Mutex.hxx"
|
2017-09-21 22:53:57 +02:00
|
|
|
#include "event/DeferEvent.hxx"
|
2014-08-30 00:46:52 +02:00
|
|
|
#include "Compiler.h"
|
2013-11-01 19:26:01 +01:00
|
|
|
|
2017-10-13 18:14:06 +02:00
|
|
|
#include <upnp.h>
|
2014-01-16 12:50:59 +01:00
|
|
|
|
2017-01-26 09:34:53 +01:00
|
|
|
#include <boost/intrusive/list.hpp>
|
|
|
|
|
2014-01-23 22:18:40 +01:00
|
|
|
#include <list>
|
2013-11-01 19:26:01 +01:00
|
|
|
#include <vector>
|
2014-01-16 12:50:59 +01:00
|
|
|
#include <string>
|
2016-02-06 22:26:14 +01:00
|
|
|
#include <memory>
|
2016-12-28 22:03:19 +01:00
|
|
|
#include <chrono>
|
2013-11-01 19:26:01 +01:00
|
|
|
|
2017-11-16 11:39:11 +01:00
|
|
|
#if UPNP_VERSION < 10800
|
|
|
|
#define UpnpDiscovery Upnp_Discovery
|
|
|
|
#endif
|
|
|
|
|
2013-11-01 19:26:01 +01:00
|
|
|
class ContentDirectoryService;
|
|
|
|
|
2014-01-26 13:14:15 +01:00
|
|
|
class UPnPDiscoveryListener {
|
|
|
|
public:
|
|
|
|
virtual void FoundUPnP(const ContentDirectoryService &service) = 0;
|
|
|
|
virtual void LostUPnP(const ContentDirectoryService &service) = 0;
|
|
|
|
};
|
|
|
|
|
2013-11-01 19:26:01 +01:00
|
|
|
/**
|
|
|
|
* Manage UPnP discovery and maintain a directory of active devices. Singleton.
|
|
|
|
*
|
|
|
|
* We are only interested in MediaServers with a ContentDirectory service
|
|
|
|
* for now, but this could be made more general, by removing the filtering.
|
|
|
|
*/
|
2014-01-26 15:04:56 +01:00
|
|
|
class UPnPDeviceDirectory final : UpnpCallback {
|
2014-01-16 12:50:59 +01:00
|
|
|
/**
|
|
|
|
* Descriptor for one device having a Content Directory
|
|
|
|
* service found on the network.
|
|
|
|
*/
|
|
|
|
class ContentDirectoryDescriptor {
|
|
|
|
public:
|
2014-01-23 22:18:40 +01:00
|
|
|
std::string id;
|
|
|
|
|
2014-01-16 12:50:59 +01:00
|
|
|
UPnPDevice device;
|
2014-01-18 16:26:11 +01:00
|
|
|
|
|
|
|
/**
|
2016-12-28 22:03:19 +01:00
|
|
|
* The time stamp when this device expires.
|
2014-01-18 16:26:11 +01:00
|
|
|
*/
|
2016-12-28 22:03:19 +01:00
|
|
|
std::chrono::steady_clock::time_point expires;
|
2014-01-18 13:47:12 +01:00
|
|
|
|
|
|
|
ContentDirectoryDescriptor() = default;
|
|
|
|
|
2014-01-23 22:18:40 +01:00
|
|
|
ContentDirectoryDescriptor(std::string &&_id,
|
2016-12-28 22:03:19 +01:00
|
|
|
std::chrono::steady_clock::time_point last,
|
2017-11-12 18:26:32 +01:00
|
|
|
std::chrono::steady_clock::duration exp) noexcept
|
2016-12-28 22:03:19 +01:00
|
|
|
:id(std::move(_id)),
|
|
|
|
expires(last + exp + std::chrono::seconds(20)) {}
|
2014-01-18 13:47:12 +01:00
|
|
|
|
2016-02-06 08:45:19 +01:00
|
|
|
void Parse(const std::string &url, const char *description) {
|
|
|
|
device.Parse(url, description);
|
2014-01-18 13:47:12 +01:00
|
|
|
}
|
2014-01-16 12:50:59 +01:00
|
|
|
};
|
|
|
|
|
2017-01-26 09:34:53 +01:00
|
|
|
class Downloader final
|
|
|
|
: public boost::intrusive::list_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>>,
|
2017-09-21 22:53:57 +02:00
|
|
|
CurlResponseHandler {
|
|
|
|
|
|
|
|
DeferEvent defer_start_event;
|
2017-01-26 09:34:53 +01:00
|
|
|
|
|
|
|
UPnPDeviceDirectory &parent;
|
|
|
|
|
|
|
|
std::string id;
|
|
|
|
const std::string url;
|
|
|
|
const std::chrono::steady_clock::duration expires;
|
|
|
|
|
|
|
|
CurlRequest request;
|
|
|
|
|
|
|
|
std::string data;
|
|
|
|
|
|
|
|
public:
|
|
|
|
Downloader(UPnPDeviceDirectory &_parent,
|
2017-11-25 19:46:07 +01:00
|
|
|
const UpnpDiscovery &disco);
|
2017-01-26 09:34:53 +01:00
|
|
|
|
2017-11-12 18:26:32 +01:00
|
|
|
void Start() noexcept {
|
2017-09-21 22:53:57 +02:00
|
|
|
defer_start_event.Schedule();
|
2017-08-25 11:01:30 +02:00
|
|
|
}
|
|
|
|
|
2017-11-12 18:26:32 +01:00
|
|
|
void Destroy() noexcept;
|
2017-01-26 09:34:53 +01:00
|
|
|
|
|
|
|
private:
|
2017-11-12 18:26:32 +01:00
|
|
|
void OnDeferredStart() noexcept {
|
2017-11-14 20:05:44 +01:00
|
|
|
try {
|
|
|
|
request.Start();
|
|
|
|
} catch (...) {
|
|
|
|
OnError(std::current_exception());
|
|
|
|
}
|
2017-09-21 22:53:57 +02:00
|
|
|
}
|
2017-08-25 11:01:30 +02:00
|
|
|
|
2017-01-26 09:34:53 +01:00
|
|
|
/* virtual methods from CurlResponseHandler */
|
|
|
|
void OnHeaders(unsigned status,
|
|
|
|
std::multimap<std::string, std::string> &&headers) override;
|
|
|
|
void OnData(ConstBuffer<void> data) override;
|
|
|
|
void OnEnd() override;
|
2017-11-12 17:49:58 +01:00
|
|
|
void OnError(std::exception_ptr e) noexcept override;
|
2017-01-26 09:34:53 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
CurlInit curl;
|
|
|
|
|
2014-01-26 15:22:33 +01:00
|
|
|
const UpnpClient_Handle handle;
|
2014-01-26 13:14:15 +01:00
|
|
|
UPnPDiscoveryListener *const listener;
|
2014-01-16 09:06:01 +01:00
|
|
|
|
2014-01-16 12:50:59 +01:00
|
|
|
Mutex mutex;
|
2017-01-26 09:34:53 +01:00
|
|
|
|
|
|
|
boost::intrusive::list<Downloader,
|
|
|
|
boost::intrusive::constant_time_size<false>> downloaders;
|
|
|
|
|
2014-01-23 22:18:40 +01:00
|
|
|
std::list<ContentDirectoryDescriptor> directories;
|
2014-01-16 12:50:59 +01:00
|
|
|
|
2013-11-01 19:26:01 +01:00
|
|
|
/**
|
|
|
|
* The UPnP device search timeout, which should actually be
|
|
|
|
* called delay because it's the base of a random delay that
|
|
|
|
* the devices apply to avoid responding all at the same time.
|
|
|
|
*/
|
2016-12-28 22:05:31 +01:00
|
|
|
int search_timeout = 2;
|
2013-11-01 19:26:01 +01:00
|
|
|
|
2014-01-18 16:26:11 +01:00
|
|
|
/**
|
2016-12-28 22:03:19 +01:00
|
|
|
* The time stamp of the last search.
|
2014-01-18 16:26:11 +01:00
|
|
|
*/
|
2016-12-28 22:03:19 +01:00
|
|
|
std::chrono::steady_clock::time_point last_search = std::chrono::steady_clock::time_point();
|
2013-11-01 19:26:01 +01:00
|
|
|
|
|
|
|
public:
|
2017-01-26 09:34:53 +01:00
|
|
|
UPnPDeviceDirectory(EventLoop &event_loop, UpnpClient_Handle _handle,
|
2017-11-14 20:05:44 +01:00
|
|
|
UPnPDiscoveryListener *_listener=nullptr);
|
2017-11-12 18:26:32 +01:00
|
|
|
~UPnPDeviceDirectory() noexcept;
|
2014-01-16 09:03:41 +01:00
|
|
|
|
2013-11-01 19:26:01 +01:00
|
|
|
UPnPDeviceDirectory(const UPnPDeviceDirectory &) = delete;
|
|
|
|
UPnPDeviceDirectory& operator=(const UPnPDeviceDirectory &) = delete;
|
|
|
|
|
2017-11-12 18:26:32 +01:00
|
|
|
EventLoop &GetEventLoop() noexcept;
|
2017-08-25 10:59:49 +02:00
|
|
|
|
2016-02-07 00:29:06 +01:00
|
|
|
void Start();
|
2014-01-18 16:08:30 +01:00
|
|
|
|
2013-11-01 19:26:01 +01:00
|
|
|
/** Retrieve the directory services currently seen on the network */
|
2016-02-07 00:29:06 +01:00
|
|
|
std::vector<ContentDirectoryService> GetDirectories();
|
2013-11-01 19:26:01 +01:00
|
|
|
|
|
|
|
/**
|
2014-01-18 16:18:51 +01:00
|
|
|
* Get server by friendly name.
|
2013-11-01 19:26:01 +01:00
|
|
|
*/
|
2016-02-07 00:29:06 +01:00
|
|
|
ContentDirectoryService GetServer(const char *friendly_name);
|
2013-11-01 19:26:01 +01:00
|
|
|
|
|
|
|
private:
|
2016-02-07 00:29:06 +01:00
|
|
|
void Search();
|
2013-11-01 19:26:01 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Look at the devices and get rid of those which have not
|
|
|
|
* been seen for too long. We do this when listing the top
|
|
|
|
* directory.
|
2015-02-03 23:48:11 +01:00
|
|
|
*
|
|
|
|
* Caller must lock #mutex.
|
2013-11-01 19:26:01 +01:00
|
|
|
*/
|
2016-02-07 00:29:06 +01:00
|
|
|
void ExpireDevices();
|
2014-01-16 12:50:59 +01:00
|
|
|
|
2014-01-23 22:18:40 +01:00
|
|
|
void LockAdd(ContentDirectoryDescriptor &&d);
|
2014-01-23 22:21:45 +01:00
|
|
|
void LockRemove(const std::string &id);
|
|
|
|
|
2017-11-25 19:46:07 +01:00
|
|
|
int OnAlive(const UpnpDiscovery *disco) noexcept;
|
|
|
|
int OnByeBye(const UpnpDiscovery *disco) noexcept;
|
2014-01-26 15:04:56 +01:00
|
|
|
|
|
|
|
/* virtual methods from class UpnpCallback */
|
2017-11-25 19:46:07 +01:00
|
|
|
int Invoke(Upnp_EventType et, const void *evp) noexcept override;
|
2013-11-01 19:26:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
#endif /* _UPNPPDISC_H_X_INCLUDED_ */
|