From 70b87f2eb620bdf1d7e07141914feb2316794fb3 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 3 Jan 2013 10:33:04 +0100 Subject: client: convert to C++ --- src/AllCommands.cxx | 10 +- src/AllCommands.h | 34 ------ src/AllCommands.hxx | 34 ++++++ src/Client.cxx | 41 +++++++ src/Client.hxx | 82 +++++++++++++ src/ClientEvent.cxx | 109 +++++++++++++++++ src/ClientExpire.cxx | 90 ++++++++++++++ src/ClientFile.cxx | 70 +++++++++++ src/ClientFile.hxx | 43 +++++++ src/ClientGlobal.cxx | 76 ++++++++++++ src/ClientIdle.cxx | 99 ++++++++++++++++ src/ClientIdle.hxx | 43 +++++++ src/ClientInternal.hxx | 177 ++++++++++++++++++++++++++++ src/ClientList.cxx | 69 +++++++++++ src/ClientMessage.cxx | 96 +++++++++++++++ src/ClientMessage.hxx | 73 ++++++++++++ src/ClientNew.cxx | 4 +- src/ClientProcess.cxx | 148 +++++++++++++++++++++++ src/ClientRead.cxx | 112 ++++++++++++++++++ src/ClientSubscribe.cxx | 126 ++++++++++++++++++++ src/ClientSubscribe.hxx | 59 ++++++++++ src/ClientWrite.cxx | 284 +++++++++++++++++++++++++++++++++++++++++++++ src/CommandError.cxx | 5 +- src/DatabaseCommands.cxx | 9 +- src/DatabasePrint.cxx | 4 +- src/DecoderPrint.cxx | 5 +- src/Listen.cxx | 2 +- src/Main.cxx | 6 +- src/MessageCommands.cxx | 13 +-- src/OtherCommands.cxx | 12 +- src/OutputCommands.cxx | 4 +- src/OutputPrint.cxx | 2 +- src/PlayerCommands.cxx | 9 +- src/PlaylistCommands.cxx | 6 +- src/PlaylistPrint.cxx | 2 +- src/QueueCommands.cxx | 10 +- src/QueuePrint.cxx | 2 +- src/SongPrint.cxx | 2 +- src/Stats.cxx | 5 +- src/StickerCommands.cxx | 5 +- src/StickerPrint.cxx | 5 +- src/TagPrint.cxx | 5 +- src/TimePrint.cxx | 5 +- src/client.c | 41 ------- src/client.h | 82 ------------- src/client_event.c | 109 ----------------- src/client_expire.c | 90 -------------- src/client_file.c | 70 ----------- src/client_file.h | 43 ------- src/client_global.c | 73 ------------ src/client_idle.c | 96 --------------- src/client_idle.h | 45 ------- src/client_internal.h | 177 ---------------------------- src/client_list.c | 69 ----------- src/client_message.c | 96 --------------- src/client_message.h | 73 ------------ src/client_process.c | 148 ----------------------- src/client_read.c | 113 ------------------ src/client_subscribe.c | 123 -------------------- src/client_subscribe.h | 61 ---------- src/client_write.c | 284 --------------------------------------------- src/ls.cxx | 3 +- src/protocol/ArgParser.cxx | 191 ++++++++++++++++++++++++++++++ src/protocol/ArgParser.hxx | 49 ++++++++ src/protocol/Result.cxx | 57 +++++++++ src/protocol/Result.hxx | 43 +++++++ src/protocol/argparser.c | 191 ------------------------------ src/protocol/argparser.h | 49 -------- src/protocol/result.c | 57 --------- src/protocol/result.h | 43 ------- 70 files changed, 2223 insertions(+), 2250 deletions(-) delete mode 100644 src/AllCommands.h create mode 100644 src/AllCommands.hxx create mode 100644 src/Client.cxx create mode 100644 src/Client.hxx create mode 100644 src/ClientEvent.cxx create mode 100644 src/ClientExpire.cxx create mode 100644 src/ClientFile.cxx create mode 100644 src/ClientFile.hxx create mode 100644 src/ClientGlobal.cxx create mode 100644 src/ClientIdle.cxx create mode 100644 src/ClientIdle.hxx create mode 100644 src/ClientInternal.hxx create mode 100644 src/ClientList.cxx create mode 100644 src/ClientMessage.cxx create mode 100644 src/ClientMessage.hxx create mode 100644 src/ClientProcess.cxx create mode 100644 src/ClientRead.cxx create mode 100644 src/ClientSubscribe.cxx create mode 100644 src/ClientSubscribe.hxx create mode 100644 src/ClientWrite.cxx delete mode 100644 src/client.c delete mode 100644 src/client.h delete mode 100644 src/client_event.c delete mode 100644 src/client_expire.c delete mode 100644 src/client_file.c delete mode 100644 src/client_file.h delete mode 100644 src/client_global.c delete mode 100644 src/client_idle.c delete mode 100644 src/client_idle.h delete mode 100644 src/client_internal.h delete mode 100644 src/client_list.c delete mode 100644 src/client_message.c delete mode 100644 src/client_message.h delete mode 100644 src/client_process.c delete mode 100644 src/client_read.c delete mode 100644 src/client_subscribe.c delete mode 100644 src/client_subscribe.h delete mode 100644 src/client_write.c create mode 100644 src/protocol/ArgParser.cxx create mode 100644 src/protocol/ArgParser.hxx create mode 100644 src/protocol/Result.cxx create mode 100644 src/protocol/Result.hxx delete mode 100644 src/protocol/argparser.c delete mode 100644 src/protocol/argparser.h delete mode 100644 src/protocol/result.c delete mode 100644 src/protocol/result.h (limited to 'src') diff --git a/src/AllCommands.cxx b/src/AllCommands.cxx index 6017f9859..d7ea8d60c 100644 --- a/src/AllCommands.cxx +++ b/src/AllCommands.cxx @@ -18,11 +18,7 @@ */ #include "config.h" - -extern "C" { -#include "AllCommands.h" -} - +#include "AllCommands.hxx" #include "command.h" #include "QueueCommands.hxx" #include "PlayerCommands.hxx" @@ -33,11 +29,11 @@ extern "C" { #include "OtherCommands.hxx" #include "Permission.hxx" #include "tag.h" +#include "protocol/Result.hxx" +#include "Client.hxx" extern "C" { -#include "protocol/result.h" #include "tokenizer.h" -#include "client.h" } #ifdef ENABLE_SQLITE diff --git a/src/AllCommands.h b/src/AllCommands.h deleted file mode 100644 index 8325094f5..000000000 --- a/src/AllCommands.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2003-2012 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_ALL_COMMANDS_H -#define MPD_ALL_COMMANDS_H - -#include "command.h" - -struct client; - -void command_init(void); - -void command_finish(void); - -enum command_return -command_process(struct client *client, unsigned num, char *line); - -#endif diff --git a/src/AllCommands.hxx b/src/AllCommands.hxx new file mode 100644 index 000000000..9a0b45568 --- /dev/null +++ b/src/AllCommands.hxx @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_ALL_COMMANDS_HXX +#define MPD_ALL_COMMANDS_HXX + +#include "command.h" + +struct client; + +void command_init(void); + +void command_finish(void); + +enum command_return +command_process(struct client *client, unsigned num, char *line); + +#endif diff --git a/src/Client.cxx b/src/Client.cxx new file mode 100644 index 000000000..5214eabba --- /dev/null +++ b/src/Client.cxx @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "ClientInternal.hxx" + +bool client_is_expired(const struct client *client) +{ + return client->channel == NULL; +} + +int client_get_uid(const struct client *client) +{ + return client->uid; +} + +unsigned client_get_permission(const struct client *client) +{ + return client->permission; +} + +void client_set_permission(struct client *client, unsigned permission) +{ + client->permission = permission; +} diff --git a/src/Client.hxx b/src/Client.hxx new file mode 100644 index 000000000..51ad1eb2a --- /dev/null +++ b/src/Client.hxx @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2003-2011 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_CLIENT_H +#define MPD_CLIENT_H + +#include "gcc.h" + +#include +#include +#include + +struct client; +struct sockaddr; +struct player_control; + +void client_manager_init(void); +void client_manager_deinit(void); + +void client_new(struct player_control *player_control, + int fd, const struct sockaddr *sa, size_t sa_length, int uid); + +gcc_pure +bool client_is_expired(const struct client *client); + +/** + * returns the uid of the client process, or a negative value if the + * uid is unknown + */ +gcc_pure +int client_get_uid(const struct client *client); + +/** + * Is this client running on the same machine, connected with a local + * (UNIX domain) socket? + */ +gcc_pure +static inline bool +client_is_local(const struct client *client) +{ + return client_get_uid(client) > 0; +} + +gcc_pure +unsigned client_get_permission(const struct client *client); + +void client_set_permission(struct client *client, unsigned permission); + +/** + * Write a C string to the client. + */ +void client_puts(struct client *client, const char *s); + +/** + * Write a printf-like formatted string to the client. + */ +void client_vprintf(struct client *client, const char *fmt, va_list args); + +/** + * Write a printf-like formatted string to the client. + */ +gcc_fprintf +void +client_printf(struct client *client, const char *fmt, ...); + +#endif diff --git a/src/ClientEvent.cxx b/src/ClientEvent.cxx new file mode 100644 index 000000000..0276ea746 --- /dev/null +++ b/src/ClientEvent.cxx @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "ClientInternal.hxx" +#include "Main.hxx" + +#include + +static gboolean +client_out_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition, + gpointer data) +{ + struct client *client = (struct client *)data; + + assert(!client_is_expired(client)); + + if (condition != G_IO_OUT) { + client_set_expired(client); + return false; + } + + client_write_deferred(client); + + if (client_is_expired(client)) { + client_close(client); + return false; + } + + g_timer_start(client->last_activity); + + if (g_queue_is_empty(client->deferred_send)) { + /* done sending deferred buffers exist: schedule + read */ + client->source_id = g_io_add_watch(client->channel, + GIOCondition(G_IO_IN|G_IO_ERR|G_IO_HUP), + client_in_event, client); + return false; + } + + /* write more */ + return true; +} + +gboolean +client_in_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition, + gpointer data) +{ + struct client *client = (struct client *)data; + enum command_return ret; + + assert(!client_is_expired(client)); + + if (condition != G_IO_IN) { + client_set_expired(client); + return false; + } + + g_timer_start(client->last_activity); + + ret = client_read(client); + switch (ret) { + case COMMAND_RETURN_OK: + case COMMAND_RETURN_IDLE: + case COMMAND_RETURN_ERROR: + break; + + case COMMAND_RETURN_KILL: + client_close(client); + g_main_loop_quit(main_loop); + return false; + + case COMMAND_RETURN_CLOSE: + client_close(client); + return false; + } + + if (client_is_expired(client)) { + client_close(client); + return false; + } + + if (!g_queue_is_empty(client->deferred_send)) { + /* deferred buffers exist: schedule write */ + client->source_id = g_io_add_watch(client->channel, + GIOCondition(G_IO_OUT|G_IO_ERR|G_IO_HUP), + client_out_event, client); + return false; + } + + /* read more */ + return true; +} diff --git a/src/ClientExpire.cxx b/src/ClientExpire.cxx new file mode 100644 index 000000000..b02e2c8bb --- /dev/null +++ b/src/ClientExpire.cxx @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "ClientInternal.hxx" + +static guint expire_source_id; + +void +client_set_expired(struct client *client) +{ + if (!client_is_expired(client)) + client_schedule_expire(); + + if (client->source_id != 0) { + g_source_remove(client->source_id); + client->source_id = 0; + } + + if (client->channel != NULL) { + g_io_channel_unref(client->channel); + client->channel = NULL; + } +} + +static void +client_check_expired_callback(gpointer data, G_GNUC_UNUSED gpointer user_data) +{ + struct client *client = (struct client *)data; + + if (client_is_expired(client)) { + g_debug("[%u] expired", client->num); + client_close(client); + } else if (!client->idle_waiting && /* idle clients + never expire */ + (int)g_timer_elapsed(client->last_activity, NULL) > + client_timeout) { + g_debug("[%u] timeout", client->num); + client_close(client); + } +} + +static void +client_manager_expire(void) +{ + client_list_foreach(client_check_expired_callback, NULL); +} + +/** + * An idle event which calls client_manager_expire(). + */ +static gboolean +client_manager_expire_event(G_GNUC_UNUSED gpointer data) +{ + expire_source_id = 0; + client_manager_expire(); + return false; +} + +void +client_schedule_expire(void) +{ + if (expire_source_id == 0) + /* delayed deletion */ + expire_source_id = g_idle_add(client_manager_expire_event, + NULL); +} + +void +client_deinit_expire(void) +{ + if (expire_source_id != 0) + g_source_remove(expire_source_id); +} diff --git a/src/ClientFile.cxx b/src/ClientFile.cxx new file mode 100644 index 000000000..d06b1a03c --- /dev/null +++ b/src/ClientFile.cxx @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "ClientFile.hxx" +#include "Client.hxx" +#include "ack.h" +#include "io_error.h" + +#include +#include +#include +#include + +bool +client_allow_file(const struct client *client, const char *path_fs, + GError **error_r) +{ +#ifdef WIN32 + (void)client; + (void)path_fs; + + g_set_error(error_r, ack_quark(), ACK_ERROR_PERMISSION, + "Access denied"); + return false; +#else + const int uid = client_get_uid(client); + if (uid >= 0 && (uid_t)uid == geteuid()) + /* always allow access if user runs his own MPD + instance */ + return true; + + if (uid <= 0) { + /* unauthenticated client */ + g_set_error(error_r, ack_quark(), ACK_ERROR_PERMISSION, + "Access denied"); + return false; + } + + struct stat st; + if (stat(path_fs, &st) < 0) { + set_error_errno(error_r); + return false; + } + + if (st.st_uid != (uid_t)uid && (st.st_mode & 0444) != 0444) { + /* client is not owner */ + g_set_error(error_r, ack_quark(), ACK_ERROR_PERMISSION, + "Access denied"); + return false; + } + + return true; +#endif +} diff --git a/src/ClientFile.hxx b/src/ClientFile.hxx new file mode 100644 index 000000000..a3d0ed484 --- /dev/null +++ b/src/ClientFile.hxx @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_CLIENT_FILE_HXX +#define MPD_CLIENT_FILE_HXX + +#include "gerror.h" + +#include + +struct client; + +/** + * Is this client allowed to use the specified local file? + * + * Note that this function is vulnerable to timing/symlink attacks. + * We cannot fix this as long as there are plugins that open a file by + * its name, and not by file descriptor / callbacks. + * + * @param path_fs the absolute path name in filesystem encoding + * @return true if access is allowed + */ +bool +client_allow_file(const struct client *client, const char *path_fs, + GError **error_r); + +#endif diff --git a/src/ClientGlobal.cxx b/src/ClientGlobal.cxx new file mode 100644 index 000000000..e51eeb949 --- /dev/null +++ b/src/ClientGlobal.cxx @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "ClientInternal.hxx" + +extern "C" { +#include "conf.h" +} + +#include + +#define CLIENT_TIMEOUT_DEFAULT (60) +#define CLIENT_MAX_CONNECTIONS_DEFAULT (10) +#define CLIENT_MAX_COMMAND_LIST_DEFAULT (2048*1024) +#define CLIENT_MAX_OUTPUT_BUFFER_SIZE_DEFAULT (8192*1024) + +/* set this to zero to indicate we have no possible clients */ +unsigned int client_max_connections; +int client_timeout; +size_t client_max_command_list_size; +size_t client_max_output_buffer_size; + +void client_manager_init(void) +{ + client_timeout = config_get_positive(CONF_CONN_TIMEOUT, + CLIENT_TIMEOUT_DEFAULT); + client_max_connections = + config_get_positive(CONF_MAX_CONN, + CLIENT_MAX_CONNECTIONS_DEFAULT); + client_max_command_list_size = + config_get_positive(CONF_MAX_COMMAND_LIST_SIZE, + CLIENT_MAX_COMMAND_LIST_DEFAULT / 1024) + * 1024; + + client_max_output_buffer_size = + config_get_positive(CONF_MAX_OUTPUT_BUFFER_SIZE, + CLIENT_MAX_OUTPUT_BUFFER_SIZE_DEFAULT / 1024) + * 1024; +} + +static void client_close_all(void) +{ + while (!client_list_is_empty()) { + struct client *client = client_list_get_first(); + + client_close(client); + } + + assert(client_list_is_empty()); +} + +void client_manager_deinit(void) +{ + client_close_all(); + + client_max_connections = 0; + + client_deinit_expire(); +} diff --git a/src/ClientIdle.cxx b/src/ClientIdle.cxx new file mode 100644 index 000000000..7812c544c --- /dev/null +++ b/src/ClientIdle.cxx @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "ClientIdle.hxx" +#include "ClientInternal.hxx" + +extern "C" { +#include "idle.h" +} + +#include + +/** + * Send "idle" response to this client. + */ +static void +client_idle_notify(struct client *client) +{ + unsigned flags, i; + const char *const* idle_names; + + assert(client->idle_waiting); + assert(client->idle_flags != 0); + + flags = client->idle_flags; + client->idle_flags = 0; + client->idle_waiting = false; + + idle_names = idle_get_names(); + for (i = 0; idle_names[i]; ++i) { + if (flags & (1 << i) & client->idle_subscriptions) + client_printf(client, "changed: %s\n", + idle_names[i]); + } + + client_puts(client, "OK\n"); + g_timer_start(client->last_activity); +} + +void +client_idle_add(struct client *client, unsigned flags) +{ + if (client_is_expired(client)) + return; + + client->idle_flags |= flags; + if (client->idle_waiting + && (client->idle_flags & client->idle_subscriptions)) { + client_idle_notify(client); + client_write_output(client); + } +} + +static void +client_idle_callback(gpointer data, gpointer user_data) +{ + struct client *client = (struct client *)data; + unsigned flags = GPOINTER_TO_UINT(user_data); + + client_idle_add(client, flags); +} + +void client_manager_idle_add(unsigned flags) +{ + assert(flags != 0); + + client_list_foreach(client_idle_callback, GUINT_TO_POINTER(flags)); +} + +bool client_idle_wait(struct client *client, unsigned flags) +{ + assert(!client->idle_waiting); + + client->idle_waiting = true; + client->idle_subscriptions = flags; + + if (client->idle_flags & client->idle_subscriptions) { + client_idle_notify(client); + return true; + } else + return false; +} diff --git a/src/ClientIdle.hxx b/src/ClientIdle.hxx new file mode 100644 index 000000000..8c335b1e3 --- /dev/null +++ b/src/ClientIdle.hxx @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_CLIENT_IDLE_HXX +#define MPD_CLIENT_IDLE_HXX + +struct client; + +void +client_idle_add(struct client *client, unsigned flags); + +/** + * Adds the specified idle flags to all clients and immediately sends + * notifications to all waiting clients. + */ +void +client_manager_idle_add(unsigned flags); + +/** + * Checks whether the client has pending idle flags. If yes, they are + * sent immediately and "true" is returned". If no, it puts the + * client into waiting mode and returns false. + */ +bool +client_idle_wait(struct client *client, unsigned flags); + +#endif diff --git a/src/ClientInternal.hxx b/src/ClientInternal.hxx new file mode 100644 index 000000000..4252b5349 --- /dev/null +++ b/src/ClientInternal.hxx @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_CLIENT_INTERNAL_HXX +#define MPD_CLIENT_INTERNAL_HXX + +#include "Client.hxx" +#include "ClientMessage.hxx" +#include "command.h" + +#include + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "client" + +enum { + CLIENT_MAX_SUBSCRIPTIONS = 16, + CLIENT_MAX_MESSAGES = 64, +}; + +struct deferred_buffer { + size_t size; + char data[sizeof(long)]; +}; + +struct client { + struct player_control *player_control; + + GIOChannel *channel; + guint source_id; + + /** the buffer for reading lines from the #channel */ + struct fifo_buffer *input; + + unsigned permission; + + /** the uid of the client process, or -1 if unknown */ + int uid; + + /** + * How long since the last activity from this client? + */ + GTimer *last_activity; + + GSList *cmd_list; /* for when in list mode */ + int cmd_list_OK; /* print OK after each command execution */ + size_t cmd_list_size; /* mem cmd_list consumes */ + GQueue *deferred_send; /* for output if client is slow */ + size_t deferred_bytes; /* mem deferred_send consumes */ + unsigned int num; /* client number */ + + char send_buf[16384]; + size_t send_buf_used; /* bytes used this instance */ + + /** is this client waiting for an "idle" response? */ + bool idle_waiting; + + /** idle flags pending on this client, to be sent as soon as + the client enters "idle" */ + unsigned idle_flags; + + /** idle flags that the client wants to receive */ + unsigned idle_subscriptions; + + /** + * A list of channel names this client is subscribed to. + */ + GSList *subscriptions; + + /** + * The number of subscriptions in #subscriptions. Used to + * limit the number of subscriptions. + */ + unsigned num_subscriptions; + + /** + * A list of messages this client has received in reverse + * order (latest first). + */ + GSList *messages; + + /** + * The number of messages in #messages. + */ + unsigned num_messages; +}; + +extern unsigned int client_max_connections; +extern int client_timeout; +extern size_t client_max_command_list_size; +extern size_t client_max_output_buffer_size; + +bool +client_list_is_empty(void); + +bool +client_list_is_full(void); + +struct client * +client_list_get_first(void); + +void +client_list_add(struct client *client); + +void +client_list_foreach(GFunc func, gpointer user_data); + +void +client_list_remove(struct client *client); + +void +client_close(struct client *client); + +static inline void +new_cmd_list_ptr(struct client *client, const char *s) +{ + client->cmd_list = g_slist_prepend(client->cmd_list, g_strdup(s)); +} + +static inline void +free_cmd_list(GSList *list) +{ + for (GSList *tmp = list; tmp != NULL; tmp = g_slist_next(tmp)) + g_free(tmp->data); + + g_slist_free(list); +} + +void +client_set_expired(struct client *client); + +/** + * Schedule an "expired" check for all clients: permanently delete + * clients which have been set "expired" with client_set_expired(). + */ +void +client_schedule_expire(void); + +/** + * Removes a scheduled "expired" check. + */ +void +client_deinit_expire(void); + +enum command_return +client_read(struct client *client); + +enum command_return +client_process_line(struct client *client, char *line); + +void +client_write_deferred(struct client *client); + +void +client_write_output(struct client *client); + +gboolean +client_in_event(GIOChannel *source, GIOCondition condition, + gpointer data); + +#endif diff --git a/src/ClientList.cxx b/src/ClientList.cxx new file mode 100644 index 000000000..0e4876f85 --- /dev/null +++ b/src/ClientList.cxx @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "ClientInternal.hxx" + +#include + +static GList *clients; +static unsigned num_clients; + +bool +client_list_is_empty(void) +{ + return num_clients == 0; +} + +bool +client_list_is_full(void) +{ + return num_clients >= client_max_connections; +} + +struct client * +client_list_get_first(void) +{ + assert(clients != NULL); + + return (struct client *)clients->data; +} + +void +client_list_add(struct client *client) +{ + clients = g_list_prepend(clients, client); + ++num_clients; +} + +void +client_list_foreach(GFunc func, gpointer user_data) +{ + g_list_foreach(clients, func, user_data); +} + +void +client_list_remove(struct client *client) +{ + assert(num_clients > 0); + assert(clients != NULL); + + clients = g_list_remove(clients, client); + --num_clients; +} diff --git a/src/ClientMessage.cxx b/src/ClientMessage.cxx new file mode 100644 index 000000000..c15ea0802 --- /dev/null +++ b/src/ClientMessage.cxx @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "ClientMessage.hxx" + +#include +#include + +G_GNUC_PURE +static bool +valid_channel_char(const char ch) +{ + return g_ascii_isalnum(ch) || + ch == '_' || ch == '-' || ch == '.' || ch == ':'; +} + +bool +client_message_valid_channel_name(const char *name) +{ + do { + if (!valid_channel_char(*name)) + return false; + } while (*++name != 0); + + return true; +} + +void +client_message_init_null(struct client_message *msg) +{ + assert(msg != NULL); + + msg->channel = NULL; + msg->message = NULL; +} + +void +client_message_init(struct client_message *msg, + const char *channel, const char *message) +{ + assert(msg != NULL); + + msg->channel = g_strdup(channel); + msg->message = g_strdup(message); +} + +void +client_message_copy(struct client_message *dest, + const struct client_message *src) +{ + assert(dest != NULL); + assert(src != NULL); + assert(client_message_defined(src)); + + client_message_init(dest, src->channel, src->message); +} + +struct client_message * +client_message_dup(const struct client_message *src) +{ + struct client_message *dest = g_slice_new(struct client_message); + client_message_copy(dest, src); + return dest; +} + +void +client_message_deinit(struct client_message *msg) +{ + assert(msg != NULL); + + g_free(msg->channel); + g_free(msg->message); +} + +void +client_message_free(struct client_message *msg) +{ + client_message_deinit(msg); + g_slice_free(struct client_message, msg); +} diff --git a/src/ClientMessage.hxx b/src/ClientMessage.hxx new file mode 100644 index 000000000..6d1e734be --- /dev/null +++ b/src/ClientMessage.hxx @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2003-2011 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_CLIENT_MESSAGE_H +#define MPD_CLIENT_MESSAGE_H + +#include "gcc.h" + +#include +#include +#include + +/** + * A client-to-client message. + */ +struct client_message { + char *channel; + + char *message; +}; + +gcc_pure +bool +client_message_valid_channel_name(const char *name); + +gcc_pure +static inline bool +client_message_defined(const struct client_message *msg) +{ + assert(msg != NULL); + assert((msg->channel == NULL) == (msg->message == NULL)); + + return msg->channel != NULL; +} + +void +client_message_init_null(struct client_message *msg); + +void +client_message_init(struct client_message *msg, + const char *channel, const char *message); + +void +client_message_copy(struct client_message *dest, + const struct client_message *src); + +gcc_malloc +struct client_message * +client_message_dup(const struct client_message *src); + +void +client_message_deinit(struct client_message *msg); + +void +client_message_free(struct client_message *msg); + +#endif diff --git a/src/ClientNew.cxx b/src/ClientNew.cxx index e1c042614..bccabede9 100644 --- a/src/ClientNew.cxx +++ b/src/ClientNew.cxx @@ -18,9 +18,7 @@ */ #include "config.h" -extern "C" { -#include "client_internal.h" -} +#include "ClientInternal.hxx" #include "fd_util.h" extern "C" { #include "fifo_buffer.h" diff --git a/src/ClientProcess.cxx b/src/ClientProcess.cxx new file mode 100644 index 000000000..284c4d862 --- /dev/null +++ b/src/ClientProcess.cxx @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "ClientInternal.hxx" +#include "protocol/Result.hxx" +#include "AllCommands.hxx" + +#include + +#define CLIENT_LIST_MODE_BEGIN "command_list_begin" +#define CLIENT_LIST_OK_MODE_BEGIN "command_list_ok_begin" +#define CLIENT_LIST_MODE_END "command_list_end" + +static enum command_return +client_process_command_list(struct client *client, bool list_ok, GSList *list) +{ + enum command_return ret = COMMAND_RETURN_OK; + unsigned num = 0; + + for (GSList *cur = list; cur != NULL; cur = g_slist_next(cur)) { + char *cmd = (char *)cur->data; + + g_debug("command_process_list: process command \"%s\"", + cmd); + ret = command_process(client, num++, cmd); + g_debug("command_process_list: command returned %i", ret); + if (ret != COMMAND_RETURN_OK || client_is_expired(client)) + break; + else if (list_ok) + client_puts(client, "list_OK\n"); + } + + return ret; +} + +enum command_return +client_process_line(struct client *client, char *line) +{ + enum command_return ret; + + if (strcmp(line, "noidle") == 0) { + if (client->idle_waiting) { + /* send empty idle response and leave idle mode */ + client->idle_waiting = false; + command_success(client); + client_write_output(client); + } + + /* do nothing if the client wasn't idling: the client + has already received the full idle response from + client_idle_notify(), which he can now evaluate */ + + return COMMAND_RETURN_OK; + } else if (client->idle_waiting) { + /* during idle mode, clients must not send anything + except "noidle" */ + g_warning("[%u] command \"%s\" during idle", + client->num, line); + return COMMAND_RETURN_CLOSE; + } + + if (client->cmd_list_OK >= 0) { + if (strcmp(line, CLIENT_LIST_MODE_END) == 0) { + g_debug("[%u] process command list", + client->num); + + /* for scalability reasons, we have prepended + each new command; now we have to reverse it + to restore the correct order */ + client->cmd_list = g_slist_reverse(client->cmd_list); + + ret = client_process_command_list(client, + client->cmd_list_OK, + client->cmd_list); + g_debug("[%u] process command " + "list returned %i", client->num, ret); + + if (ret == COMMAND_RETURN_CLOSE || + client_is_expired(client)) + return COMMAND_RETURN_CLOSE; + + if (ret == COMMAND_RETURN_OK) + command_success(client); + + client_write_output(client); + free_cmd_list(client->cmd_list); + client->cmd_list = NULL; + client->cmd_list_OK = -1; + } else { + size_t len = strlen(line) + 1; + client->cmd_list_size += len; + if (client->cmd_list_size > + client_max_command_list_size) { + g_warning("[%u] command list size (%lu) " + "is larger than the max (%lu)", + client->num, + (unsigned long)client->cmd_list_size, + (unsigned long)client_max_command_list_size); + return COMMAND_RETURN_CLOSE; + } + + new_cmd_list_ptr(client, line); + ret = COMMAND_RETURN_OK; + } + } else { + if (strcmp(line, CLIENT_LIST_MODE_BEGIN) == 0) { + client->cmd_list_OK = 0; + ret = COMMAND_RETURN_OK; + } else if (strcmp(line, CLIENT_LIST_OK_MODE_BEGIN) == 0) { + client->cmd_list_OK = 1; + ret = COMMAND_RETURN_OK; + } else { + g_debug("[%u] process command \"%s\"", + client->num, line); + ret = command_process(client, 0, line); + g_debug("[%u] command returned %i", + client->num, ret); + + if (ret == COMMAND_RETURN_CLOSE || + client_is_expired(client)) + return COMMAND_RETURN_CLOSE; + + if (ret == COMMAND_RETURN_OK) + command_success(client); + + client_write_output(client); + } + } + + return ret; +} diff --git a/src/ClientRead.cxx b/src/ClientRead.cxx new file mode 100644 index 000000000..830820dea --- /dev/null +++ b/src/ClientRead.cxx @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "ClientInternal.hxx" + +extern "C" { +#include "fifo_buffer.h" +} + +#include +#include + +static char * +client_read_line(struct client *client) +{ + size_t length; + const char *p = (const char *)fifo_buffer_read(client->input, &length); + if (p == NULL) + return NULL; + + const char *newline = (const char *)memchr(p, '\n', length); + if (newline == NULL) + return NULL; + + char *line = g_strndup(p, newline - p); + fifo_buffer_consume(client->input, newline - p + 1); + + return g_strchomp(line); +} + +static enum command_return +client_input_received(struct client *client, size_t bytesRead) +{ + char *line; + + fifo_buffer_append(client->input, bytesRead); + + /* process all lines */ + + while ((line = client_read_line(client)) != NULL) { + enum command_return ret = client_process_line(client, line); + g_free(line); + + if (ret == COMMAND_RETURN_KILL || + ret == COMMAND_RETURN_CLOSE) + return ret; + if (client_is_expired(client)) + return COMMAND_RETURN_CLOSE; + } + + return COMMAND_RETURN_OK; +} + +enum command_return +client_read(struct client *client) +{ + GError *error = NULL; + GIOStatus status; + gsize bytes_read; + + assert(client != NULL); + assert(client->channel != NULL); + + size_t max_length; + char *p = (char *)fifo_buffer_write(client->input, &max_length); + if (p == NULL) { + g_warning("[%u] buffer overflow", client->num); + return COMMAND_RETURN_CLOSE; + } + + status = g_io_channel_read_chars(client->channel, p, max_length, + &bytes_read, &error); + switch (status) { + case G_IO_STATUS_NORMAL: + return client_input_received(client, bytes_read); + + case G_IO_STATUS_AGAIN: + /* try again later, after select() */ + return COMMAND_RETURN_OK; + + case G_IO_STATUS_EOF: + /* peer disconnected */ + return COMMAND_RETURN_CLOSE; + + case G_IO_STATUS_ERROR: + /* I/O error */ + g_warning("failed to read from client %d: %s", + client->num, error->message); + g_error_free(error); + return COMMAND_RETURN_CLOSE; + } + + /* unreachable */ + return COMMAND_RETURN_CLOSE; +} diff --git a/src/ClientSubscribe.cxx b/src/ClientSubscribe.cxx new file mode 100644 index 000000000..38440be7b --- /dev/null +++ b/src/ClientSubscribe.cxx @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "ClientSubscribe.hxx" +#include "ClientIdle.hxx" +#include "ClientInternal.hxx" + +extern "C" { +#include "idle.h" +} + +#include + +G_GNUC_PURE +static GSList * +client_find_subscription(const struct client *client, const char *channel) +{ + for (GSList *i = client->subscriptions; i != NULL; i = g_slist_next(i)) + if (strcmp((const char *)i->data, channel) == 0) + return i; + + return NULL; +} + +enum client_subscribe_result +client_subscribe(struct client *client, const char *channel) +{ + assert(client != NULL); + assert(channel != NULL); + + if (!client_message_valid_channel_name(channel)) + return CLIENT_SUBSCRIBE_INVALID; + + if (client_find_subscription(client, channel) != NULL) + return CLIENT_SUBSCRIBE_ALREADY; + + if (client->num_subscriptions >= CLIENT_MAX_SUBSCRIPTIONS) + return CLIENT_SUBSCRIBE_FULL; + + client->subscriptions = g_slist_prepend(client->subscriptions, + g_strdup(channel)); + ++client->num_subscriptions; + + idle_add(IDLE_SUBSCRIPTION); + + return CLIENT_SUBSCRIBE_OK; +} + +bool +client_unsubscribe(struct client *client, const char *channel) +{ + GSList *i = client_find_subscription(client, channel); + if (i == NULL) + return false; + + assert(client->num_subscriptions > 0); + + client->subscriptions = g_slist_remove(client->subscriptions, i->data); + --client->num_subscriptions; + + idle_add(IDLE_SUBSCRIPTION); + + assert((client->num_subscriptions == 0) == + (client->subscriptions == NULL)); + + return true; +} + +void +client_unsubscribe_all(struct client *client) +{ + for (GSList *i = client->subscriptions; i != NULL; i = g_slist_next(i)) + g_free(i->data); + + g_slist_free(client->subscriptions); + client->subscriptions = NULL; + client->num_subscriptions = 0; +} + +bool +client_push_message(struct client *client, const struct client_message *msg) +{ + assert(client != NULL); + assert(msg != NULL); + assert(client_message_defined(msg)); + + if (client->num_messages >= CLIENT_MAX_MESSAGES || + client_find_subscription(client, msg->channel) == NULL) + return false; + + if (client->messages == NULL) + client_idle_add(client, IDLE_MESSAGE); + + client->messages = g_slist_prepend(client->messages, + client_message_dup(msg)); + ++client->num_messages; + + return true; +} + +GSList * +client_read_messages(struct client *client) +{ + GSList *messages = g_slist_reverse(client->messages); + + client->messages = NULL; + client->num_messages = 0; + + return messages; +} diff --git a/src/ClientSubscribe.hxx b/src/ClientSubscribe.hxx new file mode 100644 index 000000000..2f0f73291 --- /dev/null +++ b/src/ClientSubscribe.hxx @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_CLIENT_SUBSCRIBE_HXX +#define MPD_CLIENT_SUBSCRIBE_HXX + +#include "gcc.h" + +typedef struct _GSList GSList; +struct client; +struct client_message; + +enum client_subscribe_result { + /** success */ + CLIENT_SUBSCRIBE_OK, + + /** invalid channel name */ + CLIENT_SUBSCRIBE_INVALID, + + /** already subscribed to this channel */ + CLIENT_SUBSCRIBE_ALREADY, + + /** too many subscriptions */ + CLIENT_SUBSCRIBE_FULL, +}; + +enum client_subscribe_result +client_subscribe(struct client *client, const char *channel); + +bool +client_unsubscribe(struct client *client, const char *channel); + +void +client_unsubscribe_all(struct client *client); + +bool +client_push_message(struct client *client, const struct client_message *msg); + +gcc_malloc +GSList * +client_read_messages(struct client *client); + +#endif diff --git a/src/ClientWrite.cxx b/src/ClientWrite.cxx new file mode 100644 index 000000000..dccd256ff --- /dev/null +++ b/src/ClientWrite.cxx @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "ClientInternal.hxx" + +#include +#include +#include + +static size_t +client_write_deferred_buffer(struct client *client, + const struct deferred_buffer *buffer) +{ + GError *error = NULL; + GIOStatus status; + gsize bytes_written; + + assert(client != NULL); + assert(client->channel != NULL); + assert(buffer != NULL); + + status = g_io_channel_write_chars + (client->channel, buffer->data, buffer->size, + &bytes_written, &error); + switch (status) { + case G_IO_STATUS_NORMAL: + return bytes_written; + + case G_IO_STATUS_AGAIN: + return 0; + + case G_IO_STATUS_EOF: + /* client has disconnected */ + + client_set_expired(client); + return 0; + + case G_IO_STATUS_ERROR: + /* I/O error */ + + client_set_expired(client); + g_warning("failed to flush buffer for %i: %s", + client->num, error->message); + g_error_free(error); + return 0; + } + + /* unreachable */ + return 0; +} + +void +client_write_deferred(struct client *client) +{ + size_t ret; + + while (!g_queue_is_empty(client->deferred_send)) { + struct deferred_buffer *buf = + (struct deferred_buffer *) + g_queue_peek_head(client->deferred_send); + + assert(buf->size > 0); + assert(buf->size <= client->deferred_bytes); + + ret = client_write_deferred_buffer(client, buf); + if (ret == 0) + break; + + if (ret < buf->size) { + assert(client->deferred_bytes >= (size_t)ret); + client->deferred_bytes -= ret; + buf->size -= ret; + memmove(buf->data, buf->data + ret, buf->size); + break; + } else { + size_t decr = sizeof(*buf) - + sizeof(buf->data) + buf->size; + + assert(client->deferred_bytes >= decr); + client->deferred_bytes -= decr; + g_free(buf); + g_queue_pop_head(client->deferred_send); + } + + g_timer_start(client->last_activity); + } + + if (g_queue_is_empty(client->deferred_send)) { + g_debug("[%u] buffer empty %lu", client->num, + (unsigned long)client->deferred_bytes); + assert(client->deferred_bytes == 0); + } +} + +static void client_defer_output(struct client *client, + const void *data, size_t length) +{ + size_t alloc; + struct deferred_buffer *buf; + + assert(length > 0); + + alloc = sizeof(*buf) - sizeof(buf->data) + length; + client->deferred_bytes += alloc; + if (client->deferred_bytes > client_max_output_buffer_size) { + g_warning("[%u] output buffer size (%lu) is " + "larger than the max (%lu)", + client->num, + (unsigned long)client->deferred_bytes, + (unsigned long)client_max_output_buffer_size); + /* cause client to close */ + client_set_expired(client); + return; + } + + buf = (struct deferred_buffer *)g_malloc(alloc); + buf->size = length; + memcpy(buf->data, data, length); + + g_queue_push_tail(client->deferred_send, buf); +} + +static void client_write_direct(struct client *client, + const char *data, size_t length) +{ + GError *error = NULL; + GIOStatus status; + gsize bytes_written; + + assert(client != NULL); + assert(client->channel != NULL); + assert(data != NULL); + assert(length > 0); + assert(g_queue_is_empty(client->deferred_send)); + + status = g_io_channel_write_chars(client->channel, data, length, + &bytes_written, &error); + switch (status) { + case G_IO_STATUS_NORMAL: + case G_IO_STATUS_AGAIN: + break; + + case G_IO_STATUS_EOF: + /* client has disconnected */ + + client_set_expired(client); + return; + + case G_IO_STATUS_ERROR: + /* I/O error */ + + client_set_expired(client); + g_warning("failed to write to %i: %s", + client->num, error->message); + g_error_free(error); + return; + } + + if (bytes_written < length) + client_defer_output(client, data + bytes_written, + length - bytes_written); + + if (!g_queue_is_empty(client->deferred_send)) + g_debug("[%u] buffer created", client->num); +} + +void +client_write_output(struct client *client) +{ + if (client_is_expired(client) || !client->send_buf_used) + return; + + if (!g_queue_is_empty(client->deferred_send)) { + client_defer_output(client, client->send_buf, + client->send_buf_used); + + if (client_is_expired(client)) + return; + + /* try to flush the deferred buffers now; the current + server command may take too long to finish, and + meanwhile try to feed output to the client, + otherwise it will time out. One reason why + deferring is slow might be that currently each + client_write() allocates a new deferred buffer. + This should be optimized after MPD 0.14. */ + client_write_deferred(client); + } else + client_write_direct(client, client->send_buf, + client->send_buf_used); + + client->send_buf_used = 0; +} + +/** + * Write a block of data to the client. + */ +static void client_write(struct client *client, const char *buffer, size_t buflen) +{ + /* if the client is going to be closed, do nothing */ + if (client_is_expired(client)) + return; + + while (buflen > 0 && !client_is_expired(client)) { + size_t copylen; + + assert(client->send_buf_used < sizeof(client->send_buf)); + + copylen = sizeof(client->send_buf) - client->send_buf_used; + if (copylen > buflen) + copylen = buflen; + + memcpy(client->send_buf + client->send_buf_used, buffer, + copylen); + buflen -= copylen; + client->send_buf_used += copylen; + buffer += copylen; + if (client->send_buf_used >= sizeof(client->send_buf)) + client_write_output(client); + } +} + +void client_puts(struct client *client, const char *s) +{ + client_write(client, s, strlen(s)); +} + +void client_vprintf(struct client *client, const char *fmt, va_list args) +{ +#ifndef G_OS_WIN32 + va_list tmp; + int length; + + va_copy(tmp, args); + length = vsnprintf(NULL, 0, fmt, tmp); + va_end(tmp); + + if (length <= 0) + /* wtf.. */ + return; + + char *buffer = (char *)g_malloc(length + 1); + vsnprintf(buffer, length + 1, fmt, args); + client_write(client, buffer, length); + g_free(buffer); +#else + /* On mingw32, snprintf() expects a 64 bit integer instead of + a "long int" for "%li". This is not consistent with our + expectation, so we're using plain sprintf() here, hoping + the static buffer is large enough. Sorry for this hack, + but WIN32 development is so painful, I'm not in the mood to + do it properly now. */ + + static char buffer[4096]; + vsprintf(buffer, fmt, args); + client_write(client, buffer, strlen(buffer)); +#endif +} + +G_GNUC_PRINTF(2, 3) void client_printf(struct client *client, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + client_vprintf(client, fmt, args); + va_end(args); +} diff --git a/src/CommandError.cxx b/src/CommandError.cxx index 32d3cb5ec..04de35e83 100644 --- a/src/CommandError.cxx +++ b/src/CommandError.cxx @@ -21,10 +21,7 @@ #include "CommandError.hxx" #include "db_error.h" #include "io_error.h" - -extern "C" { -#include "protocol/result.h" -} +#include "protocol/Result.hxx" #include #include diff --git a/src/DatabaseCommands.cxx b/src/DatabaseCommands.cxx index c3c22482e..c0f02ad24 100644 --- a/src/DatabaseCommands.cxx +++ b/src/DatabaseCommands.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2012 The Music Player Daemon Project + * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -24,14 +24,11 @@ #include "DatabasePrint.hxx" #include "DatabaseSelection.hxx" #include "CommandError.hxx" -#include "client_internal.h" +#include "ClientInternal.hxx" #include "tag.h" #include "uri.h" #include "SongFilter.hxx" - -extern "C" { -#include "protocol/result.h" -} +#include "protocol/Result.hxx" #include #include diff --git a/src/DatabasePrint.cxx b/src/DatabasePrint.cxx index 755224e9a..7ac670def 100644 --- a/src/DatabasePrint.cxx +++ b/src/DatabasePrint.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2012 The Music Player Daemon Project + * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -25,9 +25,9 @@ #include "SongPrint.hxx" #include "TimePrint.hxx" #include "Directory.hxx" +#include "Client.hxx" extern "C" { -#include "client.h" #include "song.h" #include "tag.h" } diff --git a/src/DecoderPrint.cxx b/src/DecoderPrint.cxx index a22908c0e..5dabf3490 100644 --- a/src/DecoderPrint.cxx +++ b/src/DecoderPrint.cxx @@ -21,10 +21,7 @@ #include "DecoderPrint.hxx" #include "decoder_list.h" #include "decoder_plugin.h" - -extern "C" { -#include "client.h" -} +#include "Client.hxx" #include diff --git a/src/Listen.cxx b/src/Listen.cxx index 90cfe5a03..954de08f1 100644 --- a/src/Listen.cxx +++ b/src/Listen.cxx @@ -20,10 +20,10 @@ #include "config.h" #include "Listen.hxx" #include "Main.hxx" +#include "Client.hxx" extern "C" { #include "server_socket.h" -#include "client.h" #include "conf.h" } diff --git a/src/Main.cxx b/src/Main.cxx index 88be49e4b..668a140be 100644 --- a/src/Main.cxx +++ b/src/Main.cxx @@ -29,14 +29,14 @@ #include "DatabaseSimple.hxx" #include "Permission.hxx" #include "Listen.hxx" +#include "ClientIdle.hxx" +#include "Client.hxx" +#include "AllCommands.hxx" extern "C" { #include "daemon.h" #include "io_thread.h" -#include "client.h" -#include "client_idle.h" #include "idle.h" -#include "AllCommands.h" #include "playlist.h" #include "cmdline.h" #include "conf.h" diff --git a/src/MessageCommands.cxx b/src/MessageCommands.cxx index c8ced1f35..ff22725fb 100644 --- a/src/MessageCommands.cxx +++ b/src/MessageCommands.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2012 The Music Player Daemon Project + * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,13 +19,10 @@ #include "config.h" #include "MessageCommands.hxx" - -extern "C" { -#include "protocol/argparser.h" -#include "protocol/result.h" -#include "client_internal.h" -#include "client_subscribe.h" -} +#include "ClientSubscribe.hxx" +#include "ClientInternal.hxx" +#include "protocol/Result.hxx" +#include "protocol/ArgParser.hxx" #include #include diff --git a/src/OtherCommands.cxx b/src/OtherCommands.cxx index 66d97d841..f7f225f63 100644 --- a/src/OtherCommands.cxx +++ b/src/OtherCommands.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2012 The Music Player Daemon Project + * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -29,10 +29,10 @@ #include "TimePrint.hxx" #include "Mapper.hxx" #include "DecoderPrint.hxx" +#include "protocol/ArgParser.hxx" +#include "protocol/Result.hxx" extern "C" { -#include "protocol/argparser.h" -#include "protocol/result.h" #include "ls.h" #include "uri.h" #include "volume.h" @@ -41,11 +41,11 @@ extern "C" { #include "Permission.hxx" #include "PlaylistFile.hxx" +#include "ClientIdle.hxx" +#include "ClientFile.hxx" +#include "Client.hxx" extern "C" { -#include "client.h" -#include "client_idle.h" -#include "client_file.h" #include "idle.h" } diff --git a/src/OutputCommands.cxx b/src/OutputCommands.cxx index 235bf976a..5903ac430 100644 --- a/src/OutputCommands.cxx +++ b/src/OutputCommands.cxx @@ -20,10 +20,10 @@ #include "config.h" #include "OutputCommands.hxx" #include "OutputPrint.hxx" +#include "protocol/Result.hxx" +#include "protocol/ArgParser.hxx" extern "C" { -#include "protocol/argparser.h" -#include "protocol/result.h" #include "output_command.h" } diff --git a/src/OutputPrint.cxx b/src/OutputPrint.cxx index aa3c4c043..bf20fe794 100644 --- a/src/OutputPrint.cxx +++ b/src/OutputPrint.cxx @@ -25,10 +25,10 @@ #include "config.h" #include "OutputPrint.hxx" #include "output_internal.h" +#include "Client.hxx" extern "C" { #include "output_all.h" -#include "client.h" } void diff --git a/src/PlayerCommands.cxx b/src/PlayerCommands.cxx index 3aa3f7e63..e80bb04fa 100644 --- a/src/PlayerCommands.cxx +++ b/src/PlayerCommands.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2012 The Music Player Daemon Project + * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,15 +22,14 @@ #include "CommandError.hxx" #include "PlaylistPrint.hxx" #include "UpdateGlue.hxx" +#include "ClientInternal.hxx" +#include "protocol/Result.hxx" +#include "protocol/ArgParser.hxx" extern "C" { -#include "protocol/argparser.h" -#include "protocol/result.h" #include "player_control.h" #include "playlist.h" #include "volume.h" -#include "client.h" -#include "client_internal.h" #include "replay_gain_config.h" } diff --git a/src/PlaylistCommands.cxx b/src/PlaylistCommands.cxx index 890beef79..5a767576a 100644 --- a/src/PlaylistCommands.cxx +++ b/src/PlaylistCommands.cxx @@ -26,14 +26,14 @@ #include "PlaylistFile.hxx" #include "PlaylistQueue.hxx" #include "TimePrint.hxx" +#include "ClientInternal.hxx" +#include "protocol/ArgParser.hxx" +#include "protocol/Result.hxx" extern "C" { -#include "protocol/argparser.h" -#include "protocol/result.h" #include "playlist.h" #include "ls.h" #include "uri.h" -#include "client_internal.h" } #include diff --git a/src/PlaylistPrint.cxx b/src/PlaylistPrint.cxx index 6fc354f30..0a15d2091 100644 --- a/src/PlaylistPrint.cxx +++ b/src/PlaylistPrint.cxx @@ -26,13 +26,13 @@ #include "SongPrint.hxx" #include "DatabaseGlue.hxx" #include "DatabasePlugin.hxx" +#include "Client.hxx" extern "C" { #include "playlist_list.h" #include "playlist_plugin.h" #include "playlist.h" #include "song.h" -#include "client.h" #include "input_stream.h" } diff --git a/src/QueueCommands.cxx b/src/QueueCommands.cxx index cdd58d31e..185f32990 100644 --- a/src/QueueCommands.cxx +++ b/src/QueueCommands.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2012 The Music Player Daemon Project + * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,15 +23,15 @@ #include "DatabaseQueue.hxx" #include "SongFilter.hxx" #include "PlaylistPrint.hxx" +#include "ClientFile.hxx" +#include "ClientInternal.hxx" +#include "protocol/ArgParser.hxx" +#include "protocol/Result.hxx" extern "C" { -#include "protocol/argparser.h" -#include "protocol/result.h" #include "playlist.h" #include "ls.h" #include "uri.h" -#include "client_internal.h" -#include "client_file.h" } #include diff --git a/src/QueuePrint.cxx b/src/QueuePrint.cxx index f99181ca5..de2df5531 100644 --- a/src/QueuePrint.cxx +++ b/src/QueuePrint.cxx @@ -22,11 +22,11 @@ #include "SongFilter.hxx" #include "SongPrint.hxx" #include "Mapper.hxx" +#include "Client.hxx" extern "C" { #include "queue.h" #include "song.h" -#include "client.h" } /** diff --git a/src/SongPrint.cxx b/src/SongPrint.cxx index b15a5cf1a..7b3bde2bc 100644 --- a/src/SongPrint.cxx +++ b/src/SongPrint.cxx @@ -24,9 +24,9 @@ #include "TimePrint.hxx" #include "TagPrint.hxx" #include "Mapper.hxx" +#include "Client.hxx" extern "C" { -#include "client.h" #include "uri.h" } diff --git a/src/Stats.cxx b/src/Stats.cxx index 81d083d8c..e3d906915 100644 --- a/src/Stats.cxx +++ b/src/Stats.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2012 The Music Player Daemon Project + * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,11 +21,10 @@ extern "C" { #include "stats.h" -#include "client.h" #include "player_control.h" -#include "client_internal.h" } +#include "ClientInternal.hxx" #include "DatabaseSelection.hxx" #include "DatabaseGlue.hxx" #include "DatabasePlugin.hxx" diff --git a/src/StickerCommands.cxx b/src/StickerCommands.cxx index 3383b9553..dbd97ce21 100644 --- a/src/StickerCommands.cxx +++ b/src/StickerCommands.cxx @@ -28,10 +28,7 @@ #include "StickerPrint.hxx" #include "StickerDatabase.hxx" #include "CommandError.hxx" - -extern "C" { -#include "protocol/result.h" -} +#include "protocol/Result.hxx" #include diff --git a/src/StickerPrint.cxx b/src/StickerPrint.cxx index 6099f728d..6a14620e0 100644 --- a/src/StickerPrint.cxx +++ b/src/StickerPrint.cxx @@ -20,10 +20,7 @@ #include "config.h" #include "StickerPrint.hxx" #include "StickerDatabase.hxx" - -extern "C" { -#include "client.h" -} +#include "Client.hxx" void sticker_print_value(struct client *client, diff --git a/src/TagPrint.cxx b/src/TagPrint.cxx index 9f25d95c6..ef05b6c14 100644 --- a/src/TagPrint.cxx +++ b/src/TagPrint.cxx @@ -22,10 +22,7 @@ #include "tag.h" #include "tag_internal.h" #include "song.h" - -extern "C" { -#include "client.h" -} +#include "Client.hxx" void tag_print_types(struct client *client) { diff --git a/src/TimePrint.cxx b/src/TimePrint.cxx index f9499980b..208b20e6e 100644 --- a/src/TimePrint.cxx +++ b/src/TimePrint.cxx @@ -19,10 +19,7 @@ #include "config.h" #include "TimePrint.hxx" - -extern "C" { -#include "client.h" -} +#include "Client.hxx" #include diff --git a/src/client.c b/src/client.c deleted file mode 100644 index 3fa2c9be4..000000000 --- a/src/client.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "client_internal.h" - -bool client_is_expired(const struct client *client) -{ - return client->channel == NULL; -} - -int client_get_uid(const struct client *client) -{ - return client->uid; -} - -unsigned client_get_permission(const struct client *client) -{ - return client->permission; -} - -void client_set_permission(struct client *client, unsigned permission) -{ - client->permission = permission; -} diff --git a/src/client.h b/src/client.h deleted file mode 100644 index 51ad1eb2a..000000000 --- a/src/client.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_CLIENT_H -#define MPD_CLIENT_H - -#include "gcc.h" - -#include -#include -#include - -struct client; -struct sockaddr; -struct player_control; - -void client_manager_init(void); -void client_manager_deinit(void); - -void client_new(struct player_control *player_control, - int fd, const struct sockaddr *sa, size_t sa_length, int uid); - -gcc_pure -bool client_is_expired(const struct client *client); - -/** - * returns the uid of the client process, or a negative value if the - * uid is unknown - */ -gcc_pure -int client_get_uid(const struct client *client); - -/** - * Is this client running on the same machine, connected with a local - * (UNIX domain) socket? - */ -gcc_pure -static inline bool -client_is_local(const struct client *client) -{ - return client_get_uid(client) > 0; -} - -gcc_pure -unsigned client_get_permission(const struct client *client); - -void client_set_permission(struct client *client, unsigned permission); - -/** - * Write a C string to the client. - */ -void client_puts(struct client *client, const char *s); - -/** - * Write a printf-like formatted string to the client. - */ -void client_vprintf(struct client *client, const char *fmt, va_list args); - -/** - * Write a printf-like formatted string to the client. - */ -gcc_fprintf -void -client_printf(struct client *client, const char *fmt, ...); - -#endif diff --git a/src/client_event.c b/src/client_event.c deleted file mode 100644 index 5680e557b..000000000 --- a/src/client_event.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "client_internal.h" -#include "Main.hxx" - -#include - -static gboolean -client_out_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition, - gpointer data) -{ - struct client *client = data; - - assert(!client_is_expired(client)); - - if (condition != G_IO_OUT) { - client_set_expired(client); - return false; - } - - client_write_deferred(client); - - if (client_is_expired(client)) { - client_close(client); - return false; - } - - g_timer_start(client->last_activity); - - if (g_queue_is_empty(client->deferred_send)) { - /* done sending deferred buffers exist: schedule - read */ - client->source_id = g_io_add_watch(client->channel, - G_IO_IN|G_IO_ERR|G_IO_HUP, - client_in_event, client); - return false; - } - - /* write more */ - return true; -} - -gboolean -client_in_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition, - gpointer data) -{ - struct client *client = data; - enum command_return ret; - - assert(!client_is_expired(client)); - - if (condition != G_IO_IN) { - client_set_expired(client); - return false; - } - - g_timer_start(client->last_activity); - - ret = client_read(client); - switch (ret) { - case COMMAND_RETURN_OK: - case COMMAND_RETURN_IDLE: - case COMMAND_RETURN_ERROR: - break; - - case COMMAND_RETURN_KILL: - client_close(client); - g_main_loop_quit(main_loop); - return false; - - case COMMAND_RETURN_CLOSE: - client_close(client); - return false; - } - - if (client_is_expired(client)) { - client_close(client); - return false; - } - - if (!g_queue_is_empty(client->deferred_send)) { - /* deferred buffers exist: schedule write */ - client->source_id = g_io_add_watch(client->channel, - G_IO_OUT|G_IO_ERR|G_IO_HUP, - client_out_event, client); - return false; - } - - /* read more */ - return true; -} diff --git a/src/client_expire.c b/src/client_expire.c deleted file mode 100644 index 1ca32ebcc..000000000 --- a/src/client_expire.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "client_internal.h" - -static guint expire_source_id; - -void -client_set_expired(struct client *client) -{ - if (!client_is_expired(client)) - client_schedule_expire(); - - if (client->source_id != 0) { - g_source_remove(client->source_id); - client->source_id = 0; - } - - if (client->channel != NULL) { - g_io_channel_unref(client->channel); - client->channel = NULL; - } -} - -static void -client_check_expired_callback(gpointer data, G_GNUC_UNUSED gpointer user_data) -{ - struct client *client = data; - - if (client_is_expired(client)) { - g_debug("[%u] expired", client->num); - client_close(client); - } else if (!client->idle_waiting && /* idle clients - never expire */ - (int)g_timer_elapsed(client->last_activity, NULL) > - client_timeout) { - g_debug("[%u] timeout", client->num); - client_close(client); - } -} - -static void -client_manager_expire(void) -{ - client_list_foreach(client_check_expired_callback, NULL); -} - -/** - * An idle event which calls client_manager_expire(). - */ -static gboolean -client_manager_expire_event(G_GNUC_UNUSED gpointer data) -{ - expire_source_id = 0; - client_manager_expire(); - return false; -} - -void -client_schedule_expire(void) -{ - if (expire_source_id == 0) - /* delayed deletion */ - expire_source_id = g_idle_add(client_manager_expire_event, - NULL); -} - -void -client_deinit_expire(void) -{ - if (expire_source_id != 0) - g_source_remove(expire_source_id); -} diff --git a/src/client_file.c b/src/client_file.c deleted file mode 100644 index 5214ff019..000000000 --- a/src/client_file.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2003-2012 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "client_file.h" -#include "client.h" -#include "ack.h" -#include "io_error.h" - -#include -#include -#include -#include - -bool -client_allow_file(const struct client *client, const char *path_fs, - GError **error_r) -{ -#ifdef WIN32 - (void)client; - (void)path_fs; - - g_set_error(error_r, ack_quark(), ACK_ERROR_PERMISSION, - "Access denied"); - return false; -#else - const int uid = client_get_uid(client); - if (uid >= 0 && (uid_t)uid == geteuid()) - /* always allow access if user runs his own MPD - instance */ - return true; - - if (uid <= 0) { - /* unauthenticated client */ - g_set_error(error_r, ack_quark(), ACK_ERROR_PERMISSION, - "Access denied"); - return false; - } - - struct stat st; - if (stat(path_fs, &st) < 0) { - set_error_errno(error_r); - return false; - } - - if (st.st_uid != (uid_t)uid && (st.st_mode & 0444) != 0444) { - /* client is not owner */ - g_set_error(error_r, ack_quark(), ACK_ERROR_PERMISSION, - "Access denied"); - return false; - } - - return true; -#endif -} diff --git a/src/client_file.h b/src/client_file.h deleted file mode 100644 index 2dd07dede..000000000 --- a/src/client_file.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2003-2012 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_CLIENT_FILE_H -#define MPD_CLIENT_FILE_H - -#include "gerror.h" - -#include - -struct client; - -/** - * Is this client allowed to use the specified local file? - * - * Note that this function is vulnerable to timing/symlink attacks. - * We cannot fix this as long as there are plugins that open a file by - * its name, and not by file descriptor / callbacks. - * - * @param path_fs the absolute path name in filesystem encoding - * @return true if access is allowed - */ -bool -client_allow_file(const struct client *client, const char *path_fs, - GError **error_r); - -#endif diff --git a/src/client_global.c b/src/client_global.c deleted file mode 100644 index adf3b2f9e..000000000 --- a/src/client_global.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "client_internal.h" -#include "conf.h" - -#include - -#define CLIENT_TIMEOUT_DEFAULT (60) -#define CLIENT_MAX_CONNECTIONS_DEFAULT (10) -#define CLIENT_MAX_COMMAND_LIST_DEFAULT (2048*1024) -#define CLIENT_MAX_OUTPUT_BUFFER_SIZE_DEFAULT (8192*1024) - -/* set this to zero to indicate we have no possible clients */ -unsigned int client_max_connections; -int client_timeout; -size_t client_max_command_list_size; -size_t client_max_output_buffer_size; - -void client_manager_init(void) -{ - client_timeout = config_get_positive(CONF_CONN_TIMEOUT, - CLIENT_TIMEOUT_DEFAULT); - client_max_connections = - config_get_positive(CONF_MAX_CONN, - CLIENT_MAX_CONNECTIONS_DEFAULT); - client_max_command_list_size = - config_get_positive(CONF_MAX_COMMAND_LIST_SIZE, - CLIENT_MAX_COMMAND_LIST_DEFAULT / 1024) - * 1024; - - client_max_output_buffer_size = - config_get_positive(CONF_MAX_OUTPUT_BUFFER_SIZE, - CLIENT_MAX_OUTPUT_BUFFER_SIZE_DEFAULT / 1024) - * 1024; -} - -static void client_close_all(void) -{ - while (!client_list_is_empty()) { - struct client *client = client_list_get_first(); - - client_close(client); - } - - assert(client_list_is_empty()); -} - -void client_manager_deinit(void) -{ - client_close_all(); - - client_max_connections = 0; - - client_deinit_expire(); -} diff --git a/src/client_idle.c b/src/client_idle.c deleted file mode 100644 index 930911d6e..000000000 --- a/src/client_idle.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "client_idle.h" -#include "client_internal.h" -#include "idle.h" - -#include - -/** - * Send "idle" response to this client. - */ -static void -client_idle_notify(struct client *client) -{ - unsigned flags, i; - const char *const* idle_names; - - assert(client->idle_waiting); - assert(client->idle_flags != 0); - - flags = client->idle_flags; - client->idle_flags = 0; - client->idle_waiting = false; - - idle_names = idle_get_names(); - for (i = 0; idle_names[i]; ++i) { - if (flags & (1 << i) & client->idle_subscriptions) - client_printf(client, "changed: %s\n", - idle_names[i]); - } - - client_puts(client, "OK\n"); - g_timer_start(client->last_activity); -} - -void -client_idle_add(struct client *client, unsigned flags) -{ - if (client_is_expired(client)) - return; - - client->idle_flags |= flags; - if (client->idle_waiting - && (client->idle_flags & client->idle_subscriptions)) { - client_idle_notify(client); - client_write_output(client); - } -} - -static void -client_idle_callback(gpointer data, gpointer user_data) -{ - struct client *client = data; - unsigned flags = GPOINTER_TO_UINT(user_data); - - client_idle_add(client, flags); -} - -void client_manager_idle_add(unsigned flags) -{ - assert(flags != 0); - - client_list_foreach(client_idle_callback, GUINT_TO_POINTER(flags)); -} - -bool client_idle_wait(struct client *client, unsigned flags) -{ - assert(!client->idle_waiting); - - client->idle_waiting = true; - client->idle_subscriptions = flags; - - if (client->idle_flags & client->idle_subscriptions) { - client_idle_notify(client); - return true; - } else - return false; -} diff --git a/src/client_idle.h b/src/client_idle.h deleted file mode 100644 index c56fd014c..000000000 --- a/src/client_idle.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_CLIENT_IDLE_H -#define MPD_CLIENT_IDLE_H - -#include - -struct client; - -void -client_idle_add(struct client *client, unsigned flags); - -/** - * Adds the specified idle flags to all clients and immediately sends - * notifications to all waiting clients. - */ -void -client_manager_idle_add(unsigned flags); - -/** - * Checks whether the client has pending idle flags. If yes, they are - * sent immediately and "true" is returned". If no, it puts the - * client into waiting mode and returns false. - */ -bool -client_idle_wait(struct client *client, unsigned flags); - -#endif diff --git a/src/client_internal.h b/src/client_internal.h deleted file mode 100644 index 5c2b9f11c..000000000 --- a/src/client_internal.h +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_CLIENT_INTERNAL_H -#define MPD_CLIENT_INTERNAL_H - -#include "client.h" -#include "client_message.h" -#include "command.h" - -#include - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "client" - -enum { - CLIENT_MAX_SUBSCRIPTIONS = 16, - CLIENT_MAX_MESSAGES = 64, -}; - -struct deferred_buffer { - size_t size; - char data[sizeof(long)]; -}; - -struct client { - struct player_control *player_control; - - GIOChannel *channel; - guint source_id; - - /** the buffer for reading lines from the #channel */ - struct fifo_buffer *input; - - unsigned permission; - - /** the uid of the client process, or -1 if unknown */ - int uid; - - /** - * How long since the last activity from this client? - */ - GTimer *last_activity; - - GSList *cmd_list; /* for when in list mode */ - int cmd_list_OK; /* print OK after each command execution */ - size_t cmd_list_size; /* mem cmd_list consumes */ - GQueue *deferred_send; /* for output if client is slow */ - size_t deferred_bytes; /* mem deferred_send consumes */ - unsigned int num; /* client number */ - - char send_buf[16384]; - size_t send_buf_used; /* bytes used this instance */ - - /** is this client waiting for an "idle" response? */ - bool idle_waiting; - - /** idle flags pending on this client, to be sent as soon as - the client enters "idle" */ - unsigned idle_flags; - - /** idle flags that the client wants to receive */ - unsigned idle_subscriptions; - - /** - * A list of channel names this client is subscribed to. - */ - GSList *subscriptions; - - /** - * The number of subscriptions in #subscriptions. Used to - * limit the number of subscriptions. - */ - unsigned num_subscriptions; - - /** - * A list of messages this client has received in reverse - * order (latest first). - */ - GSList *messages; - - /** - * The number of messages in #messages. - */ - unsigned num_messages; -}; - -extern unsigned int client_max_connections; -extern int client_timeout; -extern size_t client_max_command_list_size; -extern size_t client_max_output_buffer_size; - -bool -client_list_is_empty(void); - -bool -client_list_is_full(void); - -struct client * -client_list_get_first(void); - -void -client_list_add(struct client *client); - -void -client_list_foreach(GFunc func, gpointer user_data); - -void -client_list_remove(struct client *client); - -void -client_close(struct client *client); - -static inline void -new_cmd_list_ptr(struct client *client, const char *s) -{ - client->cmd_list = g_slist_prepend(client->cmd_list, g_strdup(s)); -} - -static inline void -free_cmd_list(GSList *list) -{ - for (GSList *tmp = list; tmp != NULL; tmp = g_slist_next(tmp)) - g_free(tmp->data); - - g_slist_free(list); -} - -void -client_set_expired(struct client *client); - -/** - * Schedule an "expired" check for all clients: permanently delete - * clients which have been set "expired" with client_set_expired(). - */ -void -client_schedule_expire(void); - -/** - * Removes a scheduled "expired" check. - */ -void -client_deinit_expire(void); - -enum command_return -client_read(struct client *client); - -enum command_return -client_process_line(struct client *client, char *line); - -void -client_write_deferred(struct client *client); - -void -client_write_output(struct client *client); - -gboolean -client_in_event(GIOChannel *source, GIOCondition condition, - gpointer data); - -#endif diff --git a/src/client_list.c b/src/client_list.c deleted file mode 100644 index 2c7f37aff..000000000 --- a/src/client_list.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "client_internal.h" - -#include - -static GList *clients; -static unsigned num_clients; - -bool -client_list_is_empty(void) -{ - return num_clients == 0; -} - -bool -client_list_is_full(void) -{ - return num_clients >= client_max_connections; -} - -struct client * -client_list_get_first(void) -{ - assert(clients != NULL); - - return clients->data; -} - -void -client_list_add(struct client *client) -{ - clients = g_list_prepend(clients, client); - ++num_clients; -} - -void -client_list_foreach(GFunc func, gpointer user_data) -{ - g_list_foreach(clients, func, user_data); -} - -void -client_list_remove(struct client *client) -{ - assert(num_clients > 0); - assert(clients != NULL); - - clients = g_list_remove(clients, client); - --num_clients; -} diff --git a/src/client_message.c b/src/client_message.c deleted file mode 100644 index b681b4e7f..000000000 --- a/src/client_message.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "client_message.h" - -#include -#include - -G_GNUC_PURE -static bool -valid_channel_char(const char ch) -{ - return g_ascii_isalnum(ch) || - ch == '_' || ch == '-' || ch == '.' || ch == ':'; -} - -bool -client_message_valid_channel_name(const char *name) -{ - do { - if (!valid_channel_char(*name)) - return false; - } while (*++name != 0); - - return true; -} - -void -client_message_init_null(struct client_message *msg) -{ - assert(msg != NULL); - - msg->channel = NULL; - msg->message = NULL; -} - -void -client_message_init(struct client_message *msg, - const char *channel, const char *message) -{ - assert(msg != NULL); - - msg->channel = g_strdup(channel); - msg->message = g_strdup(message); -} - -void -client_message_copy(struct client_message *dest, - const struct client_message *src) -{ - assert(dest != NULL); - assert(src != NULL); - assert(client_message_defined(src)); - - client_message_init(dest, src->channel, src->message); -} - -struct client_message * -client_message_dup(const struct client_message *src) -{ - struct client_message *dest = g_slice_new(struct client_message); - client_message_copy(dest, src); - return dest; -} - -void -client_message_deinit(struct client_message *msg) -{ - assert(msg != NULL); - - g_free(msg->channel); - g_free(msg->message); -} - -void -client_message_free(struct client_message *msg) -{ - client_message_deinit(msg); - g_slice_free(struct client_message, msg); -} diff --git a/src/client_message.h b/src/client_message.h deleted file mode 100644 index 6d1e734be..000000000 --- a/src/client_message.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_CLIENT_MESSAGE_H -#define MPD_CLIENT_MESSAGE_H - -#include "gcc.h" - -#include -#include -#include - -/** - * A client-to-client message. - */ -struct client_message { - char *channel; - - char *message; -}; - -gcc_pure -bool -client_message_valid_channel_name(const char *name); - -gcc_pure -static inline bool -client_message_defined(const struct client_message *msg) -{ - assert(msg != NULL); - assert((msg->channel == NULL) == (msg->message == NULL)); - - return msg->channel != NULL; -} - -void -client_message_init_null(struct client_message *msg); - -void -client_message_init(struct client_message *msg, - const char *channel, const char *message); - -void -client_message_copy(struct client_message *dest, - const struct client_message *src); - -gcc_malloc -struct client_message * -client_message_dup(const struct client_message *src); - -void -client_message_deinit(struct client_message *msg); - -void -client_message_free(struct client_message *msg); - -#endif diff --git a/src/client_process.c b/src/client_process.c deleted file mode 100644 index 7217b35ab..000000000 --- a/src/client_process.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "client_internal.h" -#include "protocol/result.h" -#include "AllCommands.h" - -#include - -#define CLIENT_LIST_MODE_BEGIN "command_list_begin" -#define CLIENT_LIST_OK_MODE_BEGIN "command_list_ok_begin" -#define CLIENT_LIST_MODE_END "command_list_end" - -static enum command_return -client_process_command_list(struct client *client, bool list_ok, GSList *list) -{ - enum command_return ret = COMMAND_RETURN_OK; - unsigned num = 0; - - for (GSList *cur = list; cur != NULL; cur = g_slist_next(cur)) { - char *cmd = cur->data; - - g_debug("command_process_list: process command \"%s\"", - cmd); - ret = command_process(client, num++, cmd); - g_debug("command_process_list: command returned %i", ret); - if (ret != COMMAND_RETURN_OK || client_is_expired(client)) - break; - else if (list_ok) - client_puts(client, "list_OK\n"); - } - - return ret; -} - -enum command_return -client_process_line(struct client *client, char *line) -{ - enum command_return ret; - - if (strcmp(line, "noidle") == 0) { - if (client->idle_waiting) { - /* send empty idle response and leave idle mode */ - client->idle_waiting = false; - command_success(client); - client_write_output(client); - } - - /* do nothing if the client wasn't idling: the client - has already received the full idle response from - client_idle_notify(), which he can now evaluate */ - - return COMMAND_RETURN_OK; - } else if (client->idle_waiting) { - /* during idle mode, clients must not send anything - except "noidle" */ - g_warning("[%u] command \"%s\" during idle", - client->num, line); - return COMMAND_RETURN_CLOSE; - } - - if (client->cmd_list_OK >= 0) { - if (strcmp(line, CLIENT_LIST_MODE_END) == 0) { - g_debug("[%u] process command list", - client->num); - - /* for scalability reasons, we have prepended - each new command; now we have to reverse it - to restore the correct order */ - client->cmd_list = g_slist_reverse(client->cmd_list); - - ret = client_process_command_list(client, - client->cmd_list_OK, - client->cmd_list); - g_debug("[%u] process command " - "list returned %i", client->num, ret); - - if (ret == COMMAND_RETURN_CLOSE || - client_is_expired(client)) - return COMMAND_RETURN_CLOSE; - - if (ret == COMMAND_RETURN_OK) - command_success(client); - - client_write_output(client); - free_cmd_list(client->cmd_list); - client->cmd_list = NULL; - client->cmd_list_OK = -1; - } else { - size_t len = strlen(line) + 1; - client->cmd_list_size += len; - if (client->cmd_list_size > - client_max_command_list_size) { - g_warning("[%u] command list size (%lu) " - "is larger than the max (%lu)", - client->num, - (unsigned long)client->cmd_list_size, - (unsigned long)client_max_command_list_size); - return COMMAND_RETURN_CLOSE; - } - - new_cmd_list_ptr(client, line); - ret = COMMAND_RETURN_OK; - } - } else { - if (strcmp(line, CLIENT_LIST_MODE_BEGIN) == 0) { - client->cmd_list_OK = 0; - ret = COMMAND_RETURN_OK; - } else if (strcmp(line, CLIENT_LIST_OK_MODE_BEGIN) == 0) { - client->cmd_list_OK = 1; - ret = COMMAND_RETURN_OK; - } else { - g_debug("[%u] process command \"%s\"", - client->num, line); - ret = command_process(client, 0, line); - g_debug("[%u] command returned %i", - client->num, ret); - - if (ret == COMMAND_RETURN_CLOSE || - client_is_expired(client)) - return COMMAND_RETURN_CLOSE; - - if (ret == COMMAND_RETURN_OK) - command_success(client); - - client_write_output(client); - } - } - - return ret; -} diff --git a/src/client_read.c b/src/client_read.c deleted file mode 100644 index 26ade264e..000000000 --- a/src/client_read.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "client_internal.h" -#include "fifo_buffer.h" - -#include -#include - -static char * -client_read_line(struct client *client) -{ - const char *p, *newline; - size_t length; - char *line; - - p = fifo_buffer_read(client->input, &length); - if (p == NULL) - return NULL; - - newline = memchr(p, '\n', length); - if (newline == NULL) - return NULL; - - line = g_strndup(p, newline - p); - fifo_buffer_consume(client->input, newline - p + 1); - - return g_strchomp(line); -} - -static enum command_return -client_input_received(struct client *client, size_t bytesRead) -{ - char *line; - - fifo_buffer_append(client->input, bytesRead); - - /* process all lines */ - - while ((line = client_read_line(client)) != NULL) { - enum command_return ret = client_process_line(client, line); - g_free(line); - - if (ret == COMMAND_RETURN_KILL || - ret == COMMAND_RETURN_CLOSE) - return ret; - if (client_is_expired(client)) - return COMMAND_RETURN_CLOSE; - } - - return COMMAND_RETURN_OK; -} - -enum command_return -client_read(struct client *client) -{ - char *p; - size_t max_length; - GError *error = NULL; - GIOStatus status; - gsize bytes_read; - - assert(client != NULL); - assert(client->channel != NULL); - - p = fifo_buffer_write(client->input, &max_length); - if (p == NULL) { - g_warning("[%u] buffer overflow", client->num); - return COMMAND_RETURN_CLOSE; - } - - status = g_io_channel_read_chars(client->channel, p, max_length, - &bytes_read, &error); - switch (status) { - case G_IO_STATUS_NORMAL: - return client_input_received(client, bytes_read); - - case G_IO_STATUS_AGAIN: - /* try again later, after select() */ - return COMMAND_RETURN_OK; - - case G_IO_STATUS_EOF: - /* peer disconnected */ - return COMMAND_RETURN_CLOSE; - - case G_IO_STATUS_ERROR: - /* I/O error */ - g_warning("failed to read from client %d: %s", - client->num, error->message); - g_error_free(error); - return COMMAND_RETURN_CLOSE; - } - - /* unreachable */ - return COMMAND_RETURN_CLOSE; -} diff --git a/src/client_subscribe.c b/src/client_subscribe.c deleted file mode 100644 index c65a7ed31..000000000 --- a/src/client_subscribe.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "client_subscribe.h" -#include "client_internal.h" -#include "client_idle.h" -#include "idle.h" - -#include - -G_GNUC_PURE -static GSList * -client_find_subscription(const struct client *client, const char *channel) -{ - for (GSList *i = client->subscriptions; i != NULL; i = g_slist_next(i)) - if (strcmp((const char *)i->data, channel) == 0) - return i; - - return NULL; -} - -enum client_subscribe_result -client_subscribe(struct client *client, const char *channel) -{ - assert(client != NULL); - assert(channel != NULL); - - if (!client_message_valid_channel_name(channel)) - return CLIENT_SUBSCRIBE_INVALID; - - if (client_find_subscription(client, channel) != NULL) - return CLIENT_SUBSCRIBE_ALREADY; - - if (client->num_subscriptions >= CLIENT_MAX_SUBSCRIPTIONS) - return CLIENT_SUBSCRIBE_FULL; - - client->subscriptions = g_slist_prepend(client->subscriptions, - g_strdup(channel)); - ++client->num_subscriptions; - - idle_add(IDLE_SUBSCRIPTION); - - return CLIENT_SUBSCRIBE_OK; -} - -bool -client_unsubscribe(struct client *client, const char *channel) -{ - GSList *i = client_find_subscription(client, channel); - if (i == NULL) - return false; - - assert(client->num_subscriptions > 0); - - client->subscriptions = g_slist_remove(client->subscriptions, i->data); - --client->num_subscriptions; - - idle_add(IDLE_SUBSCRIPTION); - - assert((client->num_subscriptions == 0) == - (client->subscriptions == NULL)); - - return true; -} - -void -client_unsubscribe_all(struct client *client) -{ - for (GSList *i = client->subscriptions; i != NULL; i = g_slist_next(i)) - g_free(i->data); - - g_slist_free(client->subscriptions); - client->subscriptions = NULL; - client->num_subscriptions = 0; -} - -bool -client_push_message(struct client *client, const struct client_message *msg) -{ - assert(client != NULL); - assert(msg != NULL); - assert(client_message_defined(msg)); - - if (client->num_messages >= CLIENT_MAX_MESSAGES || - client_find_subscription(client, msg->channel) == NULL) - return false; - - if (client->messages == NULL) - client_idle_add(client, IDLE_MESSAGE); - - client->messages = g_slist_prepend(client->messages, - client_message_dup(msg)); - ++client->num_messages; - - return true; -} - -GSList * -client_read_messages(struct client *client) -{ - GSList *messages = g_slist_reverse(client->messages); - - client->messages = NULL; - client->num_messages = 0; - - return messages; -} diff --git a/src/client_subscribe.h b/src/client_subscribe.h deleted file mode 100644 index 2dff982ba..000000000 --- a/src/client_subscribe.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_CLIENT_SUBSCRIBE_H -#define MPD_CLIENT_SUBSCRIBE_H - -#include "gcc.h" - -#include - -typedef struct _GSList GSList; -struct client; -struct client_message; - -enum client_subscribe_result { - /** success */ - CLIENT_SUBSCRIBE_OK, - - /** invalid channel name */ - CLIENT_SUBSCRIBE_INVALID, - - /** already subscribed to this channel */ - CLIENT_SUBSCRIBE_ALREADY, - - /** too many subscriptions */ - CLIENT_SUBSCRIBE_FULL, -}; - -enum client_subscribe_result -client_subscribe(struct client *client, const char *channel); - -bool -client_unsubscribe(struct client *client, const char *channel); - -void -client_unsubscribe_all(struct client *client); - -bool -client_push_message(struct client *client, const struct client_message *msg); - -gcc_malloc -GSList * -client_read_messages(struct client *client); - -#endif diff --git a/src/client_write.c b/src/client_write.c deleted file mode 100644 index 78cfca8a1..000000000 --- a/src/client_write.c +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "client_internal.h" - -#include -#include -#include - -static size_t -client_write_deferred_buffer(struct client *client, - const struct deferred_buffer *buffer) -{ - GError *error = NULL; - GIOStatus status; - gsize bytes_written; - - assert(client != NULL); - assert(client->channel != NULL); - assert(buffer != NULL); - - status = g_io_channel_write_chars - (client->channel, buffer->data, buffer->size, - &bytes_written, &error); - switch (status) { - case G_IO_STATUS_NORMAL: - return bytes_written; - - case G_IO_STATUS_AGAIN: - return 0; - - case G_IO_STATUS_EOF: - /* client has disconnected */ - - client_set_expired(client); - return 0; - - case G_IO_STATUS_ERROR: - /* I/O error */ - - client_set_expired(client); - g_warning("failed to flush buffer for %i: %s", - client->num, error->message); - g_error_free(error); - return 0; - } - - /* unreachable */ - return 0; -} - -void -client_write_deferred(struct client *client) -{ - size_t ret; - - while (!g_queue_is_empty(client->deferred_send)) { - struct deferred_buffer *buf = - g_queue_peek_head(client->deferred_send); - - assert(buf->size > 0); - assert(buf->size <= client->deferred_bytes); - - ret = client_write_deferred_buffer(client, buf); - if (ret == 0) - break; - - if (ret < buf->size) { - assert(client->deferred_bytes >= (size_t)ret); - client->deferred_bytes -= ret; - buf->size -= ret; - memmove(buf->data, buf->data + ret, buf->size); - break; - } else { - size_t decr = sizeof(*buf) - - sizeof(buf->data) + buf->size; - - assert(client->deferred_bytes >= decr); - client->deferred_bytes -= decr; - g_free(buf); - g_queue_pop_head(client->deferred_send); - } - - g_timer_start(client->last_activity); - } - - if (g_queue_is_empty(client->deferred_send)) { - g_debug("[%u] buffer empty %lu", client->num, - (unsigned long)client->deferred_bytes); - assert(client->deferred_bytes == 0); - } -} - -static void client_defer_output(struct client *client, - const void *data, size_t length) -{ - size_t alloc; - struct deferred_buffer *buf; - - assert(length > 0); - - alloc = sizeof(*buf) - sizeof(buf->data) + length; - client->deferred_bytes += alloc; - if (client->deferred_bytes > client_max_output_buffer_size) { - g_warning("[%u] output buffer size (%lu) is " - "larger than the max (%lu)", - client->num, - (unsigned long)client->deferred_bytes, - (unsigned long)client_max_output_buffer_size); - /* cause client to close */ - client_set_expired(client); - return; - } - - buf = g_malloc(alloc); - buf->size = length; - memcpy(buf->data, data, length); - - g_queue_push_tail(client->deferred_send, buf); -} - -static void client_write_direct(struct client *client, - const char *data, size_t length) -{ - GError *error = NULL; - GIOStatus status; - gsize bytes_written; - - assert(client != NULL); - assert(client->channel != NULL); - assert(data != NULL); - assert(length > 0); - assert(g_queue_is_empty(client->deferred_send)); - - status = g_io_channel_write_chars(client->channel, data, length, - &bytes_written, &error); - switch (status) { - case G_IO_STATUS_NORMAL: - case G_IO_STATUS_AGAIN: - break; - - case G_IO_STATUS_EOF: - /* client has disconnected */ - - client_set_expired(client); - return; - - case G_IO_STATUS_ERROR: - /* I/O error */ - - client_set_expired(client); - g_warning("failed to write to %i: %s", - client->num, error->message); - g_error_free(error); - return; - } - - if (bytes_written < length) - client_defer_output(client, data + bytes_written, - length - bytes_written); - - if (!g_queue_is_empty(client->deferred_send)) - g_debug("[%u] buffer created", client->num); -} - -void -client_write_output(struct client *client) -{ - if (client_is_expired(client) || !client->send_buf_used) - return; - - if (!g_queue_is_empty(client->deferred_send)) { - client_defer_output(client, client->send_buf, - client->send_buf_used); - - if (client_is_expired(client)) - return; - - /* try to flush the deferred buffers now; the current - server command may take too long to finish, and - meanwhile try to feed output to the client, - otherwise it will time out. One reason why - deferring is slow might be that currently each - client_write() allocates a new deferred buffer. - This should be optimized after MPD 0.14. */ - client_write_deferred(client); - } else - client_write_direct(client, client->send_buf, - client->send_buf_used); - - client->send_buf_used = 0; -} - -/** - * Write a block of data to the client. - */ -static void client_write(struct client *client, const char *buffer, size_t buflen) -{ - /* if the client is going to be closed, do nothing */ - if (client_is_expired(client)) - return; - - while (buflen > 0 && !client_is_expired(client)) { - size_t copylen; - - assert(client->send_buf_used < sizeof(client->send_buf)); - - copylen = sizeof(client->send_buf) - client->send_buf_used; - if (copylen > buflen) - copylen = buflen; - - memcpy(client->send_buf + client->send_buf_used, buffer, - copylen); - buflen -= copylen; - client->send_buf_used += copylen; - buffer += copylen; - if (client->send_buf_used >= sizeof(client->send_buf)) - client_write_output(client); - } -} - -void client_puts(struct client *client, const char *s) -{ - client_write(client, s, strlen(s)); -} - -void client_vprintf(struct client *client, const char *fmt, va_list args) -{ -#ifndef G_OS_WIN32 - va_list tmp; - int length; - char *buffer; - - va_copy(tmp, args); - length = vsnprintf(NULL, 0, fmt, tmp); - va_end(tmp); - - if (length <= 0) - /* wtf.. */ - return; - - buffer = g_malloc(length + 1); - vsnprintf(buffer, length + 1, fmt, args); - client_write(client, buffer, length); - g_free(buffer); -#else - /* On mingw32, snprintf() expects a 64 bit integer instead of - a "long int" for "%li". This is not consistent with our - expectation, so we're using plain sprintf() here, hoping - the static buffer is large enough. Sorry for this hack, - but WIN32 development is so painful, I'm not in the mood to - do it properly now. */ - - static char buffer[4096]; - vsprintf(buffer, fmt, args); - client_write(client, buffer, strlen(buffer)); -#endif -} - -G_GNUC_PRINTF(2, 3) void client_printf(struct client *client, const char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - client_vprintf(client, fmt, args); - va_end(args); -} diff --git a/src/ls.cxx b/src/ls.cxx index 00edb2bb5..9c3a307c7 100644 --- a/src/ls.cxx +++ b/src/ls.cxx @@ -22,9 +22,10 @@ extern "C" { #include "ls.h" #include "uri.h" -#include "client.h" } +#include "Client.hxx" + #include #include diff --git a/src/protocol/ArgParser.cxx b/src/protocol/ArgParser.cxx new file mode 100644 index 000000000..a92809224 --- /dev/null +++ b/src/protocol/ArgParser.cxx @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "ArgParser.hxx" +#include "Result.hxx" + +#include +#include + +bool +check_uint32(struct client *client, uint32_t *dst, const char *s) +{ + char *test; + + *dst = strtoul(s, &test, 10); + if (test == s || *test != '\0') { + command_error(client, ACK_ERROR_ARG, + "Integer expected: %s", s); + return false; + } + return true; +} + +bool +check_int(struct client *client, int *value_r, const char *s) +{ + char *test; + long value; + + value = strtol(s, &test, 10); + if (test == s || *test != '\0') { + command_error(client, ACK_ERROR_ARG, + "Integer expected: %s", s); + return false; + } + +#if G_MAXLONG > G_MAXINT + if (value < G_MININT || value > G_MAXINT) { + command_error(client, ACK_ERROR_ARG, + "Number too large: %s", s); + return false; + } +#endif + + *value_r = (int)value; + return true; +} + +bool +check_range(struct client *client, unsigned *value_r1, unsigned *value_r2, + const char *s) +{ + char *test, *test2; + long value; + + value = strtol(s, &test, 10); + if (test == s || (*test != '\0' && *test != ':')) { + command_error(client, ACK_ERROR_ARG, + "Integer or range expected: %s", s); + return false; + } + + if (value == -1 && *test == 0) { + /* compatibility with older MPD versions: specifying + "-1" makes MPD display the whole list */ + *value_r1 = 0; + *value_r2 = G_MAXUINT; + return true; + } + + if (value < 0) { + command_error(client, ACK_ERROR_ARG, + "Number is negative: %s", s); + return false; + } + +#if G_MAXLONG > G_MAXUINT + if (value > G_MAXUINT) { + command_error(client, ACK_ERROR_ARG, + "Number too large: %s", s); + return false; + } +#endif + + *value_r1 = (unsigned)value; + + if (*test == ':') { + value = strtol(++test, &test2, 10); + if (test2 == test || *test2 != '\0') { + command_error(client, ACK_ERROR_ARG, + "Integer or range expected: %s", s); + return false; + } + + if (test == test2) + value = G_MAXUINT; + + if (value < 0) { + command_error(client, ACK_ERROR_ARG, + "Number is negative: %s", s); + return false; + } + +#if G_MAXLONG > G_MAXUINT + if (value > G_MAXUINT) { + command_error(client, ACK_ERROR_ARG, + "Number too large: %s", s); + return false; + } +#endif + *value_r2 = (unsigned)value; + } else { + *value_r2 = (unsigned)value + 1; + } + + return true; +} + +bool +check_unsigned(struct client *client, unsigned *value_r, const char *s) +{ + unsigned long value; + char *endptr; + + value = strtoul(s, &endptr, 10); + if (endptr == s || *endptr != 0) { + command_error(client, ACK_ERROR_ARG, + "Integer expected: %s", s); + return false; + } + + if (value > G_MAXUINT) { + command_error(client, ACK_ERROR_ARG, + "Number too large: %s", s); + return false; + } + + *value_r = (unsigned)value; + return true; +} + +bool +check_bool(struct client *client, bool *value_r, const char *s) +{ + long value; + char *endptr; + + value = strtol(s, &endptr, 10); + if (endptr == s || *endptr != 0 || (value != 0 && value != 1)) { + command_error(client, ACK_ERROR_ARG, + "Boolean (0/1) expected: %s", s); + return false; + } + + *value_r = !!value; + return true; +} + +bool +check_float(struct client *client, float *value_r, const char *s) +{ + float value; + char *endptr; + + value = strtof(s, &endptr); + if (endptr == s || *endptr != 0) { + command_error(client, ACK_ERROR_ARG, + "Float expected: %s", s); + return false; + } + + *value_r = value; + return true; +} diff --git a/src/protocol/ArgParser.hxx b/src/protocol/ArgParser.hxx new file mode 100644 index 000000000..73b046403 --- /dev/null +++ b/src/protocol/ArgParser.hxx @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_PROTOCOL_ARGPARSER_HXX +#define MPD_PROTOCOL_ARGPARSER_HXX + +#include "check.h" + +#include +#include + +struct client; + +bool +check_uint32(struct client *client, uint32_t *dst, const char *s); + +bool +check_int(struct client *client, int *value_r, const char *s); + +bool +check_range(struct client *client, unsigned *value_r1, unsigned *value_r2, + const char *s); + +bool +check_unsigned(struct client *client, unsigned *value_r, const char *s); + +bool +check_bool(struct client *client, bool *value_r, const char *s); + +bool +check_float(struct client *client, float *value_r, const char *s); + +#endif diff --git a/src/protocol/Result.cxx b/src/protocol/Result.cxx new file mode 100644 index 000000000..f38c33815 --- /dev/null +++ b/src/protocol/Result.cxx @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "Result.hxx" +#include "Client.hxx" + +#include + +const char *current_command; +int command_list_num; + +void +command_success(struct client *client) +{ + client_puts(client, "OK\n"); +} + +void +command_error_v(struct client *client, enum ack error, + const char *fmt, va_list args) +{ + assert(client != NULL); + assert(current_command != NULL); + + client_printf(client, "ACK [%i@%i] {%s} ", + (int)error, command_list_num, current_command); + client_vprintf(client, fmt, args); + client_puts(client, "\n"); + + current_command = NULL; +} + +void +command_error(struct client *client, enum ack error, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + command_error_v(client, error, fmt, args); + va_end(args); +} diff --git a/src/protocol/Result.hxx b/src/protocol/Result.hxx new file mode 100644 index 000000000..68cb84945 --- /dev/null +++ b/src/protocol/Result.hxx @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_PROTOCOL_RESULT_HXX +#define MPD_PROTOCOL_RESULT_HXX + +#include "check.h" +#include "gcc.h" +#include "ack.h" + +struct client; + +extern const char *current_command; +extern int command_list_num; + +void +command_success(struct client *client); + +void +command_error_v(struct client *client, enum ack error, + const char *fmt, va_list args); + +gcc_fprintf_ +void +command_error(struct client *client, enum ack error, const char *fmt, ...); + +#endif diff --git a/src/protocol/argparser.c b/src/protocol/argparser.c deleted file mode 100644 index b21d4c53c..000000000 --- a/src/protocol/argparser.c +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (C) 2003-2012 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "argparser.h" -#include "result.h" - -#include -#include - -bool -check_uint32(struct client *client, uint32_t *dst, const char *s) -{ - char *test; - - *dst = strtoul(s, &test, 10); - if (test == s || *test != '\0') { - command_error(client, ACK_ERROR_ARG, - "Integer expected: %s", s); - return false; - } - return true; -} - -bool -check_int(struct client *client, int *value_r, const char *s) -{ - char *test; - long value; - - value = strtol(s, &test, 10); - if (test == s || *test != '\0') { - command_error(client, ACK_ERROR_ARG, - "Integer expected: %s", s); - return false; - } - -#if G_MAXLONG > G_MAXINT - if (value < G_MININT || value > G_MAXINT) { - command_error(client, ACK_ERROR_ARG, - "Number too large: %s", s); - return false; - } -#endif - - *value_r = (int)value; - return true; -} - -bool -check_range(struct client *client, unsigned *value_r1, unsigned *value_r2, - const char *s) -{ - char *test, *test2; - long value; - - value = strtol(s, &test, 10); - if (test == s || (*test != '\0' && *test != ':')) { - command_error(client, ACK_ERROR_ARG, - "Integer or range expected: %s", s); - return false; - } - - if (value == -1 && *test == 0) { - /* compatibility with older MPD versions: specifying - "-1" makes MPD display the whole list */ - *value_r1 = 0; - *value_r2 = G_MAXUINT; - return true; - } - - if (value < 0) { - command_error(client, ACK_ERROR_ARG, - "Number is negative: %s", s); - return false; - } - -#if G_MAXLONG > G_MAXUINT - if (value > G_MAXUINT) { - command_error(client, ACK_ERROR_ARG, - "Number too large: %s", s); - return false; - } -#endif - - *value_r1 = (unsigned)value; - - if (*test == ':') { - value = strtol(++test, &test2, 10); - if (test2 == test || *test2 != '\0') { - command_error(client, ACK_ERROR_ARG, - "Integer or range expected: %s", s); - return false; - } - - if (test == test2) - value = G_MAXUINT; - - if (value < 0) { - command_error(client, ACK_ERROR_ARG, - "Number is negative: %s", s); - return false; - } - -#if G_MAXLONG > G_MAXUINT - if (value > G_MAXUINT) { - command_error(client, ACK_ERROR_ARG, - "Number too large: %s", s); - return false; - } -#endif - *value_r2 = (unsigned)value; - } else { - *value_r2 = (unsigned)value + 1; - } - - return true; -} - -bool -check_unsigned(struct client *client, unsigned *value_r, const char *s) -{ - unsigned long value; - char *endptr; - - value = strtoul(s, &endptr, 10); - if (endptr == s || *endptr != 0) { - command_error(client, ACK_ERROR_ARG, - "Integer expected: %s", s); - return false; - } - - if (value > G_MAXUINT) { - command_error(client, ACK_ERROR_ARG, - "Number too large: %s", s); - return false; - } - - *value_r = (unsigned)value; - return true; -} - -bool -check_bool(struct client *client, bool *value_r, const char *s) -{ - long value; - char *endptr; - - value = strtol(s, &endptr, 10); - if (endptr == s || *endptr != 0 || (value != 0 && value != 1)) { - command_error(client, ACK_ERROR_ARG, - "Boolean (0/1) expected: %s", s); - return false; - } - - *value_r = !!value; - return true; -} - -bool -check_float(struct client *client, float *value_r, const char *s) -{ - float value; - char *endptr; - - value = strtof(s, &endptr); - if (endptr == s || *endptr != 0) { - command_error(client, ACK_ERROR_ARG, - "Float expected: %s", s); - return false; - } - - *value_r = value; - return true; -} diff --git a/src/protocol/argparser.h b/src/protocol/argparser.h deleted file mode 100644 index e88aea478..000000000 --- a/src/protocol/argparser.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2003-2012 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_PROTOCOL_ARGPARSER_H -#define MPD_PROTOCOL_ARGPARSER_H - -#include "check.h" - -#include -#include - -struct client; - -bool -check_uint32(struct client *client, uint32_t *dst, const char *s); - -bool -check_int(struct client *client, int *value_r, const char *s); - -bool -check_range(struct client *client, unsigned *value_r1, unsigned *value_r2, - const char *s); - -bool -check_unsigned(struct client *client, unsigned *value_r, const char *s); - -bool -check_bool(struct client *client, bool *value_r, const char *s); - -bool -check_float(struct client *client, float *value_r, const char *s); - -#endif diff --git a/src/protocol/result.c b/src/protocol/result.c deleted file mode 100644 index 30cd0a266..000000000 --- a/src/protocol/result.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2003-2012 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "result.h" -#include "client.h" - -#include - -const char *current_command; -int command_list_num; - -void -command_success(struct client *client) -{ - client_puts(client, "OK\n"); -} - -void -command_error_v(struct client *client, enum ack error, - const char *fmt, va_list args) -{ - assert(client != NULL); - assert(current_command != NULL); - - client_printf(client, "ACK [%i@%i] {%s} ", - (int)error, command_list_num, current_command); - client_vprintf(client, fmt, args); - client_puts(client, "\n"); - - current_command = NULL; -} - -void -command_error(struct client *client, enum ack error, const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - command_error_v(client, error, fmt, args); - va_end(args); -} diff --git a/src/protocol/result.h b/src/protocol/result.h deleted file mode 100644 index 09ea6c418..000000000 --- a/src/protocol/result.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2003-2012 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_PROTOCOL_RESULT_H -#define MPD_PROTOCOL_RESULT_H - -#include "check.h" -#include "gcc.h" -#include "ack.h" - -struct client; - -extern const char *current_command; -extern int command_list_num; - -void -command_success(struct client *client); - -void -command_error_v(struct client *client, enum ack error, - const char *fmt, va_list args); - -gcc_fprintf_ -void -command_error(struct client *client, enum ack error, const char *fmt, ...); - -#endif -- cgit v1.2.3