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