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
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>
51 #define MPD_WELCOME_MESSAGE "OK MPD "
54 mpd_parse_welcome(struct mpd_connection
*connection
, const char *output
)
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");
66 tmp
= &output
[strlen(MPD_WELCOME_MESSAGE
)];
67 connection
->version
[0] = strtoul(tmp
, &test
, 10);
69 mpd_error_code(&connection
->error
, MPD_ERROR_MALFORMED
);
70 mpd_error_message(&connection
->error
,
71 "Malformed version number in connect message");
76 connection
->version
[1] = strtoul(test
+ 1, &test
, 10);
78 connection
->version
[2] = strtoul(test
+ 1, &test
, 10);
80 connection
->version
[2] = 0;
82 connection
->version
[1] = 0;
83 connection
->version
[2] = 0;
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
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
)
108 struct mpd_connection
*connection
= malloc(sizeof(*connection
));
109 if (connection
== NULL
) {
110 mpd_settings_free(settings
);
114 connection
->settings
= settings
;
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
))
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
,
154 if (fd
== MPD_INVALID_SOCKET
)
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
);
165 connection
->parser
= mpd_parser_new();
166 if (connection
->parser
== NULL
) {
167 mpd_error_code(&connection
->error
, MPD_ERROR_OOM
);
171 line
= mpd_sync_recv_line(connection
->async
, &connection
->timeout
);
173 mpd_connection_sync_error(connection
);
177 success
= mpd_parse_welcome(connection
, line
);
180 const char *password
= mpd_settings_get_password(settings
);
181 if (password
!= NULL
)
182 mpd_run_password(connection
, password
);
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
)
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
))
213 connection
->parser
= mpd_parser_new();
214 if (connection
->parser
== NULL
) {
215 mpd_error_code(&connection
->error
, MPD_ERROR_OOM
);
219 mpd_parse_welcome(connection
, welcome
);
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
);
245 mpd_connection_set_keepalive(struct mpd_connection
*connection
,
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
;
262 mpd_connection_set_timeout(struct mpd_connection
*connection
,
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
);
278 mpd_connection_get_async(struct mpd_connection
*connection
)
280 return connection
->async
;
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
,
294 const unsigned *v
= connection
->version
;
296 if (v
[0] > major
|| (v
[0] == major
&&
297 (v
[1] > minor
|| (v
[1] == minor
&&
300 else if (v
[0] == major
&& v
[1] == minor
&& v
[2] == patch
)