[mpd,db] MPD protocol fixes to handling of idle/noidle command and command list.

Command handling:
1. Changed mpd_read_cb() to delegate to mpd_process_line() for each received
command line.
2. mpd_process_line() handles idle state and command list state and delegates
to mpd_process_command() to handle each command line.
If the command was successful it sends OK to the client according the the
command list state.
Error responses are sent by mpd_process_command().
3. mpd_process_command() parses the args and delegates to the individual
command handler.

mpd_input_filter:
1. Removed handling of command lists. They are handled by mpd_process_line().
2. Return BEV_OK if there's at least one complete line of input even if there's
more data in the input buffer.

Idle/noidle:
1. Changed mpd_command_idle() to never write OK to the output buffer.
Instead it is the responsibility of the caller to decide on the response.

2. Removed mpd_command_noidle() instead it is handled in mpd_process_line().
If the client is not in idle state noidle is ignored (no response sent)
If the client is in idle state then it changes idle state to false and sends
OK as the response to the idle command.

Command lists:
1. Added command list state to the client context so commands in the list are
buffered and only executed after receiving command_list_end.

Connection state:
1. Added is_closing flag in the client context to ignore messages received
after freeing the events buffer in intent to close the client connection.

Command arguments parsing:
1. Updated COMMAND_ARGV_MAX to 70 to match current MPD.
2. Changed mpd_pars_range_arg to handle open-ended range.

Command pause:
1. pause is ignored in stopped state instead returning error.

Command move:
1. Changed mpd_command_move() to support moving a range.
2. Added db_queue_move_bypos_range() to support moving a range.

Command password:
1. Password authentication flag set in handler mpd_command_password() instead
of in command processor.

Config:
1. Added config value: "max_command_list_size".
   The maximum allowed size of buffered commands in command list.
This commit is contained in:
gd 2023-05-17 19:47:25 +03:00 committed by ejurgensen
parent e9485d34ae
commit e1628ff1a9
5 changed files with 624 additions and 271 deletions

View File

@ -441,6 +441,11 @@ mpd {
# Whether to emit an output with plugin type "httpd" to tell clients
# that a stream is available for local playback.
# enable_httpd_plugin = false
# The maximum size of a command list in KB.
# It is the sum of lengths of all the command lines between command list begin and end.
# Default is 2048 (2 MiB).
# max_command_list_size = KBYTES
}
# SQLite configuration (allows to modify the operation of the SQLite databases)

View File

@ -239,6 +239,7 @@ static cfg_opt_t sec_mpd[] =
CFG_INT("port", 6600, CFGF_NONE),
CFG_INT("http_port", 0, CFGF_NONE),
CFG_BOOL("enable_httpd_plugin", cfg_false, CFGF_NONE),
CFG_INT("max_command_list_size", 2048, CFGF_NONE),
CFG_BOOL("clear_queue_on_stop_disable", cfg_false, CFGF_NODEFAULT | CFGF_DEPRECATED),
CFG_BOOL("allow_modifying_stored_playlists", cfg_false, CFGF_NODEFAULT | CFGF_DEPRECATED),
CFG_STR("default_playlist_directory", NULL, CFGF_NODEFAULT | CFGF_DEPRECATED),

View File

@ -6002,6 +6002,65 @@ db_queue_move_bypos(int pos_from, int pos_to)
return ret;
}
int
db_queue_move_bypos_range(int range_begin, int range_end, int pos_to)
{
int queue_version;
char *query;
int ret;
int changes = 0;
queue_version = queue_transaction_begin();
int count = range_end - range_begin;
int update_begin = MIN(range_begin, pos_to);
int update_end = MAX(range_begin + count, pos_to + count);
int cut_off, offset_up, offset_down;
if (range_begin < pos_to) {
cut_off = range_begin + count;
offset_up = pos_to - range_begin;
offset_down = count;
} else {
cut_off = range_begin;
offset_up = count;
offset_down = range_begin - pos_to;
}
DPRINTF(E_DBG, L_DB, "db_queue_move_bypos_range: from = %d, to = %d,"
" count = %d, cut_off = %d, offset_up = %d, offset_down = %d,"
" begin = %d, end = %d\n",
range_begin, pos_to, count, cut_off, offset_up, offset_down, update_begin, update_end);
query = "UPDATE queue SET pos ="
" CASE"
" WHEN pos < :cut_off THEN pos + :offset_up"
" ELSE pos - :offset_down"
" END,"
" queue_version = :queue_version"
" WHERE"
" pos >= :update_begin AND pos < :update_end;";
sqlite3_stmt *stmt;
if (SQLITE_OK != (ret = sqlite3_prepare_v2(hdl, query, -1, &stmt, NULL))) goto end_transaction;
if (SQLITE_OK != (ret = sqlite3_bind_int(stmt, 1, cut_off))) goto end_transaction;
if (SQLITE_OK != (ret = sqlite3_bind_int(stmt, 2, offset_up))) goto end_transaction;
if (SQLITE_OK != (ret = sqlite3_bind_int(stmt, 3, offset_down))) goto end_transaction;
if (SQLITE_OK != (ret = sqlite3_bind_int(stmt, 4, queue_version))) goto end_transaction;
if (SQLITE_OK != (ret = sqlite3_bind_int(stmt, 5, update_begin))) goto end_transaction;
if (SQLITE_OK != (ret = sqlite3_bind_int(stmt, 6, update_end))) goto end_transaction;
changes = db_statement_run(stmt, 0);
end_transaction:
DPRINTF(E_LOG, L_DB, "db_queue_move_bypos_range: changes = %d, res = %d: %s\n",
changes, ret, sqlite3_errstr(ret));
queue_transaction_end(ret, queue_version);
return ret == SQLITE_OK && changes != -1 ? 0 : -1;
}
/*
* Moves the queue item at the given position to the given target position. The positions
* are relavtive to the given base item (item id).

View File

@ -955,6 +955,9 @@ db_queue_move_byitemid(uint32_t item_id, int pos_to, char shuffle);
int
db_queue_move_bypos(int pos_from, int pos_to);
int
db_queue_move_bypos_range(int range_begin, int range_end, int pos_to);
int
db_queue_move_byposrelativetoitem(uint32_t from_pos, uint32_t to_offset, uint32_t item_id, char shuffle);

827
src/mpd.c

File diff suppressed because it is too large Load Diff