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