diff options
Diffstat (limited to 'src/neighbor/plugins')
-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 |
4 files changed, 481 insertions, 0 deletions
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 |