1 /*
2  * Copyright 2003-2021 The Music Player Daemon Project
3  * http://www.musicpd.org
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 #include "UpnpNeighborPlugin.hxx"
21 #include "lib/upnp/ClientInit.hxx"
22 #include "lib/upnp/Discovery.hxx"
23 #include "lib/upnp/ContentDirectoryService.hxx"
24 #include "neighbor/NeighborPlugin.hxx"
25 #include "neighbor/Explorer.hxx"
26 #include "neighbor/Listener.hxx"
27 #include "neighbor/Info.hxx"
28 #include "Log.hxx"
29 
30 class UpnpNeighborExplorer final
31 	: public NeighborExplorer, UPnPDiscoveryListener {
32 	struct Server {
33 		std::string name, comment;
34 
35 		bool alive;
36 
ServerUpnpNeighborExplorer::Server37 		Server(std::string &&_name, std::string &&_comment)
38 			:name(std::move(_name)), comment(std::move(_comment)),
39 			 alive(true) {}
40 		Server(const Server &) = delete;
41 		Server &operator=(const Server &) = delete;
42 
43 		[[gnu::pure]]
operator ==UpnpNeighborExplorer::Server44 		bool operator==(const Server &other) const noexcept {
45 			return name == other.name;
46 		}
47 
48 		[[nodiscard]] [[gnu::pure]]
ExportUpnpNeighborExplorer::Server49 		NeighborInfo Export() const noexcept {
50 			return { "smb://" + name + "/", comment };
51 		}
52 	};
53 
54 	EventLoop &event_loop;
55 
56 	UPnPDeviceDirectory *discovery;
57 
58 public:
UpnpNeighborExplorer(EventLoop & _event_loop,NeighborListener & _listener)59 	UpnpNeighborExplorer(EventLoop &_event_loop,
60 			     NeighborListener &_listener)
61 		:NeighborExplorer(_listener), event_loop(_event_loop) {}
62 
63 	/* virtual methods from class NeighborExplorer */
64 	void Open() override;
65 	void Close() noexcept override;
66 	[[nodiscard]] List GetList() const noexcept override;
67 
68 private:
69 	/* virtual methods from class UPnPDiscoveryListener */
70 	void FoundUPnP(const ContentDirectoryService &service) override;
71 	void LostUPnP(const ContentDirectoryService &service) override;
72 };
73 
74 void
Open()75 UpnpNeighborExplorer::Open()
76 {
77 	auto handle = UpnpClientGlobalInit(nullptr);
78 
79 	discovery = new UPnPDeviceDirectory(event_loop, handle, this);
80 
81 	try {
82 		discovery->Start();
83 	} catch (...) {
84 		delete discovery;
85 		UpnpClientGlobalFinish();
86 		throw;
87 	}
88 }
89 
90 void
Close()91 UpnpNeighborExplorer::Close() noexcept
92 {
93 	delete discovery;
94 	UpnpClientGlobalFinish();
95 }
96 
97 NeighborExplorer::List
GetList() const98 UpnpNeighborExplorer::GetList() const noexcept
99 {
100 	std::vector<ContentDirectoryService> tmp;
101 
102 	try {
103 		tmp = discovery->GetDirectories();
104 	} catch (...) {
105 		LogError(std::current_exception());
106 	}
107 
108 	List result;
109 	for (const auto &i : tmp)
110 		result.emplace_front(i.GetURI(), i.getFriendlyName());
111 	return result;
112 }
113 
114 void
FoundUPnP(const ContentDirectoryService & service)115 UpnpNeighborExplorer::FoundUPnP(const ContentDirectoryService &service)
116 {
117 	const NeighborInfo n(service.GetURI(), service.getFriendlyName());
118 	listener.FoundNeighbor(n);
119 }
120 
121 void
LostUPnP(const ContentDirectoryService & service)122 UpnpNeighborExplorer::LostUPnP(const ContentDirectoryService &service)
123 {
124 	const NeighborInfo n(service.GetURI(), service.getFriendlyName());
125 	listener.LostNeighbor(n);
126 }
127 
128 static std::unique_ptr<NeighborExplorer>
upnp_neighbor_create(EventLoop & event_loop,NeighborListener & listener,const ConfigBlock & block)129 upnp_neighbor_create(EventLoop &event_loop,
130 		     NeighborListener &listener,
131 		     [[maybe_unused]] const ConfigBlock &block)
132 {
133 	return std::make_unique<UpnpNeighborExplorer>(event_loop, listener);
134 }
135 
136 const NeighborPlugin upnp_neighbor_plugin = {
137 	"upnp",
138 	upnp_neighbor_create,
139 };
140