static bool mpd_parse_welcome(struct mpd_connection *connection, const char *output) { const char *tmp; char * test; if (strncmp(output,MPD_WELCOME_MESSAGE,strlen(MPD_WELCOME_MESSAGE))) { mpd_error_code(&connection->error, MPD_ERROR_MALFORMED); mpd_error_message(&connection->error, "Malformed connect message received"); return false; } tmp = &output[strlen(MPD_WELCOME_MESSAGE)]; connection->version[0] = strtol(tmp, &test, 10); if (test == tmp) { mpd_error_code(&connection->error, MPD_ERROR_MALFORMED); mpd_error_message(&connection->error, "Malformed version number in connect message"); return false; } if (*test == '.') { connection->version[1] = strtol(test + 1, &test, 10); if (*test == '.') connection->version[2] = strtol(test + 1, &test, 10); else connection->version[2] = 0; } else { connection->version[1] = 0; connection->version[2] = 0; } return true; }
void mpd_error_entity(struct mpd_error_info *error) { if (errno == EINVAL) { mpd_error_code(error, MPD_ERROR_MALFORMED); mpd_error_message(error, "Malformed entity response line"); } else mpd_error_code(error, MPD_ERROR_OOM); }
bool mpd_command_list_begin(struct mpd_connection *connection, bool discrete_ok) { bool success; assert(connection != NULL); if (connection->sending_command_list) { mpd_error_code(&connection->error, MPD_ERROR_STATE); mpd_error_message(&connection->error, "already in command list mode"); return false; } success = mpd_send_command2(connection, discrete_ok ? "command_list_ok_begin" : "command_list_begin"); if (!success) return false; connection->sending_command_list = true; connection->sending_command_list_ok = discrete_ok; connection->command_list_remaining = 0; connection->discrete_finished = false; return true; }
struct mpd_playlist * mpd_recv_playlist(struct mpd_connection *connection) { struct mpd_pair *pair; struct mpd_playlist *playlist; pair = mpd_recv_pair_named(connection, "playlist"); if (pair == NULL) return NULL; playlist = mpd_playlist_begin(pair); mpd_return_pair(connection, pair); if (playlist == NULL) { mpd_error_code(&connection->error, MPD_ERROR_OOM); return NULL; } while ((pair = mpd_recv_pair(connection)) != NULL && mpd_playlist_feed(playlist, pair)) mpd_return_pair(connection, pair); if (mpd_error_is_defined(&connection->error)) { assert(pair == NULL); mpd_playlist_free(playlist); return NULL; } /* unread this pair for the next mpd_recv_playlist() call */ mpd_enqueue_pair(connection, pair); return playlist; }
bool mpd_recv_queue_change_brief(struct mpd_connection *connection, unsigned *position_r, unsigned *id_r) { struct mpd_pair *pair; pair = mpd_recv_pair_named(connection, "cpos"); if (pair == NULL) return false; *position_r = atoi(pair->value); mpd_return_pair(connection, pair); pair = mpd_recv_pair_named(connection, "Id"); if (pair == NULL) { mpd_return_pair(connection, pair); if (!mpd_error_is_defined(&connection->error)) { mpd_error_code(&connection->error, MPD_ERROR_MALFORMED); mpd_error_message(&connection->error, "No id received"); } return false; } *id_r = atoi(pair->value); mpd_return_pair(connection, pair); return !mpd_error_is_defined(&connection->error); }
bool mpd_search_commit(struct mpd_connection *connection) { bool success; assert(connection != NULL); if (mpd_error_is_defined(&connection->error)) { mpd_search_cancel(connection); return false; } if (connection->request == NULL) { mpd_error_code(&connection->error, MPD_ERROR_STATE); mpd_error_message(&connection->error, "no search in progress"); return false; } success = mpd_send_command(connection, connection->request, NULL); free(connection->request); connection->request = NULL; return success; }
struct mpd_connection * mpd_connection_new_async(struct mpd_async *async, const char *welcome) { struct mpd_connection *connection = malloc(sizeof(*connection)); assert(async != NULL); assert(welcome != NULL); if (connection == NULL) return NULL; mpd_error_init(&connection->error); connection->async = async; connection->timeout.tv_sec = 30; connection->timeout.tv_usec = 0; connection->parser = NULL; connection->receiving = false; connection->sending_command_list = false; connection->pair_state = PAIR_STATE_NONE; connection->request = NULL; if (!mpd_socket_global_init(&connection->error)) return connection; connection->parser = mpd_parser_new(); if (connection->parser == NULL) { mpd_error_code(&connection->error, MPD_ERROR_OOM); return connection; } mpd_parse_welcome(connection, welcome); return connection; }
struct mpd_stats * mpd_recv_stats(struct mpd_connection *connection) { struct mpd_stats * stats; struct mpd_pair *pair; assert(connection != NULL); if (mpd_error_is_defined(&connection->error)) /* refuse to receive a response if the connection's state is not clean */ return NULL; stats = mpd_stats_begin(); if (stats == NULL) { mpd_error_code(&connection->error, MPD_ERROR_OOM); return NULL; } /* read and parse all response lines */ while ((pair = mpd_recv_pair(connection)) != NULL) { mpd_stats_feed(stats, pair); mpd_return_pair(connection, pair); } if (mpd_error_is_defined(&connection->error)) { /* an error has occurred; roll back */ mpd_stats_free(stats); return NULL; } return stats; }
static bool mpd_search_add_constraint(struct mpd_connection *connection, mpd_unused enum mpd_operator oper, const char *name, const char *value) { size_t old_length, add_length; char *arg, *request; assert(connection != NULL); assert(name != NULL); assert(value != NULL); if (mpd_error_is_defined(&connection->error)) return false; if (!connection->request) { mpd_error_code(&connection->error, MPD_ERROR_STATE); mpd_error_message(&connection->error, "no search in progress"); return false; } old_length = strlen(connection->request); arg = mpd_sanitize_arg(value); if (arg == NULL) { mpd_error_code(&connection->error, MPD_ERROR_OOM); return false; } add_length = 1 + strlen(name) + 2 + strlen(arg) + 2; request = realloc(connection->request, old_length + add_length); if (request == NULL) { free(arg); mpd_error_code(&connection->error, MPD_ERROR_OOM); return false; } connection->request = request; snprintf(connection->request + old_length, add_length, " %s \"%s\"", name, arg); free(arg); return true; }
void mpd_connection_sync_error(struct mpd_connection *connection) { if (mpd_async_copy_error(connection->async, &connection->error)) { /* no error noticed by async: must be a timeout in the sync.c code */ mpd_error_code(&connection->error, MPD_ERROR_TIMEOUT); mpd_error_message(&connection->error, "Timeout"); } }
struct mpd_message * mpd_recv_message(struct mpd_connection *connection) { struct mpd_message *message; struct mpd_pair *pair; pair = mpd_recv_pair_named(connection, "channel"); if (pair == NULL) return NULL; message = mpd_message_begin(pair); mpd_return_pair(connection, pair); if (message == NULL) { mpd_error_code(&connection->error, MPD_ERROR_OOM); return NULL; } while ((pair = mpd_recv_pair(connection)) != NULL && mpd_message_feed(message, pair)) mpd_return_pair(connection, pair); if (mpd_error_is_defined(&connection->error)) { assert(pair == NULL); mpd_message_free(message); return NULL; } mpd_enqueue_pair(connection, pair); if (mpd_message_get_text(message) == NULL) { mpd_error_code(&connection->error, MPD_ERROR_MALFORMED); mpd_error_message(&connection->error, "No 'message' line received"); mpd_message_free(message); return NULL; } return message; }
bool mpd_count_db_songs(struct mpd_connection *connection) { assert(connection != NULL); if (mpd_error_is_defined(&connection->error)) return false; if (connection->request) { mpd_error_code(&connection->error, MPD_ERROR_STATE); mpd_error_message(&connection->error, "search already in progress"); return false; } connection->request = strdup("count"); if (connection->request == NULL) { mpd_error_code(&connection->error, MPD_ERROR_OOM); return false; } return true; }
bool mpd_search_db_tags(struct mpd_connection *connection, enum mpd_tag_type type) { const char *strtype; int len; assert(connection != NULL); if (mpd_error_is_defined(&connection->error)) return false; if (connection->request) { mpd_error_code(&connection->error, MPD_ERROR_STATE); mpd_error_message(&connection->error, "search already in progress"); return false; } strtype = mpd_tag_name(type); if (strtype == NULL) { mpd_error_code(&connection->error, MPD_ERROR_ARGUMENT); mpd_error_message(&connection->error, "invalid type specified"); return false; } len = 5+strlen(strtype)+1; connection->request = malloc(len); if (connection->request == NULL) { mpd_error_code(&connection->error, MPD_ERROR_OOM); return false; } snprintf(connection->request, len, "list %s", strtype); return true; }
static bool mpd_search_init(struct mpd_connection *connection, const char *cmd) { assert(connection != NULL); assert(cmd != NULL); if (mpd_error_is_defined(&connection->error)) return false; if (connection->request) { mpd_error_code(&connection->error, MPD_ERROR_STATE); mpd_error_message(&connection->error, "search already in progress"); return false; } connection->request = strdup(cmd); if (connection->request == NULL) { mpd_error_code(&connection->error, MPD_ERROR_OOM); return false; } return true; }
/** * Checks whether it is possible to run a command now. */ bool mpd_run_check(struct mpd_connection *connection) { assert(connection != NULL); if (mpd_error_is_defined(&connection->error)) return false; if (connection->sending_command_list) { mpd_error_code(&connection->error, MPD_ERROR_STATE); mpd_error_message(&connection->error, "Not possible in command list mode"); return false; } return true; }
bool mpd_search_add_tag_constraint(struct mpd_connection *connection, enum mpd_operator oper, enum mpd_tag_type type, const char *value) { const char *strtype; assert(connection != NULL); assert(value != NULL); strtype = mpd_tag_name(type); if (strtype == NULL) { mpd_error_code(&connection->error, MPD_ERROR_ARGUMENT); mpd_error_message(&connection->error, "invalid type specified"); return false; } return mpd_search_add_constraint(connection, oper, strtype, value); }
bool mpd_command_list_end(struct mpd_connection *connection) { bool success; assert(connection != NULL); if (!connection->sending_command_list) { mpd_error_code(&connection->error, MPD_ERROR_STATE); mpd_error_message(&connection->error, "not in command list mode"); return false; } connection->sending_command_list = false; success = mpd_send_command(connection, "command_list_end", NULL); connection->sending_command_list = true; if (!success) return false; assert(connection->receiving); return true; }
struct mpd_pair * mpd_recv_pair(struct mpd_connection *connection) { struct mpd_pair *pair; char *line; enum mpd_parser_result result; const char *msg; assert(connection != NULL); if (mpd_error_is_defined(&connection->error)) return NULL; /* check if the caller has returned the previous pair */ assert(connection->pair_state != PAIR_STATE_FLOATING); if (connection->pair_state == PAIR_STATE_NULL) { /* return the enqueued NULL pair */ connection->pair_state = PAIR_STATE_NONE; return NULL; } if (connection->pair_state == PAIR_STATE_QUEUED) { /* dequeue the pair from mpd_enqueue_pair() */ pair = &connection->pair; connection->pair_state = PAIR_STATE_FLOATING; return pair; } assert(connection->pair_state == PAIR_STATE_NONE); if (!connection->receiving || (connection->sending_command_list && connection->command_list_remaining > 0 && connection->discrete_finished)) { mpd_error_code(&connection->error, MPD_ERROR_STATE); mpd_error_message(&connection->error, "already done processing current command"); return NULL; } line = mpd_sync_recv_line(connection->async, mpd_connection_timeout(connection)); if (line == NULL) { connection->receiving = false; connection->sending_command_list = false; mpd_connection_sync_error(connection); return NULL; } result = mpd_parser_feed(connection->parser, line); switch (result) { case MPD_PARSER_MALFORMED: mpd_error_code(&connection->error, MPD_ERROR_MALFORMED); mpd_error_message(&connection->error, "Failed to parse MPD response"); connection->receiving = false; return NULL; case MPD_PARSER_SUCCESS: if (!mpd_parser_is_discrete(connection->parser)) { if (connection->sending_command_list && connection->command_list_remaining > 0) { mpd_error_code(&connection->error, MPD_ERROR_MALFORMED); mpd_error_message(&connection->error, "expected more list_OK's"); connection->command_list_remaining = 0; } connection->receiving = false; connection->sending_command_list = false; connection->discrete_finished = false; } else { if (!connection->sending_command_list || connection->command_list_remaining == 0) { mpd_error_code(&connection->error, MPD_ERROR_MALFORMED); mpd_error_message(&connection->error, "got an unexpected list_OK"); } else { connection->discrete_finished = true; --connection->command_list_remaining; } } return NULL; case MPD_PARSER_ERROR: connection->receiving = false; connection->sending_command_list = false; mpd_error_server(&connection->error, mpd_parser_get_server_error(connection->parser), mpd_parser_get_at(connection->parser)); msg = mpd_parser_get_message(connection->parser); if (msg == NULL) msg = "Unspecified MPD error"; mpd_error_message(&connection->error, msg); return NULL; case MPD_PARSER_PAIR: pair = &connection->pair; pair->name = mpd_parser_get_name(connection->parser); pair->value = mpd_parser_get_value(connection->parser); connection->pair_state = PAIR_STATE_FLOATING; return pair; } /* unreachable */ assert(false); return NULL; }
struct mpd_connection * mpd_connection_new(const char *host, unsigned port, unsigned timeout_ms) { struct mpd_connection *connection = malloc(sizeof(*connection)); bool success; int fd; const char *line; char *password = NULL; if (connection == NULL) return NULL; mpd_error_init(&connection->error); connection->async = NULL; connection->parser = NULL; connection->receiving = false; connection->sending_command_list = false; connection->pair_state = PAIR_STATE_NONE; connection->request = NULL; if (!mpd_socket_global_init(&connection->error)) return connection; if (timeout_ms == 0) /* 30s is the default */ timeout_ms = 30000; mpd_connection_set_timeout(connection, timeout_ms); host = mpd_check_host(host, &password); port = mpd_check_port(port); fd = mpd_connect(host, port, &connection->timeout, &connection->error); if (fd < 0) { free(password); return connection; } connection->async = mpd_async_new(fd); if (connection->async == NULL) { free(password); mpd_socket_close(fd); mpd_error_code(&connection->error, MPD_ERROR_OOM); return connection; } connection->parser = mpd_parser_new(); if (connection->parser == NULL) { free(password); mpd_error_code(&connection->error, MPD_ERROR_OOM); return connection; } line = mpd_sync_recv_line(connection->async, &connection->timeout); if (line == NULL) { free(password); mpd_connection_sync_error(connection); return connection; } success = mpd_parse_welcome(connection, line); if (password != NULL) { if (success) mpd_run_password(connection, password); free(password); } return connection; }