From 98fe627ca41f648e0dbf4e0072efa05057f273ca Mon Sep 17 00:00:00 2001 From: Gustavo Sverzut Barbieri Date: Thu, 15 Sep 2016 21:43:19 -0300 Subject: [PATCH] efl_net_session and efl_net_control for ConnMan These are objects to allow control of networking devices (efl_net_control) as well as an application to request for connectivity (efl_net_session). They are loosely based on ConnMan.org, which we already use in Enlightenment Window Manager via DBus access with Eldbus. However they do not map 1:1 as the goal was to expose a viable subset of controls but in a simple and general way, thus nome strings were converted to enums, some arrays of strings were converted to bitwise flags, some names were made more general, such as "service" was turned into "access point" so it doesn't generate confusion with other "network services" (ie: http server), or "favorite" that was renamed to "remembered". Some behavior are slightly different (yet able to be implemented on top), such as "Service.MoveBefore" and "MoveAfter" were converted to a numeric "priority", calculated from service's list index, changing the priority will reoder the list and thus generate the MoveBefore and MoveAfter DBus commands. ConnMan was chosen not only because we already use it, but because its DBus API is sane and simple, with the server doing almost all that we need. This is visible in the efl_net_session, which is completely done in the server and do not require any extra work on our side -- aside from talking DBus and converting to Eo, which is a major work :-D NOTE: ConnMan doesn't use FreeDesktop.Org DBus interfaces such as Properties and ObjectManager, thus we cannot use eldbus_model_object. There are two examples added: - efl_net_session_example: monitors the connection available for an application and try to connect. You need a connman compiled with session_policy_local and a configuration file explained in https://github.com/aldebaran/connman/blob/master/doc/session-policy-format.txt to get a connection if nothing is connected. Otherwise it will just monitor the connectivity state. - efl_net_control_example: monitors, plays the agent and configure the network details. It can enable/disable technologies, connect to access points (services) and configure them. It's quite extensive as allows testing all of ConnMan's DBus API except P2P (Peers). --- configure.ac | 1 + src/Makefile_Ecore_Con.am | 12 +- src/examples/ecore/.gitignore | 2 + src/examples/ecore/Makefile.am | 14 +- src/examples/ecore/efl_net_control_example.c | 1438 ++++++++++++++ src/examples/ecore/efl_net_session_example.c | 181 ++ src/lib/ecore_con/Ecore_Con_Eo.h | 6 +- src/lib/ecore_con/efl_net-connman.c | 109 ++ src/lib/ecore_con/efl_net-connman.h | 122 ++ src/lib/ecore_con/efl_net_control-connman.c | 1233 ++++++++++++ src/lib/ecore_con/efl_net_control.eo | 213 +++ .../efl_net_control_access_point-connman.c | 1692 +++++++++++++++++ .../ecore_con/efl_net_control_access_point.eo | 463 +++++ .../efl_net_control_technology-connman.c | 472 +++++ .../ecore_con/efl_net_control_technology.eo | 106 ++ src/lib/ecore_con/efl_net_session-connman.c | 792 ++++++++ src/lib/ecore_con/efl_net_session.eo | 137 ++ 17 files changed, 6989 insertions(+), 4 deletions(-) create mode 100644 src/examples/ecore/efl_net_control_example.c create mode 100644 src/examples/ecore/efl_net_session_example.c create mode 100644 src/lib/ecore_con/efl_net-connman.c create mode 100644 src/lib/ecore_con/efl_net-connman.h create mode 100644 src/lib/ecore_con/efl_net_control-connman.c create mode 100644 src/lib/ecore_con/efl_net_control.eo create mode 100644 src/lib/ecore_con/efl_net_control_access_point-connman.c create mode 100644 src/lib/ecore_con/efl_net_control_access_point.eo create mode 100644 src/lib/ecore_con/efl_net_control_technology-connman.c create mode 100644 src/lib/ecore_con/efl_net_control_technology.eo create mode 100644 src/lib/ecore_con/efl_net_session-connman.c create mode 100644 src/lib/ecore_con/efl_net_session.eo diff --git a/configure.ac b/configure.ac index f4b7aa57dc..28229149bb 100644 --- a/configure.ac +++ b/configure.ac @@ -3052,6 +3052,7 @@ EFL_INTERNAL_DEPEND_PKG([ECORE_CON], [eet]) EFL_INTERNAL_DEPEND_PKG([ECORE_CON], [eina]) EFL_INTERNAL_DEPEND_PKG([ECORE_CON], [ecore]) EFL_INTERNAL_DEPEND_PKG([ECORE_CON], [emile]) +EFL_INTERNAL_DEPEND_PKG([ECORE_CON], [eldbus]) EFL_ADD_LIBS([ECORE_CON], [-lm]) diff --git a/src/Makefile_Ecore_Con.am b/src/Makefile_Ecore_Con.am index 3e9742e6ab..6fcbcba492 100644 --- a/src/Makefile_Ecore_Con.am +++ b/src/Makefile_Ecore_Con.am @@ -24,6 +24,10 @@ ecore_con_eolian_files = \ lib/ecore_con/efl_net_ssl_context.eo \ lib/ecore_con/efl_net_dialer_ssl.eo \ lib/ecore_con/efl_net_server_ssl.eo \ + lib/ecore_con/efl_net_control_access_point.eo \ + lib/ecore_con/efl_net_control_technology.eo \ + lib/ecore_con/efl_net_control.eo \ + lib/ecore_con/efl_net_session.eo \ lib/ecore_con/ecore_con_eet_base.eo \ lib/ecore_con/ecore_con_eet_server_obj.eo \ lib/ecore_con/ecore_con_eet_client_obj.eo \ @@ -104,7 +108,13 @@ lib/ecore_con/efl_net_server_udp_client.c \ lib/ecore_con/efl_net_socket_ssl.c \ lib/ecore_con/efl_net_ssl_context.c \ lib/ecore_con/efl_net_dialer_ssl.c \ -lib/ecore_con/efl_net_server_ssl.c +lib/ecore_con/efl_net_server_ssl.c \ +lib/ecore_con/efl_net-connman.h \ +lib/ecore_con/efl_net-connman.c \ +lib/ecore_con/efl_net_control_access_point-connman.c \ +lib/ecore_con/efl_net_control_technology-connman.c \ +lib/ecore_con/efl_net_control-connman.c \ +lib/ecore_con/efl_net_session-connman.c EXTRA_DIST2 += lib/ecore_con/ecore_con_legacy.c diff --git a/src/examples/ecore/.gitignore b/src/examples/ecore/.gitignore index cf090e1ccc..a00f15f14e 100644 --- a/src/examples/ecore/.gitignore +++ b/src/examples/ecore/.gitignore @@ -61,3 +61,5 @@ /efl_net_socket_ssl_dialer_example /efl_net_socket_ssl_server_example /*.pem +/efl_net_session_example +/efl_net_control_example diff --git a/src/examples/ecore/Makefile.am b/src/examples/ecore/Makefile.am index dde9e48510..2fa4350638 100644 --- a/src/examples/ecore/Makefile.am +++ b/src/examples/ecore/Makefile.am @@ -88,7 +88,9 @@ efl_net_dialer_websocket_example \ efl_net_dialer_websocket_autobahntestee \ efl_net_dialer_udp_example \ efl_net_socket_ssl_dialer_example \ -efl_net_socket_ssl_server_example +efl_net_socket_ssl_server_example \ +efl_net_session_example \ +efl_net_control_example ECORE_COMMON_LDADD = \ @@ -334,6 +336,12 @@ efl_net_socket_ssl_dialer_example_LDADD = $(ECORE_CON_COMMON_LDADD) efl_net_socket_ssl_server_example_SOURCES = efl_net_socket_ssl_server_example.c efl_net_socket_ssl_server_example_LDADD = $(ECORE_CON_COMMON_LDADD) +efl_net_session_example_SOURCES = efl_net_session_example.c +efl_net_session_example_LDADD = $(ECORE_CON_COMMON_LDADD) + +efl_net_control_example_SOURCES = efl_net_control_example.c +efl_net_control_example_LDADD = $(ECORE_CON_COMMON_LDADD) + SRCS = \ ecore_animator_example.c \ ecore_buffer_example.c \ @@ -391,7 +399,9 @@ efl_net_dialer_websocket_example.c \ efl_net_dialer_websocket_autobahntestee.c \ efl_net_dialer_udp_example.c \ efl_net_socket_ssl_dialer_example.c \ -efl_net_socket_ssl_server_example.c +efl_net_socket_ssl_server_example.c \ +efl_net_session_example.c \ +efl_net_control_example.c %.pem: echo -e "US\nOR\nPortland\nXPTO Ltd\n\nlocalhost\nroot@localhost\n" | openssl req -new -x509 -days 30 -nodes -out $@ -keyout $@ diff --git a/src/examples/ecore/efl_net_control_example.c b/src/examples/ecore/efl_net_control_example.c new file mode 100644 index 0000000000..5d5efa5c0c --- /dev/null +++ b/src/examples/ecore/efl_net_control_example.c @@ -0,0 +1,1438 @@ +#define EFL_BETA_API_SUPPORT 1 +#define EFL_EO_API_SUPPORT 1 +#include +#include +#include +#include + +static int retval = EXIT_SUCCESS; +static Eina_Bool monitoring = EINA_TRUE; + +static const char * +_access_point_state_to_str(Efl_Net_Control_Access_Point_State state) +{ + const char *strs[] = { + [EFL_NET_CONTROL_ACCESS_POINT_STATE_IDLE] = "idle", + [EFL_NET_CONTROL_ACCESS_POINT_STATE_ASSOCIATION] = "association", + [EFL_NET_CONTROL_ACCESS_POINT_STATE_CONFIGURATION] = "configuration", + [EFL_NET_CONTROL_ACCESS_POINT_STATE_LOCAL] = "local", + [EFL_NET_CONTROL_ACCESS_POINT_STATE_ONLINE] = "online", + [EFL_NET_CONTROL_ACCESS_POINT_STATE_DISCONNECT] = "disconnect", + [EFL_NET_CONTROL_ACCESS_POINT_STATE_FAILURE] = "failure", + }; + if ((unsigned)state > sizeof(strs)/sizeof(strs[0])) return "???"; + if (!strs[state]) return "???"; + return strs[state]; +} + +static const char * +_access_point_error_to_str(Efl_Net_Control_Access_Point_Error error) +{ + const char *strs[] = { + [EFL_NET_CONTROL_ACCESS_POINT_ERROR_NONE] = "none", + [EFL_NET_CONTROL_ACCESS_POINT_ERROR_OUT_OF_RANGE] = "out_of_range", + [EFL_NET_CONTROL_ACCESS_POINT_ERROR_PIN_MISSING] = "pin_missing", + [EFL_NET_CONTROL_ACCESS_POINT_ERROR_DHCP_FAILED] = "dhcp_failed", + [EFL_NET_CONTROL_ACCESS_POINT_ERROR_CONNECT_FAILED] = "connect_failed", + [EFL_NET_CONTROL_ACCESS_POINT_ERROR_LOGIN_FAILED] = "login_failed", + }; + if ((unsigned)error > sizeof(strs)/sizeof(strs[0])) return "???"; + if (!strs[error]) return "???"; + return strs[error]; +} + +static const char * +_access_point_security_to_str(Efl_Net_Control_Access_Point_Security security) +{ + static char buf[128]; + int pos = 0; + +#define MAP(x, s) \ + if ((security & EFL_NET_CONTROL_ACCESS_POINT_SECURITY_ ## x) == EFL_NET_CONTROL_ACCESS_POINT_SECURITY_ ## x) \ + snprintf(buf + pos, sizeof(buf) - pos, "%s%s", \ + (pos == 0) ? "" : ", ", s) + + MAP(NONE, "none"); + MAP(WEP, "wep"); + MAP(PSK, "psk"); + MAP(IEEE802_1X, "ieee802.1X"); +#undef MAP + + return buf; +} + +static void +_str_iterator_print(const char *header, const char *footer, Eina_Iterator *it) +{ + Eina_Bool first = EINA_TRUE; + const char *str; + + fputs(header, stdout); + EINA_ITERATOR_FOREACH(it, str) + { + if (first) + { + putc('[', stdout); + first = EINA_FALSE; + } + else fputs(", ", stdout); + printf("'%s'", str); + } + + if (first) fputs("", stdout); + else putc(']', stdout); + fputs(footer, stdout); + eina_iterator_free(it); +} + +static void +_access_point_print(Eo *ap) +{ + const char *address, *netmask, *gateway, *str; + Efl_Net_Control_Access_Point_Ipv4_Method ipv4_method; + Efl_Net_Control_Access_Point_Ipv6_Method ipv6_method; + Efl_Net_Control_Access_Point_Proxy_Method proxy_method; + uint8_t prefix_length; + Eina_Iterator *servers, *excludes; + + printf("INFO: - name=%s\n" + "INFO: - state=%s\n" + "INFO: - error=%s\n" + "INFO: - priority=%u\n" + "INFO: - technology=%s\n" + "INFO: - strength=%hhu%%\n" + "INFO: - roaming=%hhu\n" + "INFO: - auto_connect=%hhu\n" + "INFO: - remembered=%hhu\n" + "INFO: - immutable=%hhu\n" + "INFO: - security=%#x %s\n", + efl_net_control_access_point_name_get(ap), + _access_point_state_to_str(efl_net_control_access_point_state_get(ap)), + _access_point_error_to_str(efl_net_control_access_point_error_get(ap)), + efl_net_control_access_point_priority_get(ap), + efl_net_control_technology_name_get(efl_net_control_access_point_technology_get(ap)), + efl_net_control_access_point_strength_get(ap), + efl_net_control_access_point_roaming_get(ap), + efl_net_control_access_point_auto_connect_get(ap), + efl_net_control_access_point_remembered_get(ap), + efl_net_control_access_point_immutable_get(ap), + efl_net_control_access_point_security_get(ap), + _access_point_security_to_str(efl_net_control_access_point_security_get(ap))); + + /* actual values */ + efl_net_control_access_point_ipv4_get(ap, &ipv4_method, &address, &netmask, &gateway); + if (ipv4_method == EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_OFF) + printf("INFO: - ipv4=off\n"); + else + { + str = "???"; + switch (ipv4_method) + { + case EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_UNSET: + str = ""; break; + case EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_OFF: + str = "off"; break; + case EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_DHCP: + str = "dhcp"; break; + case EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_MANUAL: + str = "manual"; break; + } + printf("INFO: - ipv4=%s address=%s, netmask=%s, gateway=%s\n", + str, address, netmask, gateway); + } + + efl_net_control_access_point_ipv6_get(ap, &ipv6_method, &address, &prefix_length, &netmask, &gateway); + str = "???"; + switch (ipv6_method) + { + case EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_FIXED: + str = "fixed (operator)"; break; + case EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_MANUAL: + str = "manual"; break; + case EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_AUTO_PRIVACY_NONE: + str = "auto (privacy=none)"; break; + case EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_AUTO_PRIVACY_PUBLIC: + str = "auto (privacy=public)"; break; + case EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_AUTO_PRIVACY_TEMPORARY: + str = "auto (privacy=temporary)"; break; + case EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_TUNNEL6TO4: + str = "6-to-4"; break; + case EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_OFF: + str = "off"; break; + case EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_UNSET: + str = ""; break; + } + if ((ipv6_method == EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_OFF) || + (ipv6_method == EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_UNSET)) + printf("INFO: - ipv6=%s\n", str); + else + printf("INFO: - ipv6=%s address=%s/%hhu, netmask=%s, gateway=%s\n", + str, address, prefix_length, netmask, gateway); + + _str_iterator_print("INFO: - name_servers=", "\n", + efl_net_control_access_point_name_servers_get(ap)); + _str_iterator_print("INFO: - time_servers=", "\n", + efl_net_control_access_point_time_servers_get(ap)); + _str_iterator_print("INFO: - domains=", "\n", + efl_net_control_access_point_domains_get(ap)); + + efl_net_control_access_point_proxy_get(ap, &proxy_method, &address, &servers, &excludes); + str = "???"; + switch (proxy_method) + { + case EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_OFF: + str = "off"; break; + case EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_AUTO: + str = "auto"; break; + case EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_MANUAL: + str = "manual"; break; + case EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_UNSET: + str = ""; break; + } + printf("INFO: - proxy=%s, address=%s", str, address ? address : ""); + _str_iterator_print(", servers=", "", servers); + _str_iterator_print(", excludes=", "\n", excludes); + + + /* configured values */ + efl_net_control_access_point_configuration_ipv4_get(ap, &ipv4_method, &address, &netmask, &gateway); + if (ipv4_method == EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_UNSET) + printf("INFO: - configured ipv4=\n"); + else if (ipv4_method == EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_OFF) + printf("INFO: - configured ipv4=off\n"); + else + { + str = "???"; + switch (ipv4_method) + { + case EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_UNSET: + str = ""; break; + case EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_OFF: + str = "off"; break; + case EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_DHCP: + str = "dhcp"; break; + case EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_MANUAL: + str = "manual"; break; + } + printf("INFO: - configured ipv4=%s address=%s, netmask=%s, gateway=%s\n", + str, address, netmask, gateway); + } + + efl_net_control_access_point_configuration_ipv6_get(ap, &ipv6_method, &address, &prefix_length, &netmask, &gateway); + str = "???"; + switch (ipv6_method) + { + case EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_FIXED: + str = "fixed (operator)"; break; + case EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_MANUAL: + str = "manual"; break; + case EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_AUTO_PRIVACY_NONE: + str = "auto (privacy=none)"; break; + case EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_AUTO_PRIVACY_PUBLIC: + str = "auto (privacy=public)"; break; + case EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_AUTO_PRIVACY_TEMPORARY: + str = "auto (privacy=temporary)"; break; + case EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_TUNNEL6TO4: + str = "6-to-4"; break; + case EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_OFF: + str = "off"; break; + case EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_UNSET: + str = ""; break; + } + if ((ipv6_method == EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_OFF) || + (ipv6_method == EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_UNSET)) + printf("INFO: - configured ipv6=%s\n", str); + else + printf("INFO: - configured ipv6=%s address=%s/%hhu, netmask=%s, gateway=%s\n", + str, address, prefix_length, netmask, gateway); + + _str_iterator_print("INFO: - configured name_servers=", "\n", + efl_net_control_access_point_configuration_name_servers_get(ap)); + _str_iterator_print("INFO: - configured time_servers=", "\n", + efl_net_control_access_point_configuration_time_servers_get(ap)); + _str_iterator_print("INFO: - configured domains=", "\n", + efl_net_control_access_point_configuration_domains_get(ap)); + + efl_net_control_access_point_configuration_proxy_get(ap, &proxy_method, &address, &servers, &excludes); + str = "???"; + switch (proxy_method) + { + case EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_OFF: + str = "off"; break; + case EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_AUTO: + str = "auto"; break; + case EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_MANUAL: + str = "manual"; break; + case EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_UNSET: + str = ""; break; + } + printf("INFO: - configured proxy=%s, address=%s", str, address ? address : ""); + _str_iterator_print(", servers=", "", servers); + _str_iterator_print(", excludes=", "\n", excludes); +} + +static void +_access_point_changed(void *data EINA_UNUSED, const Efl_Event *event) +{ + Eo *ap = event->object; + if (!monitoring) return; + printf("INFO: Access Point Changed:\n"); + _access_point_print(ap); +} + +static void +_ctl_access_point_add(void *data EINA_UNUSED, const Efl_Event *event) +{ + Eo *ap = event->info; + if (!monitoring) return; + printf("INFO: Access Point Added:\n"); + _access_point_print(ap); + efl_event_callback_add(ap, EFL_NET_CONTROL_ACCESS_POINT_EVENT_CHANGED, _access_point_changed, NULL); +} + +static void +_ctl_access_point_del(void *data EINA_UNUSED, const Efl_Event *event) +{ + Eo *ap = event->info; + if (!monitoring) return; + printf("INFO: Access Point Removed: %s [%s]\n", + efl_net_control_access_point_name_get(ap), + efl_net_control_technology_name_get(efl_net_control_access_point_technology_get(ap))); +} + +static void +_access_points_list(Eina_Iterator *it) +{ + Eo *ap; + Eina_Bool first = EINA_TRUE; + + EINA_ITERATOR_FOREACH(it, ap) + { + if (first) + { + printf("INFO: PRIO | NAME | FLAGS | STATE | TECH\n" + "INFO: -----+--------------------+-------+---------------+---------------\n"); + first = EINA_FALSE; + } + + printf("INFO: %4u | %-18.18s | %c%c%c | %-13.13s | %s\n", + efl_net_control_access_point_priority_get(ap), + efl_net_control_access_point_name_get(ap), + efl_net_control_access_point_remembered_get(ap) ? 'R' : '.', + efl_net_control_access_point_immutable_get(ap) ? 'I' : '.', + efl_net_control_access_point_auto_connect_get(ap) ? 'A' : '.', + _access_point_state_to_str(efl_net_control_access_point_state_get(ap)), + efl_net_control_technology_name_get(efl_net_control_access_point_technology_get(ap))); + } + + if (!first) + { + printf("INFO: -----+--------------------+-------+---------------+---------------\n" + "INFO: FLAGS: [R]emembered, [I]mmutable, [A]uto-connect\n"); + } + eina_iterator_free(it); +} + +static void +_ctl_access_points_changed(void *data EINA_UNUSED, const Efl_Event *event) +{ + if (!monitoring) return; + _access_points_list(efl_net_control_access_points_get(event->object)); +} + +static const char * +_technology_type_str(Efl_Net_Control_Technology_Type type) +{ + const char *strs[] = { + [EFL_NET_CONTROL_TECHNOLOGY_TYPE_UNKNOWN] = "unknown", + [EFL_NET_CONTROL_TECHNOLOGY_TYPE_SYSTEM] = "system", + [EFL_NET_CONTROL_TECHNOLOGY_TYPE_ETHERNET] = "ethernet", + [EFL_NET_CONTROL_TECHNOLOGY_TYPE_WIFI] = "wifi", + [EFL_NET_CONTROL_TECHNOLOGY_TYPE_BLUETOOTH] = "bluetooth", + [EFL_NET_CONTROL_TECHNOLOGY_TYPE_CELLULAR] = "cellular", + [EFL_NET_CONTROL_TECHNOLOGY_TYPE_GPS] = "gps", + [EFL_NET_CONTROL_TECHNOLOGY_TYPE_VPN] = "vpn", + [EFL_NET_CONTROL_TECHNOLOGY_TYPE_GADGET] = "gadget", + [EFL_NET_CONTROL_TECHNOLOGY_TYPE_P2P] = "p2p", + }; + if ((unsigned)type > sizeof(strs)/sizeof(strs[0])) return "???"; + if (!strs[type]) return "???"; + return strs[type]; +} + +static void +_technology_print(Eo *tech) +{ + Eina_Bool tethering; + const char *teth_id, *teth_pass; + + efl_net_control_technology_tethering_get(tech, &tethering, &teth_id, &teth_pass); + if (!tethering) teth_id = teth_pass = ""; + + printf("INFO: - name=%s\n" + "INFO: - powered=%hhu\n" + "INFO: - connected=%hhu\n" + "INFO: - tethering=%hhu (id='%s', passphrase='%s')\n" + "INFO: - type=%s\n", + efl_net_control_technology_name_get(tech), + efl_net_control_technology_powered_get(tech), + efl_net_control_technology_connected_get(tech), + tethering, teth_id, teth_pass, + _technology_type_str(efl_net_control_technology_type_get(tech))); +} + +static void +_technology_changed(void *data EINA_UNUSED, const Efl_Event *event) +{ + Eo *ap = event->object; + if (!monitoring) return; + printf("INFO: Technology Changed:\n"); + _technology_print(ap); +} + +static void +_ctl_technology_add(void *data EINA_UNUSED, const Efl_Event *event) +{ + Eo *tech = event->info; + if (!monitoring) return; + printf("INFO: Technology Added:\n"); + _technology_print(tech); + efl_event_callback_add(tech, EFL_NET_CONTROL_TECHNOLOGY_EVENT_CHANGED, _technology_changed, NULL); +} + +static void +_ctl_technology_del(void *data EINA_UNUSED, const Efl_Event *event) +{ + Eo *tech = event->info; + if (!monitoring) return; + printf("INFO: Technology Removed: %s [%s]\n", + efl_net_control_technology_name_get(tech), + _technology_type_str(efl_net_control_technology_type_get(tech))); +} + +static void +_ctl_radios_offline_changed(void *data EINA_UNUSED, const Efl_Event *event) +{ + if (!monitoring) return; + printf("INFO: radios_offline=%hhu\n", efl_net_control_radios_offline_get(event->object)); +} + +static void +_ctl_state_changed(void *data EINA_UNUSED, const Efl_Event *event) +{ + const char *str = "???"; + Efl_Net_Control_State state = efl_net_control_state_get(event->object); + switch (state) + { + case EFL_NET_CONTROL_STATE_OFFLINE: str = "offline"; break; + case EFL_NET_CONTROL_STATE_ONLINE: str = "online"; break; + case EFL_NET_CONTROL_STATE_LOCAL: str = "local"; break; + } + if (!monitoring) return; + printf("INFO: state=%s\n", str); +} + +static void +_ctl_agent_released(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED) +{ + printf("INFO: Agent released\n"); +} + +static void +_ctl_agent_error(void *data EINA_UNUSED, const Efl_Event *event) +{ + const Efl_Net_Control_Agent_Error *e = event->info; + printf("INFO: Agent Error:\n" + "INFO: - Access Point: %s\n" + "INFO: - Message: %s\n", + efl_net_control_access_point_name_get(e->access_point), + e->message); +} + +static void +_ctl_agent_browser_url(void *data EINA_UNUSED, const Efl_Event *event) +{ + const Efl_Net_Control_Agent_Browser_Url *e = event->info; + printf("INFO: Agent requested to open browser:\n" + "INFO: - Access Point: %s\n" + "INFO: - URL: %s\n", + efl_net_control_access_point_name_get(e->access_point), + e->url); +} + +static void +_ctl_agent_request_input(void *data EINA_UNUSED, const Efl_Event *event) +{ + Eo *ctl = event->object; + Efl_Net_Control_Agent_Request_Input *ri = event->info; + Eina_List *n; + Efl_Net_Control_Agent_Request_Input_Information *info; + char *name = NULL; + char *username = NULL; + char *passphrase = NULL; + char *ssid = NULL; + char *wps = NULL; + Eina_Slice ssid_slice = { }; + ssize_t r; + size_t len; + + printf("INFO: Needs agent input!\n"); + + EINA_LIST_FOREACH(ri->informational, n, info) + printf("INFO: - %s: %s\n", info->name, info->value); + + + if (ri->fields) + { + printf("INFO: The following fields may be entered:\n"); + if (ri->fields & EFL_NET_CONTROL_AGENT_REQUEST_INPUT_FIELD_NAME) + { + printf("INFO: Name="); fflush(stdout); + len = 0; + r = getline(&name, &len, stdin); + if ((r > 1) && (name[r - 1] == '\n')) + { + name[r - 1] = '\0'; + } + else + { + free(name); + name = NULL; + } + } + + if (ri->fields & EFL_NET_CONTROL_AGENT_REQUEST_INPUT_FIELD_SSID) + { + printf("INFO: SSID="); fflush(stdout); + len = 0; + r = getline(&ssid, &len, stdin); + if ((r > 1) && (ssid[r - 1] == '\n')) + { + ssid[r - 1] = '\0'; + ssid_slice.mem = ssid; + ssid_slice.len = r - 1; + } + else + { + free(ssid); + ssid = NULL; + } + } + + if (ri->fields & EFL_NET_CONTROL_AGENT_REQUEST_INPUT_FIELD_USERNAME) + { + printf("INFO: Username="); fflush(stdout); + len = 0; + r = getline(&username, &len, stdin); + if ((r > 1) && (username[r - 1] == '\n')) + { + username[r - 1] = '\0'; + } + else + { + free(username); + username = NULL; + } + } + + if (ri->fields & EFL_NET_CONTROL_AGENT_REQUEST_INPUT_FIELD_PASSPHRASE) + { + if (ri->passphrase_type) + printf("INFO: Passphrase(%s)=", ri->passphrase_type); + else + printf("INFO: Passphrase="); + fflush(stdout); + len = 0; + r = getline(&passphrase, &len, stdin); + if ((r > 1) && (passphrase[r - 1] == '\n')) + { + passphrase[r - 1] = '\0'; + } + else + { + free(passphrase); + passphrase = NULL; + } + } + + if (ri->fields & EFL_NET_CONTROL_AGENT_REQUEST_INPUT_FIELD_WPS) + { + printf("INFO: WPS (use a single dot, '.', for pushbutton)="); fflush(stdout); + len = 0; + r = getline(&wps, &len, stdin); + if ((r > 1) && (wps[r - 1] == '\n')) + { + wps[r - 1] = '\0'; + if ((r == 2) && (wps[0] == '.')) + wps[0] = '\0'; /* API uses empty string for pushbutton */ + } + else + { + free(wps); + wps = NULL; + } + } + } + + efl_net_control_agent_reply(ctl, + name, + ssid ? &ssid_slice : NULL, + username, + passphrase, + wps); + + free(name); + free(ssid); + free(username); + free(passphrase); + free(wps); +} + +static void +_cmd_technologies_list(Eo *ctl, size_t argc EINA_UNUSED, char **argv EINA_UNUSED) +{ + Eina_Iterator *it = efl_net_control_technologies_get(ctl); + Eo *tech; + Eina_Bool first = EINA_TRUE; + + EINA_ITERATOR_FOREACH(it, tech) + { + if (first) + { + printf("INFO: NAME | ON | CONN | TYPE\n" + "INFO: --------------------+----+------+---------------\n"); + first = EINA_FALSE; + } + + printf("INFO: %-18.18s | %c | %c | %s\n", + efl_net_control_technology_name_get(tech), + efl_net_control_technology_powered_get(tech) ? 'X' : ' ', + efl_net_control_technology_connected_get(tech) ? 'X' : ' ', + _technology_type_str(efl_net_control_technology_type_get(tech))); + } + + if (!first) + { + printf("INFO: --------------------+----+------+---------------\n"); + } + eina_iterator_free(it); +} + +static Eina_Bool +_parse_bool(const char *header, const char *str, Eina_Bool *ret) +{ + if (str == NULL) + { + fprintf(stderr, "ERROR: %s requires a boolean.\n", header); + return EINA_FALSE; + } + if (strcmp(str, "on") == 0) + { + *ret = EINA_TRUE; + return EINA_TRUE; + } + else if (strcmp(str, "off") == 0) + { + *ret = EINA_FALSE; + return EINA_TRUE; + } + else + { + fprintf(stderr, "ERROR: %s required boolean 'on' or 'off', got '%s'\n", header, str); + return EINA_FALSE; + } +} + +static const char * +_fmt_bool(Eina_Bool val) +{ + return val ? "on" : "off"; +} + +static Eo * +_technology_find(Eo *ctl, const char *name) +{ + Eina_Iterator *it; + Eo *child; + + if (!name) + { + fprintf(stderr, "ERROR: required technology name\n"); + return NULL; + } + + it = efl_net_control_technologies_get(ctl); + EINA_ITERATOR_FOREACH(it, child) + { + const char *tn = efl_net_control_technology_name_get(child); + if (strcasecmp(name, tn) == 0) + { + eina_iterator_free(it); + return child; + } + } + fprintf(stderr, "ERROR: did not find technology named '%s'\n", name); + eina_iterator_free(it); + return NULL; +} + +static void +_cmd_technology_show(Eo *ctl, size_t argc EINA_UNUSED, char **argv) +{ + Eo *tech = _technology_find(ctl, argv[1]); + if (!tech) return; + printf("INFO: show technology '%s'\n", argv[1]); + _technology_print(tech); +} + +static void +_technology_scan_done(void *data, const Efl_Event *event EINA_UNUSED) +{ + Eo *tech = data; + printf("INFO: technology '%s' finished scan.\n", + efl_net_control_technology_name_get(tech)); +} + +static void +_technology_scan_error(void *data, const Efl_Event *event) +{ + Eo *tech = data; + Efl_Future_Event_Failure *f = event->info; + printf("INFO: technology '%s' could not scan: %s\n", + efl_net_control_technology_name_get(tech), + eina_error_msg_get(f->error)); +} + +static void +_cmd_technology_scan(Eo *ctl, size_t argc EINA_UNUSED, char **argv) +{ + Eo *tech = _technology_find(ctl, argv[1]); + if (!tech) return; + printf("INFO: started scan on technology '%s'\n", argv[1]); + efl_future_then(efl_net_control_technology_scan(tech), + _technology_scan_done, + _technology_scan_error, + NULL, + tech); +} + +static void +_cmd_technology_powered(Eo *ctl, size_t argc, char **argv) +{ + Eo *tech = _technology_find(ctl, argv[1]); + const char *name; + Eina_Bool powered; + + if (!tech) return; + + name = efl_net_control_technology_name_get(tech); + if (argc <= 2) + { + printf("INFO: technology '%s' powered %s\n", + name, + _fmt_bool(efl_net_control_technology_powered_get(tech))); + return; + } + + if (!_parse_bool(argv[0], argv[2], &powered)) + return; + + printf("INFO: technology '%s' set to powered %s\n", + name, _fmt_bool(powered)); + efl_net_control_technology_powered_set(tech, powered); +} + +static void +_cmd_technology_tethering(Eo *ctl, size_t argc, char **argv) +{ + Eo *tech = _technology_find(ctl, argv[1]); + const char *name; + Eina_Bool enabled; + const char *id = NULL, *pass = NULL; + + if (!tech) return; + + name = efl_net_control_technology_name_get(tech); + if (argc <= 2) + { + efl_net_control_technology_tethering_get(tech, &enabled, &id, &pass); + printf("INFO: technology '%s' tethering %s, id='%s', passphrase='%s'\n", + name, + _fmt_bool(efl_net_control_technology_powered_get(tech)), + id ? id : "", pass ? pass : ""); + return; + } + + if (!_parse_bool(argv[0], argv[2], &enabled)) + return; + + if (argc > 3) id = argv[3]; + if (argc > 4) pass = argv[4]; + + printf("INFO: technology '%s' set to tethering %s, id='%s', pass='%s'\n", + name, _fmt_bool(enabled), id ? id : "", pass ? pass : ""); + efl_net_control_technology_tethering_set(tech, enabled, id, pass); +} + +static void +_cmd_access_points_list(Eo *ctl, size_t argc EINA_UNUSED, char **argv EINA_UNUSED) +{ + _access_points_list(efl_net_control_access_points_get(ctl)); +} + +static Eo * +_access_point_find(Eo *ctl, const char *name) +{ + Eina_Iterator *it; + Eo *child; + uint32_t prio = UINT32_MAX; + + if (!name) + { + fprintf(stderr, "ERROR: required access_point name\n"); + return NULL; + } + + if (name[0] == '#') + prio = strtoul(name + 1, NULL, 10); + + it = efl_net_control_access_points_get(ctl); + EINA_ITERATOR_FOREACH(it, child) + { + if (prio == UINT32_MAX) + { + const char *n = efl_net_control_access_point_name_get(child); + if (strcasecmp(name, n) == 0) + { + eina_iterator_free(it); + return child; + } + } + else + { + uint32_t p = efl_net_control_access_point_priority_get(child); + if (p == prio) + { + eina_iterator_free(it); + return child; + } + } + } + + if (prio == UINT32_MAX) + fprintf(stderr, "ERROR: did not find access_point named '%s'\n", name); + else + fprintf(stderr, "ERROR: did not find access_point at priority '%u'\n", prio); + eina_iterator_free(it); + return NULL; +} + +static void +_cmd_access_point_show(Eo *ctl, size_t argc EINA_UNUSED, char **argv) +{ + Eo *ap = _access_point_find(ctl, argv[1]); + if (!ap) return; + printf("INFO: show access point '%s'\n", argv[1]); + _access_point_print(ap); +} + +static void +_access_point_connect_done(void *data, const Efl_Event *event EINA_UNUSED) +{ + Eo *ap = data; + printf("INFO: access point '%s' finished connect.\n", + efl_net_control_access_point_name_get(ap)); +} + +static void +_access_point_connect_error(void *data, const Efl_Event *event) +{ + Eo *ap = data; + Efl_Future_Event_Failure *f = event->info; + printf("INFO: access point '%s' could not connect: %s\n", + efl_net_control_access_point_name_get(ap), + eina_error_msg_get(f->error)); +} + +static void +_cmd_access_point_connect(Eo *ctl, size_t argc EINA_UNUSED, char **argv) +{ + Eo *ap = _access_point_find(ctl, argv[1]); + if (!ap) return; + printf("INFO: connecting point '%s'\n", argv[1]); + efl_future_then(efl_net_control_access_point_connect(ap), + _access_point_connect_done, + _access_point_connect_error, + NULL, + ap); +} + +static void +_cmd_access_point_disconnect(Eo *ctl, size_t argc EINA_UNUSED, char **argv) +{ + Eo *ap = _access_point_find(ctl, argv[1]); + if (!ap) return; + printf("INFO: disconnect access point '%s'\n", argv[1]); + efl_net_control_access_point_disconnect(ap); +} + +static void +_cmd_access_point_forget(Eo *ctl, size_t argc EINA_UNUSED, char **argv) +{ + Eo *ap = _access_point_find(ctl, argv[1]); + if (!ap) return; + printf("INFO: forget access point '%s'\n", argv[1]); + efl_net_control_access_point_forget(ap); +} + +static void +_cmd_access_point_reorder(Eo *ctl, size_t argc, char **argv) +{ + Eo *ap = _access_point_find(ctl, argv[1]); + uint32_t prio; + if (!ap) return; + + if (argc < 3) + { + fprintf(stderr, "ERROR: missing priority\n"); + return; + } + else if ((strcasecmp(argv[2], "UINT32_MAX") == 0) || + (strcasecmp(argv[2], "UINT_MAX") == 0) || + (strcasecmp(argv[2], "last") == 0)) + prio = UINT32_MAX; + else + prio = strtoul(argv[2], NULL, 10); + + printf("INFO: reorder access point '%s' %u -> %u\n", + argv[1], efl_net_control_access_point_priority_get(ap), prio); + efl_net_control_access_point_priority_set(ap, prio); +} + +static void +_cmd_access_point_auto_connect(Eo *ctl, size_t argc EINA_UNUSED, char **argv) +{ + Eo *ap = _access_point_find(ctl, argv[1]); + const char *name; + Eina_Bool auto_connect; + + if (!ap) return; + + name = efl_net_control_access_point_name_get(ap); + if (argc <= 2) + { + printf("INFO: access_point '%s' auto connect %s\n", + name, + _fmt_bool(efl_net_control_access_point_auto_connect_get(ap))); + return; + } + + if (!_parse_bool(argv[0], argv[2], &auto_connect)) + return; + + printf("INFO: access_point '%s' set to auto connect %s\n", + name, _fmt_bool(auto_connect)); + efl_net_control_access_point_auto_connect_set(ap, auto_connect); +} + +static void +_cmd_access_point_configure_name_servers(Eo *ctl, size_t argc EINA_UNUSED, char **argv) +{ + Eo *ap = _access_point_find(ctl, argv[1]); + Eina_Iterator *it; + if (!ap) return; + + it = eina_carray_iterator_new((void **)(argv + 2)); + printf("INFO: configure access point '%s' name servers\n", argv[1]); + efl_net_control_access_point_configuration_name_servers_set(ap, it); +} + +static void +_cmd_access_point_configure_time_servers(Eo *ctl, size_t argc EINA_UNUSED, char **argv) +{ + Eo *ap = _access_point_find(ctl, argv[1]); + Eina_Iterator *it; + if (!ap) return; + + it = eina_carray_iterator_new((void **)(argv + 2)); + printf("INFO: configure access point '%s' time servers\n", argv[1]); + efl_net_control_access_point_configuration_time_servers_set(ap, it); +} + +static void +_cmd_access_point_configure_domains(Eo *ctl, size_t argc EINA_UNUSED, char **argv) +{ + Eo *ap = _access_point_find(ctl, argv[1]); + Eina_Iterator *it; + if (!ap) return; + + it = eina_carray_iterator_new((void **)(argv + 2)); + printf("INFO: configure access point '%s' domains\n", argv[1]); + efl_net_control_access_point_configuration_domains_set(ap, it); +} + +static void +_cmd_monitor_set(Eo *ctl EINA_UNUSED, size_t argc, char **argv) +{ + if (argc == 1) + { + printf("INFO: monitor is %s\n", _fmt_bool(monitoring)); + return; + } + + if (!_parse_bool(argv[0], argv[1], &monitoring)) + return; + + printf("INFO: monitor is now %s\n", _fmt_bool(monitoring)); +} + +static void +_cmd_agent_set(Eo *ctl, size_t argc, char **argv) +{ + Eina_Bool enabled; + + if (argc == 1) + { + printf("INFO: agent is %s\n", _fmt_bool(efl_net_control_agent_enabled_get(ctl))); + return; + } + + if (!_parse_bool(argv[0], argv[1], &enabled)) + return; + + efl_net_control_agent_enabled_set(ctl, enabled); + printf("INFO: agent is now %s\n", _fmt_bool(enabled)); +} + +static void +_cmd_access_point_configure_ipv4(Eo *ctl, size_t argc, char **argv) +{ + Eo *ap = _access_point_find(ctl, argv[1]); + const char *name; + Efl_Net_Control_Access_Point_Ipv4_Method ipv4_method; + const char *address = NULL; + const char *netmask = NULL; + const char *gateway = NULL; + + if (!ap) return; + + if (argc <= 2) + { + fprintf(stderr, "ERROR: missing IPv4 configuration method: off, dhcp or manual.\n"); + return; + } + + name = efl_net_control_access_point_name_get(ap); + + if (strcmp(argv[2], "off") == 0) + ipv4_method = EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_OFF; + else if (strcmp(argv[2], "dhcp") == 0) + ipv4_method = EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_DHCP; + else if (strcmp(argv[2], "manual") == 0) + ipv4_method = EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_MANUAL; + else + { + fprintf(stderr, "ERROR: invalid IPv4 configuration method '%s', expected: off, dhcp or manual.\n", argv[2]); + return; + } + + if (argc > 3) address = argv[3]; + if (argc > 4) netmask = argv[4]; + if (argc > 5) gateway = argv[5]; + + printf("INFO: access point '%s' IPv4 set to %s address=%s, netmask=%s, gateway=%s\n", + name, argv[2], address, netmask, gateway); + efl_net_control_access_point_configuration_ipv4_set(ap, ipv4_method, address, netmask, gateway); +} + +static void +_cmd_access_point_configure_ipv6(Eo *ctl, size_t argc, char **argv) +{ + Eo *ap = _access_point_find(ctl, argv[1]); + const char *name; + Efl_Net_Control_Access_Point_Ipv6_Method ipv6_method; + const char *address = NULL; + const char *netmask = NULL; + const char *gateway = NULL; + uint8_t prefix = 0; + + if (!ap) return; + + if (argc <= 2) + { + fprintf(stderr, "ERROR: missing IPv6 configuration method: off, manual, auto-privacy-none, auto-privacy-public or auto-privacy-temporary.\n"); + return; + } + + name = efl_net_control_access_point_name_get(ap); + + if (strcmp(argv[2], "off") == 0) + ipv6_method = EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_OFF; + else if (strcmp(argv[2], "manual") == 0) + ipv6_method = EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_MANUAL; + else if (strcmp(argv[2], "auto-privacy-none") == 0) + ipv6_method = EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_AUTO_PRIVACY_NONE; + else if (strcmp(argv[2], "auto-privacy-public") == 0) + ipv6_method = EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_AUTO_PRIVACY_PUBLIC; + else if (strcmp(argv[2], "auto-privacy-temporary") == 0) + ipv6_method = EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_AUTO_PRIVACY_TEMPORARY; + else + { + fprintf(stderr, "ERROR: invalid IPv6 configuration method '%s', expected: off, manual, auto-privacy-none, auto-privacy-public or auto-privacy-temporary.\n", argv[2]); + return; + } + + if (argc > 3) address = argv[3]; + if (argc > 4) + { + char *endptr; + prefix = strtoul(argv[4], &endptr, 10); + if ((endptr == argv[4]) || (*endptr != '\0')) + { + fprintf(stderr, "ERROR: invalid IPv6 prefix length: %s\n", argv[4]); + return; + } + } + if (argc > 5) netmask = argv[5]; + if (argc > 6) gateway = argv[6]; + + printf("INFO: access point '%s' IPv6 set to %s address=%s/%hhu, netmask=%s, gateway=%s\n", + name, argv[2], address, prefix, netmask, gateway); + efl_net_control_access_point_configuration_ipv6_set(ap, ipv6_method, address, prefix, netmask, gateway); +} + +static void +_cmd_access_point_configure_proxy(Eo *ctl, size_t argc, char **argv) +{ + Eo *ap = _access_point_find(ctl, argv[1]); + const char *name; + Efl_Net_Control_Access_Point_Proxy_Method proxy_method; + const char *url = NULL; + Eina_Iterator *servers = NULL; + Eina_Iterator *excludes = NULL; + + if (!ap) return; + + if (argc <= 2) + { + fprintf(stderr, "ERROR: missing Proxy configuration method: off, dhcp or manual.\n"); + return; + } + + name = efl_net_control_access_point_name_get(ap); + + if (strcmp(argv[2], "off") == 0) + proxy_method = EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_OFF; + else if (strcmp(argv[2], "auto") == 0) + proxy_method = EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_AUTO; + else if (strcmp(argv[2], "manual") == 0) + proxy_method = EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_MANUAL; + else + { + fprintf(stderr, "ERROR: invalid Proxy configuration method '%s', expected: off, dhcp or manual.\n", argv[2]); + return; + } + + if (proxy_method == EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_AUTO) + { + if (argc > 3) url = argv[3]; + } + else if (proxy_method == EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_MANUAL) + { + size_t i, servers_start = 3, excludes_start = 0; + for (i = 3; i < argc; i++) + { + if (strcmp(argv[i], "--servers") == 0) + { + argv[i] = NULL; /* carray iterator operates on null terminated arrays */ + servers_start = i + 1; + } + else if (strcmp(argv[i], "--excludes") == 0) + { + argv[i] = NULL; /* carray iterator operates on null terminated arrays */ + excludes_start = i + 1; + } + } + + servers = eina_carray_iterator_new((void **)(argv + servers_start)); + if (excludes_start) + excludes = eina_carray_iterator_new((void **)(argv + excludes_start)); + } + + printf("INFO: access point '%s' Proxy set to %s url=%s, servers=%p, excludes=%p\n", + name, argv[2], url, servers, excludes); + efl_net_control_access_point_configuration_proxy_set(ap, proxy_method, url, servers, excludes); +} + +static void +_cmd_quit(Eo *ctl EINA_UNUSED, size_t argc EINA_UNUSED, char **argv EINA_UNUSED) +{ + printf("INFO: bye!\n"); + ecore_main_loop_quit(); +} + +static void +_cmd_help(Eo *ctl EINA_UNUSED, size_t argc EINA_UNUSED, char **argv EINA_UNUSED) +{ + puts("HELP:\n" +"technologies-list Show all technologies.\n" +"technology-show Show all detail of a given technology.\n" +"technology-scan Trigger technology scan.\n" +"technology-powered [on|off] Get or set technology powered state.\n" +"technology-tethering [ [identifier] [passphrase]]\n" +" Get or set technology tethering\n" +" configuration.\n" +"\n" +"access-points-list Show all access points.\n" +"access-point-show Show all detail of a given access point.\n" +"access-point-connect Connect access point.\n" +"access-point-disconnect Disconnect access point.\n" +"access-point-forget Disconnect and forget access point\n" +" configurations.\n" +"access-point-reorder Change access point priority, 0 means\n" +" first, UINT32_MAX means last.\n" +"access-point-auto-connect [on|off]\n" +" Get or set access point auto connect.\n" +"access-point-configure-name-servers [ns2] ...\n" +" Configure Name Servers to use.\n" +"access-point-configure-time-servers [ts2] ...\n" +" Configure Time Servers to use.\n" +"access-point-configure-domains [d2] ...\n" +" Configure Domains to use.\n" +"access-point-configure-ipv4 [address [netmask [gateway]]]\n" +" Configure IPv4.\n" +"access-point-configure-ipv6 [address [prefix_length [netmask [gateway]]]]\n" +" Configure IPv6.\n" +"access-point-configure-proxy off\n" +"access-point-configure-proxy auto [url]\n" +"access-point-configure-proxy manual --servers ... --excludes ...\n" +" Configure Proxy.\n" +"\n" +"agent [on|off] Enable the agent to answer for requests.\n" +"monitor [on|off] Automatically print all changed objects.\n" +"help Shows commands and their usage.\n" +"quit Quit the application.\n" +""); +} + +static char ** +_cmd_split(Eina_Rw_Slice arguments, size_t *argc) +{ + char **array = calloc(arguments.len + 1, sizeof(char *)); + char *buf = arguments.mem; + size_t len = arguments.len, src, dst, item = 0; + char quote = 0; + Eina_Bool is_escaped = EINA_FALSE; + + for (src = 0, dst = 0; src < len; src++) + { + char c = buf[src]; + + if (is_escaped) + { + is_escaped = EINA_FALSE; + switch (c) { + case 'n': buf[dst++] = '\n'; break; + case 't': buf[dst++] = '\t'; break; + case 'r': buf[dst++] = '\r'; break; + case 'f': buf[dst++] = '\f'; break; + case '"': buf[dst++] = '"'; break; + case '\'': buf[dst++] = '\''; break; + case '\\': buf[dst++] = '\\'; break; + default: + buf[dst++] = '\\'; + buf[dst++] = c; + } + } + else + { + if (c == '\\') + { + is_escaped = EINA_TRUE; + + if (!array[item]) + array[item] = buf + dst; + } + else if ((quote == 0) && ((c == '"') || (c == '\''))) + { + quote = c; + if (!array[item]) + array[item] = buf + dst; + } + else + { + if ((quote == 0) && (isspace(c))) + { + buf[dst] = '\0'; + dst++; + if (array[item]) item++; + continue; + } + else if (quote == c) + { + quote = 0; + continue; + } + else if (dst < src) + buf[dst] = buf[src]; + + if (!array[item]) + array[item] = buf + dst; + + dst++; + } + } + } + + if (dst < len) + buf[dst] = '\0'; + if (array[item]) item++; + + *argc = item; + + return array; +} + +static void +_cmd_line(void *data, const Efl_Event *event) +{ + Eo *ctl = data; + Eina_Binbuf *binbuf = efl_io_copier_binbuf_steal(event->object); + Eina_Rw_Slice rw_slice = eina_binbuf_rw_slice_get(binbuf); + const struct { + const char *cmd; + void (*cb)(Eo *ctl, size_t argc, char **argv); + } *itr, map[] = { + {"technologies-list", _cmd_technologies_list}, + {"t-list", _cmd_technologies_list}, + {"technology-show", _cmd_technology_show}, + {"t-show", _cmd_technology_show}, + {"technology-scan", _cmd_technology_scan}, + {"t-scan", _cmd_technology_scan}, + {"technology-powered", _cmd_technology_powered}, + {"t-powered", _cmd_technology_powered}, + {"technology-tethering", _cmd_technology_tethering}, + {"t-tethering", _cmd_technology_tethering}, + {"access-points-list", _cmd_access_points_list}, + {"ap-list", _cmd_access_points_list}, + {"access-point-show", _cmd_access_point_show}, + {"ap-show", _cmd_access_point_show}, + {"access-point-connect", _cmd_access_point_connect}, + {"ap-connect", _cmd_access_point_connect}, + {"access-point-disconnect", _cmd_access_point_disconnect}, + {"ap-disconnect", _cmd_access_point_disconnect}, + {"access-point-forget", _cmd_access_point_forget}, + {"ap-forget", _cmd_access_point_forget}, + {"access-point-reorder", _cmd_access_point_reorder}, + {"ap-reorder", _cmd_access_point_reorder}, + {"access-point-auto-connect", _cmd_access_point_auto_connect}, + {"ap-auto-connect", _cmd_access_point_auto_connect}, + {"access-point-configure-name-servers", _cmd_access_point_configure_name_servers}, + {"ap-configure-name-servers", _cmd_access_point_configure_name_servers}, + {"access-point-configure-time-servers", _cmd_access_point_configure_time_servers}, + {"ap-configure-time-servers", _cmd_access_point_configure_time_servers}, + {"access-point-configure-domains", _cmd_access_point_configure_domains}, + {"ap-configure-domains", _cmd_access_point_configure_domains}, + {"access-point-configure-ipv4", _cmd_access_point_configure_ipv4}, + {"ap-configure-ipv4", _cmd_access_point_configure_ipv4}, + {"access-point-configure-ipv6", _cmd_access_point_configure_ipv6}, + {"ap-configure-ipv6", _cmd_access_point_configure_ipv6}, + {"access-point-configure-proxy", _cmd_access_point_configure_proxy}, + {"ap-configure-proxy", _cmd_access_point_configure_proxy}, + {"agent", _cmd_agent_set}, + {"monitor", _cmd_monitor_set}, + {"help", _cmd_help}, + {"quit", _cmd_quit}, + { } + }; + char **argv; + size_t argc; + + if (rw_slice.len == 0) + { + char *dummy[1] = { NULL }; + _cmd_quit(ctl, 0, dummy); + return; + } + + if (eina_rw_slice_endswith(rw_slice, (Eina_Slice)EINA_SLICE_STR_LITERAL("\n"))) + { + rw_slice.len--; + rw_slice.bytes[rw_slice.len] = '\0'; + } + + argv = _cmd_split(rw_slice, &argc); + if (!argv[0]) goto end; + for (itr = map; itr->cb; itr++) + { + if (strcmp(itr->cmd, argv[0]) == 0) + { + itr->cb(ctl, argc, argv); + break; + } + } + + if (!itr->cb) + fprintf(stderr, "ERROR: command not found: '%s'\n", argv[0]); + + end: + free(argv); + eina_binbuf_free(binbuf); +} + +EFL_CALLBACKS_ARRAY_DEFINE(ctl_events_cbs, + { EFL_NET_CONTROL_EVENT_ACCESS_POINT_ADD, _ctl_access_point_add }, + { EFL_NET_CONTROL_EVENT_ACCESS_POINT_DEL, _ctl_access_point_del }, + { EFL_NET_CONTROL_EVENT_ACCESS_POINTS_CHANGED, _ctl_access_points_changed }, + { EFL_NET_CONTROL_EVENT_TECHNOLOGY_ADD, _ctl_technology_add }, + { EFL_NET_CONTROL_EVENT_TECHNOLOGY_DEL, _ctl_technology_del }, + { EFL_NET_CONTROL_EVENT_RADIOS_OFFLINE_CHANGED, _ctl_radios_offline_changed }, + { EFL_NET_CONTROL_EVENT_STATE_CHANGED, _ctl_state_changed }, + { EFL_NET_CONTROL_EVENT_AGENT_RELEASED, _ctl_agent_released }, + { EFL_NET_CONTROL_EVENT_AGENT_ERROR, _ctl_agent_error }, + { EFL_NET_CONTROL_EVENT_AGENT_BROWSER_URL, _ctl_agent_browser_url }, + { EFL_NET_CONTROL_EVENT_AGENT_REQUEST_INPUT, _ctl_agent_request_input }); + +int +main(int argc EINA_UNUSED, char **argv EINA_UNUSED) +{ + Eo *ctl; + Eo *input; + Eo *copier; + Eina_Slice line_delimiter = EINA_SLICE_STR("\n"); + + ecore_init(); + ecore_con_init(); + + ctl = efl_add(EFL_NET_CONTROL_CLASS, ecore_main_loop_get(), + efl_event_callback_array_add(efl_added, ctl_events_cbs(), NULL)); + if (!ctl) + { + fputs("ERROR: Could not create Efl.Net.Control object.\n", stderr); + retval = EXIT_FAILURE; + goto end; + } + + input = efl_add(EFL_IO_STDIN_CLASS, ecore_main_loop_get()); + copier = efl_add(EFL_IO_COPIER_CLASS, ecore_main_loop_get(), + efl_io_copier_source_set(efl_added, input), + efl_io_copier_line_delimiter_set(efl_added, &line_delimiter), + efl_io_copier_buffer_limit_set(efl_added, 8192), + efl_io_copier_read_chunk_size_set(efl_added, 8192), + efl_event_callback_add(efl_added, EFL_IO_COPIER_EVENT_LINE, _cmd_line, ctl)); + + printf("INFO: monitoring is on, disable with 'monitor off'. See 'help'.\n"); + printf("INFO: type commands, if unsure try: 'help'\n"); + + ecore_main_loop_begin(); + + efl_del(copier); + efl_del(input); + efl_del(ctl); + + end: + ecore_con_shutdown(); + ecore_shutdown(); + + return retval; +} diff --git a/src/examples/ecore/efl_net_session_example.c b/src/examples/ecore/efl_net_session_example.c new file mode 100644 index 0000000000..9409b707f9 --- /dev/null +++ b/src/examples/ecore/efl_net_session_example.c @@ -0,0 +1,181 @@ +#define EFL_BETA_API_SUPPORT 1 +#define EFL_EO_API_SUPPORT 1 +#include +#include +#include + +static int retval = EXIT_SUCCESS; + + +static const char * +_state_str(Efl_Net_Session_State state) +{ + static const char *strs[] = { + [EFL_NET_SESSION_STATE_OFFLINE] = "offline", + [EFL_NET_SESSION_STATE_LOCAL] = "local", + [EFL_NET_SESSION_STATE_ONLINE] = "online", + }; + + if ((unsigned)state > sizeof(strs)/sizeof(strs[0])) return "???"; + if (!strs[state]) return "???"; + return strs[state]; +} + +static const char * +_technology_str(Efl_Net_Session_Technology tech) +{ + static const char *strs[] = { + [EFL_NET_SESSION_TECHNOLOGY_UNKNOWN] = "none", + [EFL_NET_SESSION_TECHNOLOGY_ETHERNET] = "ethernet", + [EFL_NET_SESSION_TECHNOLOGY_WIFI] = "wifi", + [EFL_NET_SESSION_TECHNOLOGY_BLUETOOTH] = "bluetooth", + [EFL_NET_SESSION_TECHNOLOGY_CELLULAR] = "cellular", + [EFL_NET_SESSION_TECHNOLOGY_VPN] = "vpn", + [EFL_NET_SESSION_TECHNOLOGY_GADGET] = "gadget", + }; + + if ((unsigned)tech > sizeof(strs)/sizeof(strs[0])) return "???"; + if (!strs[tech]) return "???"; + return strs[tech]; +} + +static void +_changed(void *data EINA_UNUSED, const Efl_Event *event) +{ + Eo *session = event->object; + const char *ip, *netmask, *gateway; + uint8_t prefix; + + printf("INFO: session changed:\n" + "INFO: - name: '%s'\n" + "INFO: - state: %s\n" + "INFO: - technology: %s\n" + "INFO: - interface: '%s'\n", + efl_net_session_name_get(session), + _state_str(efl_net_session_state_get(session)), + _technology_str(efl_net_session_technology_get(session)), + efl_net_session_interface_get(session)); + + efl_net_session_ipv4_get(session, &ip, &netmask, &gateway); + if (ip) + { + printf("INFO: - IPv4: %s, gateway=%s, netmask=%s\n", + ip, gateway, netmask); + } + + efl_net_session_ipv6_get(session, &ip, &prefix, &netmask, &gateway); + if (ip) + { + printf("INFO: - IPv6: %s/%hhu, gateway=%s, netmask=%s\n", + ip, prefix, gateway, netmask); + } +} + +EFL_CALLBACKS_ARRAY_DEFINE(session_events_cbs, + { EFL_NET_SESSION_EVENT_CHANGED, _changed }); + +static const Ecore_Getopt options = { + "efl_net_session_example", /* program name */ + NULL, /* usage line */ + "1", /* version */ + "(C) 2016 Enlightenment Project", /* copyright */ + "BSD 2-Clause", /* license */ + /* long description, may be multiline and contain \n */ + "Example of Efl_Net_Session to request or monitor a network session for an application.\n", + EINA_FALSE, + { + ECORE_GETOPT_STORE_TRUE('c', "connect", "Require a connection to the internet (See -o/--require-online)."), + ECORE_GETOPT_STORE_TRUE('o', "require-online", "When connecting (-c/--connect), require connection to the internet. Otherwise a local network connection is enough"), + ECORE_GETOPT_APPEND('t', "technology", "Bearer technologies to use when connecting (ethernet, wifi, bluetooth, cellular, vpn, gadget or all)", ECORE_GETOPT_TYPE_STR), + + ECORE_GETOPT_VERSION('V', "version"), + ECORE_GETOPT_COPYRIGHT('C', "copyright"), + ECORE_GETOPT_LICENSE('L', "license"), + ECORE_GETOPT_HELP('h', "help"), + ECORE_GETOPT_SENTINEL + } +}; + +int +main(int argc, char **argv) +{ + char *str; + Eina_List *techs = NULL; + Eina_Bool do_connect = EINA_FALSE; + Eina_Bool require_online = EINA_FALSE; + Eina_Bool quit_option = EINA_FALSE; + Efl_Net_Session_Technology technologies = EFL_NET_SESSION_TECHNOLOGY_ALL; + Ecore_Getopt_Value values[] = { + ECORE_GETOPT_VALUE_BOOL(do_connect), + ECORE_GETOPT_VALUE_BOOL(require_online), + ECORE_GETOPT_VALUE_LIST(techs), + + /* standard block to provide version, copyright, license and help */ + ECORE_GETOPT_VALUE_BOOL(quit_option), /* -V/--version quits */ + ECORE_GETOPT_VALUE_BOOL(quit_option), /* -C/--copyright quits */ + ECORE_GETOPT_VALUE_BOOL(quit_option), /* -L/--license quits */ + ECORE_GETOPT_VALUE_BOOL(quit_option), /* -h/--help quits */ + ECORE_GETOPT_VALUE_NONE /* sentinel */ + }; + int args; + Eo *session; + + ecore_init(); + ecore_con_init(); + + args = ecore_getopt_parse(&options, values, argc, argv); + if (args < 0) + { + fputs("ERROR: Could not parse command line options.\n", stderr); + retval = EXIT_FAILURE; + goto end; + } + + if (quit_option) goto end; + + if (techs) + { + technologies = 0; + EINA_LIST_FREE(techs, str) + { + if (0) {} +#define MAP(X) else if (strcasecmp(str, #X) == 0) technologies |= EFL_NET_SESSION_TECHNOLOGY_ ## X + MAP(ETHERNET); + MAP(WIFI); + MAP(BLUETOOTH); + MAP(CELLULAR); + MAP(VPN); + MAP(GADGET); +#undef MAP + else fprintf(stderr, "WARNING: unknown technology '%s' ignored.\n", str); + free(str); + } + } + + session = efl_add(EFL_NET_SESSION_CLASS, ecore_main_loop_get(), + efl_event_callback_array_add(efl_added, session_events_cbs(), NULL)); + if (!session) + { + fputs("ERROR: Could not create Efl.Net.Session object.\n", stderr); + retval = EXIT_FAILURE; + goto end; + } + + if (do_connect) + { + printf("INFO: requesting a %s connection.\n", require_online ? "online" : "local"); + efl_net_session_connect(session, require_online, technologies); + } + + printf("INFO: the session will active while this application runs. Use ^C (Control + C) to close it\n"); + + ecore_main_loop_begin(); + + efl_del(session); + + end: + ecore_con_shutdown(); + ecore_shutdown(); + + return retval; +} diff --git a/src/lib/ecore_con/Ecore_Con_Eo.h b/src/lib/ecore_con/Ecore_Con_Eo.h index aaae290640..3c79b7a2f6 100644 --- a/src/lib/ecore_con/Ecore_Con_Eo.h +++ b/src/lib/ecore_con/Ecore_Con_Eo.h @@ -32,10 +32,14 @@ #include "efl_net_dialer_http.eo.h" #include "efl_net_dialer_websocket.eo.h" - #include "efl_net_ssl_types.eot.h" #include "efl_net_ssl_context.eo.h" #include "efl_net_socket_ssl.eo.h" #include "efl_net_dialer_ssl.eo.h" #include "efl_net_server_ssl.eo.h" + +#include "efl_net_control_technology.eo.h" +#include "efl_net_control_access_point.eo.h" +#include "efl_net_control.eo.h" +#include "efl_net_session.eo.h" diff --git a/src/lib/ecore_con/efl_net-connman.c b/src/lib/ecore_con/efl_net-connman.c new file mode 100644 index 0000000000..747263386c --- /dev/null +++ b/src/lib/ecore_con/efl_net-connman.c @@ -0,0 +1,109 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "efl_net-connman.h" + +static Eldbus_Proxy *_efl_net_connman_manager; +static Eldbus_Connection *_efl_net_connman_conn; +static int _efl_net_connman_init_count; + +static void +_efl_net_connman_manager_free(void *data EINA_UNUSED, const void *dead_ptr) +{ + DBG("ConnMan Manager was freed"); + + /* handle programming errors where 'too many unrefs' happen */ + if (dead_ptr == _efl_net_connman_manager) + _efl_net_connman_manager = NULL; +} + +Eina_Bool +efl_net_connman_init(void) +{ + Eldbus_Object *obj; + + _efl_net_connman_init_count++; + if (_efl_net_connman_init_count > 1) return EINA_TRUE; + + if (!eldbus_init()) + { + ERR("could not init eldbus"); + goto error; + } + + _efl_net_connman_conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SYSTEM); + if (!_efl_net_connman_conn) + { + ERR("could not get DBus system connection"); + goto error; + } + + DBG("DBus unique name %s", eldbus_connection_unique_name_get(_efl_net_connman_conn)); + + obj = eldbus_object_get(_efl_net_connman_conn, "net.connman", "/"); + if (!obj) + { + ERR("could not create DBus object for name='net.connman', path='/'"); + goto error_obj; + } + + _efl_net_connman_manager = eldbus_proxy_get(obj, "net.connman.Manager"); + if (!_efl_net_connman_manager) + { + ERR("could not create DBus proxy for interface='net.connman.Manager', name='net.connman', path='/'"); + goto error_proxy; + } + eldbus_proxy_free_cb_add(_efl_net_connman_manager, _efl_net_connman_manager_free, NULL); + + DBG("ConnMan support initialized"); + return EINA_TRUE; + + error_proxy: + eldbus_object_unref(obj); + + error_obj: + eldbus_connection_unref(_efl_net_connman_conn); + _efl_net_connman_conn = NULL; + + error: + _efl_net_connman_init_count = 0; + return EINA_FALSE; +} + +void +efl_net_connman_shutdown(void) +{ + _efl_net_connman_init_count--; + if (_efl_net_connman_init_count > 0) return; + + DBG("ConnMan support shutting down"); + + if (_efl_net_connman_manager) + { + Eldbus_Object *obj = eldbus_proxy_object_get(_efl_net_connman_manager); + eldbus_proxy_unref(_efl_net_connman_manager); + _efl_net_connman_manager = NULL; + eldbus_object_unref(obj); + } + + if (_efl_net_connman_conn) + { + eldbus_connection_unref(_efl_net_connman_conn); + _efl_net_connman_conn = NULL; + } + + eldbus_shutdown(); +} + +Eldbus_Connection * +efl_net_connman_connection_get(void) +{ + return _efl_net_connman_conn; +} + +Eldbus_Proxy * +efl_net_connman_manager_get(void) +{ + return _efl_net_connman_manager; +} diff --git a/src/lib/ecore_con/efl_net-connman.h b/src/lib/ecore_con/efl_net-connman.h new file mode 100644 index 0000000000..e66c4afd34 --- /dev/null +++ b/src/lib/ecore_con/efl_net-connman.h @@ -0,0 +1,122 @@ +#ifndef _EFL_NET_CONNMAN_H_ +#define _EFL_NET_CONNMAN_H_ 1 + +#include "Ecore.h" +#include "ecore_con_private.h" + +#include "Eldbus.h" + +#define DEFAULT_TIMEOUT 10000.0 + +/** + * @file efl_net-connman.h + * + * Common infrastructure to create Efl_Net_Control and Efl_Net_Session + * based on ConnMan connection manager. + * + * @note Each connection manager that needs shared infra should create + * their own file! + * + * @internal + */ + +/** + * Should be called from inside Efl.Object.constructor before using + * any of connman functions. + * + * @return #EINA_FALSE on errors, all other functions will be useless + * in this case. #EINA_TRUE on success. + * + * @internal + */ +Eina_Bool efl_net_connman_init(void); + +/** + * Should be called from inside Efl.Object.destructor after done using + * all of connman functions. + * + * @internal + */ +void efl_net_connman_shutdown(void); + +/** + * Returns the DBus connection shared by all objects. + * + * @internal + */ +Eldbus_Connection *efl_net_connman_connection_get(void); + +/** + * Returns a singleton for ConnMan's Manager object and interface + * (proxy). + * + * @note call efl_net_connman_init() before using. There is no need to + * eldbus_proxy_ref() it, but if done should have a matching + * eldbus_proxy_unref(). After done, remember to + * efl_net_connman_shutdown(). + * + * @internal + */ +Eldbus_Proxy *efl_net_connman_manager_get(void); + +/** + * Given a Efl.Net.Control find a technology instance given its name. + * + * @internal + */ +Efl_Net_Control_Technology *efl_net_connman_control_find_technology_by_type(Efl_Net_Control *ctl, const char *tech_type); + +/** + * Ask Efl.Net.Control to reload access point list. + * + * @internal + */ +void efl_net_connman_control_access_points_reload(Efl_Net_Control *ctl); + + +/** + * Creates a new Efl.Net.Control.Technology child of an + * Efl.Net.Control with path and properties + * + * @internal + */ +Efl_Net_Control_Technology *efl_net_connman_technology_new(Efl_Net_Control *parent, const char *path, Eldbus_Message_Iter *properties); + +/** + * Get the path of the given technology. + * + * @internal + */ +const char *efl_net_connman_technology_path_get(Efl_Net_Control_Technology *tech); + +/** + * Convert connman's type string to enum value. + * + * @internal + */ +Efl_Net_Control_Technology_Type efl_net_connman_technology_type_from_str(const char *tech_type); + +/** + * Creates a new Efl.Net.Control.Access_Point child of an + * Efl.Net.Control with path and properties + * + * @internal + */ +Efl_Net_Control_Access_Point *efl_net_connman_access_point_new(Efl_Net_Control *parent, const char *path, Eldbus_Message_Iter *properties, unsigned int priority); + +/** + * Get the path of the given access_point. + * + * @internal + */ +const char *efl_net_connman_access_point_path_get(Efl_Net_Control_Access_Point *ap); + +/** + * Updates Efl.Net.Control.Access_Point properties + * + * @internal + */ +void efl_net_connman_access_point_update(Efl_Net_Control *ap, Eldbus_Message_Iter *properties, unsigned int priority); + + +#endif /* _EFL_NET_CONNMAN_H_ */ diff --git a/src/lib/ecore_con/efl_net_control-connman.c b/src/lib/ecore_con/efl_net_control-connman.c new file mode 100644 index 0000000000..63f452520e --- /dev/null +++ b/src/lib/ecore_con/efl_net_control-connman.c @@ -0,0 +1,1233 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "Ecore.h" +#include "Ecore_Con.h" +#include "ecore_con_private.h" +#include "efl_net-connman.h" + +typedef struct +{ + /* Eldbus_Proxy/Eldbus_Object keeps a list of pending calls, but + * they are reference counted singletons and will only cancel + * pending calls when everything is gone. However we operate on + * our private data, that may be gone before other refs. So + * keep the pending list. + */ + Eina_List *pending; + Eina_List *signal_handlers; + Eina_List *technologies; + Eina_List *access_points; + Eldbus_Service_Interface *agent; /* net.connman.Agent */ + Eldbus_Pending *agent_pending; + struct { + Eldbus_Message *msg; + } agent_request_input; + Efl_Net_Control_State state; + Eina_Bool radios_offline; + Eina_Bool operating; /* connman exists */ + Eina_Bool agent_enabled; + struct { + unsigned char radios_offline; /* 0xff = not requested */ + Eldbus_Pending *radios_offline_pending; + } request; +} Efl_Net_Control_Data; + +#define MY_CLASS EFL_NET_CONTROL_CLASS + +static void _efl_net_control_agent_enabled_set(Eo *o, Efl_Net_Control_Data *pd, Eina_Bool agent_enabled); + +static Eo * +_efl_net_control_technology_find(const Efl_Net_Control_Data *pd, const char *path) +{ + const Eina_List *n; + Eo *child; + + EINA_LIST_FOREACH(pd->technologies, n, child) + { + Eina_Stringshare *cp = efl_net_connman_technology_path_get(child); + if (strcmp(path, cp) == 0) + return child; + } + + WRN("technology path='%s' not found!", path); + return NULL; +} + +static Eo * +_efl_net_control_access_point_find(const Efl_Net_Control_Data *pd, const char *path) +{ + const Eina_List *n; + Eo *child; + + EINA_LIST_FOREACH(pd->access_points, n, child) + { + Eina_Stringshare *cp = efl_net_connman_access_point_path_get(child); + if (strcmp(path, cp) == 0) + return child; + } + + DBG("access_point path='%s' not found!", path); + return NULL; +} + +static Eldbus_Message * +_efl_net_control_agent_release(const Eldbus_Service_Interface *service, const Eldbus_Message *msg) +{ + Eo *o = eldbus_service_object_data_get(service, "efl_net"); + Efl_Net_Control_Data *pd = efl_data_scope_get(o, MY_CLASS); + + DBG("Agent %p is released %s", o, eldbus_message_path_get(msg)); + EINA_SAFETY_ON_NULL_GOTO(o, end); + EINA_SAFETY_ON_NULL_GOTO(pd, end); + + pd->agent_enabled = EINA_FALSE; + efl_event_callback_call(o, EFL_NET_CONTROL_EVENT_AGENT_RELEASED, NULL); + + end: + return eldbus_message_method_return_new(msg); +} + +static Eldbus_Message * +_efl_net_control_agent_cancel(const Eldbus_Service_Interface *service, const Eldbus_Message *msg) +{ + Eo *o = eldbus_service_object_data_get(service, "efl_net"); + Efl_Net_Control_Data *pd = efl_data_scope_get(o, MY_CLASS); + Eldbus_Message *reply; + + DBG("Agent %p request canceled %s", o, eldbus_message_path_get(msg)); + EINA_SAFETY_ON_NULL_GOTO(o, end); + EINA_SAFETY_ON_NULL_GOTO(pd, end); + + if (!pd->agent_request_input.msg) goto end; + + reply = eldbus_message_error_new(pd->agent_request_input.msg, + "net.connman.Error.Canceled", + "Cancel requested."); + eldbus_connection_send(efl_net_connman_connection_get(), reply, NULL, NULL, -1.0); + eldbus_message_unref(pd->agent_request_input.msg); + pd->agent_request_input.msg = NULL; + + end: + return eldbus_message_method_return_new(msg); +} + +static const char * +_efl_net_control_agent_entry_type_get(Eldbus_Message_Iter *dict) +{ + Eldbus_Message_Iter *array, *entry; + + if (!eldbus_message_iter_arguments_get(dict, "a{sv}", &array)) + { + ERR("Expected dict, got %s", eldbus_message_iter_signature_get(dict)); + return NULL; + } + + while (eldbus_message_iter_get_and_next(array, 'e', &entry)) + { + Eldbus_Message_Iter *var; + const char *key, *c; + if (!eldbus_message_iter_arguments_get(entry, "sv", &key, &var)) + { + ERR("Expected dict entry, got %s", eldbus_message_iter_signature_get(entry)); + continue; + } + + if (!eldbus_message_iter_arguments_get(var, "s", &c)) + { + ERR("Expected string, got %s", eldbus_message_iter_signature_get(var)); + continue; + } + + if (strcmp(key, "Type") == 0) + return c; + } + return NULL; +} + +static Efl_Net_Control_Agent_Request_Input_Information * +_efl_net_control_agent_informational_get(const char *name, Eldbus_Message_Iter *dict) +{ + Eldbus_Message_Iter *array, *entry; + Eina_Bool is_informational = EINA_FALSE; + const char *value = NULL; + + if (!eldbus_message_iter_arguments_get(dict, "a{sv}", &array)) + { + ERR("Expected dict, got %s", eldbus_message_iter_signature_get(dict)); + return NULL; + } + + while (eldbus_message_iter_get_and_next(array, 'e', &entry)) + { + Eldbus_Message_Iter *var; + const char *key, *c; + if (!eldbus_message_iter_arguments_get(entry, "sv", &key, &var)) + { + ERR("Expected dict entry, got %s", eldbus_message_iter_signature_get(entry)); + continue; + } + + if (!eldbus_message_iter_arguments_get(var, "s", &c)) + { + ERR("Expected string, got %s", eldbus_message_iter_signature_get(var)); + continue; + } + + if (strcmp(key, "Requirement") == 0) + { + if (strcmp(c, "informational") == 0) + is_informational = EINA_TRUE; + } + else if (strcmp(key, "Value") == 0) + value = c; + else if (strcmp(key, "Alternates") == 0) + { } + else if (strcmp(key, "Type") == 0) + { } + else WRN("Unknown key '%s' for requested input entry %s", key, name); + } + + if (is_informational && value) + { + Efl_Net_Control_Agent_Request_Input_Information *info; + info = calloc(1, sizeof(*info)); + EINA_SAFETY_ON_NULL_RETURN_VAL(info, NULL); + info->name = name; + info->value = value; + DBG("Field %s=%s is informational", name, value); + return info; + } + + DBG("Field %s=%s is not informational", name, value); + return NULL; +} + +static Eldbus_Message * +_efl_net_control_agent_request_input(const Eldbus_Service_Interface *service, const Eldbus_Message *msg) +{ + Eo *o = eldbus_service_object_data_get(service, "efl_net"); + Efl_Net_Control_Data *pd = efl_data_scope_get(o, MY_CLASS); + Efl_Net_Control_Agent_Request_Input event = { }; + Efl_Net_Control_Agent_Request_Input_Information *info; + Eldbus_Message_Iter *array, *entry; + const char *path; + + DBG("Agent %p requested input %s", o, eldbus_message_path_get(msg)); + EINA_SAFETY_ON_NULL_GOTO(o, err); + EINA_SAFETY_ON_NULL_GOTO(pd, err); + + if (pd->agent_request_input.msg) + { + Eldbus_Message *reply = eldbus_message_error_new(pd->agent_request_input.msg, + "net.connman.Error.OperationAborted", + "Received new input request, aborting previous."); + eldbus_connection_send(efl_net_connman_connection_get(), reply, NULL, NULL, -1.0); + eldbus_message_unref(pd->agent_request_input.msg); + pd->agent_request_input.msg = NULL; + } + + if (!eldbus_message_arguments_get(msg, "oa{sv}", &path, &array)) + goto err; + + event.access_point = _efl_net_control_access_point_find(pd, path); + + while (eldbus_message_iter_get_and_next(array, 'e', &entry)) + { + Eldbus_Message_Iter *var; + const char *name; + if (!eldbus_message_iter_arguments_get(entry, "sv", &name, &var)) + goto err; + + if (strcmp(name, "Name") == 0) + event.fields |= EFL_NET_CONTROL_AGENT_REQUEST_INPUT_FIELD_NAME; + else if (strcmp(name, "SSID") == 0) + event.fields |= EFL_NET_CONTROL_AGENT_REQUEST_INPUT_FIELD_SSID; + else if (strcmp(name, "WPS") == 0) + event.fields |= EFL_NET_CONTROL_AGENT_REQUEST_INPUT_FIELD_WPS; + else if ((strcmp(name, "Identity") == 0) || + (strcmp(name, "Username") == 0)) + event.fields |= EFL_NET_CONTROL_AGENT_REQUEST_INPUT_FIELD_USERNAME; + else if ((strcmp(name, "Passphrase") == 0) || + (strcmp(name, "Password") == 0)) + { + const char *value = _efl_net_control_agent_entry_type_get(var); + if ((!event.passphrase_type) && (value)) + event.passphrase_type = value; + event.fields |= EFL_NET_CONTROL_AGENT_REQUEST_INPUT_FIELD_PASSPHRASE; + } + else + { + info = _efl_net_control_agent_informational_get(name, var); + if (info) + event.informational = eina_list_append(event.informational, info); + else + WRN("Unknown field name '%s'", name); + } + } + + pd->agent_request_input.msg = eldbus_message_ref((Eldbus_Message *)msg); + efl_event_callback_call(o, EFL_NET_CONTROL_EVENT_AGENT_REQUEST_INPUT, &event); + + EINA_LIST_FREE(event.informational, info) free(info); + + return NULL; /* reply later */ + + err: + return eldbus_message_method_return_new(msg); +} + +static Eldbus_Message * +_efl_net_control_agent_report_error(const Eldbus_Service_Interface *service, const Eldbus_Message *msg) +{ + Eo *o = eldbus_service_object_data_get(service, "efl_net"); + Efl_Net_Control_Data *pd = efl_data_scope_get(o, MY_CLASS); + const char *path, *err_msg; + Efl_Net_Control_Agent_Error event = { }; + + if (!eldbus_message_arguments_get(msg, "os", &path, &err_msg)) + { + ERR("Invalid net.connman.ReportError signature=%s", eldbus_message_signature_get(msg)); + goto end; + } + + DBG("Agent %p error %s: %s", o, path, err_msg); + + EINA_SAFETY_ON_NULL_GOTO(o, end); + EINA_SAFETY_ON_NULL_GOTO(pd, end); + + event.access_point = _efl_net_control_access_point_find(pd, path); + event.message = err_msg; + + efl_event_callback_call(o, EFL_NET_CONTROL_EVENT_AGENT_ERROR, &event); + + end: + return eldbus_message_method_return_new(msg); +} + +static Eldbus_Message * +_efl_net_control_agent_request_browser(const Eldbus_Service_Interface *service, const Eldbus_Message *msg) +{ + Eo *o = eldbus_service_object_data_get(service, "efl_net"); + Efl_Net_Control_Data *pd = efl_data_scope_get(o, MY_CLASS); + const char *path, *url; + Efl_Net_Control_Agent_Browser_Url event = { }; + + if (!eldbus_message_arguments_get(msg, "os", &path, &url)) + { + ERR("Invalid net.connman.RequestBrowser signature=%s", eldbus_message_signature_get(msg)); + goto end; + } + + DBG("Agent %p browser %s: %s", o, path, url); + + EINA_SAFETY_ON_NULL_GOTO(o, end); + EINA_SAFETY_ON_NULL_GOTO(pd, end); + + event.access_point = _efl_net_control_access_point_find(pd, path); + event.url = url; + + efl_event_callback_call(o, EFL_NET_CONTROL_EVENT_AGENT_BROWSER_URL, &event); + + end: + return eldbus_message_method_return_new(msg); +} + +static Eldbus_Message * +_efl_net_control_agent_report_peer_error(const Eldbus_Service_Interface *service, const Eldbus_Message *msg) +{ + Eo *o = eldbus_service_object_data_get(service, "efl_net"); + const char *path, *err_msg; + + if (!eldbus_message_arguments_get(msg, "os", &path, &err_msg)) + { + ERR("Invalid net.connman.ReportPeerError signature=%s", eldbus_message_signature_get(msg)); + goto end; + } + + DBG("Agent %p peer error %s: %s", o, path, err_msg); + + end: + return eldbus_message_method_return_new(msg); +} + + +static Eldbus_Message * +_efl_net_control_agent_request_peer_authorization(const Eldbus_Service_Interface *service, const Eldbus_Message *msg) +{ + Eo *o = eldbus_service_object_data_get(service, "efl_net"); + DBG("Agent %p peer authorization required", o); + return eldbus_message_method_return_new(msg); +} + +static const Eldbus_Service_Interface_Desc _efl_net_control_agent_desc = { + .interface = "net.connman.Agent", + .methods = (const Eldbus_Method []){ + { + .member = "Release", + .cb = _efl_net_control_agent_release, + }, + { + .member = "ReportError", + .in = ELDBUS_ARGS({"o", "service"}, {"s", "message"}), + .cb = _efl_net_control_agent_report_error, + }, + { + .member = "RequestBrowser", + .in = ELDBUS_ARGS({"o", "service"}, {"s", "url"}), + .cb = _efl_net_control_agent_request_browser, + }, + { + .member = "RequestInput", + .in = ELDBUS_ARGS({"o", "service"}, {"a{sv}", "fields"}), + .cb = _efl_net_control_agent_request_input, + }, + { + .member = "Cancel", + .cb = _efl_net_control_agent_cancel, + }, + { + .member = "ReportPeerError", + .in = ELDBUS_ARGS({"o", "peer"}, {"s", "message"}), + .cb = _efl_net_control_agent_report_peer_error, + }, + { + .member = "RequestPeerAuthorization", + .in = ELDBUS_ARGS({"o", "peer"}, {"a{sv}", "fields"}), + .cb = _efl_net_control_agent_request_peer_authorization, + }, + { } + }, +}; + +static void +_efl_net_control_technology_added_internal(Eo *o, Efl_Net_Control_Data *pd, Eldbus_Message_Iter *itr) +{ + const char *path; + Eldbus_Message_Iter *array; + Eo *child; + + if (!eldbus_message_iter_arguments_get(itr, "oa{sv}", &path, &array)) + { + ERR("could not get technology path or properties, signature=%s", eldbus_message_iter_signature_get(itr)); + return; + } + + child = efl_net_connman_technology_new(o, path, array); + if (!child) + { + ERR("could not create technology for path=%s", path); + return; + } + + pd->technologies = eina_list_append(pd->technologies, child); + DBG("Technology Added Path=%s", path); + efl_event_callback_call(o, EFL_NET_CONTROL_EVENT_TECHNOLOGY_ADD, child); +} + +static void +_efl_net_control_technology_added(void *data, const Eldbus_Message *msg) +{ + Eo *o = data; + Efl_Net_Control_Data *pd = efl_data_scope_get(o, MY_CLASS); + Eldbus_Message_Iter *itr = eldbus_message_iter_get(msg); + _efl_net_control_technology_added_internal(o, pd, itr); +} + +static void +_efl_net_control_technology_removed(void *data, const Eldbus_Message *msg) +{ + Eo *o = data; + Efl_Net_Control_Data *pd = efl_data_scope_get(o, MY_CLASS); + Eo *child; + const char *path; + + if (!eldbus_message_arguments_get(msg, "o", &path)) + { + ERR("Could not get removed technology's path"); + return; + } + + child = _efl_net_control_technology_find(pd, path); + if (!child) + { + WRN("Technology path '%s' doesn't exist", path); + return; + } + + pd->technologies = eina_list_remove(pd->technologies, child); + DBG("Technology Removed Path=%s", path); + efl_event_callback_call(o, EFL_NET_CONTROL_EVENT_TECHNOLOGY_DEL, child); + efl_del(child); +} + +static void +_efl_net_control_access_point_added(Eo *o, Efl_Net_Control_Data *pd, const char *path, Eldbus_Message_Iter *properties, unsigned int priority) +{ + Eo *child; + + child = efl_net_connman_access_point_new(o, path, properties, priority); + if (!child) + { + ERR("could not create access point for path=%s", path); + return; + } + + pd->access_points = eina_list_append(pd->access_points, child); + DBG("Access Point Added Path=%s", path); + efl_event_callback_call(o, EFL_NET_CONTROL_EVENT_ACCESS_POINT_ADD, child); +} + +static void +_efl_net_control_access_point_updated(Eo *o, Efl_Net_Control_Data *pd, const char *path, Eldbus_Message_Iter *properties, unsigned int priority) +{ + Eo *child = _efl_net_control_access_point_find(pd, path); + if (!child) + { + _efl_net_control_access_point_added(o, pd, path, properties, priority); + return; + } + + efl_net_connman_access_point_update(child, properties, priority); +} + +static void +_efl_net_control_access_point_removed(Eo *o, Efl_Net_Control_Data *pd, const char *path) +{ + Eo *child = _efl_net_control_access_point_find(pd, path); + if (!child) + { + WRN("Access Point path '%s' doesn't exist", path); + return; + } + + pd->access_points = eina_list_remove(pd->access_points, child); + DBG("Access Point Removed Path=%s", path); + efl_event_callback_call(o, EFL_NET_CONTROL_EVENT_ACCESS_POINT_DEL, child); + efl_del(child); +} + +static int +_efl_net_control_access_point_sort_cb(const void *d1, const void *d2) +{ + const Eo *a = d1; + const Eo *b = d2; + unsigned int pa = efl_net_control_access_point_priority_get(a); + unsigned int pb = efl_net_control_access_point_priority_get(b); + if (pa < pb) return -1; + else if (pa > pb) return 1; + return 0; +} + +static void +_efl_net_control_services_changed(void *data, const Eldbus_Message *msg) +{ + Eo *o = data; + Efl_Net_Control_Data *pd = efl_data_scope_get(o, MY_CLASS); + Eldbus_Message_Iter *changed, *invalidated, *sub; + const char *path; + unsigned int priority; + + if (!eldbus_message_arguments_get(msg, "a(oa{sv})ao", &changed, &invalidated)) + { + ERR("could not get services changed, signature=%s", eldbus_message_signature_get(msg)); + return; + } + + efl_ref(o); /* will trigger events, which call user which may delete us */ + + priority = 0; + while (eldbus_message_iter_get_and_next(changed, 'r', &sub)) + { + Eldbus_Message_Iter *properties; + if (!eldbus_message_iter_arguments_get(sub, "oa{sv}", &path, &properties)) + { + ERR("could not get access point path or properties, signature=%s", eldbus_message_iter_signature_get(sub)); + continue; + } + _efl_net_control_access_point_updated(o, pd, path, properties, priority++); + } + + while (eldbus_message_iter_get_and_next(invalidated, 'o', &path)) + _efl_net_control_access_point_removed(o, pd, path); + + pd->access_points = eina_list_sort(pd->access_points, 0, _efl_net_control_access_point_sort_cb); + + efl_event_callback_call(o, EFL_NET_CONTROL_EVENT_ACCESS_POINTS_CHANGED, NULL); + efl_unref(o); +} + +static Efl_Net_Control_State +_efl_net_control_state_from_str(const char *str) +{ + const struct { + const char *name; + Efl_Net_Control_State state; + } *itr, map[] = { + {"offline", EFL_NET_CONTROL_STATE_OFFLINE}, + {"idle", EFL_NET_CONTROL_STATE_OFFLINE}, + {"ready", EFL_NET_CONTROL_STATE_LOCAL}, + {"online", EFL_NET_CONTROL_STATE_ONLINE}, + { } + }; + for (itr = map; itr->name != NULL; itr++) + if (strcmp(itr->name, str) == 0) return itr->state; + + ERR("Unknown State '%s', assume offline.", str); + return EFL_NET_CONTROL_STATE_OFFLINE; +} + +static void +_efl_net_control_property_state_changed(Eo *o, Efl_Net_Control_Data *pd, Eldbus_Message_Iter *value) +{ + const char *str; + Efl_Net_Control_State state; + + if (!eldbus_message_iter_arguments_get(value, "s", &str)) + { + ERR("Expected string, got %s", eldbus_message_iter_signature_get(value)); + return; + } + + state = _efl_net_control_state_from_str(str); + if (pd->state == state) return; + pd->state = state; + DBG("state=%d (%s)", state, str); + efl_event_callback_call(o, EFL_NET_CONTROL_EVENT_STATE_CHANGED, NULL); +} + +static void +_efl_net_control_property_radios_offline_changed(Eo *o, Efl_Net_Control_Data *pd, Eldbus_Message_Iter *value) +{ + Eina_Bool offline; + + if (!eldbus_message_iter_arguments_get(value, "b", &offline)) + { + ERR("Expected boolean, got %s", eldbus_message_iter_signature_get(value)); + return; + } + + if (pd->radios_offline == offline) return; + pd->radios_offline = offline; + DBG("radios offline=%hhu", offline); + efl_event_callback_call(o, EFL_NET_CONTROL_EVENT_RADIOS_OFFLINE_CHANGED, NULL); +} + +static void +_efl_net_control_property_changed_internal(Eo *o, Efl_Net_Control_Data *pd, Eldbus_Message_Iter *itr) +{ + Eldbus_Message_Iter *value; + const char *name; + + if (!eldbus_message_iter_arguments_get(itr, "sv", &name, &value)) + { + ERR("Unexpected signature: %s", eldbus_message_iter_signature_get(itr)); + return; + } + + if (strcmp(name, "State") == 0) + _efl_net_control_property_state_changed(o, pd, value); + else if (strcmp(name, "OfflineMode") == 0) + _efl_net_control_property_radios_offline_changed(o, pd, value); + else if (strcmp(name, "SessionMode") == 0) + { } + else + WRN("Unknown property name: %s", name); +} + +static void +_efl_net_control_property_changed(void *data, const Eldbus_Message *msg) +{ + Eo *o = data; + Efl_Net_Control_Data *pd = efl_data_scope_get(o, MY_CLASS); + Eldbus_Message_Iter *itr; + + itr = eldbus_message_iter_get(msg); + _efl_net_control_property_changed_internal(o, pd, itr); +} + +static void +_efl_net_control_properties_get_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending) +{ + Eo *o = data; + Efl_Net_Control_Data *pd = efl_data_scope_get(o, MY_CLASS); + Eldbus_Message_Iter *array, *entry; + const char *err_name, *err_msg; + + pd->pending = eina_list_remove(pd->pending, pending); + if (eldbus_message_error_get(msg, &err_name, &err_msg)) + { + ERR("Could not get properties %p: %s=%s", o, err_name, err_msg); + return; + } + + if (!eldbus_message_arguments_get(msg, "a{sv}", &array)) + { + ERR("Expected dict ('a{sv}'), got '%s'", eldbus_message_signature_get(msg)); + return; + } + + efl_ref(o); /* properties will trigger events, which call user which may delete us */ + + /* force changed to be emitted */ + pd->radios_offline = 0xff; + pd->state = 0xff; + while (eldbus_message_iter_get_and_next(array, 'e', &entry)) + _efl_net_control_property_changed_internal(o, pd, entry); + + efl_unref(o); +} + +static void +_efl_net_control_technologies_get_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending) +{ + Eo *o = data; + Efl_Net_Control_Data *pd = efl_data_scope_get(o, MY_CLASS); + Eldbus_Message_Iter *array, *entry; + const char *err_name, *err_msg; + + pd->pending = eina_list_remove(pd->pending, pending); + if (eldbus_message_error_get(msg, &err_name, &err_msg)) + { + ERR("Could not get technologies %p: %s=%s", o, err_name, err_msg); + return; + } + + if (!eldbus_message_arguments_get(msg, "a(oa{sv})", &array)) + { + ERR("Expected 'a{oa{sv}}', got '%s'", eldbus_message_signature_get(msg)); + return; + } + + efl_ref(o); /* will trigger events, which call user which may delete us */ + + while (eldbus_message_iter_get_and_next(array, 'r', &entry)) + _efl_net_control_technology_added_internal(o, pd, entry); + + efl_unref(o); +} + +static void +_efl_net_control_services_get_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending) +{ + Eo *o = data; + Efl_Net_Control_Data *pd = efl_data_scope_get(o, MY_CLASS); + Eldbus_Message_Iter *array, *sub; + const char *err_name, *err_msg, *path; + unsigned int priority = 0; + + pd->pending = eina_list_remove(pd->pending, pending); + if (eldbus_message_error_get(msg, &err_name, &err_msg)) + { + ERR("Could not get services %p: %s=%s", o, err_name, err_msg); + return; + } + + if (!eldbus_message_arguments_get(msg, "a(oa{sv})", &array)) + { + ERR("Expected 'a{oa{sv}}', got '%s'", eldbus_message_signature_get(msg)); + return; + } + + efl_ref(o); /* will trigger events, which call user which may delete us */ + + while (eldbus_message_iter_get_and_next(array, 'r', &sub)) + { + Eldbus_Message_Iter *properties; + if (!eldbus_message_iter_arguments_get(sub, "oa{sv}", &path, &properties)) + { + ERR("could not get access point path or properties, signature=%s", eldbus_message_iter_signature_get(sub)); + continue; + } + _efl_net_control_access_point_updated(o, pd, path, properties, priority++); + } + + pd->access_points = eina_list_sort(pd->access_points, 0, _efl_net_control_access_point_sort_cb); + + efl_event_callback_call(o, EFL_NET_CONTROL_EVENT_ACCESS_POINTS_CHANGED, NULL); + efl_unref(o); +} + +static void +_efl_net_control_radios_offline_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending) +{ + Eo *o = data; + Efl_Net_Control_Data *pd = efl_data_scope_get(o, MY_CLASS); + const char *err_name, *err_msg; + Eina_Bool radios_offline = pd->request.radios_offline; + + if (pd->request.radios_offline_pending == pending) + { + pd->request.radios_offline_pending = NULL; + pd->request.radios_offline = 0xff; + } + + pd->pending = eina_list_remove(pd->pending, pending); + if (eldbus_message_error_get(msg, &err_name, &err_msg)) + { + ERR("Could not set property radios_offline=%hhu %p: %s=%s", radios_offline, o, err_name, err_msg); + return; + } + + DBG("Set radios_offline=%hhu", radios_offline); +} + +static void +_efl_net_control_radios_offline_apply(Eo *o, Efl_Net_Control_Data *pd) +{ + Eldbus_Proxy *mgr = efl_net_connman_manager_get(); + Eldbus_Message *msg = eldbus_proxy_method_call_new(mgr, "SetProperty"); + Eldbus_Message_Iter *msg_itr, *var; + Eldbus_Pending *p; + + if (pd->request.radios_offline_pending) + { + pd->pending = eina_list_remove(pd->pending, pd->request.radios_offline_pending); + eldbus_pending_cancel(pd->request.radios_offline_pending); + pd->request.radios_offline_pending = NULL; + } + + EINA_SAFETY_ON_NULL_RETURN(msg); + + msg_itr = eldbus_message_iter_get(msg); + EINA_SAFETY_ON_NULL_GOTO(msg_itr, error_send); + + eldbus_message_iter_basic_append(msg_itr, 's', "OfflineMode"); + var = eldbus_message_iter_container_new(msg_itr, 'v', "b"); + eldbus_message_iter_basic_append(var, 'b', pd->request.radios_offline); + eldbus_message_iter_container_close(msg_itr, var); + + p = eldbus_proxy_send(mgr, msg, _efl_net_control_radios_offline_cb, o, DEFAULT_TIMEOUT); + EINA_SAFETY_ON_NULL_GOTO(p, error_send); + + pd->pending = eina_list_append(pd->pending, p); + pd->request.radios_offline_pending = p; + DBG("setting radios offline=%hhu", pd->request.radios_offline); + return; + + error_send: + eldbus_message_unref(msg); +} + +static void +_efl_net_control_clear(Eo *o, Efl_Net_Control_Data *pd) +{ + Eo *child; + + EINA_LIST_FREE(pd->access_points, child) + { + efl_event_callback_call(o, EFL_NET_CONTROL_EVENT_ACCESS_POINT_DEL, child); + efl_del(child); + } + efl_event_callback_call(o, EFL_NET_CONTROL_EVENT_ACCESS_POINTS_CHANGED, NULL); + + EINA_LIST_FREE(pd->technologies, child) + { + efl_event_callback_call(o, EFL_NET_CONTROL_EVENT_TECHNOLOGY_DEL, child); + efl_del(child); + } + + pd->state = EFL_NET_CONTROL_STATE_OFFLINE; + efl_event_callback_call(o, EFL_NET_CONTROL_EVENT_STATE_CHANGED, NULL); + pd->radios_offline = EINA_TRUE; + efl_event_callback_call(o, EFL_NET_CONTROL_EVENT_RADIOS_OFFLINE_CHANGED, NULL); +} + +static void +_efl_net_control_connman_name_owner_changed(void *data, const char *bus, const char *old_id, const char *new_id) +{ + Eo *o = data; + Efl_Net_Control_Data *pd = efl_data_scope_get(o, MY_CLASS); + Eldbus_Proxy *mgr; + + DBG("Name Owner Changed %s: %s->%s", bus, old_id, new_id); + if ((!new_id) || (new_id[0] == '\0')) + { + _efl_net_control_clear(o, pd); + pd->operating = EINA_FALSE; + if (pd->agent_enabled) + { + pd->agent_enabled = EINA_FALSE; + efl_event_callback_call(o, EFL_NET_CONTROL_EVENT_AGENT_RELEASED, NULL); + } + return; + } + + mgr = efl_net_connman_manager_get(); + EINA_SAFETY_ON_NULL_RETURN(mgr); + +#define SH(sig, cb) \ + do { \ + Eldbus_Signal_Handler *sh = eldbus_proxy_signal_handler_add(mgr, sig, cb, o); \ + if (sh) pd->signal_handlers = eina_list_append(pd->signal_handlers, sh); \ + else ERR("could not add DBus signal handler %s", sig); \ + } while (0) + + SH("TechnologyAdded", _efl_net_control_technology_added); + SH("TechnologyRemoved", _efl_net_control_technology_removed); + SH("ServicesChanged", _efl_net_control_services_changed); + SH("PropertyChanged", _efl_net_control_property_changed); +#undef SH + +#define CALL(meth, cb) \ + do { \ + Eldbus_Pending *p = eldbus_proxy_call(mgr, meth, cb, o, DEFAULT_TIMEOUT, ""); \ + if (p) pd->pending = eina_list_append(pd->pending, p); \ + else ERR("could not call DBus method %s", meth); \ + } while (0) + + CALL("GetProperties", _efl_net_control_properties_get_cb); + CALL("GetTechnologies", _efl_net_control_technologies_get_cb); + CALL("GetServices", _efl_net_control_services_get_cb); +#undef CALL + + pd->operating = EINA_TRUE; + + if (pd->request.radios_offline != 0xff) _efl_net_control_radios_offline_apply(o, pd); + + if (pd->agent_pending) + { + eldbus_pending_cancel(pd->agent_pending); + pd->agent_pending = NULL; + } + if (pd->agent_enabled) + { + pd->agent_enabled = EINA_FALSE; + _efl_net_control_agent_enabled_set(o, pd, EINA_TRUE); + } +} + +void +efl_net_connman_control_access_points_reload(Eo *o) +{ + Efl_Net_Control_Data *pd = efl_data_scope_get(o, MY_CLASS); + Eldbus_Proxy *mgr; + Eldbus_Pending *p; + + EINA_SAFETY_ON_NULL_RETURN(pd); + mgr = efl_net_connman_manager_get(); + EINA_SAFETY_ON_NULL_RETURN(mgr); + + p = eldbus_proxy_call(mgr, "GetServices", _efl_net_control_services_get_cb, o, DEFAULT_TIMEOUT, ""); + if (p) pd->pending = eina_list_append(pd->pending, p); + else ERR("could not call DBus method %s", "GetServices"); +} + +EOLIAN static Eo * +_efl_net_control_efl_object_constructor(Eo *o, Efl_Net_Control_Data *pd EINA_UNUSED) +{ + pd->radios_offline = EINA_TRUE; + pd->request.radios_offline = 0xff; + + if (!efl_net_connman_init()) + { + ERR("could not initialize connman infrastructure"); + return NULL; + } + + return efl_constructor(efl_super(o, MY_CLASS)); +} + +EOLIAN static Eo * +_efl_net_control_efl_object_finalize(Eo *o, Efl_Net_Control_Data *pd EINA_UNUSED) +{ + Eldbus_Connection *conn; + char path[128]; + + o = efl_finalize(efl_super(o, MY_CLASS)); + if (!o) return NULL; + + conn = efl_net_connman_connection_get(); + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + + snprintf(path, sizeof(path), "/connman/agent_%p", o); + pd->agent = eldbus_service_interface_register(conn, path, &_efl_net_control_agent_desc); + EINA_SAFETY_ON_NULL_RETURN_VAL(pd->agent, NULL); + eldbus_service_object_data_set(pd->agent, "efl_net", o); + + eldbus_name_owner_changed_callback_add(conn, "net.connman", _efl_net_control_connman_name_owner_changed, o, EINA_TRUE); + DBG("waiting for net.connman to show on the DBus system bus..."); + + return o; +} + +EOLIAN static void +_efl_net_control_efl_object_destructor(Eo *o, Efl_Net_Control_Data *pd) +{ + Eldbus_Pending *p; + Eldbus_Signal_Handler *sh; + + pd->operating = EINA_FALSE; + + pd->request.radios_offline_pending = NULL; + EINA_LIST_FREE(pd->pending, p) + eldbus_pending_cancel(p); + + EINA_LIST_FREE(pd->signal_handlers, sh) + eldbus_signal_handler_del(sh); + + if (pd->agent_request_input.msg) + { + Eldbus_Message *reply = eldbus_message_error_new(pd->agent_request_input.msg, + "net.connman.Error.OperationAborted", + "Agent is gone."); + eldbus_connection_send(efl_net_connman_connection_get(), reply, NULL, NULL, -1.0); + eldbus_message_unref(pd->agent_request_input.msg); + pd->agent_request_input.msg = NULL; + } + + if (pd->agent_enabled) + { + Eldbus_Proxy *mgr = efl_net_connman_manager_get(); + + /* UnregisterAgent is required since the manager proxy and bus + * may be alive, thus connman won't get any NameOwnerChanged + * or related to know the object is gone + */ + eldbus_proxy_call(mgr, "UnregisterAgent", NULL, NULL, DEFAULT_TIMEOUT, + "o", eldbus_service_object_path_get(pd->agent)); + } + + if (pd->agent) + { + eldbus_service_object_data_del(pd->agent, "efl_net"); + eldbus_service_object_unregister(pd->agent); + pd->agent = NULL; + } + + _efl_net_control_clear(o, pd); + + efl_destructor(efl_super(o, MY_CLASS)); + efl_net_connman_shutdown(); +} + +EOLIAN static void +_efl_net_control_radios_offline_set(Eo *o, Efl_Net_Control_Data *pd, Eina_Bool radios_offline) +{ + pd->request.radios_offline = radios_offline; + if (pd->operating) _efl_net_control_radios_offline_apply(o, pd); +} + +EOLIAN static Eina_Bool +_efl_net_control_radios_offline_get(Eo *o EINA_UNUSED, Efl_Net_Control_Data *pd) +{ + return pd->radios_offline; +} + +EOLIAN static Efl_Net_Control_State +_efl_net_control_state_get(Eo *o EINA_UNUSED, Efl_Net_Control_Data *pd) +{ + return pd->state; +} + +EOLIAN static Eina_Iterator * +_efl_net_control_access_points_get(Eo *o EINA_UNUSED, Efl_Net_Control_Data *pd) +{ + return eina_list_iterator_new(pd->access_points); +} + +EOLIAN static Eina_Iterator * +_efl_net_control_technologies_get(Eo *o EINA_UNUSED, Efl_Net_Control_Data *pd) +{ + return eina_list_iterator_new(pd->technologies); +} + +Efl_Net_Control_Technology * +efl_net_connman_control_find_technology_by_type(Efl_Net_Control *o, const char *tech_type) +{ + Efl_Net_Control_Data *pd = efl_data_scope_get(o, MY_CLASS); + Efl_Net_Control_Technology_Type desired = efl_net_connman_technology_type_from_str(tech_type); + const Eina_List *n; + Eo *child; + + EINA_SAFETY_ON_NULL_RETURN_VAL(pd, NULL); + + EINA_LIST_FOREACH(pd->technologies, n, child) + { + Efl_Net_Control_Technology_Type type = efl_net_control_technology_type_get(child); + if (type == desired) + return child; + } + + WRN("Could not find technology type %#x '%s'", desired, tech_type); + return NULL; +} + +static void +_efl_net_control_agent_register_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending) +{ + Eo *o = data; + Efl_Net_Control_Data *pd = efl_data_scope_get(o, MY_CLASS); + const char *err_name, *err_msg; + + pd->pending = eina_list_remove(pd->pending, pending); + if (pd->agent_pending == pending) + pd->agent_pending = NULL; + if (eldbus_message_error_get(msg, &err_name, &err_msg)) + { + ERR("Could not Register Agent %p: %s=%s", o, err_name, err_msg); + return; + } + + DBG("Agent registered %p: %s", o, eldbus_service_object_path_get(pd->agent)); + pd->agent_enabled = EINA_TRUE; +} + +static void +_efl_net_control_agent_unregister_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending) +{ + Eo *o = data; + Efl_Net_Control_Data *pd = efl_data_scope_get(o, MY_CLASS); + const char *err_name, *err_msg; + + pd->pending = eina_list_remove(pd->pending, pending); + if (pd->agent_pending == pending) + pd->agent_pending = NULL; + if (eldbus_message_error_get(msg, &err_name, &err_msg)) + { + ERR("Could not Unregister Agent %p: %s=%s", o, err_name, err_msg); + return; + } + DBG("Agent unregistered %p: %s", o, eldbus_service_object_path_get(pd->agent)); + pd->agent_enabled = EINA_FALSE; +} + +EOLIAN static void +_efl_net_control_agent_enabled_set(Eo *o, Efl_Net_Control_Data *pd, Eina_Bool agent_enabled) +{ + Eldbus_Pending *p; + Eldbus_Proxy *mgr; + + if (!pd->operating) + { + DBG("no connman yet, postpone agent_enabled=%hhu", agent_enabled); + pd->agent_enabled = agent_enabled; + return; + } + if (pd->agent_enabled == agent_enabled) + { + DBG("Already agent_enabled=%hhu", agent_enabled); + return; + } + + if (pd->agent_pending) + { + eldbus_pending_cancel(pd->agent_pending); + pd->agent_pending = NULL; + } + + mgr = efl_net_connman_manager_get(); + EINA_SAFETY_ON_NULL_RETURN(mgr); + + if (agent_enabled) + { + p = eldbus_proxy_call(mgr, "RegisterAgent", + _efl_net_control_agent_register_cb, o, DEFAULT_TIMEOUT, + "o", eldbus_service_object_path_get(pd->agent)); + DBG("%p RegisterAgent=%p", o, p); + } + else + { + p = eldbus_proxy_call(mgr, "UnregisterAgent", + _efl_net_control_agent_unregister_cb, o, DEFAULT_TIMEOUT, + "o", eldbus_service_object_path_get(pd->agent)); + DBG("%p UnregisterAgent=%p", o, p); + } + + EINA_SAFETY_ON_NULL_RETURN(p); + + pd->pending = eina_list_append(pd->pending, p); + pd->agent_pending = p; +} + +EOLIAN static Eina_Bool +_efl_net_control_agent_enabled_get(Eo *o EINA_UNUSED, Efl_Net_Control_Data *pd) +{ + return pd->agent_enabled; +} + +static void +_append_dict_entry(Eldbus_Message_Iter *array, const char *name, const char *signature, ...) +{ + Eldbus_Message_Iter *entry, *var; + va_list ap; + + if (!eldbus_message_iter_arguments_append(array, "{sv}", &entry)) + { + ERR("could not append dict entry"); + return; + } + + eldbus_message_iter_basic_append(entry, 's', name); + var = eldbus_message_iter_container_new(entry, 'v', signature); + + va_start(ap, signature); + eldbus_message_iter_arguments_vappend(var, signature, ap); + va_end(ap); + eldbus_message_iter_container_close(entry, var); + eldbus_message_iter_container_close(array, entry); +} + + +static void +_append_dict_entry_byte_array(Eldbus_Message_Iter *array, const char *name, const Eina_Slice *slice) +{ + Eldbus_Message_Iter *entry, *var, *sub; + + if (!eldbus_message_iter_arguments_append(array, "{sv}", &entry)) + { + ERR("could not append dict entry"); + return; + } + + eldbus_message_iter_basic_append(entry, 's', name); + var = eldbus_message_iter_container_new(entry, 'v', "ay"); + sub = eldbus_message_iter_container_new(var, 'a', "y"); + + eldbus_message_iter_fixed_array_append(sub, 'y', slice->mem, slice->len); + + eldbus_message_iter_container_close(var, sub); + eldbus_message_iter_container_close(entry, var); + eldbus_message_iter_container_close(array, entry); +} + +EOLIAN static void +_efl_net_control_agent_reply(Eo *o EINA_UNUSED, Efl_Net_Control_Data *pd, const char *name, const Eina_Slice *ssid, const char *username, const char *passphrase, const char *wps) +{ + Eldbus_Message *reply; + Eldbus_Message_Iter *msg_itr, *array; + + if (!pd->agent_request_input.msg) + { + ERR("no agent input was required."); + return; + } + + reply = eldbus_message_method_return_new(pd->agent_request_input.msg); + EINA_SAFETY_ON_NULL_RETURN(reply); + eldbus_message_unref(pd->agent_request_input.msg); + pd->agent_request_input.msg = NULL; + + msg_itr = eldbus_message_iter_get(reply); + eldbus_message_iter_arguments_append(msg_itr, "a{sv}", &array); + + if (name) _append_dict_entry(array, "Name", "s", name); + if (ssid) _append_dict_entry_byte_array(array, "SSID", ssid); + if (username) + { + _append_dict_entry(array, "Username", "s", username); + _append_dict_entry(array, "Identity", "s", username); + } + if (passphrase) + { + _append_dict_entry(array, "Password", "s", passphrase); + _append_dict_entry(array, "Passphrase", "s", passphrase); + } + if (wps) _append_dict_entry(array, "WPS", "s", wps); + + eldbus_message_iter_container_close(msg_itr, array); + eldbus_connection_send(efl_net_connman_connection_get(), reply, NULL, NULL, -1.0); +} + +#include "efl_net_control.eo.c" diff --git a/src/lib/ecore_con/efl_net_control.eo b/src/lib/ecore_con/efl_net_control.eo new file mode 100644 index 0000000000..14b7ceb773 --- /dev/null +++ b/src/lib/ecore_con/efl_net_control.eo @@ -0,0 +1,213 @@ +import eina_types; +import efl_net_control_access_point; +import efl_net_control_technology; + +enum Efl.Net.Control.State { + [[Provides the global network connectivity state. + + For fine grained details, use @Efl.Net.Control access points and + their state property. + + @since 1.19 + ]] + offline, [[no access point is connected]] + local, [[at least one access point was connected and the internet connectio wasn't verified]] + online, [[at least one access point was connected and the internet was verified]] +} + +enum Efl.Net.Control.Agent_Request_Input.Field { + [[Bitwise-able fields requested to the agent. + + @since 1.19 + ]] + name = (1 << 0), [[Used for hidden WiFi access points. If ssid is present, this is an alternative to that.]] + ssid = (1 << 1), [[Used for hidden WiFi access points. If name is present, this is an alternative to that.]] + username = (1 << 2), [[Identity or username requested]] + passphrase = (1 << 3), [[password or passphrase requested]] + wps = (1 << 4), [[Use WPS authentication. If passphrase is present, this is an alternative to that.]] +} + +struct Efl.Net.Control.Agent_Request_Input.Information { + [[Name-value information pair provided to the agent. + + @since 1.19 + ]] + name: string; [[The information name, such as PreviousPassphrase, Host, Name...]] + value: string; [[The contents of the information]] +} + +struct Efl.Net.Control.Agent_Request_Input { + [[Request input to the agent. + + @since 1.19 + ]] + access_point: Efl.Net.Control.Access_Point; [[The access point that triggered this request.]] + fields: Efl.Net.Control.Agent_Request_Input.Field; [[Bitwise OR of fields present in this request.]] + passphrase_type: string; [[Extra detail on the meaning for the passphrase field, such as wep, psk, response (IEEE802.X GTC/OTP), string...]] + informational: list; [[such as the previous passphrase, VPN host]] +} + +struct Efl.Net.Control.Agent_Error { + [[Report error to the agent. + + @since 1.19 + ]] + access_point: Efl.Net.Control.Access_Point; [[The access point that triggered this error.]] + message: string; [[The error message.]] +} + +struct Efl.Net.Control.Agent_Browser_Url { + [[Report to agent that it should open a browser at given URL. + + @since 1.19 + ]] + access_point: Efl.Net.Control.Access_Point; [[The access point that triggered this request.]] + url: string; [[The URL to point the browser at.]] +} + +class Efl.Net.Control (Efl.Loop_User) { + [[Control network connectivity. + + This class and its children objects are only useful to implement + control of the network connectivity. If your application is only + interested in requesting access to the network, use the + \@Efl.Net.Session instead. + + The network connectivity is defined on top of technologies that + provide access points. A technology can be "ethernet", "wifi", + "bluetooth" or something else. Ethernet will provide a single + access point, while "wifi" will expose zero or more access + points that can come and go. + + Users willing to use access points are expected to monitor + "access_point,add" event to know when access points were + added. To know when they were deleted, "access_point,del" or an + @Efl.Net.Control.Access_Point "del" event. Finally + "access_points,changed" is relative to additions, deletions and + reordering of access point due changes in their priorities. + + The backend system is responsible to remember connection details + such as passphrase, last connected access point and + priority. The user is NOT supposed to do that. + + For ease of use, @.state tells if at least one access point is + online (verified connectivity), local (connected but unverified) + or offline. + + Due safety reasons all radio transmissions may be disabled with + @.radios_offline property. This is usually called "airplane + mode" in some platforms. + + @since 1.19 + ]] + events { + access_point,add: Efl.Net.Control.Access_Point; [[The given access point was added]] + access_point,del: Efl.Net.Control.Access_Point; [[The given access point will be deleted]] + access_points,changed; [[Access points were added, deleted or reordered.]] + technology,add: Efl.Net.Control.Technology; [[The given technology was added]] + technology,del: Efl.Net.Control.Technology; [[The given technology will be deleted]] + radios_offline,changed; [[Property @.radios_offline changed]] + state,changed; [[Property @.state changed]] + + agent_released; [[Notifies we're not the agent anymore]] + agent_error: Efl.Net.Control.Agent_Error; [[Requires the error to be reported to the user]] + agent_browser_url: Efl.Net.Control.Agent_Browser_Url; [[Requires the user to visit a web page]] + agent_request_input: Efl.Net.Control.Agent_Request_Input; [[Requires the user to enter information in order to proceed, such as hidden SSID, passphrase, etc. After the user entered information, reply by calling @.agent_reply]] + } + + methods { + @property radios_offline { + [[If $true disable all network technologies that use radio transmission, such as bluetooth and wifi. If $false, allows radios to be used.]] + get { } + set { } + values { + radios_offline: bool; + } + } + + @property state { + [[Summary of network connectivity. + + - offline means no connectivity; + + - local means local connectivity, that is, the access + point is connected but couldn't reach the internet; + + - online means verified connectivity. + ]] + get { } + values { + state: Efl.Net.Control.State; + } + } + + @property access_points { + [[The iterator of current access points. + + The iterator is valid only before the function returns + to the main loop, by then, if the events + "access_point,add" or "access_point,del" are emitted, + the iterator will become invalid. + ]] + get { } + values { + access_points: free(own(iterator), eina_iterator_free); + } + } + + @property technologies { + [[The iterator of current access points. + + The iterator is valid only before the function returns + to the main loop, by then, if the events + "technology,add" or "technology,del" are emitted, + the iterator will become invalid. + ]] + get { } + values { + technologies: free(own(iterator), eina_iterator_free); + } + } + + @property agent_enabled { + [[The agent is responsible for user interaction. + + When enabled, the local process will become the agent + for user interaction, such as requesting passphrases, + asking the user to open a browser to do + web-authentication and report connection errors. + + There can be a single agent in the system at a given + time, registering one will unregister the other and + special permissions may be required to become an agent. + + An useful agent should monitor "agent_error", + "agent_browser_url" and "agent_request_input" + events. When input is requested, reply using + @.agent_reply. + ]] + get { } + set { } + values { + agent_enabled: bool; + } + } + + agent_reply { + [[If event "agent_request_input" was emitted, this will reply with the requested data]] + params { + name: string @nullable; [[If @Efl.Net.Control.Agent_Request_Input.Field.name was present, this should contain the network name or the 'ssid' parameter should be used.]] + ssid: const(Eina.Slice)* @nullable; [[If @Efl.Net.Control.Agent_Request_Input.Field.ssid was present, this should contain the network SSID or the 'name' parameter should be used.]] + username: string @nullable; [[If @Efl.Net.Control.Agent_Request_Input.Field.username was present, this should contain the identity or username]] + passphrase: string @nullable; [[If @Efl.Net.Control.Agent_Request_Input.Field.passphrase was present, this should contain the password or passphrase, more details on how it should be interpreted was given in Efl.Net.Control.Agent_Request_Input.passphrase_type.]] + wps: string @nullable; [[If @Efl.Net.Control.Agent_Request_Input.Field.wps was present, this should contain the WPS PIN or an empty string "" to use the WPS push button instead.]] + } + } + } + + implements { + Efl.Object.destructor; + Efl.Object.constructor; + Efl.Object.finalize; + } +} diff --git a/src/lib/ecore_con/efl_net_control_access_point-connman.c b/src/lib/ecore_con/efl_net_control_access_point-connman.c new file mode 100644 index 0000000000..634bebc857 --- /dev/null +++ b/src/lib/ecore_con/efl_net_control_access_point-connman.c @@ -0,0 +1,1692 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "Ecore.h" +#include "Ecore_Con.h" +#include "ecore_con_private.h" +#include "efl_net-connman.h" + +typedef struct +{ + /* Eldbus_Proxy/Eldbus_Object keeps a list of pending calls, but + * they are reference counted singletons and will only cancel + * pending calls when everything is gone. However we operate on + * our private data, that may be gone before other refs. So + * keep the pending list. + */ + Eina_List *pending; + Eina_List *signal_handlers; + Eldbus_Proxy *proxy; + Eina_Stringshare *path; + Eina_Stringshare *name; + Eina_Stringshare *techname; + struct { + Eina_List *name_servers; + Eina_List *time_servers; + Eina_List *domains; + struct { + Efl_Net_Control_Access_Point_Ipv4_Method method; + Eina_Stringshare *address; + Eina_Stringshare *netmask; + Eina_Stringshare *gateway; + } ipv4; + struct { + Efl_Net_Control_Access_Point_Ipv6_Method method; + Eina_Stringshare *address; + Eina_Stringshare *netmask; + Eina_Stringshare *gateway; + uint8_t prefix_length; + } ipv6; + struct { + Efl_Net_Control_Access_Point_Proxy_Method method; + Eina_Stringshare *url; + Eina_List *servers; + Eina_List *excludes; + } proxy; + } actual, configured; + unsigned int priority; + Efl_Net_Control_Access_Point_State state; + Efl_Net_Control_Access_Point_Error error; + Efl_Net_Control_Access_Point_Security security; + uint8_t strength; + Eina_Bool roaming; + Eina_Bool auto_connect; + Eina_Bool remembered; + Eina_Bool immutable; +} Efl_Net_Control_Access_Point_Data; + +#define MY_CLASS EFL_NET_CONTROL_ACCESS_POINT_CLASS + + +static void +_efl_net_control_access_point_property_set_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending) +{ + Eo *o = data; + Efl_Net_Control_Access_Point_Data *pd = efl_data_scope_get(o, MY_CLASS); + const char *err_name, *err_msg; + + pd->pending = eina_list_remove(pd->pending, pending); + if (eldbus_message_error_get(msg, &err_name, &err_msg)) + { + ERR("Could not set property %p: %s=%s", o, err_name, err_msg); + return; + } +} + +static void +_efl_net_control_access_point_property_set_string_array(Eo *o, Efl_Net_Control_Access_Point_Data *pd, const char *name, Eina_Iterator *it) +{ + Eldbus_Message *msg; + Eldbus_Message_Iter *msg_itr, *var, *array; + Eldbus_Pending *p; + const char *str; + + msg = eldbus_proxy_method_call_new(pd->proxy, "SetProperty"); + EINA_SAFETY_ON_NULL_GOTO(msg, error_msg); + + msg_itr = eldbus_message_iter_get(msg); + EINA_SAFETY_ON_NULL_GOTO(msg_itr, error_send); + + eldbus_message_iter_basic_append(msg_itr, 's', name); + var = eldbus_message_iter_container_new(msg_itr, 'v', "as"); + + eldbus_message_iter_arguments_append(var, "as", &array); + EINA_ITERATOR_FOREACH(it, str) + eldbus_message_iter_basic_append(array, 's', str); + eldbus_message_iter_container_close(var, array); + eldbus_message_iter_container_close(msg_itr, var); + eina_iterator_free(it); + + p = eldbus_proxy_send(pd->proxy, msg, _efl_net_control_access_point_property_set_cb, o, DEFAULT_TIMEOUT); + EINA_SAFETY_ON_NULL_GOTO(p, error_send); + + pd->pending = eina_list_append(pd->pending, p); + DBG("Setting property %s", name); + return; + + error_send: + eldbus_message_unref(msg); + error_msg: + eina_iterator_free(it); +} + +static void +_efl_net_control_access_point_property_set(Eo *o, Efl_Net_Control_Access_Point_Data *pd, const char *name, const char *signature, ...) +{ + Eldbus_Message *msg; + Eldbus_Message_Iter *msg_itr, *var; + Eldbus_Pending *p; + va_list ap; + + msg = eldbus_proxy_method_call_new(pd->proxy, "SetProperty"); + EINA_SAFETY_ON_NULL_RETURN(msg); + + msg_itr = eldbus_message_iter_get(msg); + EINA_SAFETY_ON_NULL_GOTO(msg_itr, error_send); + + eldbus_message_iter_basic_append(msg_itr, 's', name); + var = eldbus_message_iter_container_new(msg_itr, 'v', signature); + + va_start(ap, signature); + eldbus_message_iter_arguments_vappend(var, signature, ap); + va_end(ap); + eldbus_message_iter_container_close(msg_itr, var); + + p = eldbus_proxy_send(pd->proxy, msg, _efl_net_control_access_point_property_set_cb, o, DEFAULT_TIMEOUT); + EINA_SAFETY_ON_NULL_GOTO(p, error_send); + + pd->pending = eina_list_append(pd->pending, p); + DBG("Setting property %s", name); + return; + + error_send: + eldbus_message_unref(msg); +} + +EOLIAN static void +_efl_net_control_access_point_efl_object_destructor(Eo *o, Efl_Net_Control_Access_Point_Data *pd) +{ + Eldbus_Pending *p; + Eldbus_Signal_Handler *sh; + const char *str; + + EINA_LIST_FREE(pd->pending, p) + eldbus_pending_cancel(p); + + EINA_LIST_FREE(pd->signal_handlers, sh) + eldbus_signal_handler_del(sh); + + if (pd->proxy) + { + Eldbus_Object *obj = eldbus_proxy_object_get(pd->proxy); + eldbus_proxy_unref(pd->proxy); + pd->proxy = NULL; + eldbus_object_unref(obj); + } + + efl_destructor(efl_super(o, MY_CLASS)); + eina_stringshare_replace(&pd->path, NULL); + eina_stringshare_replace(&pd->name, NULL); + + /* actual values */ + EINA_LIST_FREE(pd->actual.name_servers, str) eina_stringshare_del(str); + EINA_LIST_FREE(pd->actual.time_servers, str) eina_stringshare_del(str); + EINA_LIST_FREE(pd->actual.domains, str) eina_stringshare_del(str); + + eina_stringshare_replace(&pd->actual.ipv4.address, NULL); + eina_stringshare_replace(&pd->actual.ipv4.netmask, NULL); + eina_stringshare_replace(&pd->actual.ipv4.gateway, NULL); + + eina_stringshare_replace(&pd->actual.ipv6.address, NULL); + eina_stringshare_replace(&pd->actual.ipv6.netmask, NULL); + eina_stringshare_replace(&pd->actual.ipv6.gateway, NULL); + + eina_stringshare_replace(&pd->actual.proxy.url, NULL); + EINA_LIST_FREE(pd->actual.proxy.servers, str) eina_stringshare_del(str); + EINA_LIST_FREE(pd->actual.proxy.excludes, str) eina_stringshare_del(str); + + /* configured values */ + EINA_LIST_FREE(pd->configured.name_servers, str) eina_stringshare_del(str); + EINA_LIST_FREE(pd->configured.time_servers, str) eina_stringshare_del(str); + EINA_LIST_FREE(pd->configured.domains, str) eina_stringshare_del(str); + + eina_stringshare_replace(&pd->configured.ipv4.address, NULL); + eina_stringshare_replace(&pd->configured.ipv4.netmask, NULL); + eina_stringshare_replace(&pd->configured.ipv4.gateway, NULL); + + eina_stringshare_replace(&pd->configured.ipv6.address, NULL); + eina_stringshare_replace(&pd->configured.ipv6.netmask, NULL); + eina_stringshare_replace(&pd->configured.ipv6.gateway, NULL); + + eina_stringshare_replace(&pd->configured.proxy.url, NULL); + EINA_LIST_FREE(pd->configured.proxy.servers, str) eina_stringshare_del(str); + EINA_LIST_FREE(pd->configured.proxy.excludes, str) eina_stringshare_del(str); +} + +EOLIAN static Efl_Net_Control_Access_Point_State +_efl_net_control_access_point_state_get(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd) +{ + return pd->state; +} + +EOLIAN static Efl_Net_Control_Access_Point_Error +_efl_net_control_access_point_error_get(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd) +{ + return pd->error; +} + +EOLIAN static const char * +_efl_net_control_access_point_name_get(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd) +{ + return pd->name; +} + +static void +_efl_net_control_access_point_priority_set_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending) +{ + Eo *o = data; + Efl_Net_Control_Access_Point_Data *pd = efl_data_scope_get(o, MY_CLASS); + const char *err_name, *err_msg; + + pd->pending = eina_list_remove(pd->pending, pending); + if (eldbus_message_error_get(msg, &err_name, &err_msg)) + { + ERR("Could not reorder %p: %s=%s", o, err_name, err_msg); + return; + } + + DBG("finished reordering %p", o); + /* NOTE: it seems connman is not emiting ServicesChanged as expected */ + efl_net_connman_control_access_points_reload(efl_parent_get(o)); +} + +EOLIAN static void +_efl_net_control_access_point_priority_set(Eo *o, Efl_Net_Control_Access_Point_Data *pd, unsigned int priority) +{ + Efl_Net_Control_Access_Point_Data *other_pd; + Eldbus_Pending *p; + Eina_Iterator *it; + Eo *ap = NULL, *last_ap = NULL, *sibling = NULL; + int direction = 0; + + if (pd->priority == priority) + { + DBG("same priority %u, nothing to do for %s", priority, pd->name); + return; + } + else if (!pd->remembered) + { + ERR("cannot change priority of non-remembered access point '%s'", pd->name); + return; + } + + it = efl_net_control_access_points_get(efl_parent_get(o)); + EINA_ITERATOR_FOREACH(it, ap) + { + unsigned other_prio; + if (ap == o) continue; + else if (!efl_net_control_access_point_remembered_get(ap)) break; + else if (priority == 0) + { + sibling = ap; + direction = -1; + break; + } + + other_prio = efl_net_control_access_point_priority_get(ap); + if (priority < other_prio) break; + last_ap = ap; + } + eina_iterator_free(it); + + if ((!sibling) && (last_ap)) + { + sibling = last_ap; + if (priority <= efl_net_control_access_point_priority_get(last_ap)) + direction = -1; + else + direction = 1; + } + + if (!sibling) + { + DBG("nothing to reorder priority %u for %s", priority, pd->name); + return; + } + + other_pd = efl_data_scope_get(sibling, MY_CLASS); + EINA_SAFETY_ON_NULL_RETURN(other_pd); + + p = eldbus_proxy_call(pd->proxy, + (direction < 0) ? "MoveBefore" : "MoveAfter", + _efl_net_control_access_point_priority_set_cb, o, DEFAULT_TIMEOUT, + "o", other_pd->path); + EINA_SAFETY_ON_NULL_RETURN(p); + pd->pending = eina_list_append(pd->pending, p); + DBG("Moving %s (%s) %s %s (%s)", + pd->name, pd->path, + (direction < 0) ? "before" : "after", + other_pd->name, other_pd->path); + return; +} + +EOLIAN static unsigned int +_efl_net_control_access_point_priority_get(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd) +{ + return pd->priority; +} + +EOLIAN static Efl_Net_Control_Technology * +_efl_net_control_access_point_technology_get(Eo *o, Efl_Net_Control_Access_Point_Data *pd) +{ + return efl_net_connman_control_find_technology_by_type(efl_parent_get(o), pd->techname); +} + +EOLIAN static uint8_t +_efl_net_control_access_point_strength_get(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd) +{ + return pd->strength; +} + +EOLIAN static Eina_Bool +_efl_net_control_access_point_roaming_get(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd) +{ + return pd->roaming; +} + +EOLIAN static void +_efl_net_control_access_point_auto_connect_set(Eo *o, Efl_Net_Control_Access_Point_Data *pd, Eina_Bool auto_connect) +{ + _efl_net_control_access_point_property_set(o, pd, "AutoConnect", "b", auto_connect); +} + +EOLIAN static Eina_Bool +_efl_net_control_access_point_auto_connect_get(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd) +{ + return pd->auto_connect; +} + +EOLIAN static Eina_Bool +_efl_net_control_access_point_remembered_get(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd) +{ + return pd->remembered; +} + +EOLIAN static Eina_Bool +_efl_net_control_access_point_immutable_get(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd) +{ + return pd->immutable; +} + +EOLIAN static Efl_Net_Control_Access_Point_Security +_efl_net_control_access_point_security_get(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd) +{ + return pd->security; +} + +EOLIAN static Eina_Iterator * +_efl_net_control_access_point_name_servers_get(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd) +{ + return eina_list_iterator_new(pd->actual.name_servers); +} + +EOLIAN static Eina_Iterator * +_efl_net_control_access_point_time_servers_get(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd) +{ + return eina_list_iterator_new(pd->actual.time_servers); +} + +EOLIAN static Eina_Iterator * +_efl_net_control_access_point_domains_get(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd) +{ + return eina_list_iterator_new(pd->actual.domains); +} + +EOLIAN static void +_efl_net_control_access_point_ipv4_get(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Efl_Net_Control_Access_Point_Ipv4_Method *method, const char **address, const char **netmask, const char **gateway) +{ + if (method) *method = pd->actual.ipv4.method; + if (address) *address = pd->actual.ipv4.address; + if (netmask) *netmask = pd->actual.ipv4.netmask; + if (gateway) *gateway = pd->actual.ipv4.gateway; +} + +EOLIAN static void +_efl_net_control_access_point_ipv6_get(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Efl_Net_Control_Access_Point_Ipv6_Method *method, const char **address, uint8_t *prefix_length, const char **netmask, const char **gateway) +{ + if (method) *method = pd->actual.ipv6.method; + if (address) *address = pd->actual.ipv6.address; + if (netmask) *netmask = pd->actual.ipv6.netmask; + if (gateway) *gateway = pd->actual.ipv6.gateway; + if (prefix_length) *prefix_length = pd->actual.ipv6.prefix_length; +} + +EOLIAN static void +_efl_net_control_access_point_proxy_get(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Efl_Net_Control_Access_Point_Proxy_Method *method, const char **url, Eina_Iterator **servers, Eina_Iterator **excludes) +{ + if (method) *method = pd->actual.proxy.method; + if (url) *url = pd->actual.proxy.url; + if (servers) *servers = eina_list_iterator_new(pd->actual.proxy.servers); + if (excludes) *excludes = eina_list_iterator_new(pd->actual.proxy.excludes); +} + +EOLIAN static void +_efl_net_control_access_point_configuration_name_servers_set(Eo *o, Efl_Net_Control_Access_Point_Data *pd, Eina_Iterator *name_servers) +{ + _efl_net_control_access_point_property_set_string_array(o, pd, "Nameservers.Configuration", name_servers); +} + +EOLIAN static Eina_Iterator * +_efl_net_control_access_point_configuration_name_servers_get(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd) +{ + return eina_list_iterator_new(pd->configured.name_servers); +} + +EOLIAN static void +_efl_net_control_access_point_configuration_time_servers_set(Eo *o, Efl_Net_Control_Access_Point_Data *pd, Eina_Iterator *time_servers) +{ + _efl_net_control_access_point_property_set_string_array(o, pd, "Timeservers.Configuration", time_servers); +} + +EOLIAN static Eina_Iterator * +_efl_net_control_access_point_configuration_time_servers_get(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd) +{ + return eina_list_iterator_new(pd->configured.time_servers); +} + +EOLIAN static void +_efl_net_control_access_point_configuration_domains_set(Eo *o, Efl_Net_Control_Access_Point_Data *pd, Eina_Iterator *domains) +{ + _efl_net_control_access_point_property_set_string_array(o, pd, "Domains.Configuration", domains); +} + +EOLIAN static Eina_Iterator * +_efl_net_control_access_point_configuration_domains_get(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd) +{ + return eina_list_iterator_new(pd->configured.domains); +} + +static void +_append_dict_entry(Eldbus_Message_Iter *array, const char *name, const char *signature, ...) +{ + Eldbus_Message_Iter *entry, *var; + va_list ap; + + if (!eldbus_message_iter_arguments_append(array, "{sv}", &entry)) + { + ERR("could not append dict entry"); + return; + } + + eldbus_message_iter_basic_append(entry, 's', name); + var = eldbus_message_iter_container_new(entry, 'v', signature); + + va_start(ap, signature); + eldbus_message_iter_arguments_vappend(var, signature, ap); + va_end(ap); + eldbus_message_iter_container_close(entry, var); + eldbus_message_iter_container_close(array, entry); +} + +static void +_append_dict_entry_string_array(Eldbus_Message_Iter *array, const char *name, Eina_Iterator *it) +{ + Eldbus_Message_Iter *entry, *var, *sub; + const char *str; + + if (!eldbus_message_iter_arguments_append(array, "{sv}", &entry)) + { + ERR("could not append dict entry"); + return; + } + + eldbus_message_iter_basic_append(entry, 's', name); + var = eldbus_message_iter_container_new(entry, 'v', "as"); + + eldbus_message_iter_arguments_append(var, "as", &sub); + EINA_ITERATOR_FOREACH(it, str) + eldbus_message_iter_basic_append(sub, 's', str); + + eldbus_message_iter_container_close(var, sub); + eldbus_message_iter_container_close(entry, var); + eldbus_message_iter_container_close(array, entry); +} + +EOLIAN static void +_efl_net_control_access_point_configuration_ipv4_set(Eo *o, Efl_Net_Control_Access_Point_Data *pd, Efl_Net_Control_Access_Point_Ipv4_Method method, const char *address, const char *netmask, const char *gateway) +{ + Eldbus_Message *msg; + Eldbus_Message_Iter *msg_itr, *array, *var; + Eldbus_Pending *p; + + if (method == EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_UNSET) + { + ERR("Invalid IPv4 Method (%d) EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_UNSET\n", method); + return; + } + + msg = eldbus_proxy_method_call_new(pd->proxy, "SetProperty"); + EINA_SAFETY_ON_NULL_RETURN(msg); + + msg_itr = eldbus_message_iter_get(msg); + EINA_SAFETY_ON_NULL_GOTO(msg_itr, error_send); + + eldbus_message_iter_basic_append(msg_itr, 's', "IPv4.Configuration"); + var = eldbus_message_iter_container_new(msg_itr, 'v', "a{sv}"); + eldbus_message_iter_arguments_append(var, "a{sv}", &array); + + switch (method) + { + case EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_UNSET: goto error_send; + case EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_OFF: + _append_dict_entry(array, "Method", "s", "off"); + break; + case EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_DHCP: + _append_dict_entry(array, "Method", "s", "dhcp"); + break; + case EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_MANUAL: + _append_dict_entry(array, "Method", "s", "manual"); + if (address) + _append_dict_entry(array, "Address", "s", address); + if (netmask) + _append_dict_entry(array, "Netmask", "s", netmask); + if (gateway) + _append_dict_entry(array, "Gateway", "s", gateway); + break; + } + + eldbus_message_iter_container_close(var, array); + eldbus_message_iter_container_close(msg_itr, var); + + p = eldbus_proxy_send(pd->proxy, msg, _efl_net_control_access_point_property_set_cb, o, DEFAULT_TIMEOUT); + EINA_SAFETY_ON_NULL_GOTO(p, error_send); + + pd->pending = eina_list_append(pd->pending, p); + DBG("Setting property IPv4"); + return; + + error_send: + eldbus_message_unref(msg); +} + +EOLIAN static void +_efl_net_control_access_point_configuration_ipv4_get(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Efl_Net_Control_Access_Point_Ipv4_Method *method, const char **address, const char **netmask, const char **gateway) +{ + if (method) *method = pd->configured.ipv4.method; + if (address) *address = pd->configured.ipv4.address; + if (netmask) *netmask = pd->configured.ipv4.netmask; + if (gateway) *gateway = pd->configured.ipv4.gateway; +} + +EOLIAN static void +_efl_net_control_access_point_configuration_ipv6_set(Eo *o, Efl_Net_Control_Access_Point_Data *pd, Efl_Net_Control_Access_Point_Ipv6_Method method, const char *address, uint8_t prefix_length, const char *netmask, const char *gateway) +{ + Eldbus_Message *msg; + Eldbus_Message_Iter *msg_itr, *array, *var; + Eldbus_Pending *p; + + if (method == EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_UNSET) + { + ERR("Invalid IPv6 Method (%d) EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_UNSET\n", method); + return; + } + else if (method == EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_FIXED) + { + ERR("Invalid IPv6 Method (%d) EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_FIXED\n", method); + return; + } + else if (method == EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_TUNNEL6TO4) + { + ERR("Invalid IPv6 Method (%d) EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_TUNNEL6TO4\n", method); + return; + } + + msg = eldbus_proxy_method_call_new(pd->proxy, "SetProperty"); + EINA_SAFETY_ON_NULL_RETURN(msg); + + msg_itr = eldbus_message_iter_get(msg); + EINA_SAFETY_ON_NULL_GOTO(msg_itr, error_send); + + eldbus_message_iter_basic_append(msg_itr, 's', "IPv6.Configuration"); + var = eldbus_message_iter_container_new(msg_itr, 'v', "a{sv}"); + eldbus_message_iter_arguments_append(var, "a{sv}", &array); + + switch (method) + { + case EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_UNSET: goto error_send; + case EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_FIXED: goto error_send; + case EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_TUNNEL6TO4: goto error_send; + case EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_OFF: + _append_dict_entry(array, "Method", "s", "off"); + break; + case EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_MANUAL: + _append_dict_entry(array, "Method", "s", "manual"); + if (address) + _append_dict_entry(array, "Address", "s", address); + if (netmask) + _append_dict_entry(array, "Netmask", "s", netmask); + if (gateway) + _append_dict_entry(array, "Gateway", "s", gateway); + if (prefix_length) + _append_dict_entry(array, "PrefixLength", "y", prefix_length); + break; + case EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_AUTO_PRIVACY_NONE: + _append_dict_entry(array, "Method", "s", "auto"); + _append_dict_entry(array, "Privacy", "s", "disabled"); + break; + case EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_AUTO_PRIVACY_PUBLIC: + _append_dict_entry(array, "Method", "s", "auto"); + _append_dict_entry(array, "Privacy", "s", "enabled"); + break; + case EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_AUTO_PRIVACY_TEMPORARY: + _append_dict_entry(array, "Method", "s", "auto"); + _append_dict_entry(array, "Privacy", "s", "prefered"); + break; + } + + eldbus_message_iter_container_close(var, array); + eldbus_message_iter_container_close(msg_itr, var); + + p = eldbus_proxy_send(pd->proxy, msg, _efl_net_control_access_point_property_set_cb, o, DEFAULT_TIMEOUT); + EINA_SAFETY_ON_NULL_GOTO(p, error_send); + + pd->pending = eina_list_append(pd->pending, p); + DBG("Setting property IPv6"); + return; + + error_send: + eldbus_message_unref(msg); +} + +EOLIAN static void +_efl_net_control_access_point_configuration_ipv6_get(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Efl_Net_Control_Access_Point_Ipv6_Method *method, const char **address, uint8_t *prefix_length, const char **netmask, const char **gateway) +{ + if (method) *method = pd->configured.ipv6.method; + if (address) *address = pd->configured.ipv6.address; + if (netmask) *netmask = pd->configured.ipv6.netmask; + if (gateway) *gateway = pd->configured.ipv6.gateway; + if (prefix_length) *prefix_length = pd->configured.ipv6.prefix_length; +} + +EOLIAN static void +_efl_net_control_access_point_configuration_proxy_set(Eo *o, Efl_Net_Control_Access_Point_Data *pd, Efl_Net_Control_Access_Point_Proxy_Method method, const char *url, Eina_Iterator *servers, Eina_Iterator *excludes) +{ + Eldbus_Message *msg; + Eldbus_Message_Iter *msg_itr, *array, *var; + Eldbus_Pending *p; + + if (method == EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_UNSET) + { + ERR("Invalid Proxy Method (%d) EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_UNSET\n", method); + if (servers) eina_iterator_free(servers); + if (excludes) eina_iterator_free(excludes); + return; + } + + msg = eldbus_proxy_method_call_new(pd->proxy, "SetProperty"); + EINA_SAFETY_ON_NULL_RETURN(msg); + + msg_itr = eldbus_message_iter_get(msg); + EINA_SAFETY_ON_NULL_GOTO(msg_itr, error_send); + + eldbus_message_iter_basic_append(msg_itr, 's', "Proxy.Configuration"); + var = eldbus_message_iter_container_new(msg_itr, 'v', "a{sv}"); + eldbus_message_iter_arguments_append(var, "a{sv}", &array); + + switch (method) + { + case EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_UNSET: goto error_send; + case EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_OFF: + _append_dict_entry(array, "Method", "s", "direct"); + break; + case EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_AUTO: + _append_dict_entry(array, "Method", "s", "auto"); + if (url) + _append_dict_entry(array, "URL", "s", url); + break; + case EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_MANUAL: + _append_dict_entry(array, "Method", "s", "manual"); + if (servers) + _append_dict_entry_string_array(array, "Servers", servers); + if (excludes) + _append_dict_entry_string_array(array, "Excludes", excludes); + break; + } + + eldbus_message_iter_container_close(var, array); + eldbus_message_iter_container_close(msg_itr, var); + + p = eldbus_proxy_send(pd->proxy, msg, _efl_net_control_access_point_property_set_cb, o, DEFAULT_TIMEOUT); + EINA_SAFETY_ON_NULL_GOTO(p, error_send); + + pd->pending = eina_list_append(pd->pending, p); + DBG("Setting property Proxy"); + if (servers) eina_iterator_free(servers); + if (excludes) eina_iterator_free(excludes); + return; + + error_send: + if (servers) eina_iterator_free(servers); + if (excludes) eina_iterator_free(excludes); + eldbus_message_unref(msg); +} + +EOLIAN static void +_efl_net_control_access_point_configuration_proxy_get(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Efl_Net_Control_Access_Point_Proxy_Method *method, const char **url, Eina_Iterator **servers, Eina_Iterator **excludes) +{ + if (method) *method = pd->configured.proxy.method; + if (url) *url = pd->configured.proxy.url; + if (servers) *servers = eina_list_iterator_new(pd->configured.proxy.servers); + if (excludes) *excludes = eina_list_iterator_new(pd->configured.proxy.excludes); +} + +static void +_efl_net_control_access_point_connect_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending) +{ + Efl_Promise *promise = data; + Eo *o = efl_parent_get(promise); + Efl_Net_Control_Access_Point_Data *pd = efl_data_scope_get(o, MY_CLASS); + const char *err_name, *err_msg; + + EINA_SAFETY_ON_NULL_RETURN(pd); + + pd->pending = eina_list_remove(pd->pending, pending); + efl_key_data_set(promise, "eldbus_pending", NULL); + if (eldbus_message_error_get(msg, &err_name, &err_msg)) + { + Eina_Error err = EINVAL; + + if (strcmp(err_name, "net.connman.Error.InProgress") == 0) + err = EINPROGRESS; + else if (strcmp(err_name, "net.connman.Error.AlreadyConnected") == 0) + err = EALREADY; + WRN("Could not Connect %p: %s=%s", o, err_name, err_msg); + efl_promise_failed_set(promise, err); + return; + } + + efl_promise_value_set(promise, o, NULL); + efl_del(promise); +} + +static void +_efl_net_control_access_point_connect_promise_del(void *data, const Efl_Event *event) +{ + Eldbus_Pending *p; + Eo *o = data; + Efl_Net_Control_Access_Point_Data *pd = efl_data_scope_get(o, MY_CLASS); + + EINA_SAFETY_ON_NULL_RETURN(pd); + + p = efl_key_data_get(event->object, "eldbus_pending"); + if (!p) return; /* already gone, nothing to do */ + + pd->pending = eina_list_remove(pd->pending, p); + DBG("cancel pending connect %p", p); + eldbus_pending_cancel(p); +} + +EOLIAN static Efl_Future * +_efl_net_control_access_point_connect(Eo *o, Efl_Net_Control_Access_Point_Data *pd) +{ + Eldbus_Pending *p; + Efl_Promise *promise; + + promise = efl_add(EFL_PROMISE_CLASS, o, + efl_event_callback_add(efl_added, EFL_EVENT_DEL, _efl_net_control_access_point_connect_promise_del, o)); + EINA_SAFETY_ON_NULL_RETURN_VAL(promise, NULL); + + p = eldbus_proxy_call(pd->proxy, "Connect", + _efl_net_control_access_point_connect_cb, promise, -1.0, ""); + EINA_SAFETY_ON_NULL_GOTO(p, error_dbus); + + pd->pending = eina_list_append(pd->pending, p); + efl_key_data_set(promise, "eldbus_pending", p); + + return efl_promise_future_get(promise); + + error_dbus: + efl_promise_failed_set(promise, ENOSYS); + return promise; +} + +static void +_efl_net_control_access_point_disconnect_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending) +{ + Eo *o = data; + Efl_Net_Control_Access_Point_Data *pd = efl_data_scope_get(o, MY_CLASS); + const char *err_name, *err_msg; + + pd->pending = eina_list_remove(pd->pending, pending); + if (eldbus_message_error_get(msg, &err_name, &err_msg)) + { + ERR("Could not disconnect %p: %s=%s", o, err_name, err_msg); + return; + } +} + +EOLIAN static void +_efl_net_control_access_point_disconnect(Eo *o, Efl_Net_Control_Access_Point_Data *pd) +{ + Eldbus_Pending *p; + p = eldbus_proxy_call(pd->proxy, "Disconnect", + _efl_net_control_access_point_disconnect_cb, o, DEFAULT_TIMEOUT, ""); + EINA_SAFETY_ON_NULL_RETURN(p); + pd->pending = eina_list_append(pd->pending, p); +} + +static void +_efl_net_control_access_point_forget_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending) +{ + Eo *o = data; + Efl_Net_Control_Access_Point_Data *pd = efl_data_scope_get(o, MY_CLASS); + const char *err_name, *err_msg; + + pd->pending = eina_list_remove(pd->pending, pending); + if (eldbus_message_error_get(msg, &err_name, &err_msg)) + { + ERR("Could not forget %p: %s=%s", o, err_name, err_msg); + return; + } +} + +EOLIAN static void +_efl_net_control_access_point_forget(Eo *o, Efl_Net_Control_Access_Point_Data *pd) +{ + Eldbus_Pending *p; + p = eldbus_proxy_call(pd->proxy, "Remove", + _efl_net_control_access_point_forget_cb, o, DEFAULT_TIMEOUT, ""); + EINA_SAFETY_ON_NULL_RETURN(p); + pd->pending = eina_list_append(pd->pending, p); +} + +static void +_efl_net_control_access_point_property_name_changed(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Eldbus_Message_Iter *value) +{ + const char *name; + + if (!eldbus_message_iter_arguments_get(value, "s", &name)) + { + ERR("Expected string, got %s", eldbus_message_iter_signature_get(value)); + return; + } + + if (!eina_stringshare_replace(&pd->name, name)) return; + DBG("name=%s", name); +} + +static void +_efl_net_control_access_point_property_state_changed(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Eldbus_Message_Iter *value) +{ + const char *str; + const struct { + Efl_Net_Control_Access_Point_State val; + const char *str; + } *itr, map[] = { + {EFL_NET_CONTROL_ACCESS_POINT_STATE_IDLE, "idle"}, + {EFL_NET_CONTROL_ACCESS_POINT_STATE_ASSOCIATION, "association"}, + {EFL_NET_CONTROL_ACCESS_POINT_STATE_CONFIGURATION, "configuration"}, + {EFL_NET_CONTROL_ACCESS_POINT_STATE_LOCAL, "ready"}, + {EFL_NET_CONTROL_ACCESS_POINT_STATE_ONLINE, "online"}, + {EFL_NET_CONTROL_ACCESS_POINT_STATE_DISCONNECT, "disconnect"}, + {EFL_NET_CONTROL_ACCESS_POINT_STATE_FAILURE, "failure"}, + { } + }; + + if (!eldbus_message_iter_arguments_get(value, "s", &str)) + { + ERR("Expected string, got %s", eldbus_message_iter_signature_get(value)); + return; + } + + for (itr = map; itr->str != NULL; itr++) + if (strcmp(itr->str, str) == 0) break; + + if (!itr->str) + { + ERR("Unknown state '%s'", str); + return; + } + + if (pd->state == itr->val) return; + pd->state = itr->val; + DBG("state=%d (%s)", pd->state, str); +} + +static void +_efl_net_control_access_point_property_error_changed(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Eldbus_Message_Iter *value) +{ + const char *str; + const struct { + Efl_Net_Control_Access_Point_Error val; + const char *str; + } *itr, map[] = { + {EFL_NET_CONTROL_ACCESS_POINT_ERROR_NONE, "none"}, + {EFL_NET_CONTROL_ACCESS_POINT_ERROR_NONE, ""}, + {EFL_NET_CONTROL_ACCESS_POINT_ERROR_OUT_OF_RANGE, "range"}, + {EFL_NET_CONTROL_ACCESS_POINT_ERROR_PIN_MISSING, "missing"}, + {EFL_NET_CONTROL_ACCESS_POINT_ERROR_DHCP_FAILED, "failed"}, + {EFL_NET_CONTROL_ACCESS_POINT_ERROR_CONNECT_FAILED, "failed"}, + {EFL_NET_CONTROL_ACCESS_POINT_ERROR_LOGIN_FAILED, "failed"}, + { } + }; + + if (!eldbus_message_iter_arguments_get(value, "s", &str)) + { + ERR("Expected string, got %s", eldbus_message_iter_signature_get(value)); + return; + } + + for (itr = map; itr->str != NULL; itr++) + if (strcmp(itr->str, str) == 0) break; + + if (!itr->str) + { + ERR("Unknown error '%s'", str); + return; + } + + if (pd->error == itr->val) return; + pd->error = itr->val; + DBG("error=%d (%s)", pd->error, str); +} + +static void +_efl_net_control_access_point_property_type_changed(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Eldbus_Message_Iter *value) +{ + const char *name; + + if (!eldbus_message_iter_arguments_get(value, "s", &name)) + { + ERR("Expected string, got %s", eldbus_message_iter_signature_get(value)); + return; + } + + if (!eina_stringshare_replace(&pd->techname, name)) return; + DBG("technology name=%s", name); +} + +static void +_efl_net_control_access_point_property_security_changed(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Eldbus_Message_Iter *value) +{ + const char *str; + Eldbus_Message_Iter *array; + const struct { + Efl_Net_Control_Access_Point_Security val; + const char *str; + } *itr, map[] = { + {EFL_NET_CONTROL_ACCESS_POINT_SECURITY_NONE, "none"}, + {EFL_NET_CONTROL_ACCESS_POINT_SECURITY_WEP, "wep"}, + {EFL_NET_CONTROL_ACCESS_POINT_SECURITY_PSK, "psk"}, + {EFL_NET_CONTROL_ACCESS_POINT_SECURITY_IEEE802_1X, "ieee8021x"}, + { } + }; + Efl_Net_Control_Access_Point_Security security = 0; + + if (!eldbus_message_iter_arguments_get(value, "as", &array)) + { + ERR("Expected array of string, got %s", eldbus_message_iter_signature_get(value)); + return; + } + + while (eldbus_message_iter_get_and_next(array, 's', &str)) + { + for (itr = map; itr->str != NULL; itr++) + if (strcmp(itr->str, str) == 0) break; + + if (!itr->str) + { + ERR("Unknown security '%s'", str); + continue; + } + + security |= itr->val; + } + + if (pd->security == security) return; + pd->security = security; + DBG("security=%#x", pd->security); +} + +static void +_efl_net_control_access_point_property_strength_changed(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Eldbus_Message_Iter *value) +{ + uint8_t strength; + + if (!eldbus_message_iter_arguments_get(value, "y", &strength)) + { + ERR("Expected unsigned byte, got %s", eldbus_message_iter_signature_get(value)); + return; + } + + if (pd->strength == strength) return; + pd->strength = strength; + DBG("strength=%hhu", pd->strength); +} + +static void +_efl_net_control_access_point_property_remembered_changed(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Eldbus_Message_Iter *value) +{ + Eina_Bool remembered; + + if (!eldbus_message_iter_arguments_get(value, "b", &remembered)) + { + ERR("Expected boolean, got %s", eldbus_message_iter_signature_get(value)); + return; + } + + if (pd->remembered == remembered) return; + pd->remembered = remembered; + if (!remembered) + { + /* force this flag to be cleared */ + pd->auto_connect = EINA_FALSE; + } + DBG("remembered=%hhu", remembered); +} + +static void +_efl_net_control_access_point_property_immutable_changed(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Eldbus_Message_Iter *value) +{ + Eina_Bool immutable; + + if (!eldbus_message_iter_arguments_get(value, "b", &immutable)) + { + ERR("Expected boolean, got %s", eldbus_message_iter_signature_get(value)); + return; + } + + if (pd->immutable == immutable) return; + pd->immutable = immutable; + DBG("immutable=%hhu", immutable); +} + +static void +_efl_net_control_access_point_property_auto_connect_changed(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Eldbus_Message_Iter *value) +{ + Eina_Bool auto_connect; + + if (!eldbus_message_iter_arguments_get(value, "b", &auto_connect)) + { + ERR("Expected boolean, got %s", eldbus_message_iter_signature_get(value)); + return; + } + + if (pd->auto_connect == auto_connect) return; + pd->auto_connect = auto_connect; + DBG("auto_connect=%hhu", auto_connect); +} + +static void +_efl_net_control_access_point_property_roaming_changed(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Eldbus_Message_Iter *value) +{ + Eina_Bool roaming; + + if (!eldbus_message_iter_arguments_get(value, "b", &roaming)) + { + ERR("Expected boolean, got %s", eldbus_message_iter_signature_get(value)); + return; + } + + if (pd->roaming == roaming) return; + pd->roaming = roaming; + DBG("roaming=%hhu", roaming); +} + +static void +_efl_net_control_access_point_list_updated(const char *name, Eina_List **p_list, Eldbus_Message_Iter *value) +{ + Eldbus_Message_Iter *array; + Eina_List *old_list; + const char *str; + + if (!eldbus_message_iter_arguments_get(value, "as", &array)) + { + ERR("Expected array of strings for %s, got %s", name, eldbus_message_iter_signature_get(value)); + return; + } + + old_list = *p_list; + *p_list = NULL; + while (eldbus_message_iter_get_and_next(array, 's', &str)) + *p_list = eina_list_append(*p_list, eina_stringshare_add(str)); + + EINA_LIST_FREE(old_list, str) eina_stringshare_del(str); +} + +/* Actual Values */ + +static void +_efl_net_control_access_point_property_actual_name_servers_changed(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Eldbus_Message_Iter *value) +{ + _efl_net_control_access_point_list_updated("name_servers", &pd->actual.name_servers, value); +} + +static void +_efl_net_control_access_point_property_actual_time_servers_changed(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Eldbus_Message_Iter *value) +{ + _efl_net_control_access_point_list_updated("time_servers", &pd->actual.time_servers, value); +} + +static void +_efl_net_control_access_point_property_actual_domains_changed(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Eldbus_Message_Iter *value) +{ + _efl_net_control_access_point_list_updated("domains", &pd->actual.domains, value); +} + +static void +_efl_net_control_access_point_property_actual_ipv4_changed(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Eldbus_Message_Iter *value) +{ + Eldbus_Message_Iter *array, *entry; + + if (!eldbus_message_iter_arguments_get(value, "a{sv}", &array)) + { + ERR("Expected dict for, got %s", eldbus_message_iter_signature_get(value)); + return; + } + + pd->actual.ipv4.method = EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_UNSET; + eina_stringshare_replace(&pd->actual.ipv4.address, NULL); + eina_stringshare_replace(&pd->actual.ipv4.netmask, NULL); + eina_stringshare_replace(&pd->actual.ipv4.gateway, NULL); + + while (eldbus_message_iter_get_and_next(array, 'e', &entry)) + { + const char *name, *str; + Eldbus_Message_Iter *var; + + if (!eldbus_message_iter_arguments_get(entry, "sv", &name, &var)) + { + ERR("Unexpected dict entry signature: %s", eldbus_message_iter_signature_get(entry)); + continue; + } + + if (!eldbus_message_iter_arguments_get(var, "s", &str)) + { + ERR("Expected string value for %s, got %s", name, eldbus_message_iter_signature_get(var)); + continue; + } + + if (strcmp(name, "Method") == 0) + { + if (strcmp(str, "dhcp") == 0) + pd->actual.ipv4.method = EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_DHCP; + else if (strcmp(str, "manual") == 0) + pd->actual.ipv4.method = EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_MANUAL; + else if (strcmp(str, "off") == 0) + pd->actual.ipv4.method = EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_OFF; + else + WRN("Unexpected IPV4 Method value '%s'", str); + } + else if (strcmp(name, "Address") == 0) + eina_stringshare_replace(&pd->actual.ipv4.address, str); + else if (strcmp(name, "Netmask") == 0) + eina_stringshare_replace(&pd->actual.ipv4.netmask, str); + else if (strcmp(name, "Gateway") == 0) + eina_stringshare_replace(&pd->actual.ipv4.gateway, str); + else + WRN("Unknown property name: %s", name); + } +} + +static void +_efl_net_control_access_point_property_actual_ipv6_changed(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Eldbus_Message_Iter *value) +{ + Eldbus_Message_Iter *array, *entry; + + if (!eldbus_message_iter_arguments_get(value, "a{sv}", &array)) + { + ERR("Expected dict for, got %s", eldbus_message_iter_signature_get(value)); + return; + } + + pd->actual.ipv6.method = EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_UNSET; + eina_stringshare_replace(&pd->actual.ipv6.address, NULL); + eina_stringshare_replace(&pd->actual.ipv6.netmask, NULL); + eina_stringshare_replace(&pd->actual.ipv6.gateway, NULL); + pd->actual.ipv6.prefix_length = 0; + + while (eldbus_message_iter_get_and_next(array, 'e', &entry)) + { + const char *name, *str; + Eldbus_Message_Iter *var; + + if (!eldbus_message_iter_arguments_get(entry, "sv", &name, &var)) + { + ERR("Unexpected dict entry signature: %s", eldbus_message_iter_signature_get(entry)); + continue; + } + + if (strcmp(name, "PrefixLength") == 0) + { + if (!eldbus_message_iter_arguments_get(var, "y", &pd->actual.ipv6.prefix_length)) + ERR("Expected unsigned byte value for %s, got %s", name, eldbus_message_iter_signature_get(var)); + continue; + } + + if (!eldbus_message_iter_arguments_get(var, "s", &str)) + { + ERR("Expected string value for %s, got %s", name, eldbus_message_iter_signature_get(var)); + continue; + } + + if (strcmp(name, "Method") == 0) + { + if (strcmp(str, "off") == 0) + pd->actual.ipv6.method = EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_OFF; + else if (strcmp(str, "fixed") == 0) + pd->actual.ipv6.method = EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_FIXED; + else if (strcmp(str, "manual") == 0) + pd->actual.ipv6.method = EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_MANUAL; + else if (strcmp(str, "6to4") == 0) + pd->actual.ipv6.method = EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_TUNNEL6TO4; + else if (strcmp(str, "auto") == 0) + pd->actual.ipv6.method = EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_AUTO_PRIVACY_NONE; + else + WRN("Unexpected IPV6 Method value '%s'", str); + } + else if (strcmp(name, "Privacy") == 0) + { + if ((pd->actual.ipv6.method != EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_AUTO_PRIVACY_NONE) && + (pd->actual.ipv6.method != EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_UNSET)) + { + DBG("Skip privacy %s, method already set to %d", str, pd->actual.ipv6.method); + continue; + } + if (strcmp(str, "disabled") == 0) + pd->actual.ipv6.method = EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_AUTO_PRIVACY_NONE; + else if (strcmp(str, "enabled") == 0) + pd->actual.ipv6.method = EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_AUTO_PRIVACY_PUBLIC; + else if (strcmp(str, "prefered") == 0) + pd->actual.ipv6.method = EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_AUTO_PRIVACY_TEMPORARY; + else + WRN("Unexpected IPV6 Privacy value '%s'", str); + } + else if (strcmp(name, "Address") == 0) + eina_stringshare_replace(&pd->actual.ipv6.address, str); + else if (strcmp(name, "Netmask") == 0) + eina_stringshare_replace(&pd->actual.ipv6.netmask, str); + else if (strcmp(name, "Gateway") == 0) + eina_stringshare_replace(&pd->actual.ipv6.gateway, str); + else + WRN("Unknown property name: %s", name); + } +} + +static void +_efl_net_control_access_point_property_actual_proxy_changed(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Eldbus_Message_Iter *value) +{ + Eldbus_Message_Iter *array, *entry; + const char *str; + + if (!eldbus_message_iter_arguments_get(value, "a{sv}", &array)) + { + ERR("Expected dict for, got %s", eldbus_message_iter_signature_get(value)); + return; + } + + pd->actual.proxy.method = EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_UNSET; + eina_stringshare_replace(&pd->actual.proxy.url, NULL); + EINA_LIST_FREE(pd->actual.proxy.servers, str) eina_stringshare_del(str); + EINA_LIST_FREE(pd->actual.proxy.excludes, str) eina_stringshare_del(str); + + while (eldbus_message_iter_get_and_next(array, 'e', &entry)) + { + const char *name; + Eldbus_Message_Iter *var; + + if (!eldbus_message_iter_arguments_get(entry, "sv", &name, &var)) + { + ERR("Unexpected dict entry signature: %s", eldbus_message_iter_signature_get(entry)); + continue; + } + + if (strcmp(name, "Servers") == 0) + { + _efl_net_control_access_point_list_updated("Proxy Servers", &pd->actual.proxy.servers, var); + continue; + } + else if (strcmp(name, "Excludes") == 0) + { + _efl_net_control_access_point_list_updated("Proxy Excludes", &pd->actual.proxy.excludes, var); + continue; + } + + if (!eldbus_message_iter_arguments_get(var, "s", &str)) + { + ERR("Expected string value for %s, got %s", name, eldbus_message_iter_signature_get(var)); + continue; + } + + if (strcmp(name, "Method") == 0) + { + if (strcmp(str, "auto") == 0) + pd->actual.proxy.method = EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_AUTO; + else if (strcmp(str, "manual") == 0) + pd->actual.proxy.method = EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_MANUAL; + else if (strcmp(str, "direct") == 0) + pd->actual.proxy.method = EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_OFF; + else + WRN("Unexpected PROXY Method value '%s'", str); + } + else if (strcmp(name, "URL") == 0) + eina_stringshare_replace(&pd->actual.proxy.url, str); + else + WRN("Unknown property name: %s", name); + } +} + +/* Configured Values */ + +static void +_efl_net_control_access_point_property_configured_name_servers_changed(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Eldbus_Message_Iter *value) +{ + _efl_net_control_access_point_list_updated("name_servers", &pd->configured.name_servers, value); +} + +static void +_efl_net_control_access_point_property_configured_time_servers_changed(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Eldbus_Message_Iter *value) +{ + _efl_net_control_access_point_list_updated("time_servers", &pd->configured.time_servers, value); +} + +static void +_efl_net_control_access_point_property_configured_domains_changed(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Eldbus_Message_Iter *value) +{ + _efl_net_control_access_point_list_updated("domains", &pd->configured.domains, value); +} + +static void +_efl_net_control_access_point_property_configured_ipv4_changed(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Eldbus_Message_Iter *value) +{ + Eldbus_Message_Iter *array, *entry; + + if (!eldbus_message_iter_arguments_get(value, "a{sv}", &array)) + { + ERR("Expected dict for, got %s", eldbus_message_iter_signature_get(value)); + return; + } + + pd->configured.ipv4.method = EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_UNSET; + eina_stringshare_replace(&pd->configured.ipv4.address, NULL); + eina_stringshare_replace(&pd->configured.ipv4.netmask, NULL); + eina_stringshare_replace(&pd->configured.ipv4.gateway, NULL); + + while (eldbus_message_iter_get_and_next(array, 'e', &entry)) + { + const char *name, *str; + Eldbus_Message_Iter *var; + + if (!eldbus_message_iter_arguments_get(entry, "sv", &name, &var)) + { + ERR("Unexpected dict entry signature: %s", eldbus_message_iter_signature_get(entry)); + continue; + } + + if (!eldbus_message_iter_arguments_get(var, "s", &str)) + { + ERR("Expected string value for %s, got %s", name, eldbus_message_iter_signature_get(var)); + continue; + } + + if (strcmp(name, "Method") == 0) + { + if (strcmp(str, "dhcp") == 0) + pd->configured.ipv4.method = EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_DHCP; + else if (strcmp(str, "manual") == 0) + pd->configured.ipv4.method = EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_MANUAL; + else if (strcmp(str, "off") == 0) + pd->configured.ipv4.method = EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_OFF; + else + WRN("Unexpected IPV4 Method value '%s'", str); + } + else if (strcmp(name, "Address") == 0) + eina_stringshare_replace(&pd->configured.ipv4.address, str); + else if (strcmp(name, "Netmask") == 0) + eina_stringshare_replace(&pd->configured.ipv4.netmask, str); + else if (strcmp(name, "Gateway") == 0) + eina_stringshare_replace(&pd->configured.ipv4.gateway, str); + else + WRN("Unknown property name: %s", name); + } +} + +static void +_efl_net_control_access_point_property_configured_ipv6_changed(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Eldbus_Message_Iter *value) +{ + Eldbus_Message_Iter *array, *entry; + + if (!eldbus_message_iter_arguments_get(value, "a{sv}", &array)) + { + ERR("Expected dict for, got %s", eldbus_message_iter_signature_get(value)); + return; + } + + pd->configured.ipv6.method = EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_UNSET; + eina_stringshare_replace(&pd->configured.ipv6.address, NULL); + eina_stringshare_replace(&pd->configured.ipv6.netmask, NULL); + eina_stringshare_replace(&pd->configured.ipv6.gateway, NULL); + pd->configured.ipv6.prefix_length = 0; + + while (eldbus_message_iter_get_and_next(array, 'e', &entry)) + { + const char *name, *str; + Eldbus_Message_Iter *var; + + if (!eldbus_message_iter_arguments_get(entry, "sv", &name, &var)) + { + ERR("Unexpected dict entry signature: %s", eldbus_message_iter_signature_get(entry)); + continue; + } + + if (strcmp(name, "PrefixLength") == 0) + { + if (!eldbus_message_iter_arguments_get(var, "y", &pd->configured.ipv6.prefix_length)) + ERR("Expected unsigned byte value for %s, got %s", name, eldbus_message_iter_signature_get(var)); + continue; + } + + if (!eldbus_message_iter_arguments_get(var, "s", &str)) + { + ERR("Expected string value for %s, got %s", name, eldbus_message_iter_signature_get(var)); + continue; + } + + if (strcmp(name, "Method") == 0) + { + if (strcmp(str, "off") == 0) + pd->configured.ipv6.method = EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_OFF; + else if (strcmp(str, "fixed") == 0) + pd->configured.ipv6.method = EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_FIXED; + else if (strcmp(str, "manual") == 0) + pd->configured.ipv6.method = EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_MANUAL; + else if (strcmp(str, "6to4") == 0) + pd->configured.ipv6.method = EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_TUNNEL6TO4; + else if (strcmp(str, "auto") == 0) + pd->configured.ipv6.method = EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_AUTO_PRIVACY_NONE; + else + WRN("Unexpected IPV6 Method value '%s'", str); + } + else if (strcmp(name, "Privacy") == 0) + { + if ((pd->configured.ipv6.method != EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_AUTO_PRIVACY_NONE) && + (pd->configured.ipv6.method != EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_UNSET)) + { + DBG("Skip privacy %s, method already set to %d", str, pd->configured.ipv6.method); + continue; + } + if (strcmp(str, "disabled") == 0) + pd->configured.ipv6.method = EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_AUTO_PRIVACY_NONE; + else if (strcmp(str, "enabled") == 0) + pd->configured.ipv6.method = EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_AUTO_PRIVACY_PUBLIC; + else if (strcmp(str, "prefered") == 0) + pd->configured.ipv6.method = EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_AUTO_PRIVACY_TEMPORARY; + else + WRN("Unexpected IPV6 Privacy value '%s'", str); + } + else if (strcmp(name, "Address") == 0) + eina_stringshare_replace(&pd->configured.ipv6.address, str); + else if (strcmp(name, "Netmask") == 0) + eina_stringshare_replace(&pd->configured.ipv6.netmask, str); + else if (strcmp(name, "Gateway") == 0) + eina_stringshare_replace(&pd->configured.ipv6.gateway, str); + else + WRN("Unknown property name: %s", name); + } +} + +static void +_efl_net_control_access_point_property_configured_proxy_changed(Eo *o EINA_UNUSED, Efl_Net_Control_Access_Point_Data *pd, Eldbus_Message_Iter *value) +{ + Eldbus_Message_Iter *array, *entry; + const char *str; + + if (!eldbus_message_iter_arguments_get(value, "a{sv}", &array)) + { + ERR("Expected dict for, got %s", eldbus_message_iter_signature_get(value)); + return; + } + + pd->configured.proxy.method = EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_UNSET; + eina_stringshare_replace(&pd->configured.proxy.url, NULL); + EINA_LIST_FREE(pd->configured.proxy.servers, str) eina_stringshare_del(str); + EINA_LIST_FREE(pd->configured.proxy.excludes, str) eina_stringshare_del(str); + + while (eldbus_message_iter_get_and_next(array, 'e', &entry)) + { + const char *name; + Eldbus_Message_Iter *var; + + if (!eldbus_message_iter_arguments_get(entry, "sv", &name, &var)) + { + ERR("Unexpected dict entry signature: %s", eldbus_message_iter_signature_get(entry)); + continue; + } + + if (strcmp(name, "Servers") == 0) + { + _efl_net_control_access_point_list_updated("Proxy Servers", &pd->configured.proxy.servers, var); + continue; + } + else if (strcmp(name, "Excludes") == 0) + { + _efl_net_control_access_point_list_updated("Proxy Excludes", &pd->configured.proxy.excludes, var); + continue; + } + + if (!eldbus_message_iter_arguments_get(var, "s", &str)) + { + ERR("Expected string value for %s, got %s", name, eldbus_message_iter_signature_get(var)); + continue; + } + + if (strcmp(name, "Method") == 0) + { + if (strcmp(str, "auto") == 0) + pd->configured.proxy.method = EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_AUTO; + else if (strcmp(str, "manual") == 0) + pd->configured.proxy.method = EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_MANUAL; + else if (strcmp(str, "direct") == 0) + pd->configured.proxy.method = EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_OFF; + else + WRN("Unexpected PROXY Method value '%s'", str); + } + else if (strcmp(name, "URL") == 0) + eina_stringshare_replace(&pd->configured.proxy.url, str); + else + WRN("Unknown property name: %s", name); + } +} + + +static void +_efl_net_control_access_point_property_changed_internal(Eo *o, Efl_Net_Control_Access_Point_Data *pd, Eldbus_Message_Iter *itr) +{ + Eldbus_Message_Iter *value; + const char *name; + + if (!eldbus_message_iter_arguments_get(itr, "sv", &name, &value)) + { + ERR("Unexpected signature: %s", eldbus_message_iter_signature_get(itr)); + return; + } + + if (strcmp(name, "Name") == 0) + _efl_net_control_access_point_property_name_changed(o, pd, value); + else if (strcmp(name, "State") == 0) + _efl_net_control_access_point_property_state_changed(o, pd, value); + else if (strcmp(name, "Error") == 0) + _efl_net_control_access_point_property_error_changed(o, pd, value); + else if (strcmp(name, "Type") == 0) + _efl_net_control_access_point_property_type_changed(o, pd, value); + else if (strcmp(name, "Security") == 0) + _efl_net_control_access_point_property_security_changed(o, pd, value); + else if (strcmp(name, "Strength") == 0) + _efl_net_control_access_point_property_strength_changed(o, pd, value); + else if (strcmp(name, "Favorite") == 0) + _efl_net_control_access_point_property_remembered_changed(o, pd, value); + else if (strcmp(name, "Immutable") == 0) + _efl_net_control_access_point_property_immutable_changed(o, pd, value); + else if (strcmp(name, "AutoConnect") == 0) + _efl_net_control_access_point_property_auto_connect_changed(o, pd, value); + else if (strcmp(name, "Roaming") == 0) + _efl_net_control_access_point_property_roaming_changed(o, pd, value); + else if (strcmp(name, "Nameservers") == 0) + _efl_net_control_access_point_property_actual_name_servers_changed(o, pd, value); + else if (strcmp(name, "Timeservers") == 0) + _efl_net_control_access_point_property_actual_time_servers_changed(o, pd, value); + else if (strcmp(name, "Domains") == 0) + _efl_net_control_access_point_property_actual_domains_changed(o, pd, value); + else if (strcmp(name, "IPv4") == 0) + _efl_net_control_access_point_property_actual_ipv4_changed(o, pd, value); + else if (strcmp(name, "IPv6") == 0) + _efl_net_control_access_point_property_actual_ipv6_changed(o, pd, value); + else if (strcmp(name, "Proxy") == 0) + _efl_net_control_access_point_property_actual_proxy_changed(o, pd, value); + else if (strcmp(name, "Nameservers.Configuration") == 0) + _efl_net_control_access_point_property_configured_name_servers_changed(o, pd, value); + else if (strcmp(name, "Timeservers.Configuration") == 0) + _efl_net_control_access_point_property_configured_time_servers_changed(o, pd, value); + else if (strcmp(name, "Domains.Configuration") == 0) + _efl_net_control_access_point_property_configured_domains_changed(o, pd, value); + else if (strcmp(name, "IPv4.Configuration") == 0) + _efl_net_control_access_point_property_configured_ipv4_changed(o, pd, value); + else if (strcmp(name, "IPv6.Configuration") == 0) + _efl_net_control_access_point_property_configured_ipv6_changed(o, pd, value); + else if (strcmp(name, "Proxy.Configuration") == 0) + _efl_net_control_access_point_property_configured_proxy_changed(o, pd, value); + else if ((strcmp(name, "Provider") == 0) || + (strcmp(name, "Ethernet") == 0)) + DBG("Ignored property name: %s", name); + else + WRN("Unknown property name: %s", name); +} + +static void +_efl_net_control_access_point_property_changed(void *data, const Eldbus_Message *msg) +{ + Eo *o = data; + Efl_Net_Control_Access_Point_Data *pd = efl_data_scope_get(o, MY_CLASS); + Eldbus_Message_Iter *itr; + + itr = eldbus_message_iter_get(msg); + _efl_net_control_access_point_property_changed_internal(o, pd, itr); + efl_event_callback_call(o, EFL_NET_CONTROL_ACCESS_POINT_EVENT_CHANGED, NULL); +} + +Efl_Net_Control_Access_Point * +efl_net_connman_access_point_new(Efl_Net_Control *ctl, const char *path, Eldbus_Message_Iter *properties, unsigned int priority) +{ + Eo *o; + Efl_Net_Control_Access_Point_Data *pd; + Eldbus_Connection *conn; + Eldbus_Object *obj; + + conn = efl_net_connman_connection_get(); + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + + o = efl_add(MY_CLASS, ctl); + EINA_SAFETY_ON_NULL_RETURN_VAL(o, NULL); + + pd = efl_data_scope_get(o, MY_CLASS); + EINA_SAFETY_ON_NULL_GOTO(pd, error); + + pd->path = eina_stringshare_add(path); + EINA_SAFETY_ON_NULL_GOTO(pd->path, error); + + obj = eldbus_object_get(conn, "net.connman", pd->path); + EINA_SAFETY_ON_NULL_GOTO(obj, error); + pd->proxy = eldbus_proxy_get(obj, "net.connman.Service"); + EINA_SAFETY_ON_NULL_GOTO(pd->proxy, error); + + pd->configured.ipv4.method = EFL_NET_CONTROL_ACCESS_POINT_IPV4_METHOD_UNSET; + pd->configured.ipv6.method = EFL_NET_CONTROL_ACCESS_POINT_IPV6_METHOD_UNSET; + pd->configured.proxy.method = EFL_NET_CONTROL_ACCESS_POINT_PROXY_METHOD_UNSET; + +#define SH(sig, cb) \ + do { \ + Eldbus_Signal_Handler *sh = eldbus_proxy_signal_handler_add(pd->proxy, sig, cb, o); \ + if (sh) pd->signal_handlers = eina_list_append(pd->signal_handlers, sh); \ + else ERR("could not add DBus signal handler %s", sig); \ + } while (0) + + SH("PropertyChanged", _efl_net_control_access_point_property_changed); +#undef SH + + efl_event_freeze(o); + efl_net_connman_access_point_update(o, properties, priority); + efl_event_thaw(o); + + return o; + + error: + efl_del(o); + return NULL; +} + +const char * +efl_net_connman_access_point_path_get(Efl_Net_Control_Access_Point *o) +{ + Efl_Net_Control_Access_Point_Data *pd = efl_data_scope_get(o, MY_CLASS); + EINA_SAFETY_ON_NULL_RETURN_VAL(pd, NULL); + return pd->path; +} + +void +efl_net_connman_access_point_update(Efl_Net_Control *o, Eldbus_Message_Iter *properties, unsigned int priority) +{ + Eldbus_Message_Iter *entry; + Efl_Net_Control_Access_Point_Data *pd = efl_data_scope_get(o, MY_CLASS); + + EINA_SAFETY_ON_NULL_RETURN(pd); + + pd->priority = priority; + + while (eldbus_message_iter_get_and_next(properties, 'e', &entry)) + _efl_net_control_access_point_property_changed_internal(o, pd, entry); + + efl_event_callback_call(o, EFL_NET_CONTROL_ACCESS_POINT_EVENT_CHANGED, NULL); +} + +#include "efl_net_control_access_point.eo.c" diff --git a/src/lib/ecore_con/efl_net_control_access_point.eo b/src/lib/ecore_con/efl_net_control_access_point.eo new file mode 100644 index 0000000000..041d87314e --- /dev/null +++ b/src/lib/ecore_con/efl_net_control_access_point.eo @@ -0,0 +1,463 @@ +enum Efl.Net.Control.Access_Point.State { + [[Provides the access point state. + + @since 1.19 + ]] + idle, [[nothing is happening with this access point]] + association, [[the access point is trying to associate itself, this is the first state after a connection attempt]] + configuration, [[the access point is configuring itself, such as DHCP]] + local, [[the access point is connected, but the internet connection wasn't validated]] + online, [[the access point is connected and the internet connected was validated]] + disconnect, [[the access point is disconnecting]] + failure, [[a connection attempt failed, @Efl.Net.Control.Access_Point.error will provide more details]] +} + +enum Efl.Net.Control.Access_Point.Error { + [[The connection error reason. + + @since 1.19 + ]] + none, [[all right, no errors]] + out_of_range, [[wireless device is out of range]] + pin_missing, [[PIN was required and is missing]] + dhcp_failed, [[DHCP failed to provide configuration]] + connect_failed, [[couldn't connect to access point]] + login_failed, [[login or authentication information was incorrect, agent_request_input event may be emitted]] +} + +enum Efl.Net.Control.Access_Point.Security { + [[Bitwise-able securities supported by an access point. + + @since 1.19 + ]] + unknow = 0, + none = (1 << 0), [[open access, no security]] + wep = (1 << 1), [[WEP]] + psk = (1 << 2), [[PSK (Pre Shared Key), such as WPA or RSN]] + ieee802_1x = (1 << 3), [[IEEE 802.1X]] +} + +enum Efl.Net.Control.Access_Point.Ipv4_Method { + [[The method used to configure IPv4 + + @since 1.19 + ]] + off, [[IPv4 is disabled]] + dhcp, [[IPv4 is configured using DHCP]] + manual, [[IPv4 is manually set using address, netmask and gateway]] + unset, [[Only to be used with @Efl.Net.Control.Access_Point.configuration_ipv4]] +} + +enum Efl.Net.Control.Access_Point.Ipv6_Method { + [[The method used to configure IPv6 + + @since 1.19 + ]] + off, [[IPv6 is disabled]] + fixed, [[IPv6 is fixed by operator and cannot be changed]] + manual, [[IPv6 is manually set using address, netmask and gateway]] + auto_privacy_none, [[IPv6 is set using dhcp or using a tunnel6to4, no privacy extensions should be used]] + auto_privacy_public, [[IPv6 is set using dhcp or using a tunnel6to4, privacy extensions are used and the system prefers a public IP address over temporary addresses]] + auto_privacy_temporary, [[IPv6 is set using dhcp or using a tunnel6to4, privacy extensions are used and the system prefers a temporary IP address over public addresses]] + tunnel6to4, [[IPv6 was configured using a 6-to-4 tunnel. This cannot be set by the user, which is expected to set "auto" instead]] + unset, [[Only to be used with @Efl.Net.Control.Access_Point.configuration_ipv6]] +} + +enum Efl.Net.Control.Access_Point.Proxy_Method { + [[The method used to configure Proxy. + + @since 1.19 + ]] + off, [[direct connection to the internet, no proxy to be used]] + auto, [[proxy is autoconfigured using Proxy-Auto-Configuration (PAC) using given URL]] + manual, [[proxy is configured manually using servers and excludes]] + unset, [[Only to be used with @Efl.Net.Control.Access_Point.configuration_proxy]] +} + +class Efl.Net.Control.Access_Point (Efl.Loop_User) { + [[An access point for network connectivity. + + The @Efl.Net.Control is composed of multiple technologies, each + create access points to allow configuration and connection. + + An application willing to just get a network connection should + prefer to use the \@Efl.Net.Session instead. + + @since 1.19 + ]] + + events { + changed; [[Some properties were changed.]] + } + + methods { + connect { + [[Connect to this access point. + + This connection will happen asynchronously in the + background, with results being delivered by events in + the access point object, such as the "changed". + + Successful connections will remember the device and set + it to auto-connect using the property @.auto_connect. + + See @.forget, @.remembered, @.auto_connect and + @.disconnect + + The future may fail with non-fatal errors such as + EINPROGRESS (the connection was already ongoing) and + EALREADY (the connection was already stablished). + ]] + return: future; /* NOTE: This should be future */ + } + + disconnect { + [[Disconnect from this access point. + + When disconnected a previously connected access point it + won't be forgotten. The configuration and other details + such as priority and passphrase will be available for a + future re-connection with call to @.connect. If it is + desired to disconnect and forget all access point + information, use @.forget instead. + ]] + } + + forget { + [[Disconnect and forget about this access point. + + Successful @.connect will always remember the access + point for future re-connections. This method reverts + that by disconnecting and forgetting about the access + point and its configuration, which will set the + @.remembered to $false. + ]] + } + + @property state { + [[The current state of this access point. + + Whenever the state changes, "changed" will be emitted. + ]] + get { } + values { + state: Efl.Net.Control.Access_Point.State; + } + } + + @property error { + [[If the access point is in error state, this states the error.]] + get { } + values { + error: Efl.Net.Control.Access_Point.Error; + } + } + + @property name { + [[The user-friendly access point name. + + For hidden WiFi networks, this is empty. + ]] + get { } + values { + name: string; + } + } + + @property priority { + [[The access point priority in the current access point listing. + + This property is dynamic and reflects the index of the + access point in the current access points list. As + access points may come and go, the value may change at + any time and notified with "changed" event. + + If set, then it will reoder priorities, moving all other + services at equal or higher priority up. To move as the + first (most priority), then use 0. To move as the last + priority, use UINT32_MAX or the last known priority + 1. + + \@note Only remembered access points may be reordered + among themselves. Those that are not remembered will + always come last in random order defined by the backend. + ]] + get { } + set { } + values { + priority: uint; + } + } + + @property technology { + [[The technology that generated this access point]] + get { } + values { + technology: Efl.Net.Control.Technology; + } + } + + @property strength { + [[Signal strength percentage in 0-100]] + get { } + values { + strength: uint8; + } + } + + @property roaming { + [[If it's a cellular access point and it's on roaming.]] + get { } + values { + roaming: bool; + } + } + + @property auto_connect { + [[Whenever to auto-connect to this access point if no other is connected. + + By default successfully connected access points are + remembered and set to auto-connect. This behavior can be + changed with this property. + + An access point is only connected automatically if there + are no other connected and if it is not on roaming. + ]] + get { } + set { } + values { + auto_connect: bool; + } + } + + @property remembered { + [[Successfully connected access points are remembered. + + To forget about this access point, call @.forget. + ]] + get { } + values { + remembered: bool; + } + } + + @property immutable { + [[Immutable access points are those defined in configuration files and its properties can't be changed using API.]] + get { } + values { + immutable: bool; + } + } + + @property security { + [[Security options such as wep, wps, psk or none (open).]] + get { } + values { + security: Efl.Net.Control.Access_Point.Security; [[Bitwise OR of security supported by this access point]] + } + } + + @property name_servers { + [[DNS (Domain Name Servers) in use for this access point. + + These are the actual values in use, configure them using + @.configuration_name_servers, which may generate change + to this property, being notified with the "changed" + event. + ]] + get { } + values { + name_servers: free(own(iterator), eina_iterator_free); + } + } + + @property time_servers { + [[NTP (Time Server) in use for this access point. + + These are the actual values in use, configure them using + @.configuration_time_servers, which may generate change + to this property, being notified with the "changed" + event. + ]] + get { } + values { + time_servers: free(own(iterator), eina_iterator_free); + } + } + + @property domains { + [[Search domains in use for this access point. + + These are the actual values in use, configure them using + @.configuration_domains, which may generate change + to this property, being notified with the "changed" + event. + ]] + get { } + values { + domains: free(own(iterator), eina_iterator_free); + } + } + + @property ipv4 { + [[IPv4 in use for this access point. + + These are the actual values in use, configure them using + @.configuration_ipv4, which may generate change + to this property, being notified with the "changed" + event. + ]] + get { } + values { + method: Efl.Net.Control.Access_Point.Ipv4_Method; + address: string; + netmask: string; + gateway: string; + } + } + + @property ipv6 { + [[IPv6 in use for this access point. + + These are the actual values in use, configure them using + @.configuration_ipv6, which may generate change + to this property, being notified with the "changed" + event. + ]] + get { } + values { + method: Efl.Net.Control.Access_Point.Ipv6_Method; + address: string; + prefix_length: uint8; + netmask: string; + gateway: string; + } + } + + @property proxy { + [[Proxy in use for this access point. + + These are the actual values in use, configure them using + @.configuration_proxy, which may generate change + to this property, being notified with the "changed" + event. + ]] + get { } + values { + method: Efl.Net.Control.Access_Point.Proxy_Method; + url: string; [[if @Efl.Net.Control.Access_Point.Proxy_Method.auto, then states the URL to use for proxy auto-configuration]] + servers: free(own(iterator), eina_iterator_free); [[If @Efl.Net.Control.Access_Point.Proxy_Method.manual, then states the URI with proxy servers to use, like "http://proxy.domain.com:911"]] + excludes: free(own(iterator), eina_iterator_free); [[If @Efl.Net.Control.Access_Point.Proxy_Method.manual, then states the hosts or patterns to exclude from proxy access, such as "localhost", ".domain.com", or "10.0.0.0..."]] + } + } + + @property configuration_name_servers { + [[DNS (Domain Name Servers) configured by user for this access point. + + These are the user configured values, that will be + applied by the backend system and in turn may result in + "changed" event to notify of @.name_servers property + with the actual value in use, which may differ from + this. + ]] + set { } + get { } + values { + name_servers: free(own(iterator), eina_iterator_free); + } + } + + @property configuration_time_servers { + [[NTP (Time Server) configured by user for this access point. + + These are the user configured values, that will be + applied by the backend system and in turn may result in + "changed" event to notify of @.time_servers property + with the actual value in use, which may differ from + this. + ]] + set { } + get { } + values { + time_servers: free(own(iterator), eina_iterator_free); + } + } + + @property configuration_domains { + [[Search domains configured by user for this access point. + + These are the user configured values, that will be + applied by the backend system and in turn may result in + "changed" event to notify of @.domains property + with the actual value in use, which may differ from + this. + ]] + set { } + get { } + values { + domains: free(own(iterator), eina_iterator_free); + } + } + + @property configuration_ipv4 { + [[IPv4 configured by user for this access point. + + These are the user configured values, that will be + applied by the backend system and in turn may result in + "changed" event to notify of @.ipv4 property + with the actual value in use, which may differ from + this. + ]] + set { } + get { } + values { + method: Efl.Net.Control.Access_Point.Ipv4_Method; + address: string; + netmask: string; + gateway: string; + } + } + + @property configuration_ipv6 { + [[IPv6 configured by user for this access point. + + These are the user configured values, that will be + applied by the backend system and in turn may result in + "changed" event to notify of @.ipv6 property + with the actual value in use, which may differ from + this. + ]] + set { } + get { } + values { + method: Efl.Net.Control.Access_Point.Ipv6_Method; + address: string; + prefix_length: uint8; + netmask: string; + gateway: string; + } + } + + @property configuration_proxy { + [[Proxy configured by user for this access point. + + These are the user configured values, that will be + applied by the backend system and in turn may result in + "changed" event to notify of @.proxy property + with the actual value in use, which may differ from + this. + ]] + set { } + get { } + values { + method: Efl.Net.Control.Access_Point.Proxy_Method; + url: string; [[if @Efl.Net.Control.Access_Point.Proxy_Method.auto, then states the URL to use for proxy auto-configuration]] + servers: free(own(iterator), eina_iterator_free); [[If @Efl.Net.Control.Access_Point.Proxy_Method.manual, then states the URI with proxy servers to use, like "http://proxy.domain.com:911"]] + excludes: free(own(iterator), eina_iterator_free); [[If @Efl.Net.Control.Access_Point.Proxy_Method.manual, then states the hosts or patterns to exclude from proxy access, such as "localhost", ".domain.com", or "10.0.0.0..."]] + } + } + } + + implements { + Efl.Object.destructor; + } +} diff --git a/src/lib/ecore_con/efl_net_control_technology-connman.c b/src/lib/ecore_con/efl_net_control_technology-connman.c new file mode 100644 index 0000000000..68c0f750b5 --- /dev/null +++ b/src/lib/ecore_con/efl_net_control_technology-connman.c @@ -0,0 +1,472 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "Ecore.h" +#include "Ecore_Con.h" +#include "ecore_con_private.h" +#include "efl_net-connman.h" + +typedef struct +{ + /* Eldbus_Proxy/Eldbus_Object keeps a list of pending calls, but + * they are reference counted singletons and will only cancel + * pending calls when everything is gone. However we operate on + * our private data, that may be gone before other refs. So + * keep the pending list. + */ + Eina_List *pending; + Eina_List *signal_handlers; + Eldbus_Proxy *proxy; + Eina_Stringshare *path; + Eina_Stringshare *name; + struct { + Eina_Stringshare *identifier; + Eina_Stringshare *passphrase; + Eina_Bool enabled; + } tethering; + Efl_Net_Control_Technology_Type type; + Eina_Bool powered; + Eina_Bool connected; +} Efl_Net_Control_Technology_Data; + +#define MY_CLASS EFL_NET_CONTROL_TECHNOLOGY_CLASS + +static void +_efl_net_control_technology_property_powered_changed(Eo *o, Efl_Net_Control_Technology_Data *pd, Eldbus_Message_Iter *value) +{ + Eina_Bool powered; + + if (!eldbus_message_iter_arguments_get(value, "b", &powered)) + { + ERR("Expected boolean, got %s", eldbus_message_iter_signature_get(value)); + return; + } + + if (pd->powered == powered) return; + pd->powered = powered; + DBG("powered=%hhu", powered); + efl_event_callback_call(o, EFL_NET_CONTROL_TECHNOLOGY_EVENT_CHANGED, NULL); +} + +static void +_efl_net_control_technology_property_connected_changed(Eo *o, Efl_Net_Control_Technology_Data *pd, Eldbus_Message_Iter *value) +{ + Eina_Bool connected; + + if (!eldbus_message_iter_arguments_get(value, "b", &connected)) + { + ERR("Expected boolean, got %s", eldbus_message_iter_signature_get(value)); + return; + } + + if (pd->connected == connected) return; + pd->connected = connected; + DBG("connected=%hhu", connected); + efl_event_callback_call(o, EFL_NET_CONTROL_TECHNOLOGY_EVENT_CHANGED, NULL); +} + +static void +_efl_net_control_technology_property_name_changed(Eo *o, Efl_Net_Control_Technology_Data *pd, Eldbus_Message_Iter *value) +{ + const char *name; + + if (!eldbus_message_iter_arguments_get(value, "s", &name)) + { + ERR("Expected string, got %s", eldbus_message_iter_signature_get(value)); + return; + } + + if (!eina_stringshare_replace(&pd->name, name)) return; + DBG("name=%s", name); + efl_event_callback_call(o, EFL_NET_CONTROL_TECHNOLOGY_EVENT_CHANGED, NULL); +} + +Efl_Net_Control_Technology_Type +efl_net_connman_technology_type_from_str(const char *str) +{ + const struct { + Efl_Net_Control_Technology_Type val; + const char *str; + } *itr, map[] = { + {EFL_NET_CONTROL_TECHNOLOGY_TYPE_UNKNOWN, "unknown"}, + {EFL_NET_CONTROL_TECHNOLOGY_TYPE_SYSTEM, "system"}, + {EFL_NET_CONTROL_TECHNOLOGY_TYPE_ETHERNET, "ethernet"}, + {EFL_NET_CONTROL_TECHNOLOGY_TYPE_WIFI, "wifi"}, + {EFL_NET_CONTROL_TECHNOLOGY_TYPE_BLUETOOTH, "bluetooth"}, + {EFL_NET_CONTROL_TECHNOLOGY_TYPE_CELLULAR, "cellular"}, + {EFL_NET_CONTROL_TECHNOLOGY_TYPE_GPS, "gps"}, + {EFL_NET_CONTROL_TECHNOLOGY_TYPE_VPN, "vpn"}, + {EFL_NET_CONTROL_TECHNOLOGY_TYPE_GADGET, "gadget"}, + {EFL_NET_CONTROL_TECHNOLOGY_TYPE_P2P, "p2p"}, + { } + }; + + for (itr = map; itr->str != NULL; itr++) + if (strcmp(itr->str, str) == 0) return itr->val; + + ERR("Unknown type '%s'", str); + return EFL_NET_CONTROL_TECHNOLOGY_TYPE_UNKNOWN; +} + +static void +_efl_net_control_technology_property_type_changed(Eo *o, Efl_Net_Control_Technology_Data *pd, Eldbus_Message_Iter *value) +{ + const char *str; + Efl_Net_Control_Technology_Type type; + + if (!eldbus_message_iter_arguments_get(value, "s", &str)) + { + ERR("Expected string, got %s", eldbus_message_iter_signature_get(value)); + return; + } + + type = efl_net_connman_technology_type_from_str(str); + if (pd->type == type) return; + pd->type = type; + DBG("type=%d (%s)", type, str); + efl_event_callback_call(o, EFL_NET_CONTROL_TECHNOLOGY_EVENT_CHANGED, NULL); +} + +static void +_efl_net_control_technology_property_tethering_changed(Eo *o, Efl_Net_Control_Technology_Data *pd, Eldbus_Message_Iter *value) +{ + Eina_Bool tethering; + + if (!eldbus_message_iter_arguments_get(value, "b", &tethering)) + { + ERR("Expected boolean, got %s", eldbus_message_iter_signature_get(value)); + return; + } + + if (pd->tethering.enabled == tethering) return; + pd->tethering.enabled = tethering; + DBG("tethering=%hhu", tethering); + efl_event_callback_call(o, EFL_NET_CONTROL_TECHNOLOGY_EVENT_CHANGED, NULL); +} + +static void +_efl_net_control_technology_property_tethering_identifier_changed(Eo *o, Efl_Net_Control_Technology_Data *pd, Eldbus_Message_Iter *value) +{ + const char *tethering_identifier; + + if (!eldbus_message_iter_arguments_get(value, "s", &tethering_identifier)) + { + ERR("Expected string, got %s", eldbus_message_iter_signature_get(value)); + return; + } + + if (!eina_stringshare_replace(&pd->tethering.identifier, tethering_identifier)) return; + DBG("tethering identifier=%s", tethering_identifier); + efl_event_callback_call(o, EFL_NET_CONTROL_TECHNOLOGY_EVENT_CHANGED, NULL); +} + +static void +_efl_net_control_technology_property_tethering_passphrase_changed(Eo *o, Efl_Net_Control_Technology_Data *pd, Eldbus_Message_Iter *value) +{ + const char *tethering_passphrase; + + if (!eldbus_message_iter_arguments_get(value, "s", &tethering_passphrase)) + { + ERR("Expected string, got %s", eldbus_message_iter_signature_get(value)); + return; + } + + if (!eina_stringshare_replace(&pd->tethering.passphrase, tethering_passphrase)) return; + DBG("tethering passphrase=%s", tethering_passphrase); + efl_event_callback_call(o, EFL_NET_CONTROL_TECHNOLOGY_EVENT_CHANGED, NULL); +} + + +static void +_efl_net_control_technology_property_changed_internal(Eo *o, Efl_Net_Control_Technology_Data *pd, Eldbus_Message_Iter *itr) +{ + Eldbus_Message_Iter *value; + const char *name; + + if (!eldbus_message_iter_arguments_get(itr, "sv", &name, &value)) + { + ERR("Unexpected signature: %s", eldbus_message_iter_signature_get(itr)); + return; + } + + if (strcmp(name, "Powered") == 0) + _efl_net_control_technology_property_powered_changed(o, pd, value); + else if (strcmp(name, "Connected") == 0) + _efl_net_control_technology_property_connected_changed(o, pd, value); + else if (strcmp(name, "Name") == 0) + _efl_net_control_technology_property_name_changed(o, pd, value); + else if (strcmp(name, "Type") == 0) + _efl_net_control_technology_property_type_changed(o, pd, value); + else if (strcmp(name, "Tethering") == 0) + _efl_net_control_technology_property_tethering_changed(o, pd, value); + else if (strcmp(name, "TetheringIdentifier") == 0) + _efl_net_control_technology_property_tethering_identifier_changed(o, pd, value); + else if (strcmp(name, "TetheringPassphrase") == 0) + _efl_net_control_technology_property_tethering_passphrase_changed(o, pd, value); + else + WRN("Unknown property name: %s", name); +} + +static void +_efl_net_control_technology_property_changed(void *data, const Eldbus_Message *msg) +{ + Eo *o = data; + Efl_Net_Control_Technology_Data *pd = efl_data_scope_get(o, MY_CLASS); + Eldbus_Message_Iter *itr; + + itr = eldbus_message_iter_get(msg); + _efl_net_control_technology_property_changed_internal(o, pd, itr); +} + +EOLIAN static void +_efl_net_control_technology_efl_object_destructor(Eo *o, Efl_Net_Control_Technology_Data *pd) +{ + Eldbus_Pending *p; + Eldbus_Signal_Handler *sh; + + EINA_LIST_FREE(pd->pending, p) + eldbus_pending_cancel(p); + + EINA_LIST_FREE(pd->signal_handlers, sh) + eldbus_signal_handler_del(sh); + + if (pd->proxy) + { + Eldbus_Object *obj = eldbus_proxy_object_get(pd->proxy); + eldbus_proxy_unref(pd->proxy); + pd->proxy = NULL; + eldbus_object_unref(obj); + } + + efl_destructor(efl_super(o, MY_CLASS)); + eina_stringshare_replace(&pd->path, NULL); + eina_stringshare_replace(&pd->name, NULL); + eina_stringshare_replace(&pd->tethering.identifier, NULL); + eina_stringshare_replace(&pd->tethering.passphrase, NULL); +} + +static void +_efl_net_control_technology_property_set_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending) +{ + Eo *o = data; + Efl_Net_Control_Technology_Data *pd = efl_data_scope_get(o, MY_CLASS); + const char *err_name, *err_msg; + + pd->pending = eina_list_remove(pd->pending, pending); + if (eldbus_message_error_get(msg, &err_name, &err_msg)) + { + ERR("Could not set property %p: %s=%s", o, err_name, err_msg); + return; + } +} + +static void +_efl_net_control_technology_property_set(Eo *o, Efl_Net_Control_Technology_Data *pd, const char *name, const char *signature, ...) +{ + Eldbus_Message *msg; + Eldbus_Message_Iter *msg_itr, *var; + Eldbus_Pending *p; + va_list ap; + + msg = eldbus_proxy_method_call_new(pd->proxy, "SetProperty"); + EINA_SAFETY_ON_NULL_RETURN(msg); + + msg_itr = eldbus_message_iter_get(msg); + EINA_SAFETY_ON_NULL_GOTO(msg_itr, error_send); + + eldbus_message_iter_basic_append(msg_itr, 's', name); + var = eldbus_message_iter_container_new(msg_itr, 'v', signature); + + va_start(ap, signature); + eldbus_message_iter_arguments_vappend(var, signature, ap); + va_end(ap); + eldbus_message_iter_container_close(msg_itr, var); + + p = eldbus_proxy_send(pd->proxy, msg, _efl_net_control_technology_property_set_cb, o, DEFAULT_TIMEOUT); + EINA_SAFETY_ON_NULL_GOTO(p, error_send); + + pd->pending = eina_list_append(pd->pending, p); + DBG("Setting property %s", name); + return; + + error_send: + eldbus_message_unref(msg); +} + +EOLIAN static void +_efl_net_control_technology_powered_set(Eo *o, Efl_Net_Control_Technology_Data *pd, Eina_Bool powered) +{ + _efl_net_control_technology_property_set(o, pd, "Powered", "b", powered); +} + +EOLIAN static Eina_Bool +_efl_net_control_technology_powered_get(Eo *o EINA_UNUSED, Efl_Net_Control_Technology_Data *pd) +{ + return pd->powered; +} + +EOLIAN static void +_efl_net_control_technology_tethering_set(Eo *o, Efl_Net_Control_Technology_Data *pd, Eina_Bool enabled, const char *identifier, const char *passphrase) +{ + if (passphrase) + _efl_net_control_technology_property_set(o, pd, "TetheringPassphrase", "s", passphrase); + + if (identifier) + _efl_net_control_technology_property_set(o, pd, "TetheringIdentifier", "s", identifier); + + _efl_net_control_technology_property_set(o, pd, "Tethering", "b", enabled); +} + +EOLIAN static void +_efl_net_control_technology_tethering_get(Eo *o EINA_UNUSED, Efl_Net_Control_Technology_Data *pd, Eina_Bool *enabled, const char **identifier, const char **passphrase) +{ + if (enabled) *enabled = pd->tethering.enabled; + if (identifier) *identifier = pd->tethering.identifier; + if (passphrase) *passphrase = pd->tethering.passphrase; +} + +EOLIAN static Eina_Bool +_efl_net_control_technology_connected_get(Eo *o EINA_UNUSED, Efl_Net_Control_Technology_Data *pd) +{ + return pd->connected; +} + +EOLIAN static const char * +_efl_net_control_technology_name_get(Eo *o EINA_UNUSED, Efl_Net_Control_Technology_Data *pd) +{ + return pd->name; +} + +EOLIAN static Efl_Net_Control_Technology_Type +_efl_net_control_technology_type_get(Eo *o EINA_UNUSED, Efl_Net_Control_Technology_Data *pd) +{ + return pd->type; +} + +static void +_efl_net_control_technology_scan_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending) +{ + Efl_Promise *promise = data; + Eo *o = efl_parent_get(promise); + Efl_Net_Control_Technology_Data *pd = efl_data_scope_get(o, MY_CLASS); + const char *err_name, *err_msg; + + EINA_SAFETY_ON_NULL_RETURN(pd); + + pd->pending = eina_list_remove(pd->pending, pending); + efl_key_data_set(promise, "eldbus_pending", NULL); + if (eldbus_message_error_get(msg, &err_name, &err_msg)) + { + Eina_Error err = EINVAL; + + if (strcmp(err_name, "net.connman.Error.NotSupported") == 0) + err = ENOTSUP; + + WRN("Could not Scan %p: %s=%s", o, err_name, err_msg); + efl_promise_failed_set(promise, err); + return; + } + + efl_promise_value_set(promise, o, NULL); + efl_del(promise); +} + +static void +_efl_net_control_technology_scan_promise_del(void *data, const Efl_Event *event) +{ + Eldbus_Pending *p; + Eo *o = data; + Efl_Net_Control_Technology_Data *pd = efl_data_scope_get(o, MY_CLASS); + + EINA_SAFETY_ON_NULL_RETURN(pd); + + p = efl_key_data_get(event->object, "eldbus_pending"); + if (!p) return; /* already gone, nothing to do */ + + pd->pending = eina_list_remove(pd->pending, p); + DBG("cancel pending scan %p", p); + eldbus_pending_cancel(p); +} + +EOLIAN static Efl_Future * +_efl_net_control_technology_scan(Eo *o, Efl_Net_Control_Technology_Data *pd) +{ + Eldbus_Pending *p; + Efl_Promise *promise; + + promise = efl_add(EFL_PROMISE_CLASS, o, + efl_event_callback_add(efl_added, EFL_EVENT_DEL, _efl_net_control_technology_scan_promise_del, o)); + EINA_SAFETY_ON_NULL_RETURN_VAL(promise, NULL); + + p = eldbus_proxy_call(pd->proxy, "Scan", + _efl_net_control_technology_scan_cb, promise, -1.0, ""); + EINA_SAFETY_ON_NULL_GOTO(p, error_dbus); + + pd->pending = eina_list_append(pd->pending, p); + efl_key_data_set(promise, "eldbus_pending", p); + + return efl_promise_future_get(promise); + + error_dbus: + efl_promise_failed_set(promise, ENOSYS); + return promise; +} + +const char * +efl_net_connman_technology_path_get(Efl_Net_Control_Technology *o) +{ + Efl_Net_Control_Technology_Data *pd = efl_data_scope_get(o, MY_CLASS); + EINA_SAFETY_ON_NULL_RETURN_VAL(pd, NULL); + return pd->path; +} + +Efl_Net_Control_Technology * +efl_net_connman_technology_new(Efl_Net_Control *ctl, const char *path, Eldbus_Message_Iter *itr) +{ + Eo *o; + Efl_Net_Control_Technology_Data *pd; + Eldbus_Message_Iter *entry; + Eldbus_Connection *conn; + Eldbus_Object *obj; + + conn = efl_net_connman_connection_get(); + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + + o = efl_add(MY_CLASS, ctl); + EINA_SAFETY_ON_NULL_RETURN_VAL(o, NULL); + + pd = efl_data_scope_get(o, MY_CLASS); + EINA_SAFETY_ON_NULL_GOTO(pd, error); + + pd->path = eina_stringshare_add(path); + EINA_SAFETY_ON_NULL_GOTO(pd->path, error); + + obj = eldbus_object_get(conn, "net.connman", pd->path); + EINA_SAFETY_ON_NULL_GOTO(obj, error); + pd->proxy = eldbus_proxy_get(obj, "net.connman.Technology"); + EINA_SAFETY_ON_NULL_GOTO(pd->proxy, error); + +#define SH(sig, cb) \ + do { \ + Eldbus_Signal_Handler *sh = eldbus_proxy_signal_handler_add(pd->proxy, sig, cb, o); \ + if (sh) pd->signal_handlers = eina_list_append(pd->signal_handlers, sh); \ + else ERR("could not add DBus signal handler %s", sig); \ + } while (0) + + SH("PropertyChanged", _efl_net_control_technology_property_changed); +#undef SH + + efl_event_freeze(o); + while (eldbus_message_iter_get_and_next(itr, 'e', &entry)) + _efl_net_control_technology_property_changed_internal(o, pd, entry); + efl_event_thaw(o); + + return o; + + error: + efl_del(o); + return NULL; +} + +#include "efl_net_control_technology.eo.c" diff --git a/src/lib/ecore_con/efl_net_control_technology.eo b/src/lib/ecore_con/efl_net_control_technology.eo new file mode 100644 index 0000000000..d91f94723a --- /dev/null +++ b/src/lib/ecore_con/efl_net_control_technology.eo @@ -0,0 +1,106 @@ +enum Efl.Net.Control.Technology.Type { + [[Technology types + + @since 1.19 + ]] + unknown, + system, + ethernet, + wifi, + bluetooth, + cellular, + gps, + vpn, + gadget, + p2p, +} + +class Efl.Net.Control.Technology (Efl.Loop_User) { + [[A technology that enables network access points to be controlled. + + The @Efl.Net.Control is composed of multiple technologies, each + can be disabled/enabled with @.powered property. + + When powered, the technology will dynamically add and delete + access points to be available in + @Efl.Net.Control.access_points. + + @since 1.19 + ]] + events { + changed; [[Some properties were changed.]] + } + + methods { + @property powered { + [[If $true the technology is available. If $false technology is disabled and if possible rfkill is used.]] + get { } + set { } + values { + powered: bool; + } + } + + @property tethering { + [[Tethering allows the default access point to be bridged to all clients connected through the technology. + + A common case is to use the device as a router, such as + a phone doing tethering allows a laptop to connect to 4G + network. + ]] + get { } + set { } + values { + enabled: bool; [[Whenever to enable or disable tethering for this technology]] + identifier: string @optional; [[The name to identify this tethering, in WiFi it will translate to SSID.]] + passphrase: string @optional; [[The passphrase for this tethering access, in WiFi it will translate to WPA passphrase.]] + } + } + + @property connected { + [[If $true the technology has at least one access point connected.]] + get { } + values { + connected: bool; + } + } + + @property name { + [[The user-friendly technology name]] + get { } + values { + name: string; + } + } + + @property type { + [[The technology type, such as "ethernet" or "wifi"]] + get { } + values { + type: Efl.Net.Control.Technology.Type; + } + } + + scan { + [[Explicitly trigger a scan. + + The scan will happen asynchronously in the background, + with the results being delivered by events in the + technology, @Efl.Net.Control or @Efl.Net.Control.Access_Point + associated with the technology. For example, scan on + WiFi will add and delete access points. + + It is worth to mention that explicit scans should be + avoided. Rare cases are when user requested them, like + entering a configuration dialog which demands fresh + state. Otherwise prefer to let the system passively do + scans in a timely manner. + ]] + return: future; /* NOTE: This should be future */ + } + } + + implements { + Efl.Object.destructor; + } +} diff --git a/src/lib/ecore_con/efl_net_session-connman.c b/src/lib/ecore_con/efl_net_session-connman.c new file mode 100644 index 0000000000..f646a6e97e --- /dev/null +++ b/src/lib/ecore_con/efl_net_session-connman.c @@ -0,0 +1,792 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "Ecore.h" +#include "Ecore_Con.h" +#include "ecore_con_private.h" +#include "efl_net-connman.h" + +typedef struct +{ + Eldbus_Proxy *proxy; /* net.connman.Session */ + Eldbus_Service_Interface *notifier; /* net.connman.Notification */ + Eldbus_Pending *mgr_pending; /* on efl_net_connman_manager_get(), local proxy doesn't need it, done automatically */ + + struct { + Eldbus_Pending *pending; + Eina_Bool connected; /* if should be connected or not */ + Eina_Bool online_required; + Efl_Net_Session_Technology technologies_allowed; + } connect; + + /* properties notified by session, local cache */ + Eina_Stringshare *name; + Eina_Stringshare *interface; + struct { + Eina_Stringshare *address; + Eina_Stringshare *netmask; + Eina_Stringshare *gateway; + } ipv4; + struct { + Eina_Stringshare *address; + Eina_Stringshare *netmask; + Eina_Stringshare *gateway; + uint8_t prefix_length; + } ipv6; + Efl_Net_Session_State state; + Efl_Net_Session_Technology technology; +} Efl_Net_Session_Data; + +#define MY_CLASS EFL_NET_SESSION_CLASS + +/* will SET BIT for technology, start with '0' for multiple techs */ +static Eina_Bool +_efl_net_session_technology_from_str(const char *str, Efl_Net_Session_Technology *tech) +{ + if (0) { } +#define MAP(X, s) \ + else if (strcmp(str, s) == 0) *tech |= EFL_NET_SESSION_TECHNOLOGY_ ## X + MAP(ALL, "*"); + MAP(ETHERNET, "ethernet"); + MAP(WIFI, "wifi"); + MAP(BLUETOOTH, "bluetooth"); + MAP(CELLULAR, "cellular"); + MAP(VPN, "vpn"); + MAP(GADGET, "gadget"); +#undef MAP + else if (str[0]) /* empty bearer = no technology */ + { + WRN("Unknown technology name: %s", str); + return EINA_FALSE; + } + + return EINA_TRUE; +} + +static void _efl_net_session_connect_do(Eo *o, Efl_Net_Session_Data *pd); + +/* NOTE: unlike most DBus servers where you create paths using + * ObjectManager and monitor properties on them using PropertyManager, + * ConnMan doesn't use any of those and their API for Session is + * different from all others in ConnMan itself: + * + * 1 - create a local service 'notifier' implementing + * net.connman.Notification, with method Release() and + * Update(dict settings). + * + * 2 - call / CreateSession(dict settings, path notifier), get an + * object path representing your session + * + * 3 - call Change(setting, value), Connect(), Disconnect() on + * object path returned on #2 + * + * 4 - get Update() to be called on your local service notifier specified + * on step #1. + */ + +static Eldbus_Message * +_efl_net_session_notifier_release(const Eldbus_Service_Interface *service, const Eldbus_Message *msg) +{ + Eo *o = eldbus_service_object_data_get(service, "efl_net"); + DBG("Session %p is released %s", o, eldbus_message_path_get(msg)); + + return eldbus_message_method_return_new(msg); +} + +static Eina_Error +_efl_net_session_notifier_update_state(Efl_Net_Session_Data *pd, Eldbus_Message_Iter *var) +{ + const char *str; + + if (!eldbus_message_iter_arguments_get(var, "s", &str)) + return EINVAL; + + if (strcmp(str, "disconnected") == 0) + pd->state = EFL_NET_SESSION_STATE_OFFLINE; + else if (strcmp(str, "connected") == 0) + pd->state = EFL_NET_SESSION_STATE_LOCAL; + else if (strcmp(str, "online") == 0) + pd->state = EFL_NET_SESSION_STATE_ONLINE; + else + return EINVAL; + + return 0; +} + +static Eina_Error +_efl_net_session_notifier_update_name(Efl_Net_Session_Data *pd, Eldbus_Message_Iter *var) +{ + const char *str; + + if (!eldbus_message_iter_arguments_get(var, "s", &str)) + return EINVAL; + + eina_stringshare_replace(&pd->name, str); + return 0; +} + +static Eina_Error +_efl_net_session_notifier_update_technology(Efl_Net_Session_Data *pd, Eldbus_Message_Iter *var) +{ + const char *str; + + if (!eldbus_message_iter_arguments_get(var, "s", &str)) + return EINVAL; + + pd->technology = 0; + if (!_efl_net_session_technology_from_str(str, &pd->technology)) + return EINVAL; + + return 0; +} + +static Eina_Error +_efl_net_session_notifier_update_interface(Efl_Net_Session_Data *pd, Eldbus_Message_Iter *var) +{ + const char *str; + + if (!eldbus_message_iter_arguments_get(var, "s", &str)) + return EINVAL; + + eina_stringshare_replace(&pd->interface, str); + return 0; +} + +static Eina_Error +_efl_net_session_notifier_update_ipv4(Efl_Net_Session_Data *pd, Eldbus_Message_Iter *var) +{ + Eldbus_Message_Iter *sub, *entry; + + if (!eldbus_message_iter_arguments_get(var, "a{sv}", &sub)) + return EINVAL; + + while (eldbus_message_iter_get_and_next(sub, 'e', &entry)) + { + const void *key; + Eldbus_Message_Iter *value; + + if (!eldbus_message_iter_arguments_get(entry, "sv", &key, &value)) + { + ERR("Unexpected dict entry signature: %s", eldbus_message_iter_signature_get(entry)); + continue; + } + + if (strcmp(key, "Method") == 0) + { + const char *str; + eldbus_message_iter_arguments_get(value, "s", &str); + DBG("configuration method %s", str); + } + else if (strcmp(key, "Address") == 0) + { + const char *str; + eldbus_message_iter_arguments_get(value, "s", &str); + DBG("address %s", str); + eina_stringshare_replace(&pd->ipv4.address, str); + } + else if (strcmp(key, "Gateway") == 0) + { + const char *str; + eldbus_message_iter_arguments_get(value, "s", &str); + DBG("gateway %s", str); + eina_stringshare_replace(&pd->ipv4.gateway, str); + } + else if (strcmp(key, "Netmask") == 0) + { + const char *str; + eldbus_message_iter_arguments_get(value, "s", &str); + DBG("netmask %s", str); + eina_stringshare_replace(&pd->ipv4.netmask, str); + } + else + { + WRN("Unsupported field %s (signature=%s)", key, eldbus_message_iter_signature_get(value)); + continue; + } + } + + return 0; +} + +static Eina_Error +_efl_net_session_notifier_update_ipv6(Efl_Net_Session_Data *pd, Eldbus_Message_Iter *var) +{ + Eldbus_Message_Iter *sub, *entry; + + if (!eldbus_message_iter_arguments_get(var, "a{sv}", &sub)) + return EINVAL; + + while (eldbus_message_iter_get_and_next(sub, 'e', &entry)) + { + const void *key; + Eldbus_Message_Iter *value; + + if (!eldbus_message_iter_arguments_get(entry, "sv", &key, &value)) + { + ERR("Unexpected dict entry signature: %s", eldbus_message_iter_signature_get(entry)); + continue; + } + + if (strcmp(key, "Method") == 0) + { + const char *str; + eldbus_message_iter_arguments_get(value, "s", &str); + DBG("configuration method %s", str); + } + else if (strcmp(key, "Address") == 0) + { + const char *str; + eldbus_message_iter_arguments_get(value, "s", &str); + DBG("address %s", str); + eina_stringshare_replace(&pd->ipv6.address, str); + } + else if (strcmp(key, "Gateway") == 0) + { + const char *str; + eldbus_message_iter_arguments_get(value, "s", &str); + DBG("gateway %s", str); + eina_stringshare_replace(&pd->ipv6.gateway, str); + } + else if (strcmp(key, "Netmask") == 0) + { + const char *str; + eldbus_message_iter_arguments_get(value, "s", &str); + DBG("netmask %s", str); + eina_stringshare_replace(&pd->ipv6.netmask, str); + } + else if (strcmp(key, "Netmask") == 0) + { + const char *str; + eldbus_message_iter_arguments_get(value, "s", &str); + DBG("netmask %s", str); + eina_stringshare_replace(&pd->ipv6.netmask, str); + } + else if (strcmp(key, "PrefixLength") == 0) + { + eldbus_message_iter_arguments_get(value, "y", &pd->ipv6.prefix_length); + DBG("prefix_length %hhu", pd->ipv6.prefix_length); + } + else if (strcmp(key, "Privacy") == 0) + { + const char *str; + eldbus_message_iter_arguments_get(value, "s", &str); + DBG("privacy %s (unused)", str); + } + else + { + WRN("Unsupported field %s (signature=%s)", key, eldbus_message_iter_signature_get(value)); + continue; + } + } + + return 0; +} + +static Eina_Error +_efl_net_session_notifier_update_bearers(Efl_Net_Session_Data *pd EINA_UNUSED, Eldbus_Message_Iter *var) +{ + Eldbus_Message_Iter *sub; + const char *str; + + eldbus_message_iter_arguments_get(var, "as", &sub); + while (eldbus_message_iter_get_and_next(sub, 's', &str)) + DBG("allowed bearer '%s'", str); + + return 0; +} + +static Eina_Error +_efl_net_session_notifier_update_connection_type(Efl_Net_Session_Data *pd EINA_UNUSED, Eldbus_Message_Iter *var) +{ + const char *str; + if (!eldbus_message_iter_arguments_get(var, "s", &str)) + return EINVAL; + + DBG("connection type '%s'", str); + return 0; +} + +/* step #4: get the initial state and changed applied locally */ +static Eldbus_Message * +_efl_net_session_notifier_update(const Eldbus_Service_Interface *service, const Eldbus_Message *msg) +{ + Eo *o = eldbus_service_object_data_get(service, "efl_net"); + Efl_Net_Session_Data *pd = efl_data_scope_get(o, MY_CLASS); + Eldbus_Message_Iter *array, *entry; + Eina_Bool updated = EINA_FALSE; + + DBG("Session %p is updated %s", o, eldbus_message_path_get(msg)); + + EINA_SAFETY_ON_NULL_GOTO(o, end); + + if (!eldbus_message_arguments_get(msg, "a{sv}", &array)) + { + ERR("Unexpected net.connman.Notifier.Update() signature %s", eldbus_message_signature_get(msg)); + goto end; + } + + while (eldbus_message_iter_get_and_next(array, 'e', &entry)) + { + const void *key; + Eldbus_Message_Iter *var; + Eina_Error err; + + if (!eldbus_message_iter_arguments_get(entry, "sv", &key, &var)) + { + ERR("Unexpected dict entry signature: %s", eldbus_message_iter_signature_get(entry)); + continue; + } + + if (strcmp(key, "State") == 0) + err = _efl_net_session_notifier_update_state(pd, var); + else if (strcmp(key, "Name") == 0) + err = _efl_net_session_notifier_update_name(pd, var); + else if (strcmp(key, "Bearer") == 0) + err = _efl_net_session_notifier_update_technology(pd, var); + else if (strcmp(key, "Interface") == 0) + err = _efl_net_session_notifier_update_interface(pd, var); + else if (strcmp(key, "IPv4") == 0) + err = _efl_net_session_notifier_update_ipv4(pd, var); + else if (strcmp(key, "IPv6") == 0) + err = _efl_net_session_notifier_update_ipv6(pd, var); + else if (strcmp(key, "AllowedBearers") == 0) + err = _efl_net_session_notifier_update_bearers(pd, var); + else if (strcmp(key, "ConnectionType") == 0) + err = _efl_net_session_notifier_update_connection_type(pd, var); + else + { + WRN("Unsupported setting %s (signature=%s)", key, eldbus_message_iter_signature_get(var)); + continue; + } + + if (err) + { + ERR("Could not handle property %s: %s", key, eina_error_msg_get(err)); + continue; + } + + updated |= EINA_TRUE; + } + + if (updated) efl_event_callback_call(o, EFL_NET_SESSION_EVENT_CHANGED, NULL); + + end: + return eldbus_message_method_return_new(msg); +} + +static const Eldbus_Service_Interface_Desc _efl_net_session_notifier_desc = { + .interface = "net.connman.Notification", + .methods = (const Eldbus_Method []){ + { + .member = "Release", + .cb = _efl_net_session_notifier_release, + }, + { + .member = "Update", + .in = ELDBUS_ARGS({"a{sv}", "settings"}), + .cb = _efl_net_session_notifier_update, + }, + { } + }, +}; + +/* return of step #2: session was created, get a proxy for it */ +static void +_efl_net_session_create_session_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending) +{ + Eo *o = data; + Efl_Net_Session_Data *pd = efl_data_scope_get(o, MY_CLASS); + Eldbus_Connection *conn; + Eldbus_Object *obj; + const char *err_name, *err_msg, *path; + + if (pd->mgr_pending == pending) + pd->mgr_pending = NULL; + + if (eldbus_message_error_get(msg, &err_name, &err_msg)) + { + ERR("Could not create session %p: %s=%s", o, err_name, err_msg); + return; + } + + if (!eldbus_message_arguments_get(msg, "o", &path)) + { + ERR("Could not get session %p DBus path!", o); + return; + } + + conn = efl_net_connman_connection_get(); + obj = eldbus_object_get(conn, "net.connman", path); + pd->proxy = eldbus_proxy_get(obj, "net.connman.Session"); + if (!pd->proxy) + { + ERR("could not create DBus proxy for interface='net.connman.Session', name='net.connman', path='%s' o=%p", path, o); + eldbus_object_unref(obj); + return; + } + + DBG("Created session %p (session=%s) with ConnMan...", o, path); + + if (pd->connect.connected) + { + DBG("apply pending connect request: online_required=%d, technologies_allowed=%#x", pd->connect.online_required, pd->connect.technologies_allowed); + _efl_net_session_connect_do(o, pd); + } +} + +static void +_efl_net_session_clear(Efl_Net_Session_Data *pd) +{ + eina_stringshare_replace(&pd->name, NULL); + eina_stringshare_replace(&pd->interface, NULL); + eina_stringshare_replace(&pd->ipv4.address, NULL); + eina_stringshare_replace(&pd->ipv4.netmask, NULL); + eina_stringshare_replace(&pd->ipv4.gateway, NULL); + eina_stringshare_replace(&pd->ipv6.address, NULL); + eina_stringshare_replace(&pd->ipv6.netmask, NULL); + eina_stringshare_replace(&pd->ipv6.gateway, NULL); + pd->ipv6.prefix_length = 0; + pd->state = EFL_NET_SESSION_STATE_OFFLINE; + pd->technology = EFL_NET_SESSION_TECHNOLOGY_UNKNOWN; +} + +/* step #2: once connman is there, call CreateSession */ +static void +_efl_net_session_connman_name_owner_changed(void *data, const char *bus, const char *old_id, const char *new_id) +{ + Eo *o = data; + Efl_Net_Session_Data *pd = efl_data_scope_get(o, MY_CLASS); + Eldbus_Message_Iter *msg_itr, *cont; + Eldbus_Proxy *mgr; + Eldbus_Message *msg; + const char *path; + + DBG("Name Owner Changed %s: %s->%s", bus, old_id, new_id); + if ((!new_id) || (new_id[0] == '\0')) + { + /* connman is gone, remove proxy as it became useless */ + if (pd->proxy) + { + Eldbus_Object *obj = eldbus_proxy_object_get(pd->proxy); + eldbus_proxy_unref(pd->proxy); + pd->proxy = NULL; + eldbus_object_unref(obj); + } + _efl_net_session_clear(pd); + efl_event_callback_call(o, EFL_NET_SESSION_EVENT_CHANGED, NULL); + return; + } + + path = eldbus_service_object_path_get(pd->notifier); + EINA_SAFETY_ON_NULL_RETURN(path); + + INF("Create session %p notifier=%s with %s (%s)", o, path, bus, new_id); + + mgr = efl_net_connman_manager_get(); + EINA_SAFETY_ON_NULL_RETURN(mgr); + + msg = eldbus_proxy_method_call_new(mgr, "CreateSession"); + EINA_SAFETY_ON_NULL_RETURN(msg); + + msg_itr = eldbus_message_iter_get(msg); + EINA_SAFETY_ON_NULL_GOTO(msg_itr, error_send); + + cont = eldbus_message_iter_container_new(msg_itr, 'a', "{sv}"); + eldbus_message_iter_container_close(msg_itr, cont); /* empty, use defaults */ + + eldbus_message_iter_basic_append(msg_itr, 'o', path); + + if (pd->mgr_pending) + eldbus_pending_cancel(pd->mgr_pending); + + pd->mgr_pending = eldbus_proxy_send(mgr, msg, _efl_net_session_create_session_cb, o, DEFAULT_TIMEOUT); + EINA_SAFETY_ON_NULL_GOTO(pd->mgr_pending, error_send); + return; + + error_send: + eldbus_message_unref(msg); +} + +EOLIAN static Eo * +_efl_net_session_efl_object_constructor(Eo *o, Efl_Net_Session_Data *pd EINA_UNUSED) +{ + if (!efl_net_connman_init()) + { + ERR("could not initialize connman infrastructure"); + return NULL; + } + + return efl_constructor(efl_super(o, MY_CLASS)); +} + +EOLIAN static Eo * +_efl_net_session_efl_object_finalize(Eo *o, Efl_Net_Session_Data *pd) +{ + Eldbus_Connection *conn; + char path[128]; + + o = efl_finalize(efl_super(o, MY_CLASS)); + if (!o) return NULL; + + conn = efl_net_connman_connection_get(); + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + + /* step #1: create local notifier path */ + snprintf(path, sizeof(path), "/connman/notifier_%p", o); + pd->notifier = eldbus_service_interface_register(conn, path, &_efl_net_session_notifier_desc); + EINA_SAFETY_ON_NULL_RETURN_VAL(pd->notifier, NULL); + eldbus_service_object_data_set(pd->notifier, "efl_net", o); + + eldbus_name_owner_changed_callback_add(conn, "net.connman", _efl_net_session_connman_name_owner_changed, o, EINA_TRUE); + DBG("waiting for net.connman to show on the DBus system bus..."); + + return o; +} + +EOLIAN static void +_efl_net_session_efl_object_destructor(Eo *o, Efl_Net_Session_Data *pd) +{ + Eldbus_Connection *conn; + + conn = efl_net_connman_connection_get(); + eldbus_name_owner_changed_callback_del(conn, "net.connman", _efl_net_session_connman_name_owner_changed, o); + + if (pd->mgr_pending) + { + eldbus_pending_cancel(pd->mgr_pending); + pd->mgr_pending = NULL; + } + + if (pd->notifier) + { + eldbus_service_object_data_del(pd->notifier, "efl_net"); + eldbus_service_object_unregister(pd->notifier); + pd->notifier = NULL; + } + + if (pd->proxy) + { + Eldbus_Object *obj = eldbus_proxy_object_get(pd->proxy); + Eldbus_Proxy *mgr = efl_net_connman_manager_get(); + + /* DestroySession is required since the manager proxy and bus + * may be alive, thus connman won't get any NameOwnerChanged + * or related to know the object is gone + */ + eldbus_proxy_call(mgr, "DestroySession", NULL, NULL, DEFAULT_TIMEOUT, + "o", eldbus_object_path_get(obj)); + + eldbus_proxy_unref(pd->proxy); + eldbus_object_unref(obj); + pd->proxy = NULL; + } + + _efl_net_session_clear(pd); + + efl_destructor(efl_super(o, MY_CLASS)); + efl_net_connman_shutdown(); +} + +EOLIAN static const char * +_efl_net_session_name_get(Eo *o EINA_UNUSED, Efl_Net_Session_Data *pd) +{ + return pd->name; +} + +EOLIAN static Efl_Net_Session_State +_efl_net_session_state_get(Eo *o EINA_UNUSED, Efl_Net_Session_Data *pd) +{ + return pd->state; +} + +EOLIAN static Efl_Net_Session_Technology +_efl_net_session_technology_get(Eo *o EINA_UNUSED, Efl_Net_Session_Data *pd) +{ + return pd->technology; +} + +EOLIAN static const char * +_efl_net_session_interface_get(Eo *o EINA_UNUSED, Efl_Net_Session_Data *pd) +{ + return pd->interface; +} + +EOLIAN static void +_efl_net_session_ipv4_get(Eo *o EINA_UNUSED, Efl_Net_Session_Data *pd, const char **address, const char **netmask, const char **gateway) +{ + if (address) *address = pd->ipv4.address; + if (netmask) *netmask = pd->ipv4.netmask; + if (gateway) *gateway = pd->ipv4.gateway; +} + +EOLIAN static void +_efl_net_session_ipv6_get(Eo *o EINA_UNUSED, Efl_Net_Session_Data *pd, const char **address, uint8_t *prefix_length, const char **netmask, const char **gateway) +{ + if (address) *address = pd->ipv6.address; + if (netmask) *netmask = pd->ipv6.netmask; + if (gateway) *gateway = pd->ipv6.gateway; + if (prefix_length) *prefix_length = pd->ipv6.prefix_length; +} + +static void +_efl_net_session_connect_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending) +{ + Eo *o = data; + Efl_Net_Session_Data *pd = efl_data_scope_get(o, MY_CLASS); + const char *err_name, *err_msg; + + if (pd->connect.pending == pending) + pd->connect.pending = NULL; + + if (eldbus_message_error_get(msg, &err_name, &err_msg)) + { + WRN("Could not Connect: %s=%s", err_name, err_msg); + return; + } + DBG("Successfully requested a connection online_required=%hhu, technologies_allowed=%#x", pd->connect.online_required, pd->connect.technologies_allowed); +} + +static void +_efl_net_session_connect_do_connect(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending) +{ + Eo *o = data; + Efl_Net_Session_Data *pd = efl_data_scope_get(o, MY_CLASS); + const char *err_name, *err_msg; + + if (pd->connect.pending == pending) + pd->connect.pending = NULL; + + if (eldbus_message_error_get(msg, &err_name, &err_msg)) + { + WRN("Could not Change('ConnectionType'): %s=%s", err_name, err_msg); + return; + } + + pd->connect.pending = eldbus_proxy_call(pd->proxy, "Connect", + _efl_net_session_connect_cb, o, + DEFAULT_TIMEOUT, ""); + EINA_SAFETY_ON_NULL_RETURN(pd->connect.pending); +} + +static void +_efl_net_session_connect_change_online_required(void *data, const Eldbus_Message *msg_ret, Eldbus_Pending *pending) +{ + Eo *o = data; + Efl_Net_Session_Data *pd = efl_data_scope_get(o, MY_CLASS); + Eldbus_Message *msg; + Eldbus_Message_Iter *msg_itr, *itr; + const char *err_name, *err_msg; + Eina_Bool ret; + + if (pd->connect.pending == pending) + pd->connect.pending = NULL; + + if (eldbus_message_error_get(msg_ret, &err_name, &err_msg)) + { + WRN("Could not Change('AllowedBearers'): %s=%s", err_name, err_msg); + return; + } + + msg = eldbus_proxy_method_call_new(pd->proxy, "Change"); + EINA_SAFETY_ON_NULL_RETURN(msg); + + msg_itr = eldbus_message_iter_get(msg); + EINA_SAFETY_ON_NULL_GOTO(msg_itr, error_msg); + + eldbus_message_iter_basic_append(msg_itr, 's', "ConnectionType"); + itr = eldbus_message_iter_container_new(msg_itr, 'v', "s"); + EINA_SAFETY_ON_NULL_GOTO(itr, error_msg); + + ret = eldbus_message_iter_basic_append(itr, 's', pd->connect.online_required ? "internet" : "local"); + EINA_SAFETY_ON_FALSE_GOTO(ret, error_msg); + + eldbus_message_iter_container_close(msg_itr, itr); + + pd->connect.pending = eldbus_proxy_send(pd->proxy, msg, + _efl_net_session_connect_do_connect, o, + DEFAULT_TIMEOUT); + EINA_SAFETY_ON_NULL_RETURN(pd->connect.pending); + return; + + error_msg: + eldbus_message_unref(msg); +} + +static void +_efl_net_session_connect_do(Eo *o, Efl_Net_Session_Data *pd) +{ + Eldbus_Message_Iter *msg_itr, *itr, *array; + Eldbus_Message *msg; + + if (!pd->connect.connected) return; + + if (pd->connect.pending) + { + eldbus_pending_cancel(pd->connect.pending); + pd->connect.pending = NULL; + } + + msg = eldbus_proxy_method_call_new(pd->proxy, "Change"); + EINA_SAFETY_ON_NULL_RETURN(msg); + + msg_itr = eldbus_message_iter_get(msg); + EINA_SAFETY_ON_NULL_GOTO(msg_itr, error_msg); + + eldbus_message_iter_basic_append(msg_itr, 's', "AllowedBearers"); + itr = eldbus_message_iter_container_new(msg_itr, 'v', "as"); + EINA_SAFETY_ON_NULL_GOTO(itr, error_msg); + + array = eldbus_message_iter_container_new(itr, 'a', "s"); + +#define MAP(X, s) \ + if ((pd->connect.technologies_allowed & EFL_NET_SESSION_TECHNOLOGY_ ## X) == EFL_NET_SESSION_TECHNOLOGY_ ## X) \ + eldbus_message_iter_basic_append(array, 's', s) + MAP(ALL, "*"); + if (pd->connect.technologies_allowed == EFL_NET_SESSION_TECHNOLOGY_ALL) goto end; + MAP(ETHERNET, "ethernet"); + MAP(WIFI, "wifi"); + MAP(BLUETOOTH, "bluetooth"); + MAP(CELLULAR, "cellular"); + MAP(VPN, "vpn"); + MAP(GADGET, "gadget"); +#undef MAP + + end: + eldbus_message_iter_container_close(itr, array); + eldbus_message_iter_container_close(msg_itr, itr); + + pd->connect.pending = eldbus_proxy_send(pd->proxy, msg, + _efl_net_session_connect_change_online_required, o, + DEFAULT_TIMEOUT); + EINA_SAFETY_ON_NULL_RETURN(pd->connect.pending); + return; + + error_msg: + eldbus_message_unref(msg); +} + +EOLIAN static void +_efl_net_session_connect(Eo *o, Efl_Net_Session_Data *pd, Eina_Bool online_required, Efl_Net_Session_Technology technologies_allowed) +{ + pd->connect.connected = EINA_TRUE; + pd->connect.online_required = online_required; + pd->connect.technologies_allowed = technologies_allowed; + + if (pd->proxy) _efl_net_session_connect_do(o, pd); +} + +EOLIAN static void +_efl_net_session_disconnect(Eo *o EINA_UNUSED, Efl_Net_Session_Data *pd) +{ + if (pd->connect.pending) + { + eldbus_pending_cancel(pd->connect.pending); + pd->connect.pending = NULL; + } + pd->connect.connected = EINA_FALSE; + + eldbus_proxy_call(pd->proxy, "Disconnect", NULL, NULL, DEFAULT_TIMEOUT, ""); +} + +#include "efl_net_session.eo.c" diff --git a/src/lib/ecore_con/efl_net_session.eo b/src/lib/ecore_con/efl_net_session.eo new file mode 100644 index 0000000000..db85815158 --- /dev/null +++ b/src/lib/ecore_con/efl_net_session.eo @@ -0,0 +1,137 @@ +enum Efl.Net.Session.State { + [[Provides the session connectivity state. + + @since 1.19 + ]] + offline, [[no access point is connected]] + local, [[at least one access point was connected and the internet connectio wasn't verified]] + online, [[at least one access point was connected and the internet was verified]] +} + +/* keep in sync with efl_net_control_technology.eo, comment what doesn't make sense */ +enum Efl.Net.Session.Technology { + [[Bitwise-able technologies to allow for a network session. + + @since 1.19 + ]] + unknown = 0, +/* system = (1 << 0), */ + ethernet = (1 << 1), + wifi = (1 << 2), + bluetooth = (1 << 3), + cellular = (1 << 4), +/* gps = (1 << 5), */ + vpn = (1 << 6), + gadget = (1 << 7), +/* p2p = (1 << 8), */ + all = (Efl.Net.Session.Technology.ethernet | Efl.Net.Session.Technology.wifi | Efl.Net.Session.Technology.bluetooth | Efl.Net.Session.Technology.cellular | Efl.Net.Session.Technology.vpn | Efl.Net.Session.Technology.gadget), +} + +class Efl.Net.Session (Efl.Loop_User) { + [[Used by application to request network connectivity. + + This API is targeted at applications that need access to the + network, specifying the allowed bearer technologies to connect + to the internet, as well as if just local networking is enough + or validated internet access is required. + + \@note the @.connect method is subject to backend policy. For + instance, ConnMan uses + https://github.com/aldebaran/connman/blob/master/doc/session-policy-format.txt + + @since 1.19 + ]] + events { + changed; [[Some properties were changed.]] + } + + methods { + connect { + [[Asks the session to be connected. + + This method doesn't need to be called if all an + application want is to monitor the connectivity state, + like a poller that will only try to access the + webservice when there is an existing connection without + triggering one. + + This method is subject to backend policy. For instance, + ConnMan uses + https://github.com/aldebaran/connman/blob/master/doc/session-policy-format.txt + ]] + params { + online_required: bool; [[if $false, access points with local state are enough. If $true, the access point must be in online state, with a validated internet connection]] + technologies_allowed: Efl.Net.Session.Technology; [[Bitwise OR of technologies to allow]] + } + } + + disconnect { + [[Indicates this session doesn't need a connection anymore. + + This reverses the effect of @.connect, let the system + disconnect if nothing else needs a connection. One may + still use the session object to monitor the connectivity + state via properties and "changed" event. + ]] + } + + @property name { + [[The user-friendly access point name.]] + get { } + values { + name: string; + } + } + + @property state { + [[If the session connectivity is offline, local or online.]] + get { } + values { + state: Efl.Net.Session.State; + } + } + + @property technology { + [[The access point technology that backs this session]] + get { } + values { + technology: Efl.Net.Session.Technology; + } + } + + @property interface { + [[The interface allows the application to assign the socket to a given device using SO_BINDTODEVICE]] + get { } + values { + interface: string; + } + } + + @property ipv4 { + [[IPv4 in use for this session.]] + get { } + values { + address: string; + netmask: string; + gateway: string; + } + } + + @property ipv6 { + [[IPv6 in use for this session.]] + get { } + values { + address: string; + prefix_length: uint8; + netmask: string; + gateway: string; + } + } + } + + implements { + Efl.Object.destructor; + Efl.Object.constructor; + Efl.Object.finalize; + } +}