summaryrefslogtreecommitdiff
path: root/src/main.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.cc')
-rw-r--r--src/main.cc392
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...");