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; + } +}