1 /* libmpdclient
2    (c) 2003-2019 The Music Player Daemon Project
3    This project's homepage is: http://www.musicpd.org
4 
5    Redistribution and use in source and binary forms, with or without
6    modification, are permitted provided that the following conditions
7    are met:
8 
9    - Redistributions of source code must retain the above copyright
10    notice, this list of conditions and the following disclaimer.
11 
12    - Redistributions in binary form must reproduce the above copyright
13    notice, this list of conditions and the following disclaimer in the
14    documentation and/or other materials provided with the distribution.
15 
16    - Neither the name of the Music Player Daemon nor the names of its
17    contributors may be used to endorse or promote products derived from
18    this software without specific prior written permission.
19 
20    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23    A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
24    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 
33 #include <mpd/connection.h>
34 #include <mpd/settings.h>
35 #include <mpd/async.h>
36 #include <mpd/parser.h>
37 #include <mpd/password.h>
38 #include <mpd/socket.h>
39 
40 #include "resolver.h"
41 #include "sync.h"
42 #include "socket.h"
43 #include "internal.h"
44 #include "iasync.h"
45 #include "config.h"
46 
47 #include <assert.h>
48 #include <stdlib.h>
49 #include <string.h>
50 
51 #define MPD_WELCOME_MESSAGE	"OK MPD "
52 
53 static bool
mpd_parse_welcome(struct mpd_connection * connection,const char * output)54 mpd_parse_welcome(struct mpd_connection *connection, const char *output)
55 {
56 	const char *tmp;
57 	char * test;
58 
59 	if (strncmp(output,MPD_WELCOME_MESSAGE,strlen(MPD_WELCOME_MESSAGE)) != 0) {
60 		mpd_error_code(&connection->error, MPD_ERROR_MALFORMED);
61 		mpd_error_message(&connection->error,
62 				  "Malformed connect message received");
63 		return false;
64 	}
65 
66 	tmp = &output[strlen(MPD_WELCOME_MESSAGE)];
67 	connection->version[0] = strtoul(tmp, &test, 10);
68 	if (test == tmp) {
69 		mpd_error_code(&connection->error, MPD_ERROR_MALFORMED);
70 		mpd_error_message(&connection->error,
71 				  "Malformed version number in connect message");
72 		return false;
73 	}
74 
75 	if (*test == '.') {
76 		connection->version[1] = strtoul(test + 1, &test, 10);
77 		if (*test == '.')
78 			connection->version[2] = strtoul(test + 1, &test, 10);
79 		else
80 			connection->version[2] = 0;
81 	} else {
82 		connection->version[1] = 0;
83 		connection->version[2] = 0;
84 	}
85 
86 	return true;
87 }
88 
89 void
mpd_connection_sync_error(struct mpd_connection * connection)90 mpd_connection_sync_error(struct mpd_connection *connection)
91 {
92 	if (mpd_async_copy_error(connection->async, &connection->error)) {
93 		/* no error noticed by async: must be a timeout in the
94 		   sync.c code */
95 		mpd_error_code(&connection->error, MPD_ERROR_TIMEOUT);
96 		mpd_error_message(&connection->error, "Timeout");
97 	}
98 }
99 
100 struct mpd_connection *
mpd_connection_new(const char * host,unsigned port,unsigned timeout_ms)101 mpd_connection_new(const char *host, unsigned port, unsigned timeout_ms)
102 {
103 	struct mpd_settings *settings =
104 		mpd_settings_new(host, port, timeout_ms, NULL, NULL);
105 	if (settings == NULL)
106 		return NULL;
107 
108 	struct mpd_connection *connection = malloc(sizeof(*connection));
109 	if (connection == NULL) {
110 		mpd_settings_free(settings);
111 		return NULL;
112 	}
113 
114 	connection->settings = settings;
115 
116 	bool success;
117 	mpd_socket_t fd;
118 	const char *line;
119 
120 	mpd_error_init(&connection->error);
121 	connection->async = NULL;
122 	connection->parser = NULL;
123 	connection->receiving = false;
124 	connection->sending_command_list = false;
125 	connection->pair_state = PAIR_STATE_NONE;
126 	connection->request = NULL;
127 
128 	if (!mpd_socket_global_init(&connection->error))
129 		return connection;
130 
131 	mpd_connection_set_timeout(connection,
132 				   mpd_settings_get_timeout_ms(settings));
133 
134 	host = mpd_settings_get_host(settings);
135 	fd = mpd_socket_connect(host, mpd_settings_get_port(settings),
136 				&connection->timeout, &connection->error);
137 	if (fd == MPD_INVALID_SOCKET) {
138 #if defined(DEFAULT_SOCKET) && defined(ENABLE_TCP)
139 		if (host == NULL || strcmp(host, DEFAULT_SOCKET) == 0) {
140 			/* special case: try the default host if the
141 			   default socket failed */
142 			mpd_settings_free(settings);
143 			settings = mpd_settings_new(DEFAULT_HOST, DEFAULT_PORT,
144 						    timeout_ms, NULL, NULL);
145 			if (settings == NULL) {
146 				mpd_error_code(&connection->error,
147 					       MPD_ERROR_OOM);
148 				return connection;
149 			}
150 			connection->settings = settings;
151 
152 			mpd_error_clear(&connection->error);
153 			fd = mpd_socket_connect(DEFAULT_HOST, DEFAULT_PORT,
154 						&connection->timeout,
155 						&connection->error);
156 		}
157 #endif
158 
159 		if (fd == MPD_INVALID_SOCKET)
160 			return connection;
161 	}
162 
163 	connection->async = mpd_async_new(fd);
164 	if (connection->async == NULL) {
165 		mpd_socket_close(fd);
166 		mpd_error_code(&connection->error, MPD_ERROR_OOM);
167 		return connection;
168 	}
169 
170 	connection->parser = mpd_parser_new();
171 	if (connection->parser == NULL) {
172 		mpd_error_code(&connection->error, MPD_ERROR_OOM);
173 		return connection;
174 	}
175 
176 	line = mpd_sync_recv_line(connection->async, &connection->timeout);
177 	if (line == NULL) {
178 		mpd_connection_sync_error(connection);
179 		return connection;
180 	}
181 
182 	success = mpd_parse_welcome(connection, line);
183 
184 	if (success) {
185 		const char *password = mpd_settings_get_password(settings);
186 		if (password != NULL)
187 			mpd_run_password(connection, password);
188 	}
189 
190 	return connection;
191 }
192 
193 struct mpd_connection *
mpd_connection_new_async(struct mpd_async * async,const char * welcome)194 mpd_connection_new_async(struct mpd_async *async, const char *welcome)
195 {
196 	struct mpd_connection *connection = malloc(sizeof(*connection));
197 
198 	assert(async != NULL);
199 	assert(welcome != NULL);
200 
201 	if (connection == NULL)
202 		return NULL;
203 
204 	mpd_error_init(&connection->error);
205 	connection->settings = NULL;
206 	connection->async = async;
207 	connection->timeout.tv_sec = 30;
208 	connection->timeout.tv_usec = 0;
209 	connection->parser = NULL;
210 	connection->receiving = false;
211 	connection->sending_command_list = false;
212 	connection->pair_state = PAIR_STATE_NONE;
213 	connection->request = NULL;
214 
215 	if (!mpd_socket_global_init(&connection->error))
216 		return connection;
217 
218 	connection->parser = mpd_parser_new();
219 	if (connection->parser == NULL) {
220 		mpd_error_code(&connection->error, MPD_ERROR_OOM);
221 		return connection;
222 	}
223 
224 	mpd_parse_welcome(connection, welcome);
225 
226 	return connection;
227 }
228 
mpd_connection_free(struct mpd_connection * connection)229 void mpd_connection_free(struct mpd_connection *connection)
230 {
231 	assert(connection->pair_state != PAIR_STATE_FLOATING);
232 
233 	if (connection->parser != NULL)
234 		mpd_parser_free(connection->parser);
235 
236 	if (connection->async != NULL)
237 		mpd_async_free(connection->async);
238 
239 	if (connection->request) free(connection->request);
240 
241 	mpd_error_deinit(&connection->error);
242 
243 	if (connection->settings != NULL)
244 		mpd_settings_free(connection->settings);
245 
246 	free(connection);
247 }
248 
249 bool
mpd_connection_set_keepalive(struct mpd_connection * connection,bool keepalive)250 mpd_connection_set_keepalive(struct mpd_connection *connection,
251 			     bool keepalive)
252 {
253 	assert(connection != NULL);
254 
255 	return mpd_async_set_keepalive(connection->async, keepalive);
256 }
257 
258 const struct mpd_settings *
mpd_connection_get_settings(const struct mpd_connection * connection)259 mpd_connection_get_settings(const struct mpd_connection *connection)
260 {
261 	assert(connection != NULL);
262 
263 	return connection->settings;
264 }
265 
266 void
mpd_connection_set_timeout(struct mpd_connection * connection,unsigned timeout_ms)267 mpd_connection_set_timeout(struct mpd_connection *connection,
268 			   unsigned timeout_ms)
269 {
270 	assert(timeout_ms > 0);
271 
272 	connection->timeout.tv_sec = timeout_ms / 1000;
273 	connection->timeout.tv_usec = timeout_ms % 1000;
274 }
275 
276 int
mpd_connection_get_fd(const struct mpd_connection * connection)277 mpd_connection_get_fd(const struct mpd_connection *connection)
278 {
279 	return mpd_async_get_fd(connection->async);
280 }
281 
282 struct mpd_async *
mpd_connection_get_async(struct mpd_connection * connection)283 mpd_connection_get_async(struct mpd_connection *connection)
284 {
285 	return connection->async;
286 }
287 
288 const unsigned *
mpd_connection_get_server_version(const struct mpd_connection * connection)289 mpd_connection_get_server_version(const struct mpd_connection *connection)
290 {
291 	return connection->version;
292 }
293 
294 int
mpd_connection_cmp_server_version(const struct mpd_connection * connection,unsigned major,unsigned minor,unsigned patch)295 mpd_connection_cmp_server_version(const struct mpd_connection *connection,
296 				  unsigned major, unsigned minor,
297 				  unsigned patch)
298 {
299 	const unsigned *v = connection->version;
300 
301 	if (v[0] > major || (v[0] == major &&
302 			     (v[1] > minor || (v[1] == minor &&
303 					       v[2] > patch))))
304 		return 1;
305 	else if (v[0] == major && v[1] == minor && v[2] == patch)
306 		return 0;
307 	else
308 		return -1;
309 }
310