output: add _get_plugin()
[libmpdclient.git] / src / connection.c
blobce0f171904eee3cb3b046949436ef97374264279
1 /* libmpdclient
2 (c) 2003-2017 The Music Player Daemon Project
3 This project's homepage is: http://www.musicpd.org
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
9 - Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
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.
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.
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.
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>
40 #include "resolver.h"
41 #include "sync.h"
42 #include "socket.h"
43 #include "internal.h"
44 #include "iasync.h"
45 #include "config.h"
47 #include <assert.h>
48 #include <stdlib.h>
49 #include <string.h>
51 #define MPD_WELCOME_MESSAGE "OK MPD "
53 static bool
54 mpd_parse_welcome(struct mpd_connection *connection, const char *output)
56 const char *tmp;
57 char * test;
59 if (strncmp(output,MPD_WELCOME_MESSAGE,strlen(MPD_WELCOME_MESSAGE))) {
60 mpd_error_code(&connection->error, MPD_ERROR_MALFORMED);
61 mpd_error_message(&connection->error,
62 "Malformed connect message received");
63 return false;
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;
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;
86 return true;
89 void
90 mpd_connection_sync_error(struct mpd_connection *connection)
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");
100 struct mpd_connection *
101 mpd_connection_new(const char *host, unsigned port, unsigned timeout_ms)
103 struct mpd_settings *settings =
104 mpd_settings_new(host, port, timeout_ms, NULL, NULL);
105 if (settings == NULL)
106 return NULL;
108 struct mpd_connection *connection = malloc(sizeof(*connection));
109 if (connection == NULL) {
110 mpd_settings_free(settings);
111 return NULL;
114 connection->settings = settings;
116 bool success;
117 mpd_socket_t fd;
118 const char *line;
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;
128 if (!mpd_socket_global_init(&connection->error))
129 return connection;
131 mpd_connection_set_timeout(connection,
132 mpd_settings_get_timeout_ms(settings));
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 connection->settings = settings;
147 mpd_error_clear(&connection->error);
148 fd = mpd_socket_connect(DEFAULT_HOST, DEFAULT_PORT,
149 &connection->timeout,
150 &connection->error);
152 #endif
154 if (fd == MPD_INVALID_SOCKET)
155 return connection;
158 connection->async = mpd_async_new(fd);
159 if (connection->async == NULL) {
160 mpd_socket_close(fd);
161 mpd_error_code(&connection->error, MPD_ERROR_OOM);
162 return connection;
165 connection->parser = mpd_parser_new();
166 if (connection->parser == NULL) {
167 mpd_error_code(&connection->error, MPD_ERROR_OOM);
168 return connection;
171 line = mpd_sync_recv_line(connection->async, &connection->timeout);
172 if (line == NULL) {
173 mpd_connection_sync_error(connection);
174 return connection;
177 success = mpd_parse_welcome(connection, line);
179 if (success) {
180 const char *password = mpd_settings_get_password(settings);
181 if (password != NULL)
182 mpd_run_password(connection, password);
185 return connection;
188 struct mpd_connection *
189 mpd_connection_new_async(struct mpd_async *async, const char *welcome)
191 struct mpd_connection *connection = malloc(sizeof(*connection));
193 assert(async != NULL);
194 assert(welcome != NULL);
196 if (connection == NULL)
197 return NULL;
199 mpd_error_init(&connection->error);
200 connection->settings = NULL;
201 connection->async = async;
202 connection->timeout.tv_sec = 30;
203 connection->timeout.tv_usec = 0;
204 connection->parser = NULL;
205 connection->receiving = false;
206 connection->sending_command_list = false;
207 connection->pair_state = PAIR_STATE_NONE;
208 connection->request = NULL;
210 if (!mpd_socket_global_init(&connection->error))
211 return connection;
213 connection->parser = mpd_parser_new();
214 if (connection->parser == NULL) {
215 mpd_error_code(&connection->error, MPD_ERROR_OOM);
216 return connection;
219 mpd_parse_welcome(connection, welcome);
221 return connection;
224 void mpd_connection_free(struct mpd_connection *connection)
226 assert(connection->pair_state != PAIR_STATE_FLOATING);
228 if (connection->parser != NULL)
229 mpd_parser_free(connection->parser);
231 if (connection->async != NULL)
232 mpd_async_free(connection->async);
234 if (connection->request) free(connection->request);
236 mpd_error_deinit(&connection->error);
238 if (connection->settings != NULL)
239 mpd_settings_free(connection->settings);
241 free(connection);
244 void
245 mpd_connection_set_keepalive(struct mpd_connection *connection,
246 bool keepalive)
248 assert(connection != NULL);
250 mpd_async_set_keepalive(connection->async, keepalive);
253 const struct mpd_settings *
254 mpd_connection_get_settings(const struct mpd_connection *connection)
256 assert(connection != NULL);
258 return connection->settings;
261 void
262 mpd_connection_set_timeout(struct mpd_connection *connection,
263 unsigned timeout_ms)
265 assert(timeout_ms > 0);
267 connection->timeout.tv_sec = timeout_ms / 1000;
268 connection->timeout.tv_usec = timeout_ms % 1000;
272 mpd_connection_get_fd(const struct mpd_connection *connection)
274 return mpd_async_get_fd(connection->async);
277 struct mpd_async *
278 mpd_connection_get_async(struct mpd_connection *connection)
280 return connection->async;
283 const unsigned *
284 mpd_connection_get_server_version(const struct mpd_connection *connection)
286 return connection->version;
290 mpd_connection_cmp_server_version(const struct mpd_connection *connection,
291 unsigned major, unsigned minor,
292 unsigned patch)
294 const unsigned *v = connection->version;
296 if (v[0] > major || (v[0] == major &&
297 (v[1] > minor || (v[1] == minor &&
298 v[2] > patch))))
299 return 1;
300 else if (v[0] == major && v[1] == minor && v[2] == patch)
301 return 0;
302 else
303 return -1;