diff --git a/include/amarula/dbus/connman/gservice.hpp b/include/amarula/dbus/connman/gservice.hpp index 1a50a15..8c89d99 100644 --- a/include/amarula/dbus/connman/gservice.hpp +++ b/include/amarula/dbus/connman/gservice.hpp @@ -146,6 +146,11 @@ struct Provider : public GVariantParser { struct Proxy : public GVariantParser { public: enum class Method : uint8_t { Direct = 0, Auto, Manual }; + + explicit Proxy(Proxy::Method method, std::string url = "", + std::vector servers = {}, + std::vector excludes = {}); + friend auto operator<<(std::ostream& ostr, const Proxy& object) -> std::ostream&; [[nodiscard]] auto getMethod() const { return method_; } @@ -160,8 +165,10 @@ struct Proxy : public GVariantParser { std::vector excludes_; explicit Proxy(GVariant* variant) { parse(variant); }; void update(const gchar* key, GVariant* value) override; + [[nodiscard]] auto getVariant() const -> VariantPtr override; friend class ServProperties; + friend class Service; }; struct ServProperties { @@ -216,6 +223,9 @@ struct ServProperties { [[nodiscard]] auto getError() const { return error_; } [[nodiscard]] auto isAutoconnect() const { return autoconnect_; } [[nodiscard]] auto isMDNSEnabled() const { return mdns_; } + [[nodiscard]] auto isMDNSConfigurationEnabled() const { + return mdns_conf_; + } [[nodiscard]] auto isFavorite() const { return favorite_; } [[nodiscard]] auto isImmutable() const { return immutable_; } [[nodiscard]] auto isRoaming() const { return roaming_; } @@ -230,10 +240,22 @@ struct ServProperties { [[nodiscard]] auto getEthernet() const { return ethernet_; } [[nodiscard]] auto getProvider() const { return provider_; } [[nodiscard]] auto getProxy() const { return proxy_; } + [[nodiscard]] auto getProxyConfiguration() const { + return proxy_configuration_; + } [[nodiscard]] auto getName() const { return name_; } [[nodiscard]] auto getNameservers() const { return name_servers_; } + [[nodiscard]] auto getNameserversConfiguration() const { + return name_servers_conf_; + } [[nodiscard]] auto getDomains() const { return domains_; } + [[nodiscard]] auto getDomainsConfiguration() const { + return domains_conf_; + } [[nodiscard]] auto getTimeservers() const { return time_servers_; } + [[nodiscard]] auto getTimeserversConfiguration() const { + return time_servers_conf_; + } private: State state_{}; @@ -244,9 +266,12 @@ struct ServProperties { std::optional> name_servers_; std::optional> name_servers_conf_; std::optional> domains_; + std::optional> domains_conf_; std::optional> time_servers_; + std::optional> time_servers_conf_; bool autoconnect_{false}; bool mdns_{false}; + bool mdns_conf_{false}; bool favorite_{false}; bool immutable_{false}; bool roaming_{false}; @@ -258,6 +283,7 @@ struct ServProperties { std::optional ethernet_{std::nullopt}; std::optional provider_{std::nullopt}; std::optional proxy_{std::nullopt}; + std::optional proxy_configuration_{std::nullopt}; void update(const gchar* key, GVariant* value); @@ -277,12 +303,19 @@ class Service : public DBusProxy { void remove(PropertiesSetCallback callback = nullptr); void setAutoconnect(bool autoconnect, PropertiesSetCallback callback = nullptr); + void setMDNS(bool mdns, PropertiesSetCallback callback = nullptr); void setNameServers(const std::vector& name_servers, PropertiesSetCallback callback = nullptr); + void setTimeServers(const std::vector& time_servers, + PropertiesSetCallback callback = nullptr); + void setDomains(const std::vector& domains, + PropertiesSetCallback callback = nullptr); void setIPv4(const IPv4& ipv4_configuration, PropertiesSetCallback callback = nullptr); void setIPv6(const IPv6& ipv6_configuration, PropertiesSetCallback callback = nullptr); + void setProxy(const Proxy& proxy_configuration, + PropertiesSetCallback callback = nullptr); friend class Manager; }; diff --git a/src/dbus/gconnman_private.hpp b/src/dbus/gconnman_private.hpp index 015ea06..4b1ead3 100644 --- a/src/dbus/gconnman_private.hpp +++ b/src/dbus/gconnman_private.hpp @@ -52,7 +52,7 @@ constexpr auto PROVIDER_STR = "Provider"; constexpr auto HOST_STR = "Host"; constexpr auto DOMAIN_STR = "Domain"; constexpr auto PROXY_STR = "Proxy"; -constexpr auto URL_STR = "Url"; +constexpr auto URL_STR = "URL"; constexpr auto SERVERS_STR = "Servers"; constexpr auto EXCLUDES_STR = "Excludes"; constexpr auto SECURITY_STR = "Security"; @@ -62,8 +62,12 @@ constexpr auto REMOVE_STR = "Remove"; constexpr auto INTERFACE_STR = "Interface"; constexpr auto MTU_STR = "MTU"; constexpr auto NAMESERVERS_CONFIGURATION_STR = "Nameservers.Configuration"; +constexpr auto TIMESERVERS_CONFIGURATION_STR = "Timeservers.Configuration"; +constexpr auto DOMAINS_CONFIGURATION_STR = "Domains.Configuration"; constexpr auto IPV4_CONFIGURATION_STR = "IPv4.Configuration"; constexpr auto IPV6_CONFIGURATION_STR = "IPv6.Configuration"; +constexpr auto PROXY_CONFIGURATION_STR = "Proxy.Configuration"; +constexpr auto MDNS_CONFIGURATION_STR = "mDNS.Configuration"; // Manager interface constexpr auto MANAGER_INTERFACE = "net.connman.Manager"; diff --git a/src/dbus/gconnman_service.cpp b/src/dbus/gconnman_service.cpp index f1efc4c..2d083c6 100644 --- a/src/dbus/gconnman_service.cpp +++ b/src/dbus/gconnman_service.cpp @@ -113,6 +113,13 @@ void Service::setAutoconnect(const bool autoconnect, nullptr, &Service::finishAsyncCall, data.release()); } +void Service::setMDNS(const bool mdns, PropertiesSetCallback callback) { + auto data = prepareCallback(std::move(callback)); + setProperty(MDNS_CONFIGURATION_STR, + g_variant_new_boolean(static_cast(mdns)), nullptr, + &Service::finishAsyncCall, data.release()); +} + void Service::setNameServers(const std::vector& name_servers, PropertiesSetCallback callback) { auto data = prepareCallback(std::move(callback)); @@ -121,6 +128,22 @@ void Service::setNameServers(const std::vector& name_servers, &Service::finishAsyncCall, data.release()); } +void Service::setTimeServers(const std::vector& time_servers, + PropertiesSetCallback callback) { + auto data = prepareCallback(std::move(callback)); + auto variant = vector_to_as(time_servers); + setProperty(TIMESERVERS_CONFIGURATION_STR, variant.get(), nullptr, + &Service::finishAsyncCall, data.release()); +} + +void Service::setDomains(const std::vector& domains, + PropertiesSetCallback callback) { + auto data = prepareCallback(std::move(callback)); + auto variant = vector_to_as(domains); + setProperty(DOMAINS_CONFIGURATION_STR, variant.get(), nullptr, + &Service::finishAsyncCall, data.release()); +} + IPv4::IPv4(const IPv4::Method method, std::string address, std::string netmask, std::string gateway) : method_(method), @@ -285,6 +308,21 @@ void Provider::update(const gchar* key, GVariant* value) { } } +Proxy::Proxy(const Proxy::Method method, std::string url, + std::vector servers, + std::vector excludes) + : method_(method), + url_(std::move(url)), + servers_(std::move(servers)), + excludes_(std::move(excludes)) {} + +void Service::setProxy(const Proxy& proxy_configuration, + PropertiesSetCallback callback) { + auto data = prepareCallback(std::move(callback)); + setProperty(PROXY_CONFIGURATION_STR, proxy_configuration.getVariant().get(), + nullptr, &Service::finishAsyncCall, data.release()); +} + void Proxy::update(const gchar* key, GVariant* value) { if (g_strcmp0(key, METHOD_STR) == 0U) { method_ = @@ -300,6 +338,30 @@ void Proxy::update(const gchar* key, GVariant* value) { } } +auto Proxy::getVariant() const -> VariantPtr { + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}")); + g_variant_builder_add( + &builder, "{sv}", METHOD_STR, + g_variant_new_string( + std::string(PROXY_METHOD_MAP.toString(method_)).c_str())); + if (!url_.empty()) { + g_variant_builder_add(&builder, "{sv}", URL_STR, + g_variant_new_string(url_.c_str())); + } + if (!servers_.empty()) { + g_variant_builder_add(&builder, "{sv}", SERVERS_STR, + vector_to_as(servers_).get()); + } + if (!excludes_.empty()) { + g_variant_builder_add(&builder, "{sv}", EXCLUDES_STR, + vector_to_as(excludes_).get()); + } + + return VariantPtr{g_variant_ref_sink(g_variant_builder_end(&builder)), + &g_variant_unref}; +} + void ServProperties::update(const gchar* key, GVariant* value) { if (g_strcmp0(key, NAME_STR) == 0) { name_ = g_variant_get_string(value, nullptr); @@ -317,6 +379,8 @@ void ServProperties::update(const gchar* key, GVariant* value) { autoconnect_ = g_variant_get_boolean(value) == 1U; } else if (g_strcmp0(key, MDNS_STR) == 0U) { mdns_ = g_variant_get_boolean(value) == 1U; + } else if (g_strcmp0(key, MDNS_CONFIGURATION_STR) == 0U) { + mdns_conf_ = g_variant_get_boolean(value) == 1U; } else if (g_strcmp0(key, STRENGTH_STR) == 0U) { strength_ = g_variant_get_byte(value); } else if (g_strcmp0(key, IPV4_STR) == 0U) { @@ -347,6 +411,10 @@ void ServProperties::update(const gchar* key, GVariant* value) { proxy_ = (g_variant_n_children(value) != 0) ? std::optional(Proxy(value)) : std::nullopt; + } else if (g_strcmp0(key, PROXY_CONFIGURATION_STR) == 0U) { + proxy_configuration_ = (g_variant_n_children(value) != 0) + ? std::optional(Proxy(value)) + : std::nullopt; } else if (g_strcmp0(key, SECURITY_STR) == 0U) { security_ = (g_variant_n_children(value) != 0) ? std::optional>( @@ -367,11 +435,21 @@ void ServProperties::update(const gchar* key, GVariant* value) { (g_variant_n_children(value) != 0) ? std::optional>(as_to_vector(value)) : std::nullopt; + } else if (g_strcmp0(key, DOMAINS_CONFIGURATION_STR) == 0U) { + domains_conf_ = + (g_variant_n_children(value) != 0) + ? std::optional>(as_to_vector(value)) + : std::nullopt; } else if (g_strcmp0(key, TIMESERVERS_STR) == 0U) { time_servers_ = (g_variant_n_children(value) != 0) ? std::optional>(as_to_vector(value)) : std::nullopt; + } else if (g_strcmp0(key, TIMESERVERS_CONFIGURATION_STR) == 0U) { + time_servers_conf_ = + (g_variant_n_children(value) != 0) + ? std::optional>(as_to_vector(value)) + : std::nullopt; } else { LCM_LOG("Unknown or empty property for Service: " << key << '\n'); } @@ -388,6 +466,7 @@ auto operator<<(std::ostream& ost, const ServProperties& obj) -> std::ostream& { ost << "Strength: " << static_cast(obj.strength_) << '\n'; ost << "AutoConnect: " << std::boolalpha << obj.autoconnect_ << '\n'; ost << "mDNS: " << obj.mdns_ << '\n'; + ost << "mDNS.Configuration: " << obj.mdns_conf_ << '\n'; ost << "Favorite: " << obj.favorite_ << '\n'; ost << "Immutable: " << obj.immutable_ << '\n'; ost << "Roaming: " << obj.roaming_ << '\n'; @@ -424,6 +503,14 @@ auto operator<<(std::ostream& ost, const ServProperties& obj) -> std::ostream& { ost << '\n'; } + if (obj.domains_conf_) { + ost << "Domains.Configuration: "; + for (const auto& domain : obj.domains_conf_.value()) { + ost << domain << ' '; + } + ost << '\n'; + } + if (obj.time_servers_) { ost << "TimeServers: "; for (const auto& tserver : obj.time_servers_.value()) { @@ -432,6 +519,14 @@ auto operator<<(std::ostream& ost, const ServProperties& obj) -> std::ostream& { ost << '\n'; } + if (obj.time_servers_conf_) { + ost << "TimeServers.Configuration: "; + for (const auto& tserver : obj.time_servers_conf_.value()) { + ost << tserver << ' '; + } + ost << '\n'; + } + if (obj.ipv4_) { ost << obj.ipv4_.value(); } @@ -462,6 +557,11 @@ auto operator<<(std::ostream& ost, const ServProperties& obj) -> std::ostream& { ost << obj.proxy_.value(); } + if (obj.proxy_configuration_) { + ost << "Configuration "; + ost << obj.proxy_configuration_.value(); + } + return ost; } diff --git a/tests/gconnman_serv_test.cpp b/tests/gconnman_serv_test.cpp index f76e27f..909bb93 100644 --- a/tests/gconnman_serv_test.cpp +++ b/tests/gconnman_serv_test.cpp @@ -17,6 +17,7 @@ using Type = Amarula::DBus::G::Connman::TechProperties::Type; using ServType = Amarula::DBus::G::Connman::ServProperties::Type; using IPv4 = Amarula::DBus::G::Connman::IPv4; using IPv6 = Amarula::DBus::G::Connman::IPv6; +using Proxy = Amarula::DBus::G::Connman::Proxy; TEST(Connman, getServs) { bool called = false; @@ -126,6 +127,126 @@ TEST(Connman, setNameServers) { ASSERT_TRUE(called) << "ServicesChanged callback was never called"; } +TEST(Connman, setTimeServers) { + bool called = false; + { + const ThreadBundle thread_bundle; + const Connman connman; + const auto manager = connman.manager(); + + auto do_on_servs = [&called, main_tid = thread_bundle.main_tid, + loop_tid = thread_bundle.loop_tid]( + const auto& services, + const bool check_thread_id = true) { + if (!called) { + called = true; + if (check_thread_id) { + const auto callback_tid = std::this_thread::get_id(); + EXPECT_NE(callback_tid, main_tid); + EXPECT_NE(callback_tid, loop_tid); + } + ASSERT_FALSE(services.empty()); + for (const auto& serv : services) { + const auto props = serv->properties(); + const auto name = props.getName(); + std::cout << props; + serv->onPropertyChanged([](const auto& properties) { + std::cout << "onPropertyChange:\n"; + std::cout << properties; + }); + serv->setTimeServers( + {"pool.ntp.org", "time.google.com"}, + [serv, name, main_tid, loop_tid](auto success) { + const auto callback_tid = + std::this_thread::get_id(); + EXPECT_NE(callback_tid, main_tid); + EXPECT_NE(callback_tid, loop_tid); + EXPECT_TRUE(success) << "Set setTimeServers for " + << name << " did not succeed"; + serv->setTimeServers( + {}, [name, main_tid, loop_tid](auto success) { + const auto callback_tid = + std::this_thread::get_id(); + EXPECT_NE(callback_tid, main_tid); + EXPECT_NE(callback_tid, loop_tid); + EXPECT_TRUE(success) + << "Reset setTimeServers for " << name + << " did not succeed"; + }); + }); + } + } + }; + + if (manager->services().empty()) { + manager->onServicesChanged(do_on_servs); + } else { + do_on_servs(manager->services(), false); + } + } + ASSERT_TRUE(called) << "ServicesChanged callback was never called"; +} + +TEST(Connman, setDomains) { + bool called = false; + { + const ThreadBundle thread_bundle; + const Connman connman; + const auto manager = connman.manager(); + + auto do_on_servs = [&called, main_tid = thread_bundle.main_tid, + loop_tid = thread_bundle.loop_tid]( + const auto& services, + const bool check_thread_id = true) { + if (!called) { + called = true; + if (check_thread_id) { + const auto callback_tid = std::this_thread::get_id(); + EXPECT_NE(callback_tid, main_tid); + EXPECT_NE(callback_tid, loop_tid); + } + ASSERT_FALSE(services.empty()); + for (const auto& serv : services) { + const auto props = serv->properties(); + const auto name = props.getName(); + std::cout << props; + serv->onPropertyChanged([](const auto& properties) { + std::cout << "onPropertyChange:\n"; + std::cout << properties; + }); + serv->setDomains( + {"example.com", "example.org"}, + [serv, name, main_tid, loop_tid](auto success) { + const auto callback_tid = + std::this_thread::get_id(); + EXPECT_NE(callback_tid, main_tid); + EXPECT_NE(callback_tid, loop_tid); + EXPECT_TRUE(success) << "Set setDomains for " + << name << " did not succeed"; + serv->setDomains( + {}, [name, main_tid, loop_tid](auto success) { + const auto callback_tid = + std::this_thread::get_id(); + EXPECT_NE(callback_tid, main_tid); + EXPECT_NE(callback_tid, loop_tid); + EXPECT_TRUE(success) + << "Reset setDomains for " << name + << " did not succeed"; + }); + }); + } + } + }; + + if (manager->services().empty()) { + manager->onServicesChanged(do_on_servs); + } else { + do_on_servs(manager->services(), false); + } + } + ASSERT_TRUE(called) << "ServicesChanged callback was never called"; +} + TEST(Connman, setIPv4Configuration) { bool called = false; { @@ -252,6 +373,126 @@ TEST(Connman, setIPv6Configuration) { ASSERT_TRUE(called) << "ServicesChanged callback was never called"; } +TEST(Connman, setProxyConfiguration) { + bool called = false; + { + const ThreadBundle thread_bundle; + const Connman connman; + const auto manager = connman.manager(); + + auto do_on_servs = [&called, main_tid = thread_bundle.main_tid, + loop_tid = thread_bundle.loop_tid]( + const auto& services, + const bool check_thread_id = true) { + if (!called) { + called = true; + if (check_thread_id) { + const auto callback_tid = std::this_thread::get_id(); + EXPECT_NE(callback_tid, main_tid); + EXPECT_NE(callback_tid, loop_tid); + } + ASSERT_FALSE(services.empty()); + for (const auto& serv : services) { + const auto props = serv->properties(); + const auto name = props.getName(); + std::cout << props; + serv->onPropertyChanged([](const auto& properties) { + std::cout << "onPropertyChange:\n"; + std::cout << properties; + }); + const auto proxy_config = + Proxy(Proxy::Method::Manual, "", + {"proxy.example.com:8080"}, {"localhost"}); + serv->setProxy(proxy_config, [serv, name, main_tid, + loop_tid](auto success) { + const auto callback_tid = std::this_thread::get_id(); + EXPECT_NE(callback_tid, main_tid); + EXPECT_NE(callback_tid, loop_tid); + EXPECT_TRUE(success) + << "Set setProxyConfiguration manual for " << name + << " did not succeed"; + const auto proxy_config = Proxy(Proxy::Method::Direct); + serv->setProxy(proxy_config, [name, main_tid, + loop_tid](auto success) { + const auto callback_tid = + std::this_thread::get_id(); + EXPECT_NE(callback_tid, main_tid); + EXPECT_NE(callback_tid, loop_tid); + EXPECT_TRUE(success) + << "Set setProxyConfiguration direct for " + << name << " did not succeed"; + }); + }); + } + } + }; + + if (manager->services().empty()) { + manager->onServicesChanged(do_on_servs); + } else { + do_on_servs(manager->services(), false); + } + } + ASSERT_TRUE(called) << "ServicesChanged callback was never called"; +} + +TEST(Connman, setMDNS) { + bool called = false; + { + const ThreadBundle thread_bundle; + const Connman connman; + const auto manager = connman.manager(); + + auto do_on_servs = [&called, main_tid = thread_bundle.main_tid, + loop_tid = thread_bundle.loop_tid]( + const auto& services, + const bool check_thread_id = true) { + if (!called) { + called = true; + if (check_thread_id) { + const auto callback_tid = std::this_thread::get_id(); + EXPECT_NE(callback_tid, main_tid); + EXPECT_NE(callback_tid, loop_tid); + } + ASSERT_FALSE(services.empty()); + for (const auto& serv : services) { + const auto props = serv->properties(); + const auto name = props.getName(); + std::cout << props; + serv->onPropertyChanged([](const auto& properties) { + std::cout << "onPropertyChange:\n"; + std::cout << properties; + }); + serv->setMDNS(true, [serv, name, main_tid, + loop_tid](auto success) { + const auto callback_tid = std::this_thread::get_id(); + EXPECT_NE(callback_tid, main_tid); + EXPECT_NE(callback_tid, loop_tid); + EXPECT_TRUE(success) << "Set setMDNS true for " << name + << " did not succeed"; + serv->setMDNS(false, [name, main_tid, + loop_tid](auto success) { + const auto callback_tid = + std::this_thread::get_id(); + EXPECT_NE(callback_tid, main_tid); + EXPECT_NE(callback_tid, loop_tid); + EXPECT_TRUE(success) << "Set setMDNS false for " + << name << " did not succeed"; + }); + }); + } + } + }; + + if (manager->services().empty()) { + manager->onServicesChanged(do_on_servs); + } else { + do_on_servs(manager->services(), false); + } + } + ASSERT_TRUE(called) << "ServicesChanged callback was never called"; +} + TEST(Connman, ForgetAndDisconnectService) { bool called = false;