aboutsummaryrefslogtreecommitdiffstats
path: root/src/neighbor
diff options
context:
space:
mode:
Diffstat (limited to 'src/neighbor')
-rw-r--r--src/neighbor/Explorer.hxx71
-rw-r--r--src/neighbor/Glue.cxx111
-rw-r--r--src/neighbor/Glue.hxx76
-rw-r--r--src/neighbor/Info.hxx35
-rw-r--r--src/neighbor/Listener.hxx36
-rw-r--r--src/neighbor/NeighborPlugin.hxx40
-rw-r--r--src/neighbor/Registry.cxx46
-rw-r--r--src/neighbor/Registry.hxx37
-rw-r--r--src/neighbor/plugins/SmbclientNeighborPlugin.cxx288
-rw-r--r--src/neighbor/plugins/SmbclientNeighborPlugin.hxx27
-rw-r--r--src/neighbor/plugins/UpnpNeighborPlugin.cxx139
-rw-r--r--src/neighbor/plugins/UpnpNeighborPlugin.hxx27
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 &param, 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 &param,
+ 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 &param,
+ 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 &param,
+ 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