From e199c33c6e9eb2f1aa588073f49f44de274985d5 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Fri, 24 Jan 2014 00:26:53 +0100 Subject: Client*: move to client/ --- src/Client.cxx | 24 ----- src/Client.hxx | 192 ------------------------------------- src/ClientEvent.cxx | 37 ------- src/ClientExpire.cxx | 43 --------- src/ClientFile.cxx | 67 ------------- src/ClientFile.hxx | 40 -------- src/ClientGlobal.cxx | 45 --------- src/ClientIdle.cxx | 75 --------------- src/ClientInternal.hxx | 39 -------- src/ClientList.cxx | 62 ------------ src/ClientList.hxx | 64 ------------- src/ClientMessage.cxx | 41 -------- src/ClientMessage.hxx | 52 ---------- src/ClientNew.cxx | 117 ---------------------- src/ClientProcess.cxx | 141 --------------------------- src/ClientRead.cxx | 75 --------------- src/ClientSubscribe.cxx | 87 ----------------- src/ClientWrite.cxx | 61 ------------ src/DatabasePrint.cxx | 2 +- src/Listen.cxx | 2 +- src/Main.cxx | 4 +- src/PlaylistPrint.cxx | 2 +- src/SongPrint.cxx | 2 +- src/Stats.cxx | 2 +- src/StickerPrint.cxx | 2 +- src/TagPrint.cxx | 2 +- src/TimePrint.cxx | 2 +- src/client/Client.cxx | 24 +++++ src/client/Client.hxx | 192 +++++++++++++++++++++++++++++++++++++ src/client/ClientEvent.cxx | 37 +++++++ src/client/ClientExpire.cxx | 43 +++++++++ src/client/ClientFile.cxx | 67 +++++++++++++ src/client/ClientFile.hxx | 40 ++++++++ src/client/ClientGlobal.cxx | 45 +++++++++ src/client/ClientIdle.cxx | 75 +++++++++++++++ src/client/ClientInternal.hxx | 39 ++++++++ src/client/ClientList.cxx | 62 ++++++++++++ src/client/ClientList.hxx | 64 +++++++++++++ src/client/ClientMessage.cxx | 41 ++++++++ src/client/ClientMessage.hxx | 52 ++++++++++ src/client/ClientNew.cxx | 117 ++++++++++++++++++++++ src/client/ClientProcess.cxx | 141 +++++++++++++++++++++++++++ src/client/ClientRead.cxx | 75 +++++++++++++++ src/client/ClientSubscribe.cxx | 87 +++++++++++++++++ src/client/ClientWrite.cxx | 61 ++++++++++++ src/command/AllCommands.cxx | 2 +- src/command/CommandListBuilder.cxx | 2 +- src/command/DatabaseCommands.cxx | 2 +- src/command/FileCommands.cxx | 4 +- src/command/MessageCommands.cxx | 4 +- src/command/OtherCommands.cxx | 4 +- src/command/PlayerCommands.cxx | 2 +- src/command/PlaylistCommands.cxx | 2 +- src/command/QueueCommands.cxx | 4 +- src/command/TagCommands.cxx | 2 +- src/decoder/DecoderPrint.cxx | 2 +- src/ls.cxx | 2 +- src/output/OutputPrint.cxx | 2 +- src/protocol/Result.cxx | 2 +- src/queue/QueuePrint.cxx | 2 +- 60 files changed, 1291 insertions(+), 1291 deletions(-) delete mode 100644 src/Client.cxx delete mode 100644 src/Client.hxx delete mode 100644 src/ClientEvent.cxx delete mode 100644 src/ClientExpire.cxx delete mode 100644 src/ClientFile.cxx delete mode 100644 src/ClientFile.hxx delete mode 100644 src/ClientGlobal.cxx delete mode 100644 src/ClientIdle.cxx delete mode 100644 src/ClientInternal.hxx delete mode 100644 src/ClientList.cxx delete mode 100644 src/ClientList.hxx delete mode 100644 src/ClientMessage.cxx delete mode 100644 src/ClientMessage.hxx delete mode 100644 src/ClientNew.cxx delete mode 100644 src/ClientProcess.cxx delete mode 100644 src/ClientRead.cxx delete mode 100644 src/ClientSubscribe.cxx delete mode 100644 src/ClientWrite.cxx create mode 100644 src/client/Client.cxx create mode 100644 src/client/Client.hxx create mode 100644 src/client/ClientEvent.cxx create mode 100644 src/client/ClientExpire.cxx create mode 100644 src/client/ClientFile.cxx create mode 100644 src/client/ClientFile.hxx create mode 100644 src/client/ClientGlobal.cxx create mode 100644 src/client/ClientIdle.cxx create mode 100644 src/client/ClientInternal.hxx create mode 100644 src/client/ClientList.cxx create mode 100644 src/client/ClientList.hxx create mode 100644 src/client/ClientMessage.cxx create mode 100644 src/client/ClientMessage.hxx create mode 100644 src/client/ClientNew.cxx create mode 100644 src/client/ClientProcess.cxx create mode 100644 src/client/ClientRead.cxx create mode 100644 src/client/ClientSubscribe.cxx create mode 100644 src/client/ClientWrite.cxx (limited to 'src') diff --git a/src/Client.cxx b/src/Client.cxx deleted file mode 100644 index ce99faa89..000000000 --- a/src/Client.cxx +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2003-2014 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 "util/Domain.hxx" - -const Domain client_domain("client"); diff --git a/src/Client.hxx b/src/Client.hxx deleted file mode 100644 index ec7d2d741..000000000 --- a/src/Client.hxx +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (C) 2003-2014 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 "check.h" -#include "ClientMessage.hxx" -#include "command/CommandListBuilder.hxx" -#include "event/FullyBufferedSocket.hxx" -#include "event/TimeoutMonitor.hxx" -#include "Compiler.h" - -#include -#include -#include - -#include -#include - -struct sockaddr; -class EventLoop; -struct Partition; - -class Client final : private FullyBufferedSocket, TimeoutMonitor { -public: - Partition &partition; - struct playlist &playlist; - struct PlayerControl &player_control; - - unsigned permission; - - /** the uid of the client process, or -1 if unknown */ - int uid; - - CommandListBuilder cmd_list; - - unsigned int num; /* client number */ - - /** 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. - */ - std::set 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. - */ - std::list messages; - - Client(EventLoop &loop, Partition &partition, - int fd, int uid, int num); - - ~Client() { - if (FullyBufferedSocket::IsDefined()) - FullyBufferedSocket::Close(); - } - - bool IsConnected() const { - return FullyBufferedSocket::IsDefined(); - } - - gcc_pure - bool IsExpired() const { - return !FullyBufferedSocket::IsDefined(); - } - - void Close(); - void SetExpired(); - - using FullyBufferedSocket::Write; - - /** - * returns the uid of the client process, or a negative value - * if the uid is unknown - */ - int GetUID() const { - return uid; - } - - /** - * Is this client running on the same machine, connected with - * a local (UNIX domain) socket? - */ - bool IsLocal() const { - return uid > 0; - } - - unsigned GetPermission() const { - return permission; - } - - void SetPermission(unsigned _permission) { - permission = _permission; - } - - /** - * Send "idle" response to this client. - */ - void IdleNotify(); - void IdleAdd(unsigned flags); - bool IdleWait(unsigned flags); - - enum class SubscribeResult { - /** success */ - OK, - - /** invalid channel name */ - INVALID, - - /** already subscribed to this channel */ - ALREADY, - - /** too many subscriptions */ - FULL, - }; - - gcc_pure - bool IsSubscribed(const char *channel_name) const { - return subscriptions.find(channel_name) != subscriptions.end(); - } - - SubscribeResult Subscribe(const char *channel); - bool Unsubscribe(const char *channel); - void UnsubscribeAll(); - bool PushMessage(const ClientMessage &msg); - -private: - /* virtual methods from class BufferedSocket */ - virtual InputResult OnSocketInput(void *data, size_t length) override; - virtual void OnSocketError(Error &&error) override; - virtual void OnSocketClosed() override; - - /* virtual methods from class TimeoutMonitor */ - virtual void OnTimeout() override; -}; - -void client_manager_init(void); - -void -client_new(EventLoop &loop, Partition &partition, - int fd, const sockaddr *sa, size_t sa_length, int uid); - -/** - * Write a C string to the client. - */ -void client_puts(Client &client, const char *s); - -/** - * Write a printf-like formatted string to the client. - */ -void client_vprintf(Client &client, const char *fmt, va_list args); - -/** - * Write a printf-like formatted string to the client. - */ -gcc_printf(2,3) -void -client_printf(Client &client, const char *fmt, ...); - -#endif diff --git a/src/ClientEvent.cxx b/src/ClientEvent.cxx deleted file mode 100644 index fd9f24b0d..000000000 --- a/src/ClientEvent.cxx +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2003-2014 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 "util/Error.hxx" -#include "Log.hxx" - -void -Client::OnSocketError(Error &&error) -{ - FormatError(error, "error on client %d", num); - - SetExpired(); -} - -void -Client::OnSocketClosed() -{ - SetExpired(); -} diff --git a/src/ClientExpire.cxx b/src/ClientExpire.cxx deleted file mode 100644 index 5891756b6..000000000 --- a/src/ClientExpire.cxx +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2003-2014 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 "Log.hxx" - -void -Client::SetExpired() -{ - if (IsExpired()) - return; - - FullyBufferedSocket::Close(); - TimeoutMonitor::Schedule(0); -} - -void -Client::OnTimeout() -{ - if (!IsExpired()) { - assert(!idle_waiting); - FormatDebug(client_domain, "[%u] timeout", num); - } - - Close(); -} diff --git a/src/ClientFile.cxx b/src/ClientFile.cxx deleted file mode 100644 index bdd9b0426..000000000 --- a/src/ClientFile.cxx +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2003-2014 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 "ClientFile.hxx" -#include "Client.hxx" -#include "protocol/Ack.hxx" -#include "fs/Path.hxx" -#include "fs/FileSystem.hxx" -#include "util/Error.hxx" - -#include -#include - -bool -client_allow_file(const Client &client, Path path_fs, Error &error) -{ -#ifdef WIN32 - (void)client; - (void)path_fs; - - error.Set(ack_domain, ACK_ERROR_PERMISSION, "Access denied"); - return false; -#else - const int uid = client.GetUID(); - if (uid >= 0 && (uid_t)uid == geteuid()) - /* always allow access if user runs his own MPD - instance */ - return true; - - if (uid <= 0) { - /* unauthenticated client */ - error.Set(ack_domain, ACK_ERROR_PERMISSION, "Access denied"); - return false; - } - - struct stat st; - if (!StatFile(path_fs, st)) { - error.SetErrno(); - return false; - } - - if (st.st_uid != (uid_t)uid && (st.st_mode & 0444) != 0444) { - /* client is not owner */ - error.Set(ack_domain, ACK_ERROR_PERMISSION, "Access denied"); - return false; - } - - return true; -#endif -} diff --git a/src/ClientFile.hxx b/src/ClientFile.hxx deleted file mode 100644 index 5a02a8df7..000000000 --- a/src/ClientFile.hxx +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2003-2014 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 - -class Client; -class Path; -class Error; - -/** - * 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 Client &client, Path path_fs, Error &error); - -#endif diff --git a/src/ClientGlobal.cxx b/src/ClientGlobal.cxx deleted file mode 100644 index 8d90721e9..000000000 --- a/src/ClientGlobal.cxx +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2003-2014 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 "config/ConfigGlobal.hxx" - -#define CLIENT_TIMEOUT_DEFAULT (60) -#define CLIENT_MAX_COMMAND_LIST_DEFAULT (2048*1024) -#define CLIENT_MAX_OUTPUT_BUFFER_SIZE_DEFAULT (8192*1024) - -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_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; -} diff --git a/src/ClientIdle.cxx b/src/ClientIdle.cxx deleted file mode 100644 index 0b4fa5751..000000000 --- a/src/ClientIdle.cxx +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2003-2014 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 "Idle.hxx" - -#include - -void -Client::IdleNotify() -{ - assert(idle_waiting); - assert(idle_flags != 0); - - unsigned flags = idle_flags; - idle_flags = 0; - idle_waiting = false; - - const char *const*idle_names = idle_get_names(); - for (unsigned i = 0; idle_names[i]; ++i) { - if (flags & (1 << i) & idle_subscriptions) - client_printf(*this, "changed: %s\n", - idle_names[i]); - } - - client_puts(*this, "OK\n"); - - TimeoutMonitor::ScheduleSeconds(client_timeout); -} - -void -Client::IdleAdd(unsigned flags) -{ - if (IsExpired()) - return; - - idle_flags |= flags; - if (idle_waiting && (idle_flags & idle_subscriptions)) - IdleNotify(); -} - -bool -Client::IdleWait(unsigned flags) -{ - assert(!idle_waiting); - - idle_waiting = true; - idle_subscriptions = flags; - - if (idle_flags & idle_subscriptions) { - IdleNotify(); - return true; - } else { - /* disable timeouts while in "idle" */ - TimeoutMonitor::Cancel(); - return false; - } -} diff --git a/src/ClientInternal.hxx b/src/ClientInternal.hxx deleted file mode 100644 index a819d64b8..000000000 --- a/src/ClientInternal.hxx +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2003-2014 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 "check.h" -#include "Client.hxx" -#include "command/CommandResult.hxx" - -static constexpr unsigned CLIENT_MAX_SUBSCRIPTIONS = 16; -static constexpr unsigned CLIENT_MAX_MESSAGES = 64; - -extern const class Domain client_domain; - -extern int client_timeout; -extern size_t client_max_command_list_size; -extern size_t client_max_output_buffer_size; - -CommandResult -client_process_line(Client &client, char *line); - -#endif diff --git a/src/ClientList.cxx b/src/ClientList.cxx deleted file mode 100644 index 101802479..000000000 --- a/src/ClientList.cxx +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2003-2014 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 "ClientList.hxx" -#include "ClientInternal.hxx" - -#include - -#include - -void -ClientList::Remove(Client &client) -{ - assert(size > 0); - assert(!list.empty()); - - auto i = std::find(list.begin(), list.end(), &client); - assert(i != list.end()); - list.erase(i); - --size; -} - -void -ClientList::CloseAll() -{ - while (!list.empty()) { - delete list.front(); - list.pop_front(); - -#ifndef NDEBUG - --size; -#endif - } - - assert(size == 0); -} - -void -ClientList::IdleAdd(unsigned flags) -{ - assert(flags != 0); - - for (const auto &client : list) - client->IdleAdd(flags); -} diff --git a/src/ClientList.hxx b/src/ClientList.hxx deleted file mode 100644 index 35022fbf1..000000000 --- a/src/ClientList.hxx +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2003-2014 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_LIST_HXX -#define MPD_CLIENT_LIST_HXX - -#include - -class Client; - -class ClientList { - const unsigned max_size; - - unsigned size; - std::list list; - -public: - ClientList(unsigned _max_size) - :max_size(_max_size), size(0) {} - ~ClientList() { - CloseAll(); - } - - std::list::iterator begin() { - return list.begin(); - } - - std::list::iterator end() { - return list.end(); - } - - bool IsFull() const { - return size >= max_size; - } - - void Add(Client &client) { - list.push_front(&client); - ++size; - } - - void Remove(Client &client); - - void CloseAll(); - - void IdleAdd(unsigned flags); -}; - -#endif diff --git a/src/ClientMessage.cxx b/src/ClientMessage.cxx deleted file mode 100644 index be6d2f007..000000000 --- a/src/ClientMessage.cxx +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2003-2014 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 "util/CharUtil.hxx" -#include "Compiler.h" - -gcc_const -static bool -valid_channel_char(const char ch) -{ - return IsAlphaNumericASCII(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; -} diff --git a/src/ClientMessage.hxx b/src/ClientMessage.hxx deleted file mode 100644 index 3d28e01dc..000000000 --- a/src/ClientMessage.hxx +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2003-2014 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 "Compiler.h" - -#include - -/** - * A client-to-client message. - */ -class ClientMessage { - std::string channel, message; - -public: - template - ClientMessage(T &&_channel, U &&_message) - :channel(std::forward(_channel)), - message(std::forward(_message)) {} - - const char *GetChannel() const { - return channel.c_str(); - } - - const char *GetMessage() const { - return message.c_str(); - } -}; - -gcc_pure -bool -client_message_valid_channel_name(const char *name); - -#endif diff --git a/src/ClientNew.cxx b/src/ClientNew.cxx deleted file mode 100644 index a080e9ec6..000000000 --- a/src/ClientNew.cxx +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2003-2014 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 "ClientList.hxx" -#include "Partition.hxx" -#include "Instance.hxx" -#include "system/fd_util.h" -#include "system/Resolver.hxx" -#include "Permission.hxx" -#include "util/Error.hxx" -#include "Log.hxx" - -#include -#ifdef WIN32 -#include -#else -#include -#endif - -#ifdef HAVE_LIBWRAP -#include -#endif - -static const char GREETING[] = "OK MPD " PROTOCOL_VERSION "\n"; - -Client::Client(EventLoop &_loop, Partition &_partition, - int _fd, int _uid, int _num) - :FullyBufferedSocket(_fd, _loop, 16384, client_max_output_buffer_size), - TimeoutMonitor(_loop), - partition(_partition), - playlist(partition.playlist), player_control(partition.pc), - permission(getDefaultPermissions()), - uid(_uid), - num(_num), - idle_waiting(false), idle_flags(0), - num_subscriptions(0) -{ - TimeoutMonitor::ScheduleSeconds(client_timeout); -} - -void -client_new(EventLoop &loop, Partition &partition, - int fd, const struct sockaddr *sa, size_t sa_length, int uid) -{ - static unsigned int next_client_num; - const auto remote = sockaddr_to_string(sa, sa_length); - - assert(fd >= 0); - -#ifdef HAVE_LIBWRAP - if (sa->sa_family != AF_UNIX) { - // TODO: shall we obtain the program name from argv[0]? - const char *progname = "mpd"; - - struct request_info req; - request_init(&req, RQ_FILE, fd, RQ_DAEMON, progname, 0); - - fromhost(&req); - - if (!hosts_access(&req)) { - /* tcp wrappers says no */ - FormatWarning(client_domain, - "libwrap refused connection (libwrap=%s) from %s", - progname, remote.c_str()); - - close_socket(fd); - return; - } - } -#endif /* HAVE_WRAP */ - - ClientList &client_list = *partition.instance.client_list; - if (client_list.IsFull()) { - LogWarning(client_domain, "Max connections reached"); - close_socket(fd); - return; - } - - Client *client = new Client(loop, partition, fd, uid, - next_client_num++); - - (void)send(fd, GREETING, sizeof(GREETING) - 1, 0); - - client_list.Add(*client); - - FormatInfo(client_domain, "[%u] opened from %s", - client->num, remote.c_str()); -} - -void -Client::Close() -{ - partition.instance.client_list->Remove(*this); - - SetExpired(); - - FormatInfo(client_domain, "[%u] closed", num); - delete this; -} diff --git a/src/ClientProcess.cxx b/src/ClientProcess.cxx deleted file mode 100644 index 96099a91c..000000000 --- a/src/ClientProcess.cxx +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2003-2014 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 "command/AllCommands.hxx" -#include "Log.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 CommandResult -client_process_command_list(Client &client, bool list_ok, - std::list &&list) -{ - CommandResult ret = CommandResult::OK; - unsigned num = 0; - - for (auto &&i : list) { - char *cmd = &*i.begin(); - - FormatDebug(client_domain, "process command \"%s\"", cmd); - ret = command_process(client, num++, cmd); - FormatDebug(client_domain, "command returned %i", ret); - if (ret != CommandResult::OK || client.IsExpired()) - break; - else if (list_ok) - client_puts(client, "list_OK\n"); - } - - return ret; -} - -CommandResult -client_process_line(Client &client, char *line) -{ - CommandResult 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); - } - - /* 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 CommandResult::OK; - } else if (client.idle_waiting) { - /* during idle mode, clients must not send anything - except "noidle" */ - FormatWarning(client_domain, - "[%u] command \"%s\" during idle", - client.num, line); - return CommandResult::CLOSE; - } - - if (client.cmd_list.IsActive()) { - if (strcmp(line, CLIENT_LIST_MODE_END) == 0) { - FormatDebug(client_domain, - "[%u] process command list", - client.num); - - auto &&cmd_list = client.cmd_list.Commit(); - - ret = client_process_command_list(client, - client.cmd_list.IsOKMode(), - std::move(cmd_list)); - FormatDebug(client_domain, - "[%u] process command " - "list returned %i", client.num, ret); - - if (ret == CommandResult::CLOSE || - client.IsExpired()) - return CommandResult::CLOSE; - - if (ret == CommandResult::OK) - command_success(client); - - client.cmd_list.Reset(); - } else { - if (!client.cmd_list.Add(line)) { - FormatWarning(client_domain, - "[%u] command list size " - "is larger than the max (%lu)", - client.num, - (unsigned long)client_max_command_list_size); - return CommandResult::CLOSE; - } - - ret = CommandResult::OK; - } - } else { - if (strcmp(line, CLIENT_LIST_MODE_BEGIN) == 0) { - client.cmd_list.Begin(false); - ret = CommandResult::OK; - } else if (strcmp(line, CLIENT_LIST_OK_MODE_BEGIN) == 0) { - client.cmd_list.Begin(true); - ret = CommandResult::OK; - } else { - FormatDebug(client_domain, - "[%u] process command \"%s\"", - client.num, line); - ret = command_process(client, 0, line); - FormatDebug(client_domain, - "[%u] command returned %i", - client.num, ret); - - if (ret == CommandResult::CLOSE || - client.IsExpired()) - return CommandResult::CLOSE; - - if (ret == CommandResult::OK) - command_success(client); - } - } - - return ret; -} diff --git a/src/ClientRead.cxx b/src/ClientRead.cxx deleted file mode 100644 index 19deebd52..000000000 --- a/src/ClientRead.cxx +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2003-2014 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 "event/Loop.hxx" -#include "util/CharUtil.hxx" - -#include - -BufferedSocket::InputResult -Client::OnSocketInput(void *data, size_t length) -{ - char *p = (char *)data; - char *newline = (char *)memchr(p, '\n', length); - if (newline == nullptr) - return InputResult::MORE; - - TimeoutMonitor::ScheduleSeconds(client_timeout); - - BufferedSocket::ConsumeInput(newline + 1 - p); - - /* skip whitespace at the end of the line */ - while (newline > p && IsWhitespaceOrNull(newline[-1])) - --newline; - - /* terminate the string at the end of the line */ - *newline = 0; - - CommandResult result = client_process_line(*this, p); - switch (result) { - case CommandResult::OK: - case CommandResult::IDLE: - case CommandResult::ERROR: - break; - - case CommandResult::KILL: - Close(); - main_loop->Break(); - return InputResult::CLOSED; - - case CommandResult::FINISH: - if (Flush()) - Close(); - return InputResult::CLOSED; - - case CommandResult::CLOSE: - Close(); - return InputResult::CLOSED; - } - - if (IsExpired()) { - Close(); - return InputResult::CLOSED; - } - - return InputResult::AGAIN; -} diff --git a/src/ClientSubscribe.cxx b/src/ClientSubscribe.cxx deleted file mode 100644 index 8ea2e363b..000000000 --- a/src/ClientSubscribe.cxx +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2003-2014 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 "Idle.hxx" - -#include - -Client::SubscribeResult -Client::Subscribe(const char *channel) -{ - assert(channel != nullptr); - - if (!client_message_valid_channel_name(channel)) - return Client::SubscribeResult::INVALID; - - if (num_subscriptions >= CLIENT_MAX_SUBSCRIPTIONS) - return Client::SubscribeResult::FULL; - - auto r = subscriptions.insert(channel); - if (!r.second) - return Client::SubscribeResult::ALREADY; - - ++num_subscriptions; - - idle_add(IDLE_SUBSCRIPTION); - - return Client::SubscribeResult::OK; -} - -bool -Client::Unsubscribe(const char *channel) -{ - const auto i = subscriptions.find(channel); - if (i == subscriptions.end()) - return false; - - assert(num_subscriptions > 0); - - subscriptions.erase(i); - --num_subscriptions; - - idle_add(IDLE_SUBSCRIPTION); - - assert((num_subscriptions == 0) == - subscriptions.empty()); - - return true; -} - -void -Client::UnsubscribeAll() -{ - subscriptions.clear(); - num_subscriptions = 0; -} - -bool -Client::PushMessage(const ClientMessage &msg) -{ - if (messages.size() >= CLIENT_MAX_MESSAGES || - !IsSubscribed(msg.GetChannel())) - return false; - - if (messages.empty()) - IdleAdd(IDLE_MESSAGE); - - messages.push_back(msg); - return true; -} diff --git a/src/ClientWrite.cxx b/src/ClientWrite.cxx deleted file mode 100644 index b5d172a8d..000000000 --- a/src/ClientWrite.cxx +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2003-2014 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 "util/FormatString.hxx" - -#include - -/** - * Write a block of data to the client. - */ -static void -client_write(Client &client, const char *data, size_t length) -{ - /* if the client is going to be closed, do nothing */ - if (client.IsExpired() || length == 0) - return; - - client.Write(data, length); -} - -void -client_puts(Client &client, const char *s) -{ - client_write(client, s, strlen(s)); -} - -void -client_vprintf(Client &client, const char *fmt, va_list args) -{ - char *p = FormatNewV(fmt, args); - client_write(client, p, strlen(p)); - delete[] p; -} - -void -client_printf(Client &client, const char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - client_vprintf(client, fmt, args); - va_end(args); -} diff --git a/src/DatabasePrint.cxx b/src/DatabasePrint.cxx index fc149316d..b22141aef 100644 --- a/src/DatabasePrint.cxx +++ b/src/DatabasePrint.cxx @@ -23,7 +23,7 @@ #include "SongFilter.hxx" #include "SongPrint.hxx" #include "TimePrint.hxx" -#include "Client.hxx" +#include "client/Client.hxx" #include "tag/Tag.hxx" #include "LightSong.hxx" #include "LightDirectory.hxx" diff --git a/src/Listen.cxx b/src/Listen.cxx index 9cdf1b823..faa3e0db1 100644 --- a/src/Listen.cxx +++ b/src/Listen.cxx @@ -21,7 +21,7 @@ #include "Listen.hxx" #include "Main.hxx" #include "Instance.hxx" -#include "Client.hxx" +#include "client/Client.hxx" #include "config/ConfigData.hxx" #include "config/ConfigGlobal.hxx" #include "config/ConfigOption.hxx" diff --git a/src/Main.cxx b/src/Main.cxx index f91c63d99..a07f02389 100644 --- a/src/Main.cxx +++ b/src/Main.cxx @@ -32,8 +32,8 @@ #include "DatabaseSimple.hxx" #include "Permission.hxx" #include "Listen.hxx" -#include "Client.hxx" -#include "ClientList.hxx" +#include "client/Client.hxx" +#include "client/ClientList.hxx" #include "command/AllCommands.hxx" #include "Partition.hxx" #include "Volume.hxx" diff --git a/src/PlaylistPrint.cxx b/src/PlaylistPrint.cxx index 56fbddfaf..2555acc2a 100644 --- a/src/PlaylistPrint.cxx +++ b/src/PlaylistPrint.cxx @@ -25,7 +25,7 @@ #include "SongPrint.hxx" #include "DatabaseGlue.hxx" #include "DatabasePlugin.hxx" -#include "Client.hxx" +#include "client/Client.hxx" #include "InputStream.hxx" #include "DetachedSong.hxx" #include "fs/Traits.hxx" diff --git a/src/SongPrint.cxx b/src/SongPrint.cxx index cde6ef7d1..18d732161 100644 --- a/src/SongPrint.cxx +++ b/src/SongPrint.cxx @@ -24,7 +24,7 @@ #include "TimePrint.hxx" #include "TagPrint.hxx" #include "Mapper.hxx" -#include "Client.hxx" +#include "client/Client.hxx" #include "util/UriUtil.hxx" #define SONG_FILE "file: " diff --git a/src/Stats.cxx b/src/Stats.cxx index 39a553513..5b979b322 100644 --- a/src/Stats.cxx +++ b/src/Stats.cxx @@ -20,7 +20,7 @@ #include "config.h" #include "Stats.hxx" #include "PlayerControl.hxx" -#include "Client.hxx" +#include "client/Client.hxx" #include "DatabaseSelection.hxx" #include "DatabaseGlue.hxx" #include "DatabasePlugin.hxx" diff --git a/src/StickerPrint.cxx b/src/StickerPrint.cxx index d849b0d15..a952ff203 100644 --- a/src/StickerPrint.cxx +++ b/src/StickerPrint.cxx @@ -20,7 +20,7 @@ #include "config.h" #include "StickerPrint.hxx" #include "StickerDatabase.hxx" -#include "Client.hxx" +#include "client/Client.hxx" void sticker_print_value(Client &client, diff --git a/src/TagPrint.cxx b/src/TagPrint.cxx index 4f5ea8be3..228b5fd90 100644 --- a/src/TagPrint.cxx +++ b/src/TagPrint.cxx @@ -21,7 +21,7 @@ #include "TagPrint.hxx" #include "tag/Tag.hxx" #include "tag/TagSettings.h" -#include "Client.hxx" +#include "client/Client.hxx" #define SONG_TIME "Time: " diff --git a/src/TimePrint.cxx b/src/TimePrint.cxx index f02c25225..5526ec7d6 100644 --- a/src/TimePrint.cxx +++ b/src/TimePrint.cxx @@ -19,7 +19,7 @@ #include "config.h" #include "TimePrint.hxx" -#include "Client.hxx" +#include "client/Client.hxx" void time_print(Client &client, const char *name, time_t t) diff --git a/src/client/Client.cxx b/src/client/Client.cxx new file mode 100644 index 000000000..ce99faa89 --- /dev/null +++ b/src/client/Client.cxx @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2003-2014 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 "util/Domain.hxx" + +const Domain client_domain("client"); diff --git a/src/client/Client.hxx b/src/client/Client.hxx new file mode 100644 index 000000000..ec7d2d741 --- /dev/null +++ b/src/client/Client.hxx @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2003-2014 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 "check.h" +#include "ClientMessage.hxx" +#include "command/CommandListBuilder.hxx" +#include "event/FullyBufferedSocket.hxx" +#include "event/TimeoutMonitor.hxx" +#include "Compiler.h" + +#include +#include +#include + +#include +#include + +struct sockaddr; +class EventLoop; +struct Partition; + +class Client final : private FullyBufferedSocket, TimeoutMonitor { +public: + Partition &partition; + struct playlist &playlist; + struct PlayerControl &player_control; + + unsigned permission; + + /** the uid of the client process, or -1 if unknown */ + int uid; + + CommandListBuilder cmd_list; + + unsigned int num; /* client number */ + + /** 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. + */ + std::set 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. + */ + std::list messages; + + Client(EventLoop &loop, Partition &partition, + int fd, int uid, int num); + + ~Client() { + if (FullyBufferedSocket::IsDefined()) + FullyBufferedSocket::Close(); + } + + bool IsConnected() const { + return FullyBufferedSocket::IsDefined(); + } + + gcc_pure + bool IsExpired() const { + return !FullyBufferedSocket::IsDefined(); + } + + void Close(); + void SetExpired(); + + using FullyBufferedSocket::Write; + + /** + * returns the uid of the client process, or a negative value + * if the uid is unknown + */ + int GetUID() const { + return uid; + } + + /** + * Is this client running on the same machine, connected with + * a local (UNIX domain) socket? + */ + bool IsLocal() const { + return uid > 0; + } + + unsigned GetPermission() const { + return permission; + } + + void SetPermission(unsigned _permission) { + permission = _permission; + } + + /** + * Send "idle" response to this client. + */ + void IdleNotify(); + void IdleAdd(unsigned flags); + bool IdleWait(unsigned flags); + + enum class SubscribeResult { + /** success */ + OK, + + /** invalid channel name */ + INVALID, + + /** already subscribed to this channel */ + ALREADY, + + /** too many subscriptions */ + FULL, + }; + + gcc_pure + bool IsSubscribed(const char *channel_name) const { + return subscriptions.find(channel_name) != subscriptions.end(); + } + + SubscribeResult Subscribe(const char *channel); + bool Unsubscribe(const char *channel); + void UnsubscribeAll(); + bool PushMessage(const ClientMessage &msg); + +private: + /* virtual methods from class BufferedSocket */ + virtual InputResult OnSocketInput(void *data, size_t length) override; + virtual void OnSocketError(Error &&error) override; + virtual void OnSocketClosed() override; + + /* virtual methods from class TimeoutMonitor */ + virtual void OnTimeout() override; +}; + +void client_manager_init(void); + +void +client_new(EventLoop &loop, Partition &partition, + int fd, const sockaddr *sa, size_t sa_length, int uid); + +/** + * Write a C string to the client. + */ +void client_puts(Client &client, const char *s); + +/** + * Write a printf-like formatted string to the client. + */ +void client_vprintf(Client &client, const char *fmt, va_list args); + +/** + * Write a printf-like formatted string to the client. + */ +gcc_printf(2,3) +void +client_printf(Client &client, const char *fmt, ...); + +#endif diff --git a/src/client/ClientEvent.cxx b/src/client/ClientEvent.cxx new file mode 100644 index 000000000..fd9f24b0d --- /dev/null +++ b/src/client/ClientEvent.cxx @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2003-2014 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 "util/Error.hxx" +#include "Log.hxx" + +void +Client::OnSocketError(Error &&error) +{ + FormatError(error, "error on client %d", num); + + SetExpired(); +} + +void +Client::OnSocketClosed() +{ + SetExpired(); +} diff --git a/src/client/ClientExpire.cxx b/src/client/ClientExpire.cxx new file mode 100644 index 000000000..5891756b6 --- /dev/null +++ b/src/client/ClientExpire.cxx @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2003-2014 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 "Log.hxx" + +void +Client::SetExpired() +{ + if (IsExpired()) + return; + + FullyBufferedSocket::Close(); + TimeoutMonitor::Schedule(0); +} + +void +Client::OnTimeout() +{ + if (!IsExpired()) { + assert(!idle_waiting); + FormatDebug(client_domain, "[%u] timeout", num); + } + + Close(); +} diff --git a/src/client/ClientFile.cxx b/src/client/ClientFile.cxx new file mode 100644 index 000000000..bdd9b0426 --- /dev/null +++ b/src/client/ClientFile.cxx @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2003-2014 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 "ClientFile.hxx" +#include "Client.hxx" +#include "protocol/Ack.hxx" +#include "fs/Path.hxx" +#include "fs/FileSystem.hxx" +#include "util/Error.hxx" + +#include +#include + +bool +client_allow_file(const Client &client, Path path_fs, Error &error) +{ +#ifdef WIN32 + (void)client; + (void)path_fs; + + error.Set(ack_domain, ACK_ERROR_PERMISSION, "Access denied"); + return false; +#else + const int uid = client.GetUID(); + if (uid >= 0 && (uid_t)uid == geteuid()) + /* always allow access if user runs his own MPD + instance */ + return true; + + if (uid <= 0) { + /* unauthenticated client */ + error.Set(ack_domain, ACK_ERROR_PERMISSION, "Access denied"); + return false; + } + + struct stat st; + if (!StatFile(path_fs, st)) { + error.SetErrno(); + return false; + } + + if (st.st_uid != (uid_t)uid && (st.st_mode & 0444) != 0444) { + /* client is not owner */ + error.Set(ack_domain, ACK_ERROR_PERMISSION, "Access denied"); + return false; + } + + return true; +#endif +} diff --git a/src/client/ClientFile.hxx b/src/client/ClientFile.hxx new file mode 100644 index 000000000..5a02a8df7 --- /dev/null +++ b/src/client/ClientFile.hxx @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2003-2014 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 + +class Client; +class Path; +class Error; + +/** + * 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 Client &client, Path path_fs, Error &error); + +#endif diff --git a/src/client/ClientGlobal.cxx b/src/client/ClientGlobal.cxx new file mode 100644 index 000000000..8d90721e9 --- /dev/null +++ b/src/client/ClientGlobal.cxx @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2003-2014 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 "config/ConfigGlobal.hxx" + +#define CLIENT_TIMEOUT_DEFAULT (60) +#define CLIENT_MAX_COMMAND_LIST_DEFAULT (2048*1024) +#define CLIENT_MAX_OUTPUT_BUFFER_SIZE_DEFAULT (8192*1024) + +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_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; +} diff --git a/src/client/ClientIdle.cxx b/src/client/ClientIdle.cxx new file mode 100644 index 000000000..0b4fa5751 --- /dev/null +++ b/src/client/ClientIdle.cxx @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2003-2014 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 "Idle.hxx" + +#include + +void +Client::IdleNotify() +{ + assert(idle_waiting); + assert(idle_flags != 0); + + unsigned flags = idle_flags; + idle_flags = 0; + idle_waiting = false; + + const char *const*idle_names = idle_get_names(); + for (unsigned i = 0; idle_names[i]; ++i) { + if (flags & (1 << i) & idle_subscriptions) + client_printf(*this, "changed: %s\n", + idle_names[i]); + } + + client_puts(*this, "OK\n"); + + TimeoutMonitor::ScheduleSeconds(client_timeout); +} + +void +Client::IdleAdd(unsigned flags) +{ + if (IsExpired()) + return; + + idle_flags |= flags; + if (idle_waiting && (idle_flags & idle_subscriptions)) + IdleNotify(); +} + +bool +Client::IdleWait(unsigned flags) +{ + assert(!idle_waiting); + + idle_waiting = true; + idle_subscriptions = flags; + + if (idle_flags & idle_subscriptions) { + IdleNotify(); + return true; + } else { + /* disable timeouts while in "idle" */ + TimeoutMonitor::Cancel(); + return false; + } +} diff --git a/src/client/ClientInternal.hxx b/src/client/ClientInternal.hxx new file mode 100644 index 000000000..a819d64b8 --- /dev/null +++ b/src/client/ClientInternal.hxx @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2003-2014 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 "check.h" +#include "Client.hxx" +#include "command/CommandResult.hxx" + +static constexpr unsigned CLIENT_MAX_SUBSCRIPTIONS = 16; +static constexpr unsigned CLIENT_MAX_MESSAGES = 64; + +extern const class Domain client_domain; + +extern int client_timeout; +extern size_t client_max_command_list_size; +extern size_t client_max_output_buffer_size; + +CommandResult +client_process_line(Client &client, char *line); + +#endif diff --git a/src/client/ClientList.cxx b/src/client/ClientList.cxx new file mode 100644 index 000000000..101802479 --- /dev/null +++ b/src/client/ClientList.cxx @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2003-2014 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 "ClientList.hxx" +#include "ClientInternal.hxx" + +#include + +#include + +void +ClientList::Remove(Client &client) +{ + assert(size > 0); + assert(!list.empty()); + + auto i = std::find(list.begin(), list.end(), &client); + assert(i != list.end()); + list.erase(i); + --size; +} + +void +ClientList::CloseAll() +{ + while (!list.empty()) { + delete list.front(); + list.pop_front(); + +#ifndef NDEBUG + --size; +#endif + } + + assert(size == 0); +} + +void +ClientList::IdleAdd(unsigned flags) +{ + assert(flags != 0); + + for (const auto &client : list) + client->IdleAdd(flags); +} diff --git a/src/client/ClientList.hxx b/src/client/ClientList.hxx new file mode 100644 index 000000000..35022fbf1 --- /dev/null +++ b/src/client/ClientList.hxx @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2003-2014 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_LIST_HXX +#define MPD_CLIENT_LIST_HXX + +#include + +class Client; + +class ClientList { + const unsigned max_size; + + unsigned size; + std::list list; + +public: + ClientList(unsigned _max_size) + :max_size(_max_size), size(0) {} + ~ClientList() { + CloseAll(); + } + + std::list::iterator begin() { + return list.begin(); + } + + std::list::iterator end() { + return list.end(); + } + + bool IsFull() const { + return size >= max_size; + } + + void Add(Client &client) { + list.push_front(&client); + ++size; + } + + void Remove(Client &client); + + void CloseAll(); + + void IdleAdd(unsigned flags); +}; + +#endif diff --git a/src/client/ClientMessage.cxx b/src/client/ClientMessage.cxx new file mode 100644 index 000000000..be6d2f007 --- /dev/null +++ b/src/client/ClientMessage.cxx @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2003-2014 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 "util/CharUtil.hxx" +#include "Compiler.h" + +gcc_const +static bool +valid_channel_char(const char ch) +{ + return IsAlphaNumericASCII(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; +} diff --git a/src/client/ClientMessage.hxx b/src/client/ClientMessage.hxx new file mode 100644 index 000000000..3d28e01dc --- /dev/null +++ b/src/client/ClientMessage.hxx @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2003-2014 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 "Compiler.h" + +#include + +/** + * A client-to-client message. + */ +class ClientMessage { + std::string channel, message; + +public: + template + ClientMessage(T &&_channel, U &&_message) + :channel(std::forward(_channel)), + message(std::forward(_message)) {} + + const char *GetChannel() const { + return channel.c_str(); + } + + const char *GetMessage() const { + return message.c_str(); + } +}; + +gcc_pure +bool +client_message_valid_channel_name(const char *name); + +#endif diff --git a/src/client/ClientNew.cxx b/src/client/ClientNew.cxx new file mode 100644 index 000000000..a080e9ec6 --- /dev/null +++ b/src/client/ClientNew.cxx @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2003-2014 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 "ClientList.hxx" +#include "Partition.hxx" +#include "Instance.hxx" +#include "system/fd_util.h" +#include "system/Resolver.hxx" +#include "Permission.hxx" +#include "util/Error.hxx" +#include "Log.hxx" + +#include +#ifdef WIN32 +#include +#else +#include +#endif + +#ifdef HAVE_LIBWRAP +#include +#endif + +static const char GREETING[] = "OK MPD " PROTOCOL_VERSION "\n"; + +Client::Client(EventLoop &_loop, Partition &_partition, + int _fd, int _uid, int _num) + :FullyBufferedSocket(_fd, _loop, 16384, client_max_output_buffer_size), + TimeoutMonitor(_loop), + partition(_partition), + playlist(partition.playlist), player_control(partition.pc), + permission(getDefaultPermissions()), + uid(_uid), + num(_num), + idle_waiting(false), idle_flags(0), + num_subscriptions(0) +{ + TimeoutMonitor::ScheduleSeconds(client_timeout); +} + +void +client_new(EventLoop &loop, Partition &partition, + int fd, const struct sockaddr *sa, size_t sa_length, int uid) +{ + static unsigned int next_client_num; + const auto remote = sockaddr_to_string(sa, sa_length); + + assert(fd >= 0); + +#ifdef HAVE_LIBWRAP + if (sa->sa_family != AF_UNIX) { + // TODO: shall we obtain the program name from argv[0]? + const char *progname = "mpd"; + + struct request_info req; + request_init(&req, RQ_FILE, fd, RQ_DAEMON, progname, 0); + + fromhost(&req); + + if (!hosts_access(&req)) { + /* tcp wrappers says no */ + FormatWarning(client_domain, + "libwrap refused connection (libwrap=%s) from %s", + progname, remote.c_str()); + + close_socket(fd); + return; + } + } +#endif /* HAVE_WRAP */ + + ClientList &client_list = *partition.instance.client_list; + if (client_list.IsFull()) { + LogWarning(client_domain, "Max connections reached"); + close_socket(fd); + return; + } + + Client *client = new Client(loop, partition, fd, uid, + next_client_num++); + + (void)send(fd, GREETING, sizeof(GREETING) - 1, 0); + + client_list.Add(*client); + + FormatInfo(client_domain, "[%u] opened from %s", + client->num, remote.c_str()); +} + +void +Client::Close() +{ + partition.instance.client_list->Remove(*this); + + SetExpired(); + + FormatInfo(client_domain, "[%u] closed", num); + delete this; +} diff --git a/src/client/ClientProcess.cxx b/src/client/ClientProcess.cxx new file mode 100644 index 000000000..96099a91c --- /dev/null +++ b/src/client/ClientProcess.cxx @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2003-2014 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 "command/AllCommands.hxx" +#include "Log.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 CommandResult +client_process_command_list(Client &client, bool list_ok, + std::list &&list) +{ + CommandResult ret = CommandResult::OK; + unsigned num = 0; + + for (auto &&i : list) { + char *cmd = &*i.begin(); + + FormatDebug(client_domain, "process command \"%s\"", cmd); + ret = command_process(client, num++, cmd); + FormatDebug(client_domain, "command returned %i", ret); + if (ret != CommandResult::OK || client.IsExpired()) + break; + else if (list_ok) + client_puts(client, "list_OK\n"); + } + + return ret; +} + +CommandResult +client_process_line(Client &client, char *line) +{ + CommandResult 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); + } + + /* 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 CommandResult::OK; + } else if (client.idle_waiting) { + /* during idle mode, clients must not send anything + except "noidle" */ + FormatWarning(client_domain, + "[%u] command \"%s\" during idle", + client.num, line); + return CommandResult::CLOSE; + } + + if (client.cmd_list.IsActive()) { + if (strcmp(line, CLIENT_LIST_MODE_END) == 0) { + FormatDebug(client_domain, + "[%u] process command list", + client.num); + + auto &&cmd_list = client.cmd_list.Commit(); + + ret = client_process_command_list(client, + client.cmd_list.IsOKMode(), + std::move(cmd_list)); + FormatDebug(client_domain, + "[%u] process command " + "list returned %i", client.num, ret); + + if (ret == CommandResult::CLOSE || + client.IsExpired()) + return CommandResult::CLOSE; + + if (ret == CommandResult::OK) + command_success(client); + + client.cmd_list.Reset(); + } else { + if (!client.cmd_list.Add(line)) { + FormatWarning(client_domain, + "[%u] command list size " + "is larger than the max (%lu)", + client.num, + (unsigned long)client_max_command_list_size); + return CommandResult::CLOSE; + } + + ret = CommandResult::OK; + } + } else { + if (strcmp(line, CLIENT_LIST_MODE_BEGIN) == 0) { + client.cmd_list.Begin(false); + ret = CommandResult::OK; + } else if (strcmp(line, CLIENT_LIST_OK_MODE_BEGIN) == 0) { + client.cmd_list.Begin(true); + ret = CommandResult::OK; + } else { + FormatDebug(client_domain, + "[%u] process command \"%s\"", + client.num, line); + ret = command_process(client, 0, line); + FormatDebug(client_domain, + "[%u] command returned %i", + client.num, ret); + + if (ret == CommandResult::CLOSE || + client.IsExpired()) + return CommandResult::CLOSE; + + if (ret == CommandResult::OK) + command_success(client); + } + } + + return ret; +} diff --git a/src/client/ClientRead.cxx b/src/client/ClientRead.cxx new file mode 100644 index 000000000..19deebd52 --- /dev/null +++ b/src/client/ClientRead.cxx @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2003-2014 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 "event/Loop.hxx" +#include "util/CharUtil.hxx" + +#include + +BufferedSocket::InputResult +Client::OnSocketInput(void *data, size_t length) +{ + char *p = (char *)data; + char *newline = (char *)memchr(p, '\n', length); + if (newline == nullptr) + return InputResult::MORE; + + TimeoutMonitor::ScheduleSeconds(client_timeout); + + BufferedSocket::ConsumeInput(newline + 1 - p); + + /* skip whitespace at the end of the line */ + while (newline > p && IsWhitespaceOrNull(newline[-1])) + --newline; + + /* terminate the string at the end of the line */ + *newline = 0; + + CommandResult result = client_process_line(*this, p); + switch (result) { + case CommandResult::OK: + case CommandResult::IDLE: + case CommandResult::ERROR: + break; + + case CommandResult::KILL: + Close(); + main_loop->Break(); + return InputResult::CLOSED; + + case CommandResult::FINISH: + if (Flush()) + Close(); + return InputResult::CLOSED; + + case CommandResult::CLOSE: + Close(); + return InputResult::CLOSED; + } + + if (IsExpired()) { + Close(); + return InputResult::CLOSED; + } + + return InputResult::AGAIN; +} diff --git a/src/client/ClientSubscribe.cxx b/src/client/ClientSubscribe.cxx new file mode 100644 index 000000000..8ea2e363b --- /dev/null +++ b/src/client/ClientSubscribe.cxx @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2003-2014 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 "Idle.hxx" + +#include + +Client::SubscribeResult +Client::Subscribe(const char *channel) +{ + assert(channel != nullptr); + + if (!client_message_valid_channel_name(channel)) + return Client::SubscribeResult::INVALID; + + if (num_subscriptions >= CLIENT_MAX_SUBSCRIPTIONS) + return Client::SubscribeResult::FULL; + + auto r = subscriptions.insert(channel); + if (!r.second) + return Client::SubscribeResult::ALREADY; + + ++num_subscriptions; + + idle_add(IDLE_SUBSCRIPTION); + + return Client::SubscribeResult::OK; +} + +bool +Client::Unsubscribe(const char *channel) +{ + const auto i = subscriptions.find(channel); + if (i == subscriptions.end()) + return false; + + assert(num_subscriptions > 0); + + subscriptions.erase(i); + --num_subscriptions; + + idle_add(IDLE_SUBSCRIPTION); + + assert((num_subscriptions == 0) == + subscriptions.empty()); + + return true; +} + +void +Client::UnsubscribeAll() +{ + subscriptions.clear(); + num_subscriptions = 0; +} + +bool +Client::PushMessage(const ClientMessage &msg) +{ + if (messages.size() >= CLIENT_MAX_MESSAGES || + !IsSubscribed(msg.GetChannel())) + return false; + + if (messages.empty()) + IdleAdd(IDLE_MESSAGE); + + messages.push_back(msg); + return true; +} diff --git a/src/client/ClientWrite.cxx b/src/client/ClientWrite.cxx new file mode 100644 index 000000000..b5d172a8d --- /dev/null +++ b/src/client/ClientWrite.cxx @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2003-2014 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 "util/FormatString.hxx" + +#include + +/** + * Write a block of data to the client. + */ +static void +client_write(Client &client, const char *data, size_t length) +{ + /* if the client is going to be closed, do nothing */ + if (client.IsExpired() || length == 0) + return; + + client.Write(data, length); +} + +void +client_puts(Client &client, const char *s) +{ + client_write(client, s, strlen(s)); +} + +void +client_vprintf(Client &client, const char *fmt, va_list args) +{ + char *p = FormatNewV(fmt, args); + client_write(client, p, strlen(p)); + delete[] p; +} + +void +client_printf(Client &client, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + client_vprintf(client, fmt, args); + va_end(args); +} diff --git a/src/command/AllCommands.cxx b/src/command/AllCommands.cxx index 47dd70aa0..ed1858ea8 100644 --- a/src/command/AllCommands.cxx +++ b/src/command/AllCommands.cxx @@ -31,7 +31,7 @@ #include "Permission.hxx" #include "tag/TagType.h" #include "protocol/Result.hxx" -#include "Client.hxx" +#include "client/Client.hxx" #include "util/Tokenizer.hxx" #include "util/Error.hxx" diff --git a/src/command/CommandListBuilder.cxx b/src/command/CommandListBuilder.cxx index d6b6318bc..1dcbf2946 100644 --- a/src/command/CommandListBuilder.cxx +++ b/src/command/CommandListBuilder.cxx @@ -19,7 +19,7 @@ #include "config.h" #include "CommandListBuilder.hxx" -#include "ClientInternal.hxx" +#include "client/ClientInternal.hxx" #include diff --git a/src/command/DatabaseCommands.cxx b/src/command/DatabaseCommands.cxx index f650aca0c..eaff1e3ec 100644 --- a/src/command/DatabaseCommands.cxx +++ b/src/command/DatabaseCommands.cxx @@ -24,7 +24,7 @@ #include "DatabasePrint.hxx" #include "DatabaseSelection.hxx" #include "CommandError.hxx" -#include "Client.hxx" +#include "client/Client.hxx" #include "tag/Tag.hxx" #include "util/Error.hxx" #include "SongFilter.hxx" diff --git a/src/command/FileCommands.cxx b/src/command/FileCommands.cxx index 9a9e3ad37..b3676fa48 100644 --- a/src/command/FileCommands.cxx +++ b/src/command/FileCommands.cxx @@ -22,8 +22,8 @@ #include "CommandError.hxx" #include "protocol/Ack.hxx" #include "protocol/Result.hxx" -#include "ClientFile.hxx" -#include "Client.hxx" +#include "client/ClientFile.hxx" +#include "client/Client.hxx" #include "util/CharUtil.hxx" #include "util/UriUtil.hxx" #include "util/Error.hxx" diff --git a/src/command/MessageCommands.cxx b/src/command/MessageCommands.cxx index cb3c9d306..b04e72c07 100644 --- a/src/command/MessageCommands.cxx +++ b/src/command/MessageCommands.cxx @@ -19,8 +19,8 @@ #include "config.h" #include "MessageCommands.hxx" -#include "Client.hxx" -#include "ClientList.hxx" +#include "client/Client.hxx" +#include "client/ClientList.hxx" #include "Instance.hxx" #include "Main.hxx" #include "protocol/Result.hxx" diff --git a/src/command/OtherCommands.cxx b/src/command/OtherCommands.cxx index b7b84bcde..4d61884c1 100644 --- a/src/command/OtherCommands.cxx +++ b/src/command/OtherCommands.cxx @@ -43,8 +43,8 @@ #include "Permission.hxx" #include "PlaylistFile.hxx" #include "PlaylistVector.hxx" -#include "ClientFile.hxx" -#include "Client.hxx" +#include "client/ClientFile.hxx" +#include "client/Client.hxx" #include "Idle.hxx" #include diff --git a/src/command/PlayerCommands.cxx b/src/command/PlayerCommands.cxx index 20de2af92..759a37030 100644 --- a/src/command/PlayerCommands.cxx +++ b/src/command/PlayerCommands.cxx @@ -23,7 +23,7 @@ #include "Playlist.hxx" #include "PlaylistPrint.hxx" #include "update/UpdateGlue.hxx" -#include "Client.hxx" +#include "client/Client.hxx" #include "Volume.hxx" #include "output/OutputAll.hxx" #include "Partition.hxx" diff --git a/src/command/PlaylistCommands.cxx b/src/command/PlaylistCommands.cxx index 2f203678b..0441811c4 100644 --- a/src/command/PlaylistCommands.cxx +++ b/src/command/PlaylistCommands.cxx @@ -28,7 +28,7 @@ #include "playlist/PlaylistQueue.hxx" #include "playlist/Print.hxx" #include "TimePrint.hxx" -#include "Client.hxx" +#include "client/Client.hxx" #include "protocol/ArgParser.hxx" #include "protocol/Result.hxx" #include "ls.hxx" diff --git a/src/command/QueueCommands.cxx b/src/command/QueueCommands.cxx index 7ffaa97ed..e884c71c3 100644 --- a/src/command/QueueCommands.cxx +++ b/src/command/QueueCommands.cxx @@ -25,8 +25,8 @@ #include "DatabaseSelection.hxx" #include "Playlist.hxx" #include "PlaylistPrint.hxx" -#include "ClientFile.hxx" -#include "Client.hxx" +#include "client/ClientFile.hxx" +#include "client/Client.hxx" #include "Partition.hxx" #include "protocol/ArgParser.hxx" #include "protocol/Result.hxx" diff --git a/src/command/TagCommands.cxx b/src/command/TagCommands.cxx index d5544eac5..02e95af71 100644 --- a/src/command/TagCommands.cxx +++ b/src/command/TagCommands.cxx @@ -20,7 +20,7 @@ #include "config.h" #include "TagCommands.hxx" #include "CommandError.hxx" -#include "Client.hxx" +#include "client/Client.hxx" #include "protocol/ArgParser.hxx" #include "protocol/Result.hxx" #include "tag/Tag.hxx" diff --git a/src/decoder/DecoderPrint.cxx b/src/decoder/DecoderPrint.cxx index 06ef1f05e..54b89c36c 100644 --- a/src/decoder/DecoderPrint.cxx +++ b/src/decoder/DecoderPrint.cxx @@ -21,7 +21,7 @@ #include "DecoderPrint.hxx" #include "DecoderList.hxx" #include "DecoderPlugin.hxx" -#include "Client.hxx" +#include "client/Client.hxx" #include diff --git a/src/ls.cxx b/src/ls.cxx index c8061c5c1..7d8a3a447 100644 --- a/src/ls.cxx +++ b/src/ls.cxx @@ -21,7 +21,7 @@ #include "ls.hxx" #include "util/StringUtil.hxx" #include "util/UriUtil.hxx" -#include "Client.hxx" +#include "client/Client.hxx" #include diff --git a/src/output/OutputPrint.cxx b/src/output/OutputPrint.cxx index ee4424df2..66826f140 100644 --- a/src/output/OutputPrint.cxx +++ b/src/output/OutputPrint.cxx @@ -26,7 +26,7 @@ #include "OutputPrint.hxx" #include "OutputAll.hxx" #include "OutputInternal.hxx" -#include "Client.hxx" +#include "client/Client.hxx" void printAudioDevices(Client &client) diff --git a/src/protocol/Result.cxx b/src/protocol/Result.cxx index 11a73a9af..3cc5fc33e 100644 --- a/src/protocol/Result.cxx +++ b/src/protocol/Result.cxx @@ -19,7 +19,7 @@ #include "config.h" #include "Result.hxx" -#include "Client.hxx" +#include "client/Client.hxx" #include diff --git a/src/queue/QueuePrint.cxx b/src/queue/QueuePrint.cxx index 1f3c8fd57..831ecafb9 100644 --- a/src/queue/QueuePrint.cxx +++ b/src/queue/QueuePrint.cxx @@ -22,7 +22,7 @@ #include "Queue.hxx" #include "SongFilter.hxx" #include "SongPrint.hxx" -#include "Client.hxx" +#include "client/Client.hxx" /** * Send detailed information about a range of songs in the queue to a -- cgit v1.2.3