diff options
Diffstat (limited to 'src/main.cc')
| -rw-r--r-- | src/main.cc | 392 |
1 files changed, 7 insertions, 385 deletions
diff --git a/src/main.cc b/src/main.cc index 1f4bfd4..ff05a55 100644 --- a/src/main.cc +++ b/src/main.cc @@ -3,15 +3,13 @@ #include "cfg.hh" #include "config.h" #include "http.hh" -#include "json.hh" #include "logger.hh" #include "looper.hh" +#include "server.hh" #include "signals.hh" -#include "uri.hh" #include "websocket.hh" #include <cerrno> -#include <cstdint> #include <cstring> #include <format> #include <functional> @@ -30,391 +28,15 @@ namespace { -class Api { - public: - virtual ~Api() = default; - - [[nodiscard]] - virtual bt::Adapter* adapter() const = 0; - - protected: - Api() = default; -}; - -const std::string_view kSignalUpdateAdapter("controller/update"); -const std::string kSignalUpdateDevicePrefix("device/update/"); - -class Signaler { - public: - virtual ~Signaler() = default; - - virtual void send(std::string_view signal) = 0; - virtual std::unique_ptr<http::Response> handle( - http::Request const& request) = 0; - - protected: - Signaler() = default; -}; - -std::optional<bool> match_bool(std::string_view str) { - if (str == "true") - return true; - if (str == "false") - return false; - return std::nullopt; -} - -class HttpServerDelegate : public http::Server::Delegate { - public: - HttpServerDelegate(Api& api, Signaler& signaler) - : api_(api), - signaler_(signaler), - json_writer_(json::writer(json_tmp_)), - json_mimetype_(http::MimeType::create("application", "json")) {} - - std::unique_ptr<http::Response> handle( - http::Request const& request) override { - if (request.path().starts_with("/api/v1/")) { - auto path = request.path().substr(8); - if (path == "status") { - if (request.method() != "GET") { - return http::Response::status(http::StatusCode::kMethodNotAllowed); - } - - return status_ok(); - } - - if (path == "controller") { - if (request.method() != "GET") { - return http::Response::status(http::StatusCode::kMethodNotAllowed); - } - - auto const* adapter = api_.adapter(); - json_writer_->clear(); - write_adapter(adapter); - - return http::Response::content(json_tmp_, *json_mimetype_); - } - - if (path == "controller/discoverable") { - if (request.method() != "POST") { - return http::Response::status(http::StatusCode::kMethodNotAllowed); - } - std::optional<bool> value = match_bool(request.body()); - auto* adapter = api_.adapter(); - if (adapter && value.has_value()) { - adapter->set_discoverable(value.value()); - return status_ok(); - } - return status_error("Bad state"); - } - - if (path.starts_with("device/")) { - if (request.method() != "GET") { - return http::Response::status(http::StatusCode::kMethodNotAllowed); - } - - std::string tmp; - auto address = uri::decode(path.substr(7), tmp); - auto const* adapter = api_.adapter(); - if (adapter) { - for (auto* device : adapter->devices()) { - if (device->address() == address) { - json_writer_->clear(); - write_device(*device); - return http::Response::content(json_tmp_, *json_mimetype_); - } - } - } - return http::Response::status(http::StatusCode::kNotFound); - } - - if (path == "events") { - if (request.method() != "GET") { - return http::Response::status(http::StatusCode::kMethodNotAllowed); - } - - auto resp = signaler_.handle(request); - if (resp) - return resp; - return http::Response::status(http::StatusCode::kBadRequest); - } - } - - return http::Response::status(http::StatusCode::kNotFound); - } - - private: - std::unique_ptr<http::Response> status_ok() { - json_writer_->clear(); - json_writer_->start_object(); - json_writer_->key("status"); - json_writer_->value("OK"); - json_writer_->end_object(); - return http::Response::content(json_tmp_, *json_mimetype_); - } - - std::unique_ptr<http::Response> status_error(std::string_view message) { - json_writer_->clear(); - json_writer_->start_object(); - json_writer_->key("status"); - json_writer_->value("error"); - json_writer_->key("message"); - json_writer_->value(message); - json_writer_->end_object(); - return http::Response::content(json_tmp_, *json_mimetype_); - } - - void write_adapter(bt::Adapter const* adapter) { - json_writer_->start_object(); - json_writer_->key("name"); - json_writer_->value(adapter ? adapter->name() : "unknown"); - json_writer_->key("pairable"); - json_writer_->value(adapter ? adapter->pairable() : false); - json_writer_->key("discoverable"); - json_writer_->value(adapter ? adapter->discoverable() : false); - json_writer_->key("discoverable_timeout_seconds"); - json_writer_->value(adapter ? adapter->discoverable_timeout_seconds() : 0); - json_writer_->key("pairing"); - json_writer_->value(adapter ? adapter->pairing() : false); - - json_writer_->key("devices"); - json_writer_->start_array(); - - if (adapter) { - for (auto* device : adapter->devices()) { - write_device(*device); - } - } - - json_writer_->end_array(); - json_writer_->end_object(); - } - - void write_device(bt::Device const& device) { - json_writer_->start_object(); - json_writer_->key("name"); - json_writer_->value(device.name()); - json_writer_->key("address"); - json_writer_->value(device.address()); - json_writer_->key("paired"); - json_writer_->value(device.paired()); - json_writer_->key("connected"); - json_writer_->value(device.connected()); - - json_writer_->key("playing"); - if (auto* player = device.player()) { - json_writer_->start_object(); - json_writer_->key("status"); - switch (player->status()) { - case bt::Player::Status::kPlaying: - case bt::Player::Status::kForwardSeek: - case bt::Player::Status::kReverseSeek: - json_writer_->value("playing"); - break; - case bt::Player::Status::kStopped: - json_writer_->value("stopped"); - break; - case bt::Player::Status::kPaused: - json_writer_->value("paused"); - break; - case bt::Player::Status::kError: - json_writer_->value("error"); - break; - } - json_writer_->key("title"); - json_writer_->value(player->track_title()); - json_writer_->key("album"); - json_writer_->value(player->track_album()); - json_writer_->key("artist"); - json_writer_->value(player->track_artist()); - json_writer_->end_object(); - } else { - json_writer_->value(nullptr); - } - - json_writer_->end_object(); - } - - Api& api_; - Signaler& signaler_; - std::unique_ptr<json::Writer> json_writer_; - std::string json_tmp_; - std::unique_ptr<http::MimeType> json_mimetype_; -}; - -class BluetoothManagerDelegate : public bt::Manager::Delegate, public Api { - public: - BluetoothManagerDelegate(logger::Logger& logger, Signaler& signaler) - : logger_(logger), signaler_(signaler) {} - - [[nodiscard]] - bt::Adapter* adapter() const override { - return adapter_; - } - - void new_adapter(bt::Adapter* adapter) override { - adapter_ = adapter; - - signaler_.send(kSignalUpdateAdapter); - - if (adapter) { - logger_.info(std::format("New adapter: {} [{}]", adapter->name(), - adapter->address())); - - // Assuming pairable doesn't have a timeout - make it so. - if (adapter_->pairable_timeout_seconds() > 0) { - adapter_->set_pairable_timeout_seconds(0); - } - - // Assuming discoverable has one - if (adapter_->discoverable_timeout_seconds() == 0) { - adapter_->set_discoverable_timeout_seconds(180); - } - } else { - logger_.info("No adapter"); - } - } - - void updated_adapter(bt::Adapter& adapter) override { - if (adapter_ == &adapter) - signaler_.send(kSignalUpdateAdapter); - } - - void added_device(bt::Device& device) override { - logger_.info( - std::format("New device: {} [{}]", device.name(), device.address())); - - if (adapter_) - signaler_.send(kSignalUpdateAdapter); - } - - void removed_device(std::string const& address) override { - logger_.info(std::format("Remove device: [{}]", address)); - - if (adapter_) - signaler_.send(kSignalUpdateAdapter); - } - - void added_player(bt::Device& device, bt::Player& /* player */) override { - logger_.info(std::format("New player for {}", device.name())); - - if (adapter_) - signaler_.send(kSignalUpdateDevicePrefix + device.address()); - } - - void removed_player(bt::Device& device) override { - logger_.info(std::format("Remove player for {}", device.name())); - - if (adapter_) - signaler_.send(kSignalUpdateDevicePrefix + device.address()); - } - - void updated_player(bt::Device& device, bt::Player& /* player */) override { - if (adapter_) - signaler_.send(kSignalUpdateDevicePrefix + device.address()); - } - - void agent_request_pincode( - bt::Device& device, - std::function<void(std::optional<std::string>)> callback) override { - logger_.dbg(std::format("Device request pincode: {}", device.name())); - callback(std::nullopt); - } - - void agent_display_pincode(bt::Device& device, - std::string const& pincode) override { - logger_.dbg(std::format("Device pincode: {} {}", device.name(), pincode)); - } - - void agent_request_passkey( - bt::Device& device, - std::function<void(std::optional<uint32_t>)> callback) override { - logger_.dbg(std::format("Device request passkey: {}", device.name())); - callback(std::nullopt); - } - - void agent_display_passkey(bt::Device& device, uint32_t passkey, - uint16_t /* entered */) override { - logger_.dbg(std::format("Device passkey: {} {}", device.name(), passkey)); - } - - void agent_request_confirmation(bt::Device& device, uint32_t passkey, - std::function<void(bool)> callback) override { - logger_.dbg(std::format("Device request confirmation: {} {}", device.name(), - passkey)); - // Confirm all - callback(true); - } - - void agent_request_authorization( - bt::Device& device, std::function<void(bool)> callback) override { - logger_.dbg(std::format("Device request authorization: {}", device.name())); - callback(false); - } - - void agent_authorize_service(bt::Device& device, std::string const& uuid, - std::function<void(bool)> callback) override { - if (uuid == "0000110d-0000-1000-8000-00805f9b34fb") { - // Advanced Audio Distribution Profile - callback(true); - return; - } - if (uuid == "0000111e-0000-1000-8000-00805f9b34fb") { - // Hands-Free Service Class and Profile - // Not interrested. - callback(false); - return; - } - - logger_.dbg(std::format("Device request authorize unknown service: {} {}", - device.name(), uuid)); - - // Do not authorize unknown services. - callback(false); - } - - void agent_cancel() override {} - - private: - logger::Logger& logger_; - Signaler& signaler_; - bt::Adapter* adapter_{nullptr}; -}; - -class SignalerImpl : public Signaler, ws::Server::Delegate { - public: - SignalerImpl(logger::Logger& logger, cfg::Config const& cfg, - looper::Looper& looper) - : server_(ws::create_server(logger, cfg, looper, *this)) {} - - void send(std::string_view signal) override { - server_->send_text_to_all(signal); - } - - std::unique_ptr<http::Response> handle( - http::Request const& request) override { - return server_->handle(request); - } - - std::unique_ptr<ws::Message> handle(ws::Message const& /* msg */) override { - // Ignore anything sent by clients - return nullptr; - } - - private: - std::unique_ptr<ws::Server> server_; -}; - bool run(logger::Logger& logger, cfg::Config const& cfg, std::unique_ptr<http::OpenPort> port) { auto looper = looper::create(); - SignalerImpl signaler(logger, cfg, *looper); - BluetoothManagerDelegate bt_delegate(logger, signaler); - HttpServerDelegate http_delegate(bt_delegate, signaler); - auto server = - http::create_server(logger, cfg, *looper, std::move(port), http_delegate); - auto manager = bt::create_manager(logger, cfg, *looper, bt_delegate); + auto signaler = server::create_signaler(logger, cfg, *looper); + auto bt_delegate = server::create_bt_delegate(logger, *signaler); + auto http_delegate = server::create_http_delegate(*bt_delegate, *signaler); + auto server = http::create_server(logger, cfg, *looper, std::move(port), + *http_delegate); + auto manager = bt::create_manager(logger, cfg, *looper, *bt_delegate); auto sigint_handler = signals::Handler::create( *looper, signals::Signal::INT, [&looper, &logger]() { logger.info("Received SIGINT, quitting..."); |
