diff options
Diffstat (limited to '')
-rw-r--r-- | src/neighbor/Explorer.hxx | 71 | ||||
-rw-r--r-- | src/neighbor/Glue.cxx | 111 | ||||
-rw-r--r-- | src/neighbor/Glue.hxx | 76 | ||||
-rw-r--r-- | src/neighbor/Info.hxx | 35 | ||||
-rw-r--r-- | src/neighbor/Listener.hxx | 36 | ||||
-rw-r--r-- | src/neighbor/NeighborPlugin.hxx | 40 | ||||
-rw-r--r-- | src/neighbor/Registry.cxx | 46 | ||||
-rw-r--r-- | src/neighbor/Registry.hxx | 37 | ||||
-rw-r--r-- | src/neighbor/plugins/SmbclientNeighborPlugin.cxx | 288 | ||||
-rw-r--r-- | src/neighbor/plugins/SmbclientNeighborPlugin.hxx | 27 | ||||
-rw-r--r-- | src/neighbor/plugins/UpnpNeighborPlugin.cxx | 139 | ||||
-rw-r--r-- | src/neighbor/plugins/UpnpNeighborPlugin.hxx | 27 |
12 files changed, 933 insertions, 0 deletions
diff --git a/src/neighbor/Explorer.hxx b/src/neighbor/Explorer.hxx new file mode 100644 index 000000000..84a54840c --- /dev/null +++ b/src/neighbor/Explorer.hxx @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * 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 MPD_NEIGHBOR_EXPLORER_HXX +#define MPD_NEIGHBOR_EXPLORER_HXX + +#include <forward_list> + +class Error; +class NeighborListener; +struct NeighborInfo; + +/** + * An object that explores the neighborhood for music servers. + * + * As soon as this object is opened, it will start exploring, and + * notify the #NeighborListener when it found or lost something. + * + * The implementation is supposed to be non-blocking. This can be + * implemented either using the #EventLoop instance that was passed to + * the NeighborPlugin or by moving the blocking parts in a dedicated + * thread. + */ +class NeighborExplorer { +protected: + NeighborListener &listener; + + explicit NeighborExplorer(NeighborListener &_listener) + :listener(_listener) {} + +public: + typedef std::forward_list<NeighborInfo> List; + + /** + * Free instance data. + */ + virtual ~NeighborExplorer() {} + + /** + * Start exploring the neighborhood. + */ + virtual bool Open(Error &error) = 0; + + /** + * Stop exploring. + */ + virtual void Close() = 0; + + /** + * Obtain a list of currently known neighbors. + */ + virtual List GetList() const = 0; +}; + +#endif diff --git a/src/neighbor/Glue.cxx b/src/neighbor/Glue.cxx new file mode 100644 index 000000000..fbf25cc8d --- /dev/null +++ b/src/neighbor/Glue.cxx @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * 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. + */ + +#include "config.h" +#include "Glue.hxx" +#include "Registry.hxx" +#include "Explorer.hxx" +#include "NeighborPlugin.hxx" +#include "Info.hxx" +#include "config/ConfigGlobal.hxx" +#include "config/ConfigData.hxx" +#include "config/ConfigError.hxx" +#include "util/Error.hxx" + +NeighborGlue::Explorer::~Explorer() +{ + delete explorer; +} + +NeighborGlue::~NeighborGlue() {} + +static NeighborExplorer * +CreateNeighborExplorer(EventLoop &loop, NeighborListener &listener, + const config_param ¶m, Error &error) +{ + const char *plugin_name = param.GetBlockValue("plugin"); + if (plugin_name == nullptr) { + error.Set(config_domain, + "Missing \"plugin\" configuration"); + return nullptr; + } + + const NeighborPlugin *plugin = GetNeighborPluginByName(plugin_name); + if (plugin == nullptr) { + error.Format(config_domain, "No such neighbor plugin: %s", + plugin_name); + return nullptr; + } + + return plugin->create(loop, listener, param, error); +} + +bool +NeighborGlue::Init(EventLoop &loop, NeighborListener &listener, Error &error) +{ + for (const config_param *param = config_get_param(CONF_NEIGHBORS); + param != nullptr; param = param->next) { + NeighborExplorer *explorer = + CreateNeighborExplorer(loop, listener, *param, error); + if (explorer == nullptr) { + error.FormatPrefix("Line %i: ", param->line); + return false; + } + + explorers.emplace_front(explorer); + } + + return true; +} + +bool +NeighborGlue::Open(Error &error) +{ + for (auto i = explorers.begin(), end = explorers.end(); + i != end; ++i) { + if (!i->explorer->Open(error)) { + /* roll back */ + for (auto k = explorers.begin(); k != i; ++k) + k->explorer->Close(); + return false; + } + } + + return true; +} + +void +NeighborGlue::Close() +{ + for (auto i = explorers.begin(), end = explorers.end(); i != end; ++i) + i->explorer->Close(); +} + +NeighborGlue::List +NeighborGlue::GetList() const +{ + List result; + + for (const auto &i : explorers) + result.splice_after(result.before_begin(), + i.explorer->GetList()); + + return result; +} + diff --git a/src/neighbor/Glue.hxx b/src/neighbor/Glue.hxx new file mode 100644 index 000000000..92c612d22 --- /dev/null +++ b/src/neighbor/Glue.hxx @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * 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 MPD_NEIGHBOR_ALL_HXX +#define MPD_NEIGHBOR_ALL_HXX + +#include "check.h" +#include "Compiler.h" +#include "thread/Mutex.hxx" + +#include <forward_list> + +struct config_param; +class Error; +class EventLoop; +class NeighborExplorer; +class NeighborListener; +struct NeighborInfo; + +/** + * A class that initializes and opens all configured neighbor plugins. + */ +class NeighborGlue { + struct Explorer { + NeighborExplorer *const explorer; + + Explorer(NeighborExplorer *_explorer):explorer(_explorer) {} + Explorer(const Explorer &) = delete; + ~Explorer(); + }; + + Mutex mutex; + + std::forward_list<Explorer> explorers; + +public: + typedef std::forward_list<NeighborInfo> List; + + NeighborGlue() = default; + NeighborGlue(const NeighborGlue &) = delete; + ~NeighborGlue(); + + bool IsEmpty() const { + return explorers.empty(); + } + + bool Init(EventLoop &loop, NeighborListener &listener, Error &error); + + bool Open(Error &error); + void Close(); + + /** + * Get the combined list of all neighbors from all active + * plugins. + */ + gcc_pure + List GetList() const; +}; + +#endif diff --git a/src/neighbor/Info.hxx b/src/neighbor/Info.hxx new file mode 100644 index 000000000..ac4806f14 --- /dev/null +++ b/src/neighbor/Info.hxx @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * 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 MPD_NEIGHBOR_INFO_HXX +#define MPD_NEIGHBOR_INFO_HXX + +#include <string> + +struct NeighborInfo { + std::string uri; + std::string display_name; + + template<typename U, typename DN> + NeighborInfo(U &&_uri, DN &&_display_name) + :uri(std::forward<U>(_uri)), + display_name(std::forward<DN>(_display_name)) {} +}; + +#endif diff --git a/src/neighbor/Listener.hxx b/src/neighbor/Listener.hxx new file mode 100644 index 000000000..20295f5a9 --- /dev/null +++ b/src/neighbor/Listener.hxx @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * 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 MPD_NEIGHBOR_LISTENER_HXX +#define MPD_NEIGHBOR_LISTENER_HXX + +struct NeighborInfo; +class NeighborExplorer; + +/** + * An interface that listens on events from neighbor plugins. The + * methods must be thread-safe and non-blocking. + */ +class NeighborListener { +public: + virtual void FoundNeighbor(const NeighborInfo &info) = 0; + virtual void LostNeighbor(const NeighborInfo &info) = 0; +}; + +#endif diff --git a/src/neighbor/NeighborPlugin.hxx b/src/neighbor/NeighborPlugin.hxx new file mode 100644 index 000000000..0d4ebaa7b --- /dev/null +++ b/src/neighbor/NeighborPlugin.hxx @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * 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 MPD_NEIGHBOR_PLUGIN_HXX +#define MPD_NEIGHBOR_PLUGIN_HXX + +struct config_param; +class Error; +class EventLoop; +class NeighborListener; +class NeighborExplorer; + +struct NeighborPlugin { + const char *name; + + /** + * Allocates and configures a #NeighborExplorer instance. + */ + NeighborExplorer *(*create)(EventLoop &loop, NeighborListener &listener, + const config_param ¶m, + Error &error); +}; + +#endif diff --git a/src/neighbor/Registry.cxx b/src/neighbor/Registry.cxx new file mode 100644 index 000000000..f6d1f97b3 --- /dev/null +++ b/src/neighbor/Registry.cxx @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * 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. + */ + +#include "config.h" +#include "Registry.hxx" +#include "NeighborPlugin.hxx" +#include "plugins/SmbclientNeighborPlugin.hxx" +#include "plugins/UpnpNeighborPlugin.hxx" + +#include <string.h> + +const NeighborPlugin *const neighbor_plugins[] = { +#ifdef ENABLE_SMBCLIENT + &smbclient_neighbor_plugin, +#endif +#ifdef HAVE_LIBUPNP + &upnp_neighbor_plugin, +#endif + nullptr +}; + +const NeighborPlugin * +GetNeighborPluginByName(const char *name) +{ + for (auto i = neighbor_plugins; *i != nullptr; ++i) + if (strcmp((*i)->name, name) == 0) + return *i; + + return nullptr; +} diff --git a/src/neighbor/Registry.hxx b/src/neighbor/Registry.hxx new file mode 100644 index 000000000..0b89e537d --- /dev/null +++ b/src/neighbor/Registry.hxx @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * 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 MPD_NEIGHBOR_REGISTRY_HXX +#define MPD_NEIGHBOR_REGISTRY_HXX + +#include "Compiler.h" + +struct NeighborPlugin; + +/** + * nullptr terminated list of all neighbor plugins which were enabled at + * compile time. + */ +extern const NeighborPlugin *const neighbor_plugins[]; + +gcc_pure +const NeighborPlugin * +GetNeighborPluginByName(const char *name); + +#endif diff --git a/src/neighbor/plugins/SmbclientNeighborPlugin.cxx b/src/neighbor/plugins/SmbclientNeighborPlugin.cxx new file mode 100644 index 000000000..2701b0ccd --- /dev/null +++ b/src/neighbor/plugins/SmbclientNeighborPlugin.cxx @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * 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. + */ + +#include "config.h" +#include "SmbclientNeighborPlugin.hxx" +#include "lib/smbclient/Init.hxx" +#include "lib/smbclient/Domain.hxx" +#include "lib/smbclient/Mutex.hxx" +#include "neighbor/NeighborPlugin.hxx" +#include "neighbor/Explorer.hxx" +#include "neighbor/Listener.hxx" +#include "neighbor/Info.hxx" +#include "thread/Mutex.hxx" +#include "thread/Cond.hxx" +#include "thread/Thread.hxx" +#include "thread/Name.hxx" +#include "util/Macros.hxx" +#include "util/Domain.hxx" +#include "util/Error.hxx" +#include "Log.hxx" + +#include <libsmbclient.h> + +#include <list> +#include <algorithm> + +class SmbclientNeighborExplorer final : public NeighborExplorer { + struct Server { + std::string name, comment; + + bool alive; + + Server(std::string &&_name, std::string &&_comment) + :name(std::move(_name)), comment(std::move(_comment)), + alive(true) {} + Server(const Server &) = delete; + + gcc_pure + bool operator==(const Server &other) const { + return name == other.name; + } + + gcc_pure + NeighborInfo Export() const { + return { "smb://" + name + "/", comment }; + } + }; + + Thread thread; + + mutable Mutex mutex; + Cond cond; + + List list; + + bool quit; + +public: + SmbclientNeighborExplorer(NeighborListener &_listener) + :NeighborExplorer(_listener) {} + + /* virtual methods from class NeighborExplorer */ + virtual bool Open(Error &error) override; + virtual void Close() override; + virtual List GetList() const override; + +private: + void Run(); + void ThreadFunc(); + static void ThreadFunc(void *ctx); +}; + +bool +SmbclientNeighborExplorer::Open(Error &error) +{ + quit = false; + return thread.Start(ThreadFunc, this, error); +} + +void +SmbclientNeighborExplorer::Close() +{ + mutex.lock(); + quit = true; + cond.signal(); + mutex.unlock(); + + thread.Join(); +} + +NeighborExplorer::List +SmbclientNeighborExplorer::GetList() const +{ + const ScopeLock protect(mutex); + /* + List list; + for (const auto &i : servers) + list.emplace_front(i.Export()); + */ + return list; +} + +static void +ReadServer(NeighborExplorer::List &list, const smbc_dirent &e) +{ + const std::string name(e.name, e.namelen); + const std::string comment(e.comment, e.commentlen); + + list.emplace_front("smb://" + name, name + " (" + comment + ")"); +} + +static void +ReadServers(NeighborExplorer::List &list, const char *uri); + +static void +ReadWorkgroup(NeighborExplorer::List &list, const std::string &name) +{ + std::string uri = "smb://" + name; + ReadServers(list, uri.c_str()); +} + +static void +ReadEntry(NeighborExplorer::List &list, const smbc_dirent &e) +{ + switch (e.smbc_type) { + case SMBC_WORKGROUP: + ReadWorkgroup(list, std::string(e.name, e.namelen)); + break; + + case SMBC_SERVER: + ReadServer(list, e); + break; + } +} + +static void +ReadServers(NeighborExplorer::List &list, int fd) +{ + smbc_dirent *e; + while ((e = smbc_readdir(fd)) != nullptr) + ReadEntry(list, *e); + + smbc_closedir(fd); +} + +static void +ReadServers(NeighborExplorer::List &list, const char *uri) +{ + int fd = smbc_opendir(uri); + if (fd >= 0) { + ReadServers(list, fd); + smbc_closedir(fd); + } else + FormatErrno(smbclient_domain, "smbc_opendir('%s') failed", + uri); +} + +gcc_pure +static NeighborExplorer::List +DetectServers() +{ + NeighborExplorer::List list; + const ScopeLock protect(smbclient_mutex); + ReadServers(list, "smb://"); + return list; +} + +gcc_pure +static NeighborExplorer::List::const_iterator +FindBeforeServerByURI(NeighborExplorer::List::const_iterator prev, + NeighborExplorer::List::const_iterator end, + const std::string &uri) +{ + for (auto i = std::next(prev); i != end; prev = i, i = std::next(prev)) + if (i->uri == uri) + return prev; + + return end; +} + +inline void +SmbclientNeighborExplorer::Run() +{ + List found = DetectServers(), lost; + + mutex.lock(); + + const auto found_before_begin = found.before_begin(); + const auto found_end = found.end(); + + for (auto prev = list.before_begin(), i = std::next(prev), end = list.end(); + i != end; i = std::next(prev)) { + auto f = FindBeforeServerByURI(found_before_begin, found_end, + i->uri); + if (f != found_end) { + /* still visible: remove from "found" so we + don't believe it's a new one */ + *i = std::move(*std::next(f)); + found.erase_after(f); + prev = i; + } else { + /* can't see it anymore: move to "lost" */ +#if defined(__clang__) || GCC_CHECK_VERSION(4,7) + lost.splice_after(lost.before_begin(), list, prev); +#else + /* the forward_list::splice_after() lvalue + reference overload is missing in gcc 4.6 */ + lost.emplace_front(std::move(*i)); + list.erase_after(prev); +#endif + } + } + + for (auto prev = found_before_begin, i = std::next(prev); + i != found_end; prev = i, i = std::next(prev)) + list.push_front(*i); + + mutex.unlock(); + + for (auto &i : lost) + listener.LostNeighbor(i); + + for (auto &i : found) + listener.FoundNeighbor(i); +} + +inline void +SmbclientNeighborExplorer::ThreadFunc() +{ + mutex.lock(); + + while (!quit) { + mutex.unlock(); + + Run(); + + mutex.lock(); + if (quit) + break; + + // TODO: sleep for how long? + cond.timed_wait(mutex, 10000); + } + + mutex.unlock(); +} + +void +SmbclientNeighborExplorer::ThreadFunc(void *ctx) +{ + SetThreadName("smbclient"); + + SmbclientNeighborExplorer &e = *(SmbclientNeighborExplorer *)ctx; + e.ThreadFunc(); +} + +static NeighborExplorer * +smbclient_neighbor_create(gcc_unused EventLoop &loop, + NeighborListener &listener, + gcc_unused const config_param ¶m, + gcc_unused Error &error) +{ + if (!SmbclientInit(error)) + return nullptr; + + return new SmbclientNeighborExplorer(listener); +} + +const NeighborPlugin smbclient_neighbor_plugin = { + "smbclient", + smbclient_neighbor_create, +}; diff --git a/src/neighbor/plugins/SmbclientNeighborPlugin.hxx b/src/neighbor/plugins/SmbclientNeighborPlugin.hxx new file mode 100644 index 000000000..12ec9c0cd --- /dev/null +++ b/src/neighbor/plugins/SmbclientNeighborPlugin.hxx @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * 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 MPD_NEIGHBOR_SMBCLIENT_HXX +#define MPD_NEIGHBOR_SMBCLIENT_HXX + +struct NeighborPlugin; + +extern const NeighborPlugin smbclient_neighbor_plugin; + +#endif diff --git a/src/neighbor/plugins/UpnpNeighborPlugin.cxx b/src/neighbor/plugins/UpnpNeighborPlugin.cxx new file mode 100644 index 000000000..253e4c13b --- /dev/null +++ b/src/neighbor/plugins/UpnpNeighborPlugin.cxx @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * 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. + */ + +#include "config.h" +#include "UpnpNeighborPlugin.hxx" +#include "lib/upnp/Domain.hxx" +#include "lib/upnp/ClientInit.hxx" +#include "lib/upnp/Discovery.hxx" +#include "lib/upnp/ContentDirectoryService.hxx" +#include "neighbor/NeighborPlugin.hxx" +#include "neighbor/Explorer.hxx" +#include "neighbor/Listener.hxx" +#include "neighbor/Info.hxx" +#include "Log.hxx" + +class UpnpNeighborExplorer final + : public NeighborExplorer, UPnPDiscoveryListener { + struct Server { + std::string name, comment; + + bool alive; + + Server(std::string &&_name, std::string &&_comment) + :name(std::move(_name)), comment(std::move(_comment)), + alive(true) {} + Server(const Server &) = delete; + + gcc_pure + bool operator==(const Server &other) const { + return name == other.name; + } + + gcc_pure + NeighborInfo Export() const { + return { "smb://" + name + "/", comment }; + } + }; + + UPnPDeviceDirectory *discovery; + +public: + UpnpNeighborExplorer(NeighborListener &_listener) + :NeighborExplorer(_listener) {} + + /* virtual methods from class NeighborExplorer */ + virtual bool Open(Error &error) override; + virtual void Close() override; + virtual List GetList() const override; + +private: + /* virtual methods from class UPnPDiscoveryListener */ + virtual void FoundUPnP(const ContentDirectoryService &service) override; + virtual void LostUPnP(const ContentDirectoryService &service) override; +}; + +bool +UpnpNeighborExplorer::Open(Error &error) +{ + UpnpClient_Handle handle; + if (!UpnpClientGlobalInit(handle, error)) + return false; + + discovery = new UPnPDeviceDirectory(handle, this); + if (!discovery->Start(error)) { + delete discovery; + UpnpClientGlobalFinish(); + return false; + } + + return true; +} + +void +UpnpNeighborExplorer::Close() +{ + delete discovery; + UpnpClientGlobalFinish(); +} + +NeighborExplorer::List +UpnpNeighborExplorer::GetList() const +{ + std::vector<ContentDirectoryService> tmp; + + { + Error error; + if (!discovery->getDirServices(tmp, error)) + LogError(error); + } + + List result; + for (const auto &i : tmp) + result.emplace_front(i.GetURI(), i.getFriendlyName()); + return result; +} + +void +UpnpNeighborExplorer::FoundUPnP(const ContentDirectoryService &service) +{ + const NeighborInfo n(service.GetURI(), service.getFriendlyName()); + listener.FoundNeighbor(n); +} + +void +UpnpNeighborExplorer::LostUPnP(const ContentDirectoryService &service) +{ + const NeighborInfo n(service.GetURI(), service.getFriendlyName()); + listener.LostNeighbor(n); +} + +static NeighborExplorer * +upnp_neighbor_create(gcc_unused EventLoop &loop, + NeighborListener &listener, + gcc_unused const config_param ¶m, + gcc_unused Error &error) +{ + return new UpnpNeighborExplorer(listener); +} + +const NeighborPlugin upnp_neighbor_plugin = { + "upnp", + upnp_neighbor_create, +}; diff --git a/src/neighbor/plugins/UpnpNeighborPlugin.hxx b/src/neighbor/plugins/UpnpNeighborPlugin.hxx new file mode 100644 index 000000000..78e4ccf13 --- /dev/null +++ b/src/neighbor/plugins/UpnpNeighborPlugin.hxx @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * 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 MPD_NEIGHBOR_UPNP_HXX +#define MPD_NEIGHBOR_UPNP_HXX + +struct NeighborPlugin; + +extern const NeighborPlugin upnp_neighbor_plugin; + +#endif |