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 #ifndef MPD_CLIENT_H
21 #define MPD_CLIENT_H
22 
23 #include "Message.hxx"
24 #include "command/CommandResult.hxx"
25 #include "command/CommandListBuilder.hxx"
26 #include "input/LastInputStream.hxx"
27 #include "tag/Mask.hxx"
28 #include "event/FullyBufferedSocket.hxx"
29 #include "event/CoarseTimerEvent.hxx"
30 
31 #include <boost/intrusive/link_mode.hpp>
32 #include <boost/intrusive/list_hook.hpp>
33 
34 #include <cstddef>
35 #include <list>
36 #include <memory>
37 #include <set>
38 #include <string>
39 
40 class SocketAddress;
41 class UniqueSocketDescriptor;
42 class EventLoop;
43 class Path;
44 struct Instance;
45 struct Partition;
46 class PlayerControl;
47 struct playlist;
48 class Database;
49 class Storage;
50 class BackgroundCommand;
51 
52 class Client final
53 	: FullyBufferedSocket,
54 	  public boost::intrusive::list_base_hook<boost::intrusive::tag<Partition>,
55 						  boost::intrusive::link_mode<boost::intrusive::normal_link>>,
56 	  public boost::intrusive::list_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>> {
57 	CoarseTimerEvent timeout_event;
58 
59 	Partition *partition;
60 
61 	unsigned permission;
62 
63 	/** the uid of the client process, or -1 if unknown */
64 	const int uid;
65 
66 	CommandListBuilder cmd_list;
67 
68 	const unsigned int num;	/* client number */
69 
70 	/** is this client waiting for an "idle" response? */
71 	bool idle_waiting = false;
72 
73 	/** idle flags pending on this client, to be sent as soon as
74 	    the client enters "idle" */
75 	unsigned idle_flags = 0;
76 
77 	/** idle flags that the client wants to receive */
78 	unsigned idle_subscriptions;
79 
80 public:
81 	// TODO: make this attribute "private"
82 	/**
83 	 * The tags this client is interested in.
84 	 */
85 	TagMask tag_mask = TagMask::All();
86 
87 	/**
88 	 * The maximum number of bytes transmitted in a binary
89 	 * response.  Can be changed with the "binarylimit" command.
90 	 */
91 	size_t binary_limit = 8192;
92 
93 	/**
94 	 * This caches the last "albumart" InputStream instance, to
95 	 * avoid repeating the search for each chunk requested by this
96 	 * client.
97 	 */
98 	LastInputStream last_album_art;
99 
100 private:
101 	static constexpr size_t MAX_SUBSCRIPTIONS = 16;
102 
103 	/**
104 	 * A list of channel names this client is subscribed to.
105 	 */
106 	std::set<std::string> subscriptions;
107 
108 	/**
109 	 * The number of subscriptions in #subscriptions.  Used to
110 	 * limit the number of subscriptions.
111 	 */
112 	unsigned num_subscriptions = 0;
113 
114 	static constexpr size_t MAX_MESSAGES = 64;
115 
116 	/**
117 	 * A list of messages this client has received.
118 	 */
119 	std::list<ClientMessage> messages;
120 
121 	/**
122 	 * The command currently running in background.  If this is
123 	 * set, then the client is occupied and will not process any
124 	 * new input.  If the connection gets closed, the
125 	 * #BackgroundCommand will be cancelled.
126 	 */
127 	std::unique_ptr<BackgroundCommand> background_command;
128 
129 public:
130 	Client(EventLoop &loop, Partition &partition,
131 	       UniqueSocketDescriptor fd, int uid,
132 	       unsigned _permission,
133 	       int num) noexcept;
134 
135 	~Client() noexcept;
136 
137 	using FullyBufferedSocket::GetEventLoop;
138 	using FullyBufferedSocket::GetOutputMaxSize;
139 
140 	[[gnu::pure]]
IsExpired() const141 	bool IsExpired() const noexcept {
142 		return !FullyBufferedSocket::IsDefined();
143 	}
144 
145 	void Close() noexcept;
146 	void SetExpired() noexcept;
147 
148 	bool Write(const void *data, size_t length) noexcept;
149 
150 	/**
151 	 * Write a null-terminated string.
152 	 */
Write(std::string_view s)153 	bool Write(std::string_view s) noexcept {
154 		return Write(s.data(), s.size());
155 	}
156 
WriteOK()157 	bool WriteOK() noexcept {
158 		return Write("OK\n");
159 	}
160 
161 	/**
162 	 * returns the uid of the client process, or a negative value
163 	 * if the uid is unknown
164 	 */
GetUID() const165 	int GetUID() const noexcept {
166 		return uid;
167 	}
168 
169 	/**
170 	 * Is this client running on the same machine, connected with
171 	 * a local (UNIX domain) socket?
172 	 */
IsLocal() const173 	bool IsLocal() const noexcept {
174 		return uid >= 0;
175 	}
176 
GetPermission() const177 	unsigned GetPermission() const noexcept {
178 		return permission;
179 	}
180 
SetPermission(unsigned _permission)181 	void SetPermission(unsigned _permission) noexcept {
182 		permission = _permission;
183 	}
184 
185 	/**
186 	 * Send "idle" response to this client.
187 	 */
188 	void IdleNotify() noexcept;
189 	void IdleAdd(unsigned flags) noexcept;
190 	bool IdleWait(unsigned flags) noexcept;
191 
192 	/**
193 	 * Called by a command handler to defer execution to a
194 	 * #BackgroundCommand.
195 	 */
196 	void SetBackgroundCommand(std::unique_ptr<BackgroundCommand> _bc) noexcept;
197 
198 	/**
199 	 * Called by the current #BackgroundCommand when it has
200 	 * finished, after sending the response.  This method then
201 	 * deletes the #BackgroundCommand.
202 	 */
203 	void OnBackgroundCommandFinished() noexcept;
204 
205 	enum class SubscribeResult {
206 		/** success */
207 		OK,
208 
209 		/** invalid channel name */
210 		INVALID,
211 
212 		/** already subscribed to this channel */
213 		ALREADY,
214 
215 		/** too many subscriptions */
216 		FULL,
217 	};
218 
219 	[[gnu::pure]]
IsSubscribed(const char * channel_name) const220 	bool IsSubscribed(const char *channel_name) const noexcept {
221 		return subscriptions.find(channel_name) != subscriptions.end();
222 	}
223 
GetSubscriptions() const224 	const auto &GetSubscriptions() const noexcept {
225 		return subscriptions;
226 	}
227 
228 	SubscribeResult Subscribe(const char *channel) noexcept;
229 	bool Unsubscribe(const char *channel) noexcept;
230 	void UnsubscribeAll() noexcept;
231 	bool PushMessage(const ClientMessage &msg) noexcept;
232 
233 	template<typename F>
ConsumeMessages(F && f)234 	void ConsumeMessages(F &&f) {
235 		while (!messages.empty()) {
236 			f(messages.front());
237 			messages.pop_front();
238 		}
239 	}
240 
241 	/**
242 	 * Is this client allowed to use the specified local file?
243 	 *
244 	 * Note that this function is vulnerable to timing/symlink attacks.
245 	 * We cannot fix this as long as there are plugins that open a file by
246 	 * its name, and not by file descriptor / callbacks.
247 	 *
248 	 * Throws #std::runtime_error on error.
249 	 *
250 	 * @param path_fs the absolute path name in filesystem encoding
251 	 */
252 	void AllowFile(Path path_fs) const;
253 
GetPartition() const254 	Partition &GetPartition() const noexcept {
255 		return *partition;
256 	}
257 
258 	void SetPartition(Partition &new_partition) noexcept;
259 
260 	[[gnu::pure]]
261 	Instance &GetInstance() const noexcept;
262 
263 	[[gnu::pure]]
264 	playlist &GetPlaylist() const noexcept;
265 
266 	[[gnu::pure]]
267 	PlayerControl &GetPlayerControl() const noexcept;
268 
269 	/**
270 	 * Wrapper for Instance::GetDatabase().
271 	 */
272 	[[gnu::pure]]
273 	const Database *GetDatabase() const noexcept;
274 
275 	/**
276 	 * Wrapper for Instance::GetDatabaseOrThrow().
277 	 */
278 	const Database &GetDatabaseOrThrow() const;
279 
280 	[[gnu::pure]]
281 	const Storage *GetStorage() const noexcept;
282 
283 private:
284 	CommandResult ProcessCommandList(bool list_ok,
285 					 std::list<std::string> &&list) noexcept;
286 
287 	CommandResult ProcessLine(char *line) noexcept;
288 
289 	/* virtual methods from class BufferedSocket */
290 	InputResult OnSocketInput(void *data, size_t length) noexcept override;
291 	void OnSocketError(std::exception_ptr ep) noexcept override;
292 	void OnSocketClosed() noexcept override;
293 
294 	/* callback for TimerEvent */
295 	void OnTimeout() noexcept;
296 };
297 
298 void
299 client_new(EventLoop &loop, Partition &partition,
300 	   UniqueSocketDescriptor fd, SocketAddress address, int uid,
301 	   unsigned permission) noexcept;
302 
303 #endif
304