discomfitor pushed a commit to branch master. http://git.enlightenment.org/core/enlightenment.git/commit/?id=24fe43d735b26895025f2aa7706819d4a6663104
commit 24fe43d735b26895025f2aa7706819d4a6663104 Author: Mike Blumenkrantz <zm...@osg.samsung.com> Date: Wed Mar 2 16:58:27 2016 -0500 add new wireless module/gadget this is the successor to the connman module/gadget. it does not use or depend on econnman. --- configure.ac | 2 + src/bin/e_module.c | 1 + src/modules/Makefile.mk | 2 + src/modules/Makefile_wireless.mk | 27 + src/modules/wireless/connman.c | 1634 ++++++++++++++++++++++++++++ src/modules/wireless/e-module-wireless.edj | Bin 0 -> 21146 bytes src/modules/wireless/mod.c | 33 + src/modules/wireless/module.desktop.in | 8 + src/modules/wireless/wireless.c | 1476 +++++++++++++++++++++++++ src/modules/wireless/wireless.h | 134 +++ 10 files changed, 3317 insertions(+) diff --git a/configure.ac b/configure.ac index 917d652..7b286c6 100644 --- a/configure.ac +++ b/configure.ac @@ -924,6 +924,7 @@ AC_E_OPTIONAL_MODULE([wl_weekeyboard], $have_wayland, [CHECK_MODULE_WL_WEEKEYBOA AC_E_OPTIONAL_MODULE([policy_mobile], true) AC_E_OPTIONAL_MODULE([geolocation], true) AC_E_OPTIONAL_MODULE([xwayland], $have_wayland, [CHECK_MODULE_XWAYLAND]) +AC_E_OPTIONAL_MODULE([wireless], true) if test "x${HAVE_WL_X11}" != "xyes" && test "x${have_wayland}" = "xyes" && test "x${HAVE_XWAYLAND}" != "xyes"; then AC_DEFINE_UNQUOTED([HAVE_WAYLAND_ONLY],[1],[enable wayland-only version of enlightenment]) @@ -1125,6 +1126,7 @@ src/modules/tiling/module.desktop src/modules/music-control/module.desktop src/modules/packagekit/module.desktop src/modules/wl_desktop_shell/module.desktop +src/modules/wireless/module.desktop data/xsession/enlightenment.desktop data/etc/sysactions.conf data/units/enlightenment.service diff --git a/src/bin/e_module.c b/src/bin/e_module.c index c211b93..945788d 100644 --- a/src/bin/e_module.c +++ b/src/bin/e_module.c @@ -1025,6 +1025,7 @@ _e_module_whitelist_check(void) "temperature", "tiling", "winlist", + "wireless", "wizard", "wl_desktop_shell", "wl_x11", diff --git a/src/modules/Makefile.mk b/src/modules/Makefile.mk index f4d5f6a..bb618da 100644 --- a/src/modules/Makefile.mk +++ b/src/modules/Makefile.mk @@ -127,3 +127,5 @@ include src/modules/Makefile_wl_weekeyboard.mk include src/modules/Makefile_policy_mobile.mk include src/modules/Makefile_geolocation.mk + +include src/modules/Makefile_wireless.mk diff --git a/src/modules/Makefile_wireless.mk b/src/modules/Makefile_wireless.mk new file mode 100644 index 0000000..0d28d40 --- /dev/null +++ b/src/modules/Makefile_wireless.mk @@ -0,0 +1,27 @@ +EXTRA_DIST += \ +src/modules/wireless/module.desktop.in \ +src/modules/wireless/e-module-wireless.edj + +if USE_MODULE_WIRELESS +wirelessdir = $(MDIR)/wireless +wireless_DATA = \ +src/modules/wireless/module.desktop \ +src/modules/wireless/e-module-wireless.edj + +wirelesspkgdir = $(MDIR)/wireless/$(MODULE_ARCH) +wirelesspkg_LTLIBRARIES = src/modules/wireless/module.la + +src_modules_wireless_module_la_LDFLAGS = $(MOD_LDFLAGS) +src_modules_wireless_module_la_SOURCES = \ +src/modules/wireless/connman.c \ +src/modules/wireless/mod.c \ +src/modules/wireless/wireless.c \ +src/modules/wireless/wireless.h + +src_modules_wireless_module_la_CPPFLAGS = $(MOD_CPPFLAGS) +src_modules_wireless_module_la_LIBADD = $(MOD_LIBS) + +PHONIES += wireless install-wireless +wireless: $(wirelesspkg_LTLIBRARIES) $(wireless_DATA) +install-wireless: install-wirelessDATA install-wirelesspkgLTLIBRARIES +endif diff --git a/src/modules/wireless/connman.c b/src/modules/wireless/connman.c new file mode 100644 index 0000000..662e23a --- /dev/null +++ b/src/modules/wireless/connman.c @@ -0,0 +1,1634 @@ +#include "wireless.h" + +#define CONNMAN_BUS_NAME "net.connman" +#define CONNMAN_MANAGER_IFACE CONNMAN_BUS_NAME ".Manager" +#define CONNMAN_SERVICE_IFACE CONNMAN_BUS_NAME ".Service" +#define CONNMAN_TECHNOLOGY_IFACE CONNMAN_BUS_NAME ".Technology" +#define CONNMAN_TECHNOLOGY_PATH_ETHERNET "/net/connman/technology/ethernet" +#define CONNMAN_TECHNOLOGY_PATH_WIFI "/net/connman/technology/wifi" +#define CONNMAN_TECHNOLOGY_PATH_BT "/net/connman/technology/bluetooth" +#define CONNMAN_TECHNOLOGY_PATH_CELLULAR "/net/connman/technology/cellular" +#define CONNMAN_AGENT_IFACE "net.connman.Agent" +#define CONNMAN_AGENT_PATH "/org/enlightenment/wireless/agent" + +#define CONNMAN_SERVICE_TYPE_ITER(i) \ + for ((i) = 0; (i) < CONNMAN_SERVICE_TYPE_LAST; (i)++) + +#define MILLI_PER_SEC 1000 +#define CONNMAN_CONNECTION_TIMEOUT 60 * MILLI_PER_SEC + +#undef DBG +#undef INF +#undef WRN +#undef ERR + +#define DBG(...) EINA_LOG_DOM_DBG(_connman_log_dom, __VA_ARGS__) +#define INF(...) EINA_LOG_DOM_INFO(_connman_log_dom, __VA_ARGS__) +#define WRN(...) EINA_LOG_DOM_WARN(_connman_log_dom, __VA_ARGS__) +#define ERR(...) EINA_LOG_DOM_ERR(_connman_log_dom, __VA_ARGS__) + +typedef enum +{ + CONNMAN_STATE_NONE = -1, /* All unknown states */ + CONNMAN_STATE_OFFLINE, + CONNMAN_STATE_IDLE, + CONNMAN_STATE_ASSOCIATION, + CONNMAN_STATE_CONFIGURATION, + CONNMAN_STATE_READY, + CONNMAN_STATE_ONLINE, + CONNMAN_STATE_DISCONNECT, + CONNMAN_STATE_FAILURE, +} Connman_State; + +typedef enum +{ + CONNMAN_SERVICE_TYPE_NONE = -1, /* All non-supported types */ + CONNMAN_SERVICE_TYPE_ETHERNET = 0, + CONNMAN_SERVICE_TYPE_WIFI = 1, + CONNMAN_SERVICE_TYPE_BLUETOOTH = 2, + CONNMAN_SERVICE_TYPE_CELLULAR = 3, + CONNMAN_SERVICE_TYPE_LAST = 4, +} Connman_Service_Type; + +typedef struct Connman_Technology +{ + Connman_Service_Type type; + Eldbus_Proxy *proxy; + Eina_Stringshare *tethering_ssid; + Eina_Stringshare *tethering_passwd; + Eina_Bool powered : 1; + Eina_Bool connected : 1; + Eina_Bool tethering : 1; +} Connman_Technology; + +typedef struct +{ + EINA_INLIST; + Eldbus_Proxy *proxy; + + /* Private */ + struct + { + Eldbus_Pending *connect; + Eldbus_Pending *disconnect; + Eldbus_Pending *remov; + void *data; + } pending; + Eldbus_Signal_Handler *handler; + + /* Properties */ + Eina_Stringshare *path; + Eina_Stringshare *name; + Wireless_Network_Security security; + Connman_State state; + Connman_Service_Type type; + uint8_t strength; + + /* Connection */ + unsigned int method; + Eina_Stringshare *address; + Eina_Stringshare *gateway; + union + { + struct + { + Eina_Stringshare *netmask; + } v4; + struct + { + Eina_Stringshare *prefixlength; + Wireless_Network_IPv6_Privacy privacy; + } v6; + } ip; + + Eina_Array *domain_servers; + Eina_Array *name_servers; + Eina_Array *time_servers; + + /* Proxy */ + unsigned int proxy_type; + Eina_Stringshare *proxy_url; + Eina_Array *proxy_servers; + Eina_Array *proxy_excludes; + Eina_Bool ipv6 : 1; + Eina_Bool favorite : 1; +} Connman_Service; + +typedef enum +{ + CONNMAN_FIELD_STATE_MANDATORY, + CONNMAN_FIELD_STATE_OPTIONAL, + CONNMAN_FIELD_STATE_ALTERNATE, + CONNMAN_FIELD_STATE_INFO, +} Connman_Field_State; + +typedef struct Connman_Field +{ + const char *name; + + Connman_Field_State requirement; + const char *type; + const char *value; +} Connman_Field; + +static int _connman_log_dom = -1; + +static Eldbus_Proxy *proxy_manager; + +static Eldbus_Pending *pending_gettechnologies; +static Eldbus_Pending *pending_getservices; +static Eldbus_Pending *pending_getproperties_manager; + +static Eina_List *signal_handlers; + +static Eina_Inlist *connman_services_list[CONNMAN_SERVICE_TYPE_LAST]; +static Eina_Hash *connman_services[CONNMAN_SERVICE_TYPE_LAST]; +static Eldbus_Service_Interface *agent_iface; + +static Connman_Service *connman_current_service[CONNMAN_SERVICE_TYPE_LAST]; +static Wireless_Connection *connman_current_connection[CONNMAN_SERVICE_TYPE_LAST]; + +static Connman_Technology connman_technology[CONNMAN_SERVICE_TYPE_LAST]; + +/* connman -> wireless */ +static Eina_Hash *connman_services_map[CONNMAN_SERVICE_TYPE_LAST]; + +static inline Eina_Bool +_connman_service_is_connected(const Connman_Service *cs) +{ + return (cs->state >= CONNMAN_STATE_ASSOCIATION) && (cs->state <= CONNMAN_STATE_ONLINE); +} + +static void +_eldbus_proxy_del(Eldbus_Proxy *proxy) +{ + Eldbus_Object *obj; + + obj = eldbus_proxy_object_get(proxy); + eldbus_proxy_unref(proxy); + eldbus_object_unref(obj); +} + +static void +_connman_service_connect_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) +{ + Connman_Service *cs = data; + const char *error; + + /* FIXME */ + cs->pending.connect = NULL; + eldbus_message_error_get(msg, NULL, &error); +} + +static Eina_Bool +_connman_service_connect(Wireless_Network *wn) +{ + Connman_Service *cs; + + cs = eina_hash_find(connman_services[wn->type], wn->path); + EINA_SAFETY_ON_NULL_RETURN_VAL(cs, EINA_FALSE); + if (!cs->pending.connect) + cs->pending.connect = eldbus_proxy_call(cs->proxy, "Connect", + _connman_service_connect_cb, cs, + CONNMAN_CONNECTION_TIMEOUT, ""); + return !!cs->pending.connect; +} + +static void +_connman_update_technologies(void) +{ + Eina_Bool avail[CONNMAN_SERVICE_TYPE_LAST]; + int i; + + CONNMAN_SERVICE_TYPE_ITER(i) + avail[i] = connman_technology[i].type > -1; + wireless_service_type_available_set(avail); +} + +static void +_connman_update_enabled_technologies(void) +{ + Eina_Bool enabled[CONNMAN_SERVICE_TYPE_LAST]; + int i; + + CONNMAN_SERVICE_TYPE_ITER(i) + enabled[i] = connman_technology[i].powered; + wireless_service_type_enabled_set(enabled); +} + +static Wireless_Network_State +_connman_wifi_state_convert(Connman_State state) +{ + Wireless_Network_State wifi_state; + switch (state) + { + case CONNMAN_STATE_ASSOCIATION: + case CONNMAN_STATE_CONFIGURATION: + wifi_state = WIRELESS_NETWORK_STATE_CONFIGURING; + break; + case CONNMAN_STATE_READY: + wifi_state = WIRELESS_NETWORK_STATE_CONNECTED; + break; + case CONNMAN_STATE_ONLINE: + wifi_state = WIRELESS_NETWORK_STATE_ONLINE; + break; + case CONNMAN_STATE_FAILURE: + wifi_state = WIRELESS_NETWORK_STATE_FAILURE; + break; + case CONNMAN_STATE_NONE: + case CONNMAN_STATE_OFFLINE: + case CONNMAN_STATE_IDLE: + case CONNMAN_STATE_DISCONNECT: + default: + wifi_state = WIRELESS_NETWORK_STATE_NONE; + } + return wifi_state; +} + +static Wireless_Network * +_connman_service_convert(Connman_Service *cs) +{ + Wireless_Network *wn; + + wn = E_NEW(Wireless_Network, 1); + memcpy(wn, &cs->path, offsetof(Wireless_Network, connect_cb)); + wn->state = _connman_wifi_state_convert(cs->state); + wn->connect_cb = _connman_service_connect; + return wn; +} + +static void +_connman_update_current_network(Connman_Service *cs, Connman_Service_Type type) +{ + if (connman_current_service[type] != cs) + { + E_FREE(connman_current_connection[type]); + if (cs) + connman_current_connection[type] = E_NEW(Wireless_Connection, 1); + } + connman_current_service[type] = cs; + if (cs) + { + connman_current_connection[type]->wn = eina_hash_find(connman_services_map[type], &cs); + memcpy(&connman_current_connection[type]->method, + &cs->method, sizeof(Wireless_Connection) - sizeof(void*)); + } + else + connman_current_connection[type] = NULL; + wireless_wifi_current_networks_set(connman_current_connection); +} + +static void +_connman_update_networks(Connman_Service_Type type) +{ + Eina_Array *arr; + Connman_Service *cs; + Wireless_Network *wn; + Eina_Hash *map; + Connman_Service *services[CONNMAN_SERVICE_TYPE_LAST] = {NULL}; + + map = connman_services_map[type]; + connman_services_map[type] = eina_hash_pointer_new(free); + arr = eina_array_new(eina_hash_population(connman_services[type])); + EINA_INLIST_FOREACH(connman_services_list[type], cs) + { + wn = _connman_service_convert(cs); + eina_hash_add(connman_services_map[type], &cs, wn); + eina_array_push(arr, wn); + if (connman_current_service[type] && _connman_service_is_connected(cs)) + services[type] = cs; + } + memcpy(&connman_current_service, services, CONNMAN_SERVICE_TYPE_LAST * sizeof(void*)); + arr = wireless_networks_set(arr); + _connman_update_current_network(connman_current_service[type], type); + eina_hash_free(map); + eina_array_free(arr); +} + +static void +_connman_update_airplane_mode(Eina_Bool offline) +{ + wireless_airplane_mode_set(offline); +} + +static Connman_State +str_to_state(const char *s) +{ + if (!strcmp(s, "offline")) + return CONNMAN_STATE_OFFLINE; + if (!strcmp(s, "idle")) + return CONNMAN_STATE_IDLE; + if (!strcmp(s, "association")) + return CONNMAN_STATE_ASSOCIATION; + if (!strcmp(s, "configuration")) + return CONNMAN_STATE_CONFIGURATION; + if (!strcmp(s, "ready")) + return CONNMAN_STATE_READY; + if (!strcmp(s, "online")) + return CONNMAN_STATE_ONLINE; + if (!strcmp(s, "disconnect")) + return CONNMAN_STATE_DISCONNECT; + if (!strcmp(s, "failure")) + return CONNMAN_STATE_FAILURE; + + ERR("Unknown state %s", s); + return CONNMAN_STATE_NONE; +} + +static Connman_Service_Type +str_to_type(const char *s) +{ + if (!strcmp(s, "ethernet")) + return CONNMAN_SERVICE_TYPE_ETHERNET; + if (!strcmp(s, "wifi")) + return CONNMAN_SERVICE_TYPE_WIFI; + if (!strcmp(s, "bluetooth")) + return CONNMAN_SERVICE_TYPE_BLUETOOTH; + if (!strcmp(s, "cellular")) + return CONNMAN_SERVICE_TYPE_CELLULAR; + + DBG("Unknown type %s", s); + return CONNMAN_SERVICE_TYPE_NONE; +} + +static Wireless_Network_Security +str_to_security(const char *s) +{ + if (!strcmp(s, "none")) return WIRELESS_NETWORK_SECURITY_NONE; + if (!strcmp(s, "wep")) return WIRELESS_NETWORK_SECURITY_WEP; + if (!strcmp(s, "psk")) return WIRELESS_NETWORK_SECURITY_PSK; + if (!strcmp(s, "ieee8021x")) return WIRELESS_NETWORK_SECURITY_IEEE8021X; + if (!strcmp(s, "wps")) return WIRELESS_NETWORK_SECURITY_WPS; + CRI("UNKNOWN TYPE %s", s); + return WIRELESS_NETWORK_SECURITY_NONE; +} + +static void +_connman_service_free(Connman_Service *cs) +{ + if (!cs) return; + + if (cs->pending.connect) + { + eldbus_pending_cancel(cs->pending.connect); + free(cs->pending.data); + } + else if (cs->pending.disconnect) + { + eldbus_pending_cancel(cs->pending.disconnect); + free(cs->pending.data); + } + else if (cs->pending.remov) + { + eldbus_pending_cancel(cs->pending.remov); + free(cs->pending.data); + } + eina_stringshare_del(cs->address); + eina_stringshare_del(cs->gateway); + if (cs->ipv6) + eina_stringshare_del(cs->ip.v6.prefixlength); + else + eina_stringshare_del(cs->ip.v4.netmask); + + eina_stringshare_del(cs->proxy_url); + + array_clear(cs->domain_servers); + array_clear(cs->name_servers); + array_clear(cs->time_servers); + + array_clear(cs->proxy_servers); + array_clear(cs->proxy_excludes); + + eina_stringshare_del(cs->name); + eina_stringshare_del(cs->path); + eldbus_signal_handler_del(cs->handler); + DBG("service free %p || proxy %p", cs, cs->proxy); + _eldbus_proxy_del(cs->proxy); + connman_services_list[cs->type] = eina_inlist_remove(connman_services_list[cs->type], EINA_INLIST_GET(cs)); + + free(cs); +} + +static void +_connman_service_parse_stringarray(Eldbus_Message_Iter *value, Eina_Array **arr) +{ + Eldbus_Message_Iter *itr_array; + const char *s; + + EINA_SAFETY_ON_FALSE_RETURN(eldbus_message_iter_arguments_get(value, "as", + &itr_array)); + if (*arr) + { + while (eina_array_count(*arr)) + eina_stringshare_del(eina_array_pop(*arr)); + } + else + *arr = eina_array_new(1); + while (eldbus_message_iter_get_and_next(itr_array, 's', &s)) + eina_array_push(*arr, eina_stringshare_add(s)); +} + +static void +_connman_service_parse_prop_changed(Connman_Service *cs, const char *prop_name, Eldbus_Message_Iter *value) +{ + DBG("service %p %s prop %s", cs, cs->path, prop_name); + + if (!strcmp(prop_name, "State")) + { + const char *state; + EINA_SAFETY_ON_FALSE_RETURN(eldbus_message_iter_arguments_get(value, + "s", + &state)); + cs->state = str_to_state(state); + DBG("New state: %s %d", state, cs->state); + } + if (!strcmp(prop_name, "Favorite")) + { + Eina_Bool state; + EINA_SAFETY_ON_FALSE_RETURN(eldbus_message_iter_arguments_get(value, + "b", + &state)); + cs->favorite = !!state; + DBG("New favorite state: %d", cs->favorite); + } + else if (!strcmp(prop_name, "Name")) + { + const char *name; + EINA_SAFETY_ON_FALSE_RETURN(eldbus_message_iter_arguments_get(value, + "s", + &name)); + eina_stringshare_replace(&cs->name, name); + DBG("New name: %s", cs->name); + } + else if (!strcmp(prop_name, "Type")) + { + const char *type; + EINA_SAFETY_ON_FALSE_RETURN(eldbus_message_iter_arguments_get(value, + "s", + &type)); + cs->type = str_to_type(type); + DBG("New type: %s %d", type, cs->type); + } + else if (!strcmp(prop_name, "Strength")) + { + uint8_t strength; + EINA_SAFETY_ON_FALSE_RETURN(eldbus_message_iter_arguments_get(value, + "y", + &strength)); + cs->strength = strength; + DBG("New strength: %d", strength); + } + else if (!strcmp(prop_name, "Security")) + { + const char *s; + Eldbus_Message_Iter *itr_array; + + DBG("Old security: %u", cs->security); + cs->security = WIRELESS_NETWORK_SECURITY_NONE; + + EINA_SAFETY_ON_FALSE_RETURN(eldbus_message_iter_arguments_get(value, "as", + &itr_array)); + while (eldbus_message_iter_get_and_next(itr_array, 's', &s)) + cs->security |= str_to_security(s); + DBG("New security %u", cs->security); + } + else if (!strcmp(prop_name, "IPv4")) + { + Eldbus_Message_Iter *array, *dict; + + EINA_SAFETY_ON_FALSE_RETURN(eldbus_message_iter_arguments_get(value, "a{sv}", &array)); + while (eldbus_message_iter_get_and_next(array, 'e', &dict)) + { + Eldbus_Message_Iter *var; + const char *name, *val; + + EINA_SAFETY_ON_FALSE_RETURN(eldbus_message_iter_arguments_get(dict, "sv", &name, &var)); + if (!strcmp(name, "Method")) + { + cs->method = WIRELESS_NETWORK_IPV4_METHOD_OFF; + EINA_SAFETY_ON_FALSE_RETURN(eldbus_message_iter_arguments_get(var, "s", &val)); + if (!strcmp(val, "off")) + cs->method = WIRELESS_NETWORK_IPV4_METHOD_OFF; + else if (!strcmp(val, "dhcp")) + cs->method = WIRELESS_NETWORK_IPV4_METHOD_DHCP; + else if (!strcmp(val, "manual")) + cs->method = WIRELESS_NETWORK_IPV4_METHOD_MANUAL; + else if (!strcmp(val, "fixed")) + cs->method = WIRELESS_NETWORK_IPV4_METHOD_FIXED; + } + else if (!strcmp(name, "Address")) + { + EINA_SAFETY_ON_FALSE_RETURN(eldbus_message_iter_arguments_get(var, "s", &val)); + eina_stringshare_replace(&cs->address, val); + } + else if (!strcmp(name, "Netmask")) + { + EINA_SAFETY_ON_FALSE_RETURN(eldbus_message_iter_arguments_get(var, "s", &val)); + eina_stringshare_replace(&cs->ip.v4.netmask, val); + } + else if (!strcmp(name, "Gateway")) + { + EINA_SAFETY_ON_FALSE_RETURN(eldbus_message_iter_arguments_get(var, "s", &val)); + eina_stringshare_replace(&cs->gateway, val); + } + } + } + else if (!strcmp(prop_name, "IPv6")) + { + Eldbus_Message_Iter *array, *dict; + + EINA_SAFETY_ON_FALSE_RETURN(eldbus_message_iter_arguments_get(value, "a{sv}", &array)); + while (eldbus_message_iter_get_and_next(array, 'e', &dict)) + { + Eldbus_Message_Iter *var; + const char *name, *val; + + EINA_SAFETY_ON_FALSE_RETURN(eldbus_message_iter_arguments_get(dict, "sv", &name, &var)); + if (!strcmp(name, "Method")) + { + cs->method = WIRELESS_NETWORK_IPV6_METHOD_OFF; + EINA_SAFETY_ON_FALSE_RETURN(eldbus_message_iter_arguments_get(var, "s", &val)); + if (!strcmp(val, "auto")) + cs->method = WIRELESS_NETWORK_IPV6_METHOD_AUTO; + else if (!strcmp(val, "manual")) + cs->method = WIRELESS_NETWORK_IPV6_METHOD_MANUAL; + else if (!strcmp(val, "6to4")) + cs->method = WIRELESS_NETWORK_IPV6_METHOD_6TO4; + else if (!strcmp(val, "fixed")) + cs->method = WIRELESS_NETWORK_IPV6_METHOD_FIXED; + } + else if (!strcmp(name, "Privacy")) + { + cs->ip.v6.privacy = WIRELESS_NETWORK_IPV6_PRIVACY_DISABLED; + EINA_SAFETY_ON_FALSE_RETURN(eldbus_message_iter_arguments_get(var, "s", &val)); + if (!strcmp(val, "disabled")) + cs->ip.v6.privacy = WIRELESS_NETWORK_IPV6_PRIVACY_DISABLED; + else if (!strcmp(val, "enabled")) + cs->ip.v6.privacy = WIRELESS_NETWORK_IPV6_PRIVACY_ENABLED; + else if (!strcmp(val, "preferred")) + cs->ip.v6.privacy = WIRELESS_NETWORK_IPV6_PRIVACY_PREFERRED; + } + else if (!strcmp(name, "Address")) + { + EINA_SAFETY_ON_FALSE_RETURN(eldbus_message_iter_arguments_get(var, "s", &val)); + eina_stringshare_replace(&cs->address, val); + } + else if (!strcmp(name, "PrefixLength")) + { + EINA_SAFETY_ON_FALSE_RETURN(eldbus_message_iter_arguments_get(var, "s", &val)); + eina_stringshare_replace(&cs->ip.v6.prefixlength, val); + } + else if (!strcmp(name, "Gateway")) + { + EINA_SAFETY_ON_FALSE_RETURN(eldbus_message_iter_arguments_get(var, "s", &val)); + eina_stringshare_replace(&cs->gateway, val); + } + } + } + else if (!strcmp(prop_name, "Proxy")) + { + Eldbus_Message_Iter *array, *dict; + + EINA_SAFETY_ON_FALSE_RETURN(eldbus_message_iter_arguments_get(value, "a{sv}", &array)); + while (eldbus_message_iter_get_and_next(array, 'e', &dict)) + { + Eldbus_Message_Iter *var; + const char *name, *val; + + EINA_SAFETY_ON_FALSE_RETURN(eldbus_message_iter_arguments_get(dict, "sv", &name, &var)); + if (!strcmp(name, "Method")) + { + cs->proxy_type = WIRELESS_PROXY_TYPE_DIRECT; + EINA_SAFETY_ON_FALSE_RETURN(eldbus_message_iter_arguments_get(var, "s", &val)); + if (!strcmp(val, "manual")) + cs->proxy_type = WIRELESS_PROXY_TYPE_MANUAL; + else if (!strcmp(val, "auto")) + cs->proxy_type = WIRELESS_PROXY_TYPE_AUTO; + } + else if (!strcmp(name, "URL")) + { + EINA_SAFETY_ON_FALSE_RETURN(eldbus_message_iter_arguments_get(var, "s", &val)); + eina_stringshare_replace(&cs->proxy_url, val); + } + else if (!strcmp(name, "Servers")) + _connman_service_parse_stringarray(value, &cs->proxy_servers); + else if (!strcmp(name, "Excludes")) + _connman_service_parse_stringarray(value, &cs->proxy_excludes); + } + } + else if (!strcmp(prop_name, "Nameservers")) + _connman_service_parse_stringarray(value, &cs->name_servers); + else if (!strcmp(prop_name, "Timeservers")) + _connman_service_parse_stringarray(value, &cs->time_servers); + else if (!strcmp(prop_name, "Domains")) + _connman_service_parse_stringarray(value, &cs->domain_servers); +} + +static void +_connman_service_prop_dict_changed(Connman_Service *cs, Eldbus_Message_Iter *props) +{ + Eldbus_Message_Iter *dict; + + while (eldbus_message_iter_get_and_next(props, 'e', &dict)) + { + char *name; + Eldbus_Message_Iter *var; + + if (eldbus_message_iter_arguments_get(dict, "sv", &name, &var)) + _connman_service_parse_prop_changed(cs, name, var); + } + if (_connman_service_is_connected(cs)) + _connman_update_current_network(cs, cs->type); +} + +static void +_connman_service_property(void *data, const Eldbus_Message *msg) +{ + Connman_Service *cs = data; + Eldbus_Message_Iter *var; + const char *name; + + if (eldbus_message_arguments_get(msg, "sv", &name, &var)) + _connman_service_parse_prop_changed(cs, name, var); + if (_connman_service_is_connected(cs)) + _connman_update_current_network(cs, cs->type); +} + +static Connman_Service * +_connman_service_new(const char *path, Eldbus_Message_Iter *props) +{ + Connman_Service *cs; + Eldbus_Object *obj; + + cs = E_NEW(Connman_Service, 1); + cs->path = eina_stringshare_add(path); + + obj = eldbus_object_get(dbus_conn, CONNMAN_BUS_NAME, path); + cs->proxy = eldbus_proxy_get(obj, CONNMAN_SERVICE_IFACE); + cs->handler = eldbus_proxy_signal_handler_add(cs->proxy, "PropertyChanged", + _connman_service_property, cs); + + _connman_service_prop_dict_changed(cs, props); + connman_services_list[cs->type] = eina_inlist_append(connman_services_list[cs->type], EINA_INLIST_GET(cs)); + eina_hash_add(connman_services[cs->type], cs->path, cs); + DBG("Added service: %p %s || proxy %p", cs, path, cs->proxy); + return cs; +} + +static void +_connman_manager_agent_register(void *data EINA_UNUSED, const Eldbus_Message *msg EINA_UNUSED, Eldbus_Pending *pending EINA_UNUSED) +{ + /* FIXME: should this do something? */ +} + +static Eina_Bool +_connman_technology_parse_prop_changed(Connman_Technology *ct, const char *name, Eldbus_Message_Iter *value) +{ + Eina_Bool val; + const char *str; + Eina_Bool ret = EINA_FALSE; + + if (!strcmp(name, "Powered")) + { + eldbus_message_iter_arguments_get(value, "b", &val); + val = !!val; + if (val != ct->powered) ret = EINA_TRUE; + ct->powered = !!val; + } + else if (!strcmp(name, "Connected")) + { + eldbus_message_iter_arguments_get(value, "b", &val); + ct->connected = !!val; + } + else if (!strcmp(name, "Tethering")) + { + eldbus_message_iter_arguments_get(value, "b", &val); + ct->tethering = !!val; + } + else if (!strcmp(name, "TetheringIdentifier")) + { + eldbus_message_iter_arguments_get(value, "b", &str); + ct->tethering_ssid = eina_stringshare_add(str); + } + else if (!strcmp(name, "TetheringPassphrase")) + { + eldbus_message_iter_arguments_get(value, "b", &str); + ct->tethering_passwd = eina_stringshare_add(str); + } + return ret; +} + +static void +_connman_technology_event_property(void *data, const Eldbus_Message *msg) +{ + Eldbus_Message_Iter *var; + const char *name; + Connman_Technology *ct = NULL; + int i; + + CONNMAN_SERVICE_TYPE_ITER(i) + if (data == connman_technology[i].proxy) + { + ct = &connman_technology[i]; + break; + } + if (!ct) return; + + if (!eldbus_message_arguments_get(msg, "sv", &name, &var)) + ERR("Could not parse message %p", msg); + else if (_connman_technology_parse_prop_changed(ct, name, var)) + _connman_update_enabled_technologies(); +} + +static Eina_Bool +_connman_manager_parse_prop_changed(const char *name, Eldbus_Message_Iter *value) +{ + if (!strcmp(name, "State")) + { + const char *state; + + if (!eldbus_message_iter_arguments_get(value, "s", &state)) + return EINA_FALSE; + DBG("New state: %s", state); + //_connman_update_state(str_to_state(state)); + } + else if (!strcmp(name, "OfflineMode")) + { + Eina_Bool offline; + if (!eldbus_message_iter_arguments_get(value, "b", &offline)) + return EINA_FALSE; + _connman_update_airplane_mode(offline); + } + else + return EINA_FALSE; + + return EINA_TRUE; +} + +static void +_connman_manager_getproperties(void *data EINA_UNUSED, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) +{ + Eldbus_Message_Iter *array, *dict; + const char *name, *text; + + pending_getproperties_manager = NULL; + if (eldbus_message_error_get(msg, &name, &text)) + { + ERR("Could not get properties. %s: %s", name, text); + return; + } + + if (!eldbus_message_arguments_get(msg, "a{sv}", &array)) + { + ERR("Error getting arguments."); + return; + } + + while (eldbus_message_iter_get_and_next(array, 'e', &dict)) + { + const char *key; + Eldbus_Message_Iter *var; + + if (eldbus_message_iter_arguments_get(dict, "sv", &key, &var)) + _connman_manager_parse_prop_changed(key, var); + } +} + +static void +_connman_manager_getservices(void *data EINA_UNUSED, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) +{ + Eldbus_Message_Iter *array, *s; + const char *name, *text; + int i; + Eina_Bool update[CONNMAN_SERVICE_TYPE_LAST] = {0}; + + pending_getservices = NULL; + CONNMAN_SERVICE_TYPE_ITER(i) + eina_hash_free_buckets(connman_services[i]); + if (eldbus_message_error_get(msg, &name, &text)) + { + ERR("Could not get services. %s: %s", name, text); + return; + } + + if (!eldbus_message_arguments_get(msg, "a(oa{sv})", &array)) + { + ERR("Error getting array"); + return; + } + + while (eldbus_message_iter_get_and_next(array, 'r', &s)) + { + const char *path; + Eldbus_Message_Iter *inner_array; + Connman_Service *cs; + + if (!eldbus_message_iter_arguments_get(s, "oa{sv}", &path, &inner_array)) + continue; + + cs = _connman_service_new(path, inner_array); + update[cs->type] = 1; + } + CONNMAN_SERVICE_TYPE_ITER(i) + if (update[i]) _connman_update_networks(i); +} + +static void +_connman_manager_gettechnologies(void *data EINA_UNUSED, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) +{ + Eldbus_Message_Iter *array, *s; + const char *name, *text; + + pending_gettechnologies = NULL; + if (eldbus_message_error_get(msg, &name, &text)) + { + ERR("Could not get technologies. %s: %s", name, text); + return; + } + + if (!eldbus_message_arguments_get(msg, "a(oa{sv})", &array)) + { + ERR("Error getting array"); + return; + } + + while (eldbus_message_iter_get_and_next(array, 'r', &s)) + { + const char *path; + Eldbus_Message_Iter *inner_array, *dict; + Connman_Technology *ct = NULL; + Eldbus_Object *obj; + int i; + const char *paths[] = + { + CONNMAN_TECHNOLOGY_PATH_ETHERNET, + CONNMAN_TECHNOLOGY_PATH_WIFI, + CONNMAN_TECHNOLOGY_PATH_BT, + CONNMAN_TECHNOLOGY_PATH_CELLULAR, + }; + + if (!eldbus_message_iter_arguments_get(s, "oa{sv}", &path, &inner_array)) + continue; + CONNMAN_SERVICE_TYPE_ITER(i) + { + if (strcmp(path, paths[i])) continue; + ct = &connman_technology[i]; + ct->type = i; + + obj = eldbus_object_get(dbus_conn, CONNMAN_BUS_NAME, paths[i]); + ct->proxy = eldbus_proxy_get(obj, CONNMAN_TECHNOLOGY_IFACE); + signal_handlers = eina_list_append(signal_handlers, + eldbus_proxy_signal_handler_add(ct->proxy, "PropertyChanged", + _connman_technology_event_property, ct->proxy)); + } + if (!ct) + { + ERR("No handler for technology: %s", path); + continue; + } + while (eldbus_message_iter_get_and_next(inner_array, 'e', &dict)) + { + Eldbus_Message_Iter *var; + + if (eldbus_message_iter_arguments_get(dict, "sv", &name, &var)) + _connman_technology_parse_prop_changed(ct, name, var); + } + } + /* scan not supported on bluetooth */ + if (connman_technology[CONNMAN_SERVICE_TYPE_BLUETOOTH].proxy) + pending_getservices = eldbus_proxy_call(proxy_manager, "GetServices", _connman_manager_getservices, + NULL, -1, ""); + else if (connman_technology[CONNMAN_SERVICE_TYPE_WIFI].proxy) + eldbus_proxy_call(connman_technology[CONNMAN_SERVICE_TYPE_WIFI].proxy, "Scan", NULL, NULL, -1, ""); + _connman_update_technologies(); + _connman_update_enabled_technologies(); +} + +static void +_connman_manager_event_services(void *data EINA_UNUSED, const Eldbus_Message *msg) +{ + Eldbus_Message_Iter *changed, *removed, *s; + const char *path; + int i; + Eina_Bool update[CONNMAN_SERVICE_TYPE_LAST] = {0}; + + if (pending_getservices) return; + + if (!eldbus_message_arguments_get(msg, "a(oa{sv})ao", &changed, &removed)) + { + ERR("Error getting arguments"); + return; + } + + while (eldbus_message_iter_get_and_next(removed, 'o', &path)) + { + CONNMAN_SERVICE_TYPE_ITER(i) + { + if (!eina_hash_del_by_key(connman_services[i], path)) continue; + DBG("Removed service: %s", path); + update[i] = 1; + break; + } + } + + while (eldbus_message_iter_get_and_next(changed, 'r', &s)) + { + Connman_Service *cs; + Eldbus_Message_Iter *array; + Eina_Bool found = EINA_FALSE; + + if (!eldbus_message_iter_arguments_get(s, "oa{sv}", &path, &array)) + continue; + + CONNMAN_SERVICE_TYPE_ITER(i) + { + cs = eina_hash_find(connman_services[i], path); + if (!cs) continue; + _connman_service_prop_dict_changed(cs, array); + found = update[cs->type] = 1; + DBG("Changed service: %p %s", cs, path); + break; + } + if (!found) + { + cs = _connman_service_new(path, array); + update[cs->type] = 1; + } + } + CONNMAN_SERVICE_TYPE_ITER(i) + if (update[i]) _connman_update_networks(i); +} + +static void +_connman_manager_event_property(void *data EINA_UNUSED, const Eldbus_Message *msg) +{ + Eldbus_Message_Iter *var; + const char *name; + + if (pending_getproperties_manager) return; + if (!eldbus_message_arguments_get(msg, "sv", &name, &var)) + { + ERR("Could not parse message %p", msg); + return; + } + + _connman_manager_parse_prop_changed(name, var); +} + +static Eldbus_Message * +_connman_agent_release(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg) +{ + DBG("Agent released"); + wireless_authenticate_cancel(); + return eldbus_message_method_return_new(msg); +} + +static Eldbus_Message * +_connman_agent_report_error(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg EINA_UNUSED) +{ +#warning FIXME + return NULL; +} + +static Eldbus_Message * +_connman_agent_request_browser(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg) +{ + const char *path, *url; + int i; + Connman_Service *cs; + Wireless_Network *wn; + + if (!eldbus_message_arguments_get(msg, "ss", &path, &url)) + { + ERR("Could not parse message %p", msg); + return NULL; + } + + CONNMAN_SERVICE_TYPE_ITER(i) + { + cs = eina_hash_find(connman_services[i], path); + if (cs) break; + } + if (!cs) return NULL; + wn = eina_hash_find(connman_services_map[i], &cs); + EINA_SAFETY_ON_NULL_RETURN_VAL(wn, NULL); + wireless_authenticate_external(wn, url); + return NULL; +} + +static Eina_Bool +_connman_field_parse_value(Connman_Field *field, const char *key, Eldbus_Message_Iter *value, const char *signature) +{ + if (!strcmp(key, "Type")) + { + EINA_SAFETY_ON_FALSE_RETURN_VAL(signature[0] == 's', EINA_FALSE); + eldbus_message_iter_basic_get(value, &field->type); + return EINA_TRUE; + } + + if (!strcmp(key, "Requirement")) + { + const char *req; + const char *types[] = + { + [CONNMAN_FIELD_STATE_MANDATORY] = "mandatory", + [CONNMAN_FIELD_STATE_OPTIONAL] = "optional", + [CONNMAN_FIELD_STATE_ALTERNATE] = "alternate", + [CONNMAN_FIELD_STATE_INFO] = "informational", + }; + int i; + EINA_SAFETY_ON_FALSE_RETURN_VAL(signature[0] == 's', EINA_FALSE); + eldbus_message_iter_basic_get(value, &req); + for (i = 0; i <= CONNMAN_FIELD_STATE_INFO; i++) + if (!strcmp(req, types[i])) + { + field->requirement = i; + break; + } + return EINA_TRUE; + } + + if (!strcmp(key, "Alternates")) + { + EINA_SAFETY_ON_FALSE_RETURN_VAL(signature[0] == 'a', EINA_FALSE); + /* ignore alternates */ + return EINA_TRUE; + } + + if (!strcmp(key, "Value")) + { + EINA_SAFETY_ON_FALSE_RETURN_VAL(signature[0] == 's', EINA_FALSE); + eldbus_message_iter_basic_get(value, &field->value); + return EINA_TRUE; + } + + DBG("Ignored unknown argument: %s", key); + return EINA_FALSE; +} + +static Eina_Bool +_connman_field_parse(Connman_Field *field, Eldbus_Message_Iter *value, const char *signature EINA_UNUSED) +{ + Eldbus_Message_Iter *array, *dict; + + eldbus_message_iter_arguments_get(value, "a{sv}", &array); + EINA_SAFETY_ON_NULL_RETURN_VAL(array, EINA_FALSE); + + while (eldbus_message_iter_get_and_next(array, 'e', &dict)) + { + Eldbus_Message_Iter *var; + const char *key; + char *sig2; + + if (!eldbus_message_iter_arguments_get(dict, "sv", &key, &var)) + return EINA_FALSE; + sig2 = eldbus_message_iter_signature_get(var); + if (!sig2) + return EINA_FALSE; + + if (!_connman_field_parse_value(field, key, var, sig2)) + { + free(sig2); + return EINA_FALSE; + } + free(sig2); + } + + return EINA_TRUE; +} + +static void +_connman_dbus_dict_append_array(Eldbus_Message_Iter *array, const char *key, Eina_Array *val) +{ + Eldbus_Message_Iter *dict, *variant, *array2; + Eina_Stringshare *str; + unsigned int i; + Eina_Array_Iterator it; + + eldbus_message_iter_arguments_append(array, "{sv}", &dict); + eldbus_message_iter_basic_append(dict, 's', key); + variant = eldbus_message_iter_container_new(dict, 'v', "as"); + array2 = eldbus_message_iter_container_new(variant, 'a', "s"); + EINA_ARRAY_ITER_NEXT(val, i, str, it) + eldbus_message_iter_basic_append(array2, 's', str ?: ""); + eldbus_message_iter_container_close(variant, array2); + eldbus_message_iter_container_close(dict, variant); + eldbus_message_iter_container_close(array, dict); +} + +static void +_connman_dbus_dict_append_string(Eldbus_Message_Iter *array, const char *key, const char *val) +{ + Eldbus_Message_Iter *dict, *variant; + + eldbus_message_iter_arguments_append(array, "{sv}", &dict); + eldbus_message_iter_basic_append(dict, 's', key); + variant = eldbus_message_iter_container_new(dict, 'v', "s"); + eldbus_message_iter_basic_append(variant, 's', val ?: ""); + eldbus_message_iter_container_close(dict, variant); + eldbus_message_iter_container_close(array, dict); +} + +static void +_connman_dbus_dict_append_bool(Eldbus_Message_Iter *array, const char *key, Eina_Bool val) +{ + Eldbus_Message_Iter *dict, *variant; + + eldbus_message_iter_arguments_append(array, "{sv}", &dict); + eldbus_message_iter_basic_append(dict, 's', key); + variant = eldbus_message_iter_container_new(dict, 'v', "b"); + eldbus_message_iter_basic_append(variant, 'b', !!val); + eldbus_message_iter_container_close(dict, variant); + eldbus_message_iter_container_close(array, dict); +} + +static void +_connman_agent_auth_send(void *data, const Eina_Array *fields) +{ + Eldbus_Message *reply; + Eldbus_Message_Iter *iter, *array; + const char *f, *fprev; + unsigned int i; + Eina_Array_Iterator it; + + if (!fields) + { + reply = eldbus_message_error_new(data, + "net.connman.Agent.Error.Canceled", + "User canceled dialog"); + eldbus_connection_send(dbus_conn, reply, NULL, NULL, -1); + return; + } + reply = eldbus_message_method_return_new(data); + iter = eldbus_message_iter_get(reply); + eldbus_message_iter_arguments_append(iter, "a{sv}", &array); + + EINA_ARRAY_ITER_NEXT(fields, i, f, it) + { + if (i % 2) + _connman_dbus_dict_append_string(array, fprev, f); + else + fprev = f; + } + eldbus_message_iter_container_close(iter, array); + + eldbus_connection_send(dbus_conn, reply, NULL, NULL, -1); +} + +static Eldbus_Message * +_connman_agent_request_input(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg) +{ + Eldbus_Message_Iter *array, *dict; + const char *path; + Eina_Array *arr = NULL; + + if (!eldbus_message_arguments_get(msg, "oa{sv}", &path, &array)) + return eldbus_message_method_return_new(msg); + + /* FIXME: WISPr - net.connman.Agent.Error.LaunchBrowser */ + while (eldbus_message_iter_get_and_next(array, 'e', &dict)) + { + Eldbus_Message_Iter *var; + char *signature; + Connman_Field field = { NULL }; + + if (!eldbus_message_iter_arguments_get(dict, "sv", &field.name, &var)) + goto err; + signature = eldbus_message_iter_signature_get(var); + if (!signature) goto err; + + if (!_connman_field_parse(&field, var, signature)) + { + free(signature); + goto err; + } + free(signature); + + DBG("AGENT Got field:\n" + "\tName: %s\n" + "\tType: %s\n" + "\tRequirement: %d\n" + "\tAlternates: (omit array)\n" + "\tValue: %s", + field.name, field.type, field.requirement, field.value); + + if (field.requirement != CONNMAN_FIELD_STATE_MANDATORY) continue; + if (!arr) arr = eina_array_new(1); + eina_array_push(arr, eina_stringshare_add(field.name)); + } + wireless_authenticate(arr, _connman_agent_auth_send, eldbus_message_ref((Eldbus_Message *)msg)); + array_clear(arr); + return NULL; + +err: + eina_array_free(arr); + WRN("Failed to parse msg"); + return eldbus_message_method_return_new(msg); +} + +static Eldbus_Message * +_connman_agent_cancel(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg) +{ + Eldbus_Message *reply = eldbus_message_method_return_new(msg); + + DBG("Agent canceled"); + wireless_authenticate_cancel(); + + return reply; +} + +static const Eldbus_Method methods[] = { + { "Release", NULL, NULL, _connman_agent_release, 0 }, + { + "ReportError", ELDBUS_ARGS({"o", "service"}, {"s", "error"}), NULL, + _connman_agent_report_error, 0 + }, + //{ + //"ReportPeerError", ELDBUS_ARGS({"o", "peer"}, {"s", "error"}), NULL, + //_connman_agent_report_peer_error, 0 + //}, + { + "RequestBrowser", ELDBUS_ARGS({"o", "service"}, {"s", "url"}), NULL, + _connman_agent_request_browser, 0 + }, + { + "RequestInput", ELDBUS_ARGS({"o", "service"}, {"a{sv}", "fields"}), + ELDBUS_ARGS({"a{sv}", ""}), _connman_agent_request_input, 0 + }, + //{ + //"RequestPeerAuthorization", ELDBUS_ARGS({"o", "peer"}, {"a{sv}", "fields"}), + //ELDBUS_ARGS({"a{sv}", ""}), _connman_agent_request_peer_auth, 0 + //}, + { "Cancel", NULL, NULL, _connman_agent_cancel, 0 }, + { NULL, NULL, NULL, NULL, 0 } +}; + +static const Eldbus_Service_Interface_Desc desc = { + CONNMAN_AGENT_IFACE, methods, NULL, NULL, NULL, NULL +}; + +static void +_connman_start(void) +{ + Eldbus_Object *obj; + int i; + + CONNMAN_SERVICE_TYPE_ITER(i) + connman_services[i] = eina_hash_string_superfast_new((Eina_Free_Cb)_connman_service_free); + + obj = eldbus_object_get(dbus_conn, CONNMAN_BUS_NAME, "/"); + proxy_manager = eldbus_proxy_get(obj, CONNMAN_MANAGER_IFACE); + + signal_handlers = eina_list_append(signal_handlers, + eldbus_proxy_signal_handler_add(proxy_manager, "PropertyChanged", + _connman_manager_event_property, NULL)); + signal_handlers = eina_list_append(signal_handlers, + eldbus_proxy_signal_handler_add(proxy_manager, "ServicesChanged", + _connman_manager_event_services, NULL)); + + pending_gettechnologies = eldbus_proxy_call(proxy_manager, "GetTechnologies", _connman_manager_gettechnologies, + NULL, -1, ""); + pending_getproperties_manager = eldbus_proxy_call(proxy_manager, "GetProperties", _connman_manager_getproperties, + NULL, -1, ""); + + agent_iface = eldbus_service_interface_register(dbus_conn, CONNMAN_AGENT_PATH, &desc); + eldbus_proxy_call(proxy_manager, "RegisterAgent", + _connman_manager_agent_register, NULL, -1, "o", CONNMAN_AGENT_PATH); +} + +static void +_connman_end(void) +{ + int i; + + if (!proxy_manager) return; + eldbus_proxy_call(proxy_manager, "UnregisterAgent", NULL, NULL, -1, "o", CONNMAN_AGENT_PATH); + + CONNMAN_SERVICE_TYPE_ITER(i) + { + E_FREE_FUNC(connman_services[i], eina_hash_free); + if (!connman_technology[i].proxy) continue; + E_FREE_FUNC(connman_technology[i].proxy, _eldbus_proxy_del); + } + E_FREE_FUNC(pending_getservices, eldbus_pending_cancel); + E_FREE_FUNC(pending_getproperties_manager, eldbus_pending_cancel); + signal_handlers = eina_list_free(signal_handlers); + + E_FREE_FUNC(proxy_manager, _eldbus_proxy_del); + E_FREE_FUNC(agent_iface, eldbus_service_object_unregister); +} + +static void +_connman_name_owner_changed(void *data EINA_UNUSED, const char *bus EINA_UNUSED, const char *from EINA_UNUSED, const char *to) +{ + if (to[0]) + _connman_start(); + else + _connman_end(); +} + +EINTERN void +connman_init(void) +{ + int i; + + if (_connman_log_dom > -1) return; + CONNMAN_SERVICE_TYPE_ITER(i) + connman_technology[i].type = -1; + eldbus_name_owner_changed_callback_add(dbus_conn, CONNMAN_BUS_NAME, + _connman_name_owner_changed, + NULL, EINA_TRUE); + _connman_log_dom = eina_log_domain_register("wireless.connman", EINA_COLOR_ORANGE); +} + +EINTERN void +connman_shutdown(void) +{ + int i; + CONNMAN_SERVICE_TYPE_ITER(i) + { + E_FREE_FUNC(connman_services_map[i], eina_hash_free); + E_FREE(connman_current_connection[i]); + connman_current_service[i] = NULL; + } + _connman_end(); + eldbus_name_owner_changed_callback_del(dbus_conn, CONNMAN_BUS_NAME, _connman_name_owner_changed, NULL); + eina_log_domain_unregister(_connman_log_dom); + _connman_log_dom = -1; +} + +EINTERN void +connman_technology_enabled_set(Wireless_Service_Type type, Eina_Bool state) +{ + Eldbus_Message_Iter *main_iter, *var; + Eldbus_Message *msg; + + EINA_SAFETY_ON_NULL_RETURN(connman_technology[type].proxy); + msg = eldbus_proxy_method_call_new(connman_technology[type].proxy, "SetProperty"); + main_iter = eldbus_message_iter_get(msg); + eldbus_message_iter_basic_append(main_iter, 's', "Powered"); + var = eldbus_message_iter_container_new(main_iter, 'v', "b"); + eldbus_message_iter_basic_append(var, 'b', state); + eldbus_message_iter_container_close(main_iter, var); + + eldbus_proxy_send(connman_technology[type].proxy, msg, NULL, NULL, -1); +} + +static void +_connman_service_edit_timeservers_cb(void *data EINA_UNUSED, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) +{ + const char *name, *text; + /* FIXME */ + if (eldbus_message_error_get(msg, &name, &text)) + { + ERR("Could not set properties. %s: %s", name, text); + } +} + +static void +_connman_service_edit_nameservers_cb(void *data EINA_UNUSED, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) +{ + const char *name, *text; + /* FIXME */ + if (eldbus_message_error_get(msg, &name, &text)) + { + ERR("Could not set properties. %s: %s", name, text); + } +} + +static void +_connman_service_edit_domains_cb(void *data EINA_UNUSED, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) +{ + const char *name, *text; + /* FIXME */ + if (eldbus_message_error_get(msg, &name, &text)) + { + ERR("Could not set properties. %s: %s", name, text); + } +} + +static void +_connman_service_edit_proxy_cb(void *data EINA_UNUSED, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) +{ + const char *name, *text; + /* FIXME */ + if (eldbus_message_error_get(msg, &name, &text)) + { + ERR("Could not set properties. %s: %s", name, text); + } +} + +static void +_connman_service_edit_cb(void *data EINA_UNUSED, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) +{ + const char *name, *text; + /* FIXME */ + if (eldbus_message_error_get(msg, &name, &text)) + { + ERR("Could not set properties. %s: %s", name, text); + } +} + +static void +_connman_service_remove_cb(void *data EINA_UNUSED, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) +{ + const char *name, *text; + + if (eldbus_message_error_get(msg, &name, &text)) + ERR("Could not remove service. %s: %s", name, text); +} + +EINTERN void +connman_service_edit(const char *path, Wireless_Connection *wc) +{ + int i; + Eldbus_Message *msg; + Eldbus_Message_Iter *iter, *variant, *array; + Connman_Service *cs = NULL; + const char *prop[] = + { + "IPv4.Configuration", + "IPv6.Configuration", + }; + const char *method[] = + { + "off", + "manual", + "dhcp", + "fixed", + }; + + CONNMAN_SERVICE_TYPE_ITER(i) + { + cs = eina_hash_find(connman_services[i], path); + if (cs) break; + } + EINA_SAFETY_ON_NULL_RETURN(cs); + + msg = eldbus_proxy_method_call_new(cs->proxy, "SetProperty"); + iter = eldbus_message_iter_get(msg); + eldbus_message_iter_basic_append(iter, 's', prop[cs->ipv6]); + variant = eldbus_message_iter_container_new(iter, 'v', "a{sv}"); + eldbus_message_iter_arguments_append(variant, "a{sv}", &array); + _connman_dbus_dict_append_string(array, "Method", method[wc->method]); + _connman_dbus_dict_append_string(array, "Address", wc->address); + _connman_dbus_dict_append_string(array, "Gateway", wc->gateway); + if (wc->ipv6) + { + const char *privacy[] = + { + [WIRELESS_NETWORK_IPV6_PRIVACY_DISABLED] = "disabled", + [WIRELESS_NETWORK_IPV6_PRIVACY_ENABLED] = "enabled", + [WIRELESS_NETWORK_IPV6_PRIVACY_PREFERRED] = "preferred", + }; + _connman_dbus_dict_append_string(array, "PrefixLength", wc->ip.v6.prefixlength); + if (wc->method == WIRELESS_NETWORK_IPV6_METHOD_AUTO) + _connman_dbus_dict_append_string(array, "Privacy", privacy[wc->ip.v6.privacy]); + } + else + _connman_dbus_dict_append_string(array, "Netmask", wc->ip.v4.netmask); + eldbus_message_iter_container_close(variant, array); + eldbus_message_iter_container_close(iter, variant); + + eldbus_proxy_send(cs->proxy, msg, _connman_service_edit_cb, NULL, -1); +} + +EINTERN void +connman_service_edit_proxy(const char *path, Wireless_Connection *wc) +{ + int i; + Eldbus_Message *msg; + Eldbus_Message_Iter *iter, *variant, *array; + Connman_Service *cs = NULL; + const char *method[] = + { + "direct", + "manual", + "auto", + }; + + CONNMAN_SERVICE_TYPE_ITER(i) + { + cs = eina_hash_find(connman_services[i], path); + if (cs) break; + } + EINA_SAFETY_ON_NULL_RETURN(cs); + + msg = eldbus_proxy_method_call_new(cs->proxy, "SetProperty"); + iter = eldbus_message_iter_get(msg); + eldbus_message_iter_basic_append(iter, 's', "Proxy"); + variant = eldbus_message_iter_container_new(iter, 'v', "a{sv}"); + eldbus_message_iter_arguments_append(variant, "a{sv}", &array); + _connman_dbus_dict_append_string(array, "Method", method[wc->proxy_type]); + switch (wc->proxy_type) + { + case WIRELESS_PROXY_TYPE_DIRECT: break; + case WIRELESS_PROXY_TYPE_MANUAL: + _connman_dbus_dict_append_array(array, "Servers", wc->proxy_servers); + _connman_dbus_dict_append_array(array, "Excludes", wc->proxy_excludes); + break; + case WIRELESS_PROXY_TYPE_AUTO: + _connman_dbus_dict_append_string(array, "Address", wc->proxy_url); + break; + } + eldbus_message_iter_container_close(variant, array); + eldbus_message_iter_container_close(iter, variant); + + eldbus_proxy_send(cs->proxy, msg, _connman_service_edit_proxy_cb, NULL, -1); +} + +EINTERN void +connman_service_edit_domains(const char *path, Wireless_Connection *wc) +{ + int i; + Eldbus_Message *msg; + Eldbus_Message_Iter *iter; + Connman_Service *cs = NULL; + + CONNMAN_SERVICE_TYPE_ITER(i) + { + cs = eina_hash_find(connman_services[i], path); + if (cs) break; + } + EINA_SAFETY_ON_NULL_RETURN(cs); + + msg = eldbus_proxy_method_call_new(cs->proxy, "SetProperty"); + iter = eldbus_message_iter_get(msg); + _connman_dbus_dict_append_array(iter, "Domains.Configuration", wc->domain_servers); + + eldbus_proxy_send(cs->proxy, msg, _connman_service_edit_domains_cb, NULL, -1); +} + +EINTERN void +connman_service_edit_nameservers(const char *path, Wireless_Connection *wc) +{ + int i; + Eldbus_Message *msg; + Eldbus_Message_Iter *iter; + Connman_Service *cs = NULL; + + CONNMAN_SERVICE_TYPE_ITER(i) + { + cs = eina_hash_find(connman_services[i], path); + if (cs) break; + } + EINA_SAFETY_ON_NULL_RETURN(cs); + + msg = eldbus_proxy_method_call_new(cs->proxy, "SetProperty"); + iter = eldbus_message_iter_get(msg); + _connman_dbus_dict_append_array(iter, "Nameservers.Configuration", wc->name_servers); + + eldbus_proxy_send(cs->proxy, msg, _connman_service_edit_nameservers_cb, NULL, -1); +} + +EINTERN void +connman_service_edit_timeservers(const char *path, Wireless_Connection *wc) +{ + int i; + Eldbus_Message *msg; + Eldbus_Message_Iter *iter; + Connman_Service *cs = NULL; + + CONNMAN_SERVICE_TYPE_ITER(i) + { + cs = eina_hash_find(connman_services[i], path); + if (cs) break; + } + EINA_SAFETY_ON_NULL_RETURN(cs); + + msg = eldbus_proxy_method_call_new(cs->proxy, "SetProperty"); + iter = eldbus_message_iter_get(msg); + _connman_dbus_dict_append_array(iter, "Timeservers.Configuration", wc->time_servers); + + eldbus_proxy_send(cs->proxy, msg, _connman_service_edit_timeservers_cb, NULL, -1); +} + +EINTERN void +connman_service_remove(const char *path) +{ + int i; + Eldbus_Message *msg; + Connman_Service *cs = NULL; + + CONNMAN_SERVICE_TYPE_ITER(i) + { + cs = eina_hash_find(connman_services[i], path); + if (cs) break; + } + EINA_SAFETY_ON_NULL_RETURN(cs); + + msg = eldbus_proxy_method_call_new(cs->proxy, "Remove"); + eldbus_proxy_send(cs->proxy, msg, _connman_service_remove_cb, NULL, -1); +} + +EINTERN void +connman_airplane_mode_set(Eina_Bool set) +{ + Eldbus_Message *msg; + Eldbus_Message_Iter *iter; + + msg = eldbus_proxy_method_call_new(proxy_manager, "SetProperty"); + iter = eldbus_message_iter_get(msg); + _connman_dbus_dict_append_bool(iter, "OfflineMode", set); + eldbus_proxy_send(proxy_manager, msg, NULL, NULL, -1); +} diff --git a/src/modules/wireless/e-module-wireless.edj b/src/modules/wireless/e-module-wireless.edj new file mode 100644 index 0000000..2b7ed6f Binary files /dev/null and b/src/modules/wireless/e-module-wireless.edj differ diff --git a/src/modules/wireless/mod.c b/src/modules/wireless/mod.c new file mode 100644 index 0000000..3934ef6 --- /dev/null +++ b/src/modules/wireless/mod.c @@ -0,0 +1,33 @@ +#include "e.h" + +EINTERN void wireless_gadget_init(void); +EINTERN void wireless_gadget_shutdown(void); + +EINTERN void connman_init(void); +EINTERN void connman_shutdown(void); + +EINTERN Eldbus_Connection *dbus_conn; + +E_API E_Module_Api e_modapi = +{ + E_MODULE_API_VERSION, + "Wireless" +}; + +E_API void * +e_modapi_init(E_Module *m) +{ + dbus_conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SYSTEM); + connman_init(); + wireless_gadget_init(); + return m; +} + +E_API int +e_modapi_shutdown(E_Module *m EINA_UNUSED) +{ + wireless_gadget_shutdown(); + connman_shutdown(); + E_FREE_FUNC(dbus_conn, eldbus_connection_unref); + return 1; +} diff --git a/src/modules/wireless/module.desktop.in b/src/modules/wireless/module.desktop.in new file mode 100644 index 0000000..0efa788 --- /dev/null +++ b/src/modules/wireless/module.desktop.in @@ -0,0 +1,8 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=Link +Name=Wireless +GenericName=Wireless +Comment=Manage wifi connections +Icon=e-module-wireless +X-Enlightenment-ModuleType=system diff --git a/src/modules/wireless/wireless.c b/src/modules/wireless/wireless.c new file mode 100644 index 0000000..4102653 --- /dev/null +++ b/src/modules/wireless/wireless.c @@ -0,0 +1,1476 @@ +#include "wireless.h" + + +/* FIXME */ +void connman_technology_enabled_set(Wireless_Service_Type type, Eina_Bool state); + +static const char *wireless_theme_groups[] = +{ + [WIRELESS_SERVICE_TYPE_ETHERNET] = "e/gadget/wireless/ethernet", + [WIRELESS_SERVICE_TYPE_WIFI] = "e/gadget/wireless/wifi", + [WIRELESS_SERVICE_TYPE_BLUETOOTH] = "e/gadget/wireless/bluetooth", + [WIRELESS_SERVICE_TYPE_CELLULAR] = "e/gadget/wireless/cellular", +}; + +static const char *wireless_ipv4_methods[] = +{ + N_("Disabled"), + N_("Manual"), + "DHCP", + N_("Fixed"), +}; + +static const char *wireless_ipv6_methods[] = +{ + N_("Off"), + N_("Manual"), + N_("Auto"), + "6to4", + N_("Fixed"), +}; + +static const char *wireless_proxy_methods[] = +{ + N_("Direct"), + N_("Manual"), + N_("Auto"), +}; + +typedef struct Instance +{ + int id; + E_Gadget_Site_Orient orient; + Evas_Object *box; + Evas_Object *icon[WIRELESS_SERVICE_TYPE_LAST]; + + Eina_Bool popup; + + struct + { + Evas_Object *error; + Evas_Object *address; + Evas_Object *method; + Evas_Object *signal; + Wireless_Service_Type type; + } tooltip; +} Instance; + +typedef struct Wireless_Auth_Popup +{ + Evas_Object *popup; + Wireless_Auth_Cb cb; + void *data; + Eina_Bool sent : 1; +} Wireless_Auth_Popup; + +static Eina_Array *wireless_networks; +static Wireless_Connection *wireless_current[WIRELESS_SERVICE_TYPE_LAST]; +static Eina_Bool wireless_type_enabled[WIRELESS_SERVICE_TYPE_LAST]; +static Eina_Bool wireless_type_available[WIRELESS_SERVICE_TYPE_LAST]; +static Eina_List *instances; +static Eina_List *wireless_auth_pending; +static Wireless_Auth_Popup *wireless_auth_popup; +static Eina_Bool wireless_offline; +static Evas_Object *wireless_edit_popup; +static Wireless_Connection *wireless_edit[2]; +static unsigned int wireless_network_count[WIRELESS_SERVICE_TYPE_LAST]; + +static struct +{ + Evas_Object *popup; + Evas_Object *box; + Evas_Object *content; + Eina_Stringshare *name_servers; + Eina_Stringshare *time_servers; + Eina_Stringshare *domain_servers; + Eina_Stringshare *proxy_servers; + Eina_Stringshare *proxy_excludes; + Eina_Hash *items; + Eina_List *entries; + Wireless_Service_Type type; +} wireless_popup; + +static Eina_Bool auth_popup; + +#undef DBG +#undef INF +#undef WRN +#undef ERR + +#define DBG(...) EINA_LOG_DOM_DBG(_wireless_gadget_log_dom, __VA_ARGS__) +#define INF(...) EINA_LOG_DOM_INFO(_wireless_gadget_log_dom, __VA_ARGS__) +#define WRN(...) EINA_LOG_DOM_WARN(_wireless_gadget_log_dom, __VA_ARGS__) +#define ERR(...) EINA_LOG_DOM_ERR(_wireless_gadget_log_dom, __VA_ARGS__) +static int _wireless_gadget_log_dom = -1; + +static void +_wifi_icon_signal(Evas_Object *icon, int state, int strength) +{ + Edje_Message_Int_Set *msg; + + DBG("icon msg: %d %d%%", state, strength); + msg = alloca(sizeof(Edje_Message_Int_Set) + sizeof(int)); + msg->count = 2; + msg->val[0] = state; + msg->val[1] = strength; + edje_object_message_send(elm_layout_edje_get(icon), EDJE_MESSAGE_INT_SET, 1, msg); +} + +static void +_wifi_icon_init(Evas_Object *icon, Wireless_Network *wn, int type) +{ + int state = 0, strength = 0; + + if (wn) + { + state = wn->state; + strength = wn->strength; + } + _wifi_icon_signal(icon, state, strength); + + if (!wn) + { + if (wireless_type_available[type]) + elm_object_signal_emit(icon, "e,state,default", "e"); + else + elm_object_signal_emit(icon, "e,state,error", "e"); + elm_object_signal_emit(icon, "e,state,unsecured", "e"); + return; + } + if (wn->state == WIRELESS_NETWORK_STATE_FAILURE) + { + elm_object_signal_emit(icon, "e,state,error", "e"); + return; + } + elm_object_signal_emit(icon, "e,state,default", "e"); + switch (wn->type) + { + case WIRELESS_SERVICE_TYPE_WIFI: + if (wn->security > WIRELESS_NETWORK_SECURITY_WEP) + elm_object_signal_emit(icon, "e,state,secure", "e"); + else if (wn->security == WIRELESS_NETWORK_SECURITY_WEP) + elm_object_signal_emit(icon, "e,state,insecure", "e"); + else if (!wn->security) + elm_object_signal_emit(icon, "e,state,unsecured", "e"); + break; + default: break; + } +} + +static void +_wireless_popup_toggle(void *data EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) +{ + connman_technology_enabled_set(wireless_popup.type, elm_check_state_get(obj)); +} + +static void +_wireless_popup_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Instance *inst = data; + inst->popup = 0; + E_FREE_FUNC(wireless_popup.items, eina_hash_free); + E_FREE_FUNC(wireless_popup.entries, eina_list_free); + eina_stringshare_replace(&wireless_popup.proxy_servers, NULL); + eina_stringshare_replace(&wireless_popup.proxy_excludes, NULL); + wireless_popup.box = NULL; + wireless_popup.content = NULL; + wireless_popup.popup = NULL; + wireless_popup.type = -1; +} + +static void +_wireless_edit_basic_entries_update(void) +{ + Eina_List *l; + Evas_Object *ent; + Eina_Bool disabled; + + if (wireless_edit[1]->ipv6) + disabled = wireless_edit[1]->method != WIRELESS_NETWORK_IPV6_METHOD_MANUAL; + else + disabled = wireless_edit[1]->method != WIRELESS_NETWORK_IPV4_METHOD_MANUAL; + EINA_LIST_FOREACH(wireless_popup.entries, l, ent) + elm_object_disabled_set(ent, disabled); +} + +static Evas_Object * +_wireless_popup_table_entry_row(Evas_Object *tb, const char *name, Evas_Smart_Cb cb, void *data, int *row) +{ + Evas_Object *fr, *entry; + + fr = elm_frame_add(tb); + evas_object_show(fr); + E_EXPAND(fr); + E_FILL(fr); + elm_object_text_set(fr, name); + elm_table_pack(tb, fr, 0, *row, 2, 2); + *row += 2; + + entry = elm_entry_add(tb); + evas_object_show(entry); + elm_entry_single_line_set(entry, 1); + elm_entry_scrollable_set(entry, 1); + evas_object_data_set(entry, "table", tb); + evas_object_smart_callback_add(entry, "activated", cb, data); + elm_object_content_set(fr, entry); + return entry; +} + +static void +_wireless_edit_entry_changed(void *data, Evas_Object *obj, void *event_info EINA_UNUSED) +{ + Eina_Stringshare **str = data; + + eina_stringshare_replace(str, elm_entry_entry_get(obj)); +} + +static void +_wireless_gadget_edit_array_entry(Eina_Array *arr, Eina_Stringshare **ptr) +{ + Eina_Stringshare *str; + unsigned int i; + Eina_Array_Iterator it; + Eina_Strbuf *buf; + + if (!arr) return; + buf = eina_strbuf_new(); + EINA_ARRAY_ITER_NEXT(arr, i, str, it) + { + if (eina_strbuf_length_get(buf)) eina_strbuf_append(buf, ", "); + eina_strbuf_append(buf, str); + } + eina_stringshare_replace(ptr, eina_strbuf_string_get(buf)); + eina_strbuf_free(buf); +} + +static void +_wireless_gadget_edit_proxy_method_update(void) +{ + Evas_Object *ent, *tb = wireless_popup.content; + int row = 1; + Wireless_Connection *wc = wireless_edit[1]; + + evas_object_del(elm_table_child_get(wireless_popup.content, 0, 1)); + evas_object_del(elm_table_child_get(wireless_popup.content, 0, 3)); + evas_object_del(elm_table_child_get(wireless_popup.content, 0, 5)); + switch (wc->proxy_type) + { + case WIRELESS_PROXY_TYPE_DIRECT: + _wireless_popup_table_entry_row(tb, NULL, NULL, NULL, &row); + evas_object_hide(elm_table_child_get(wireless_popup.content, 0, 1)); + _wireless_popup_table_entry_row(tb, NULL, NULL, NULL, &row); + evas_object_hide(elm_table_child_get(wireless_popup.content, 0, 3)); + _wireless_popup_table_entry_row(tb, NULL, NULL, NULL, &row); + evas_object_hide(elm_table_child_get(wireless_popup.content, 0, 5)); + break; + case WIRELESS_PROXY_TYPE_MANUAL: + ent = _wireless_popup_table_entry_row(tb, _("Proxy Servers"), NULL, NULL, &row); + elm_entry_entry_set(ent, wireless_popup.proxy_servers); + evas_object_smart_callback_add(ent, "changed,user", _wireless_edit_entry_changed, &wireless_popup.proxy_servers); + ent = _wireless_popup_table_entry_row(tb, _("Proxy Excludes"), NULL, NULL, &row); + elm_entry_entry_set(ent, wireless_popup.proxy_excludes); + evas_object_smart_callback_add(ent, "changed,user", _wireless_edit_entry_changed, &wireless_popup.proxy_excludes); + _wireless_popup_table_entry_row(tb, NULL, NULL, NULL, &row); + evas_object_hide(elm_table_child_get(wireless_popup.content, 0, 5)); + break; + case WIRELESS_PROXY_TYPE_AUTO: + ent = _wireless_popup_table_entry_row(tb, _("Proxy Address"), NULL, NULL, &row); + elm_entry_entry_set(ent, wc->proxy_url); + evas_object_smart_callback_add(ent, "changed,user", _wireless_edit_entry_changed, &wireless_edit[1]->address); + _wireless_popup_table_entry_row(tb, NULL, NULL, NULL, &row); + evas_object_hide(elm_table_child_get(wireless_popup.content, 0, 3)); + _wireless_popup_table_entry_row(tb, NULL, NULL, NULL, &row); + evas_object_hide(elm_table_child_get(wireless_popup.content, 0, 5)); + break; + } +} + +static void +_wireless_gadget_edit_proxy_method(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) +{ + wireless_edit[1]->proxy_type = (intptr_t)elm_object_item_data_get(event_info); + _wireless_gadget_edit_proxy_method_update(); +} + +static void +_wireless_gadget_edit_method(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) +{ + wireless_edit[1]->method = (intptr_t)elm_object_item_data_get(event_info); + _wireless_edit_basic_entries_update(); +} + +static void +_wireless_gadget_edit_method_open(void *data EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) +{ + int i, fixed; + const char **methods; + + elm_hoversel_clear(obj); + evas_object_layer_set(obj, E_LAYER_MENU); + if (wireless_edit[1]->ipv6) + { + fixed = WIRELESS_NETWORK_IPV6_METHOD_FIXED; + methods = wireless_ipv6_methods; + } + else + { + fixed = WIRELESS_NETWORK_IPV6_METHOD_FIXED; + methods = wireless_ipv4_methods; + } + for (i = 0; i < fixed; i++) + { + if ((int)wireless_edit[1]->method != i) + elm_hoversel_item_add(obj, methods[i], NULL, ELM_ICON_NONE, NULL, (intptr_t*)(long)i); + } +} + +static void +_wireless_edit_del(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + int i; + + wireless_popup.entries = eina_list_free(wireless_popup.entries); + eina_stringshare_del(wireless_edit[0]->wn->path); + free(wireless_edit[0]->wn); + for (i = 0; i <= 1; i++) + { + eina_stringshare_del(wireless_edit[i]->address); + eina_stringshare_del(wireless_edit[i]->gateway); + if (wireless_edit[i]->ipv6) + eina_stringshare_del(wireless_edit[i]->ip.v6.prefixlength); + else + eina_stringshare_del(wireless_edit[i]->ip.v4.netmask); + eina_stringshare_del(wireless_edit[i]->proxy_url); + array_clear(wireless_edit[i]->proxy_excludes); + array_clear(wireless_edit[i]->proxy_servers); + E_FREE(wireless_edit[i]); + } + wireless_edit_popup = NULL; +} + +static Eina_Array * +string_to_array(const char *str) +{ + const char *p = str; + Eina_Array *arr; + + arr = eina_array_new(1); + do + { + const char *start, *end; + + start = p; + p = strchr(p, ','); + if (!p) break; + end = p - 1; + while (isspace(start[0])) start++; + while (isspace(end[0])) end--; + end++; + + if (start == end) break; + eina_array_push(arr, eina_stringshare_add_length(start, end - start)); + p++; + } while (p[0]); + return arr; +} + +static Eina_Bool +_wireless_array_notequal(Eina_Array *a, Eina_Array *b) +{ + unsigned int i; + + if ((!!a) != (!!b)) return EINA_TRUE; + if (eina_array_count(a) != eina_array_count(b)) return EINA_TRUE; + for (i = 0; i < eina_array_count(a); i++) + if (eina_array_data_get(a, i) != eina_array_data_get(b, i)) return EINA_TRUE; + return EINA_FALSE; +} + +static void +_wireless_edit_send() +{ + Eina_Bool basic = EINA_FALSE, proxy = EINA_FALSE; + + EINTERN void connman_service_edit(const char *path, Wireless_Connection *wc); + EINTERN void connman_service_edit_proxy(const char *path, Wireless_Connection *wc); + EINTERN void connman_service_edit_domains(const char *path, Wireless_Connection *wc); + EINTERN void connman_service_edit_nameservers(const char *path, Wireless_Connection *wc); + EINTERN void connman_service_edit_timeservers(const char *path, Wireless_Connection *wc); + + if (wireless_edit[0]->method == wireless_edit[1]->method) + { + if (wireless_edit[1]->ipv6) + switch (wireless_edit[1]->method) + { + case WIRELESS_NETWORK_IPV6_METHOD_AUTO: + basic = wireless_edit[0]->ip.v6.privacy != wireless_edit[1]->ip.v6.privacy; + break; + case WIRELESS_NETWORK_IPV6_METHOD_MANUAL: + basic = wireless_edit[0]->address != wireless_edit[1]->address; + if (basic) break; + basic = wireless_edit[0]->gateway != wireless_edit[1]->gateway; + if (basic) break; + basic = wireless_edit[0]->ip.v6.prefixlength != wireless_edit[1]->ip.v6.prefixlength; + break; + default: break; + } + else + switch (wireless_edit[1]->method) + { + case WIRELESS_NETWORK_IPV4_METHOD_MANUAL: + basic = wireless_edit[0]->address != wireless_edit[1]->address; + if (basic) break; + basic = wireless_edit[0]->gateway != wireless_edit[1]->gateway; + if (basic) break; + basic = wireless_edit[0]->ip.v4.netmask != wireless_edit[1]->ip.v4.netmask; + break; + default: break; + } + } + else + basic = EINA_TRUE; + + if (basic) + connman_service_edit(wireless_edit[1]->wn->path, wireless_edit[1]); + + if (wireless_edit[1]->proxy_type == WIRELESS_PROXY_TYPE_MANUAL) + { + array_clear(wireless_edit[1]->proxy_servers); + array_clear(wireless_edit[1]->proxy_excludes); + if (wireless_popup.proxy_servers) + wireless_edit[1]->proxy_servers = string_to_array(wireless_popup.proxy_servers); + if (wireless_popup.proxy_excludes) + wireless_edit[1]->proxy_excludes = string_to_array(wireless_popup.proxy_excludes); + } + if (wireless_edit[0]->proxy_type == wireless_edit[1]->proxy_type) + { + switch (wireless_edit[0]->proxy_type) + { + case WIRELESS_PROXY_TYPE_MANUAL: + proxy = _wireless_array_notequal(wireless_edit[0]->proxy_servers, + wireless_edit[1]->proxy_servers); + if (proxy) break; + proxy = _wireless_array_notequal(wireless_edit[0]->proxy_excludes, + wireless_edit[1]->proxy_excludes); + break; + case WIRELESS_PROXY_TYPE_AUTO: + proxy = wireless_edit[0]->proxy_url != wireless_edit[1]->proxy_url; + break; + break; + default: break; + } + + } + else + proxy = EINA_TRUE; + if (proxy) + connman_service_edit_proxy(wireless_edit[1]->wn->path, wireless_edit[1]); + + array_clear(wireless_edit[1]->domain_servers); + if (wireless_popup.domain_servers) + wireless_edit[1]->domain_servers = string_to_array(wireless_popup.domain_servers); + array_clear(wireless_edit[1]->name_servers); + if (wireless_popup.name_servers) + wireless_edit[1]->name_servers = string_to_array(wireless_popup.name_servers); + array_clear(wireless_edit[1]->name_servers); + if (wireless_popup.name_servers) + wireless_edit[1]->name_servers = string_to_array(wireless_popup.name_servers); + + if (_wireless_array_notequal(wireless_edit[0]->domain_servers, wireless_edit[1]->domain_servers)) + connman_service_edit_domains(wireless_edit[1]->wn->path, wireless_edit[1]); + if (_wireless_array_notequal(wireless_edit[0]->name_servers, wireless_edit[1]->name_servers)) + connman_service_edit_nameservers(wireless_edit[1]->wn->path, wireless_edit[1]); + if (_wireless_array_notequal(wireless_edit[0]->time_servers, wireless_edit[1]->time_servers)) + connman_service_edit_timeservers(wireless_edit[1]->wn->path, wireless_edit[1]); +} + +static void +_wireless_edit_send_button() +{ + e_comp_object_util_autoclose(NULL, NULL, NULL, NULL); + _wireless_edit_send(); +} + +static void +_wireless_edit_remove() +{ + EINTERN void connman_service_remove(const char *path); + e_comp_object_util_autoclose(NULL, NULL, NULL, NULL); + connman_service_remove(wireless_edit[1]->wn->path); +} + +static Eina_Bool +_wireless_edit_key(void *d EINA_UNUSED, Ecore_Event_Key *ev) +{ + if ((!strcmp(ev->key, "Return")) || (!strcmp(ev->key, "KP_Enter"))) + { + _wireless_edit_send(); + return EINA_FALSE; + } + return strcmp(ev->key, "Escape"); +} + +static void +_wireless_gadget_edit_proxy_method_open(void *data EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) +{ + int i; + + elm_hoversel_clear(obj); + for (i = 0; i <= WIRELESS_PROXY_TYPE_AUTO; i++) + { + if ((int)wireless_edit[1]->proxy_type != i) + elm_hoversel_item_add(obj, wireless_proxy_methods[i], NULL, ELM_ICON_NONE, NULL, (intptr_t*)(long)i); + } +} + +static void +_wireless_gadget_edit_proxy(void) +{ + Evas_Object *tb, *fr, *hoversel; + int row = 0; + Wireless_Connection *wc = wireless_edit[1]; + + wireless_popup.content = tb = elm_table_add(wireless_popup.popup); + E_FILL(tb); + E_EXPAND(tb); + evas_object_show(tb); + elm_box_pack_end(wireless_popup.box, tb); + + fr = elm_frame_add(tb); + E_EXPAND(fr); + E_FILL(fr); + evas_object_show(fr); + elm_object_text_set(fr, _("Proxy Type")); + elm_table_pack(tb, fr, 0, row++, 2, 1); + + hoversel = elm_hoversel_add(tb); + elm_hoversel_hover_parent_set(hoversel, wireless_popup.popup); + elm_hoversel_auto_update_set(hoversel, 1); + evas_object_show(hoversel); + elm_object_content_set(fr, hoversel); + evas_object_smart_callback_add(hoversel, "selected", _wireless_gadget_edit_proxy_method, NULL); + evas_object_smart_callback_add(hoversel, "clicked", _wireless_gadget_edit_proxy_method_open, NULL); + elm_object_text_set(hoversel, wireless_proxy_methods[wc->proxy_type]); + _wireless_gadget_edit_proxy_method_update(); +} + +static void +_wireless_gadget_edit_dnstime(void) +{ + Evas_Object *tb, *ent; + int row = 0; + + wireless_popup.content = tb = elm_table_add(wireless_popup.popup); + E_FILL(tb); + E_EXPAND(tb); + evas_object_show(tb); + elm_box_pack_end(wireless_popup.box, tb); + + ent = _wireless_popup_table_entry_row(tb, _("Nameservers"), NULL, NULL, &row); + elm_entry_entry_set(ent, wireless_popup.name_servers); + evas_object_smart_callback_add(ent, "changed,user", _wireless_edit_entry_changed, &wireless_popup.name_servers); + + ent = _wireless_popup_table_entry_row(tb, _("Timeservers"), NULL, NULL, &row); + elm_entry_entry_set(ent, wireless_popup.time_servers); + evas_object_smart_callback_add(ent, "changed,user", _wireless_edit_entry_changed, &wireless_popup.time_servers); + + ent = _wireless_popup_table_entry_row(tb, _("Search Domains"), NULL, NULL, &row); + elm_entry_entry_set(ent, wireless_popup.domain_servers); + evas_object_smart_callback_add(ent, "changed,user", _wireless_edit_entry_changed, &wireless_popup.domain_servers); + + _wireless_popup_table_entry_row(tb, NULL, NULL, NULL, &row); + evas_object_hide(elm_table_child_get(tb, 0, 6)); +} + +static Evas_Object * +_wireless_gadget_edit_basic(void) +{ + Evas_Object *tb, *fr, *hoversel, *ent, *entry; + Eina_Bool disabled; + int row = 0, fixed; + const char **methods; + Wireless_Connection *wc = wireless_edit[1]; + + wireless_popup.content = tb = elm_table_add(wireless_popup.box); + E_FILL(tb); + E_EXPAND(tb); + evas_object_show(tb); + elm_box_pack_end(wireless_popup.box, tb); + + fr = elm_frame_add(tb); + E_EXPAND(fr); + E_FILL(fr); + evas_object_show(fr); + elm_object_text_set(fr, _("Method")); + elm_table_pack(tb, fr, 0, row++, 2, 1); + + hoversel = elm_hoversel_add(tb); + elm_hoversel_hover_parent_set(hoversel, wireless_popup.popup); + elm_hoversel_auto_update_set(hoversel, 1); + evas_object_show(hoversel); + elm_object_content_set(fr, hoversel); + evas_object_smart_callback_add(hoversel, "selected", _wireless_gadget_edit_method, NULL); + if (wc->ipv6) + { + fixed = WIRELESS_NETWORK_IPV6_METHOD_FIXED; + methods = wireless_ipv6_methods; + } + else + { + fixed = WIRELESS_NETWORK_IPV6_METHOD_FIXED; + methods = wireless_ipv4_methods; + } + disabled = (int)wc->method == fixed; + elm_object_disabled_set(hoversel, disabled); + if (disabled) + elm_hoversel_item_add(hoversel, _("Fixed"), NULL, ELM_ICON_NONE, NULL, NULL); + else + { + elm_object_text_set(hoversel, methods[wc->method]); + evas_object_smart_callback_add(hoversel, "clicked", _wireless_gadget_edit_method_open, NULL); + } + + ent = entry = _wireless_popup_table_entry_row(tb, _("Address"), NULL, NULL, &row); + elm_object_disabled_set(ent, disabled); + wireless_popup.entries = eina_list_append(wireless_popup.entries, ent); + elm_entry_entry_set(ent, wc->address); + evas_object_smart_callback_add(ent, "changed,user", _wireless_edit_entry_changed, &wireless_edit[1]->address); + if (wc->ipv6) + { + ent = _wireless_popup_table_entry_row(tb, _("PrefixLength"), NULL, NULL, &row); + elm_entry_entry_set(ent, wc->ip.v6.prefixlength); + evas_object_smart_callback_add(ent, "changed,user", _wireless_edit_entry_changed, &wireless_edit[1]->ip.v6.prefixlength); + } + else + { + ent = _wireless_popup_table_entry_row(tb, _("Netmask"), NULL, NULL, &row); + elm_entry_entry_set(ent, wc->ip.v4.netmask); + evas_object_smart_callback_add(ent, "changed,user", _wireless_edit_entry_changed, &wireless_edit[1]->ip.v4.netmask); + } + elm_object_disabled_set(ent, disabled); + wireless_popup.entries = eina_list_append(wireless_popup.entries, ent); + ent = _wireless_popup_table_entry_row(tb, _("Gateway"), NULL, NULL, &row); + elm_entry_entry_set(ent, wc->gateway); + elm_object_disabled_set(ent, disabled); + evas_object_smart_callback_add(ent, "changed,user", _wireless_edit_entry_changed, &wireless_edit[1]->gateway); + wireless_popup.entries = eina_list_append(wireless_popup.entries, ent); + _wireless_edit_basic_entries_update(); + + return entry; +} + +static void +_wireless_gadget_edit_select_pre(void) +{ + elm_box_unpack(wireless_popup.box, wireless_popup.content); + evas_object_del(wireless_popup.content); + wireless_popup.entries = eina_list_free(wireless_popup.entries); +} + +static void +_wireless_gadget_edit_select_basic(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + _wireless_gadget_edit_select_pre(); + _wireless_gadget_edit_basic(); +} + +static void +_wireless_gadget_edit_select_proxy(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + _wireless_gadget_edit_select_pre(); + _wireless_gadget_edit_proxy(); +} + +static void +_wireless_gadget_edit_select_dnstime(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + _wireless_gadget_edit_select_pre(); + _wireless_gadget_edit_dnstime(); +} + +static void +_wireless_gadget_edit(int type) +{ + Evas_Object *popup, *entry, *box1, *box, *list, *lbl, *bt; + Elm_Object_Item *it; + Eina_Bool disabled; + int i; + char buf[1024] = {0}; + Wireless_Connection *wc = wireless_current[type]; + Wireless_Network *wn; + + if (!wc) return; + wireless_edit[0] = E_NEW(Wireless_Connection, 1); + wireless_edit[1] = E_NEW(Wireless_Connection, 1); + wn = E_NEW(Wireless_Network, 1); + wn->path = eina_stringshare_ref(wc->wn->path); + for (i = 0; i <= 1; i++) + { + Eina_Array *arrays[] = + { wc->domain_servers, wc->name_servers, wc->time_servers, wc->proxy_servers, + wc->proxy_excludes, NULL }; + Eina_Array **arrays2[] = + { &wireless_edit[i]->domain_servers, &wireless_edit[i]->name_servers, + &wireless_edit[i]->time_servers, &wireless_edit[i]->proxy_servers, + &wireless_edit[i]->proxy_excludes, NULL }; + unsigned int ii; + + wireless_edit[i]->wn = wn; + wireless_edit[i]->method = wc->method; + wireless_edit[i]->address = eina_stringshare_ref(wc->address); + wireless_edit[i]->gateway = eina_stringshare_ref(wc->gateway); + wireless_edit[i]->ipv6 = wc->ipv6; + if (wc->ipv6) + { + wireless_edit[i]->ip.v6.prefixlength = eina_stringshare_ref(wc->ip.v6.prefixlength); + wireless_edit[i]->ip.v6.privacy = wc->ip.v6.privacy; + } + else + wireless_edit[i]->ip.v4.netmask = eina_stringshare_ref(wc->ip.v4.netmask); + wireless_edit[i]->proxy_type = wc->proxy_type; + wireless_edit[i]->proxy_url = eina_stringshare_ref(wc->proxy_url); + /* fuuuuck thiiiiiiis */ + for (ii = 0; ii < EINA_C_ARRAY_LENGTH(arrays); ii++) + { + unsigned int iii; + Eina_Stringshare *str; + Eina_Array_Iterator itr; + + if (!arrays[ii]) continue; + *arrays2[ii] = eina_array_new(eina_array_count(arrays[ii])); + EINA_ARRAY_ITER_NEXT(arrays[ii], iii, str, itr) + eina_array_push(*arrays2[ii], eina_stringshare_ref(str)); + } + } + _wireless_gadget_edit_array_entry(wc->domain_servers, &wireless_popup.domain_servers); + _wireless_gadget_edit_array_entry(wc->name_servers, &wireless_popup.name_servers); + _wireless_gadget_edit_array_entry(wc->time_servers, &wireless_popup.time_servers); + _wireless_gadget_edit_array_entry(wc->proxy_servers, &wireless_popup.proxy_servers); + _wireless_gadget_edit_array_entry(wc->proxy_excludes, &wireless_popup.proxy_excludes); + + wireless_popup.popup = popup = elm_popup_add(e_comp->elm); + evas_object_layer_set(popup, E_LAYER_MENU); + elm_popup_allow_events_set(popup, 1); + elm_popup_scrollable_set(popup, 1); + + box = elm_box_add(popup); + E_EXPAND(box); + E_FILL(box); + evas_object_show(box); + elm_object_content_set(popup, box); + + lbl = elm_label_add(box); + elm_object_style_set(lbl, "marker"); + evas_object_show(lbl); + if (wireless_popup.type == WIRELESS_SERVICE_TYPE_ETHERNET) + strncpy(buf, _("Edit Connection Details: <b>Ethernet</b>"), sizeof(buf) - 1); + else + snprintf(buf, sizeof(buf), "%s: <hilight>%s</hilight>", _("Edit Connection Details"), wc->wn->name); + elm_object_text_set(lbl, buf); + elm_box_pack_end(box, lbl); + + wireless_popup.box = box1 = elm_box_add(popup); + E_EXPAND(box1); + E_FILL(box1); + elm_box_horizontal_set(box1, 1); + evas_object_show(box1); + elm_box_pack_end(box, box1); + + list = elm_list_add(box1); + E_ALIGN(list, 0, EVAS_HINT_FILL); + E_WEIGHT(list, 0, EVAS_HINT_EXPAND); + elm_box_pack_end(box1, list); + elm_list_select_mode_set(list, ELM_OBJECT_SELECT_MODE_ALWAYS); + elm_scroller_content_min_limit(list, 1, 1); + + entry = _wireless_gadget_edit_basic(); + it = elm_list_item_append(list, _("Basic"), NULL, NULL, _wireless_gadget_edit_select_basic, NULL); + elm_list_item_selected_set(it, 1); + elm_list_item_append(list, _("Proxy"), NULL, NULL, _wireless_gadget_edit_select_proxy, NULL); + elm_list_item_append(list, _("DNS/Time"), NULL, NULL, _wireless_gadget_edit_select_dnstime, NULL); + elm_list_go(list); + evas_object_show(list); + + if (wc->ipv6) + disabled = wc->method == WIRELESS_NETWORK_IPV4_METHOD_FIXED; + else + disabled = wc->method == WIRELESS_NETWORK_IPV6_METHOD_FIXED; + if (!disabled) + { + bt = elm_button_add(box); + E_EXPAND(bt); + E_FILL(bt); + evas_object_show(bt); + elm_object_text_set(bt, _("Deal with it")); + evas_object_smart_callback_add(bt, "clicked", _wireless_edit_send_button, NULL); + elm_box_pack_end(box, bt); + + bt = elm_button_add(box); + E_EXPAND(bt); + E_FILL(bt); + evas_object_show(bt); + elm_object_text_set(bt, _("Forget Network")); + evas_object_smart_callback_add(bt, "clicked", _wireless_edit_remove, NULL); + elm_box_pack_end(box, bt); + } + wireless_edit_popup = e_comp_object_util_add(popup, E_COMP_OBJECT_TYPE_NONE); + evas_object_layer_set(wireless_edit_popup, E_LAYER_POPUP); + evas_object_resize(wireless_edit_popup, e_zone_current_get()->w / 3, e_zone_current_get()->h / 2); + e_comp_object_util_center(wireless_edit_popup); + evas_object_show(wireless_edit_popup); + e_comp_object_util_autoclose(wireless_edit_popup, NULL, _wireless_edit_key, NULL); + evas_object_event_callback_add(wireless_edit_popup, EVAS_CALLBACK_DEL, _wireless_edit_del, NULL); + elm_object_focus_set(entry, 1); +} + +static void +_wireless_popup_network_click(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Wireless_Network *wn = data; + + if ((wn->state == WIRELESS_NETWORK_STATE_CONNECTED) || (wn->state == WIRELESS_NETWORK_STATE_ONLINE)) + { + int type = wireless_popup.type; + + evas_object_hide(wireless_popup.popup); + evas_object_del(wireless_popup.popup); + _wireless_gadget_edit(type); + } + else + { + /* FIXME */ + if (!wn->connect_cb(wn)) + {} + } +} + +static void +_wireless_popup_list_populate(void) +{ + Eina_Iterator *it; + Wireless_Network *wn; + + if (!wireless_networks) return; + it = eina_array_iterator_new(wireless_networks); + EINA_ITERATOR_FOREACH(it, wn) + { + Evas_Object *icon; + Elm_Object_Item *item; + const char *name = wn->name; + + if (wn->type != wireless_popup.type) continue; + icon = elm_layout_add(wireless_popup.content); + e_theme_edje_object_set(icon, NULL, wireless_theme_groups[wireless_popup.type]); + _wifi_icon_init(icon, wn, wn->type); + if (!name) + name = _("<SSID hidden>"); + item = elm_list_item_append(wireless_popup.content, name, icon, NULL, _wireless_popup_network_click, wn); + eina_hash_add(wireless_popup.items, &wn, item); + } + eina_iterator_free(it); +} + +static void +_wireless_gadget_mouse_down(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info) +{ + Evas_Event_Mouse_Down *ev = event_info; + Instance *inst = data; + Evas_Object *ctx, *box, *list, *toggle; + int type; + E_Zone *zone; + const char *names[] = + { + _("Ethernet"), + _("Wifi"), + _("Bluetooth"), + _("Cellular"), + }; + + if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return; + if (auth_popup) return; + for (type = 0; type < WIRELESS_SERVICE_TYPE_LAST; type++) + if (obj == inst->icon[type]) + break; + if (ev->button == 2) connman_technology_enabled_set(type, !wireless_type_enabled[type]); + if (ev->button == 3) _wireless_gadget_edit(type); + if (ev->button != 1) return; + if (wireless_popup.popup) + { + evas_object_hide(wireless_popup.popup); + evas_object_del(wireless_popup.popup); + if (wireless_popup.type == type) + return; + } + inst->popup = 1; + wireless_popup.type = type; + wireless_popup.items = eina_hash_pointer_new(NULL); + ctx = elm_ctxpopup_add(e_comp->elm); + elm_object_style_set(ctx, "noblock"); + + box = elm_box_add(ctx); + E_EXPAND(box); + E_FILL(box); + + wireless_popup.content = list = elm_list_add(ctx); + elm_list_mode_set(list, ELM_LIST_LIMIT); + E_EXPAND(list); + E_FILL(list); + _wireless_popup_list_populate(); + elm_list_go(list); + evas_object_show(list); + elm_box_pack_end(box, list); + toggle = elm_check_add(ctx); + evas_object_show(toggle); + elm_object_style_set(toggle, "toggle"); + elm_object_text_set(toggle, names[type]); + elm_object_part_text_set(toggle, "on", _("On")); + elm_object_part_text_set(toggle, "off", _("Off")); + elm_check_state_set(toggle, wireless_type_enabled[type]); + evas_object_smart_callback_add(toggle, "changed", _wireless_popup_toggle, inst); + elm_box_pack_end(box, toggle); + elm_object_content_set(ctx, box); + e_gadget_util_ctxpopup_place(inst->box, ctx, inst->icon[type]); + wireless_popup.popup = e_comp_object_util_add(ctx, E_COMP_OBJECT_TYPE_NONE); + evas_object_layer_set(wireless_popup.popup, E_LAYER_POPUP); + e_comp_object_util_autoclose(wireless_popup.popup, NULL, e_comp_object_util_autoclose_on_escape, NULL); + + zone = e_zone_current_get(); + evas_object_resize(wireless_popup.popup, zone->w / 5, zone->h / 3); + evas_object_show(wireless_popup.popup); + evas_object_event_callback_add(wireless_popup.popup, EVAS_CALLBACK_DEL, _wireless_popup_del, inst); +} + +static Evas_Object * +_wireless_tooltip_row(Evas_Object *tb, const char *label, const char *value, int row) +{ + Evas_Object *lbl; + + lbl = elm_label_add(tb); + evas_object_show(lbl); + E_ALIGN(lbl, 0, 0.5); + elm_object_text_set(lbl, label); + elm_table_pack(tb, lbl, 0, row, 1, 1); + + lbl = elm_label_add(tb); + evas_object_show(lbl); + E_ALIGN(lbl, 0, 0.5); + elm_object_text_set(lbl, value); + elm_table_pack(tb, lbl, 1, row, 1, 1); + return lbl; +} + +static const char * +_wireless_tooltip_method_name(void) +{ + if (wireless_current[WIRELESS_SERVICE_TYPE_WIFI]->ipv6) + return wireless_ipv6_methods[wireless_current[WIRELESS_SERVICE_TYPE_WIFI]->method]; + return wireless_ipv4_methods[wireless_current[WIRELESS_SERVICE_TYPE_WIFI]->method]; +} + +static void +_wireless_tooltip_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Instance *inst = data; + + inst->tooltip.error = inst->tooltip.address = + inst->tooltip.method = inst->tooltip.signal = NULL; + inst->tooltip.type = -1; +} + +static Evas_Object * +_wireless_tooltip(void *data, Evas_Object *obj EINA_UNUSED, Evas_Object *tooltip) +{ + Instance *inst = data; + Evas_Object *tb; + int row = 0; + char buf[1024]; + int type = WIRELESS_SERVICE_TYPE_WIFI; + + if (!wireless_current[type]) + { + if (!wireless_type_available[type])//connman not found + { + inst->tooltip.error = elm_label_add(tooltip); + elm_object_text_set(inst->tooltip.error, _("Error: Connman not detected!")); + evas_object_event_callback_add(inst->tooltip.error, EVAS_CALLBACK_DEL, _wireless_tooltip_del, inst); + return inst->tooltip.error; + } + return NULL; + } + tb = elm_table_add(tooltip); + elm_table_padding_set(tb, 5, 1); + + _wireless_tooltip_row(tb, _("Name:"), wireless_current[type]->wn->name, row++); + inst->tooltip.method = _wireless_tooltip_row(tb, _("Method:"), _wireless_tooltip_method_name(), row++); + + inst->tooltip.address = _wireless_tooltip_row(tb, _("Address:"), wireless_current[type]->address, row++); + snprintf(buf, sizeof(buf), "%u%%", wireless_current[type]->wn->strength); + inst->tooltip.signal = _wireless_tooltip_row(tb, _("Signal:"), buf, row++); + + evas_object_event_callback_add(tb, EVAS_CALLBACK_DEL, _wireless_tooltip_del, inst); + return tb; +} + +static void +wireless_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Instance *inst = data; + + if (inst->popup) + { + evas_object_hide(wireless_popup.popup); + evas_object_del(wireless_popup.popup); + } + + instances = eina_list_remove(instances, inst); + evas_object_hide(wireless_popup.popup); + evas_object_del(wireless_popup.popup); + free(inst); + + if (instances) return; + eina_log_domain_unregister(_wireless_gadget_log_dom); + _wireless_gadget_log_dom = -1; +} + +static void +_wireless_gadget_icon_add(Instance *inst, int type) +{ + if (!inst->icon[type]) + { + Evas_Object *g; + + inst->icon[type] = g = elm_layout_add(inst->box); + E_EXPAND(g); + E_FILL(g); + e_theme_edje_object_set(g, NULL, wireless_theme_groups[type]); + elm_object_tooltip_content_cb_set(g, _wireless_tooltip, inst, NULL); + evas_object_event_callback_add(g, EVAS_CALLBACK_MOUSE_DOWN, _wireless_gadget_mouse_down, inst); + } + DBG("Updating icon for %d", type); + _wifi_icon_init(inst->icon[type], wireless_current[type] ? wireless_current[type]->wn : NULL, type); + evas_object_hide(inst->icon[type]); +} + +static void +_wireless_gadget_refresh(Instance *inst) +{ + int type; + int avail = 0; + + if (inst->id < 0) return; + for (type = 0; type < WIRELESS_SERVICE_TYPE_LAST; type++) + { + if (wireless_type_available[type]) + _wireless_gadget_icon_add(inst, type); + else + { + if (inst->tooltip.type == type) + elm_object_tooltip_hide(inst->icon[type]); + if (wireless_popup.type == type) + { + evas_object_hide(wireless_popup.popup); + evas_object_del(wireless_popup.popup); + } + E_FREE_FUNC(inst->icon[type], evas_object_del); + } + } + elm_box_unpack_all(inst->box); + type = WIRELESS_SERVICE_TYPE_ETHERNET; + if (inst->icon[type]) + { + /* only show ethernet if it's connected or there's no wifi available */ + if ((!inst->icon[WIRELESS_SERVICE_TYPE_WIFI]) || + wireless_network_count[WIRELESS_SERVICE_TYPE_ETHERNET] || + (wireless_current[type] && + wireless_current[type]->wn && + (wireless_current[type]->wn->state == WIRELESS_NETWORK_STATE_ONLINE))) + { + elm_box_pack_end(inst->box, inst->icon[type]); + evas_object_show(inst->icon[type]); + avail++; + } + } + for (type = WIRELESS_SERVICE_TYPE_WIFI; type < WIRELESS_SERVICE_TYPE_LAST; type++) + { + if (!inst->icon[type]) continue; + if (wireless_type_enabled[type] && (!wireless_network_count[type])) continue; + + elm_box_pack_end(inst->box, inst->icon[type]); + evas_object_show(inst->icon[type]); + avail++; + } + if (!avail) + { + type = WIRELESS_SERVICE_TYPE_ETHERNET; + _wireless_gadget_icon_add(inst, type); + elm_box_pack_end(inst->box, inst->icon[type]); + evas_object_show(inst->icon[type]); + avail++; + } + if (inst->orient == E_GADGET_SITE_ORIENT_VERTICAL) + evas_object_size_hint_aspect_set(inst->box, EVAS_ASPECT_CONTROL_BOTH, 1, avail); + else + evas_object_size_hint_aspect_set(inst->box, EVAS_ASPECT_CONTROL_BOTH, avail, 1); +} + +static Evas_Object * +wireless_create(Evas_Object *parent, int *id, E_Gadget_Site_Orient orient) +{ + Evas_Object *g; + Instance *inst; + + if (!instances) + _wireless_gadget_log_dom = eina_log_domain_register("wireless", EINA_COLOR_CYAN); + inst = E_NEW(Instance, 1); + inst->id = *id; + inst->orient = orient; + wireless_popup.type = inst->tooltip.type = -1; + inst->box = elm_box_add(parent); + elm_box_horizontal_set(inst->box, orient != E_GADGET_SITE_ORIENT_VERTICAL); + elm_box_homogeneous_set(inst->box, 1); + evas_object_event_callback_add(inst->box, EVAS_CALLBACK_DEL, wireless_del, inst); + + if (*id < 0) + { + inst->icon[WIRELESS_SERVICE_TYPE_WIFI] = g = elm_layout_add(inst->box); + E_EXPAND(g); + E_FILL(g); + e_theme_edje_object_set(g, NULL, "e/gadget/wireless/wifi"); + elm_object_signal_emit(g, "e,state,default", "e"); + _wifi_icon_signal(g, WIRELESS_NETWORK_STATE_ONLINE, 100); + evas_object_show(g); + elm_box_pack_end(inst->box, g); + evas_object_size_hint_aspect_set(inst->box, EVAS_ASPECT_CONTROL_BOTH, 1, 1); + } + else + _wireless_gadget_refresh(inst); + instances = eina_list_append(instances, inst); + + return inst->box; +} + +static Ecore_Event_Handler *handler; + +static Eina_Bool +_wireless_mode_change() +{ + EINTERN void connman_airplane_mode_set(Eina_Bool set); + + if (wireless_offline != e_config->mode.offline) + connman_airplane_mode_set(e_config->mode.offline); + return ECORE_CALLBACK_RENEW; +} + +EINTERN void +wireless_gadget_init(void) +{ + e_gadget_type_add("Wireless", wireless_create, NULL); + handler = ecore_event_handler_add(E_EVENT_CONFIG_MODE_CHANGED, _wireless_mode_change, NULL); +} + +EINTERN void +wireless_gadget_shutdown(void) +{ + e_gadget_type_del("Wireless"); + E_FREE_FUNC(handler, ecore_event_handler_del); +} + +EINTERN void +wireless_service_type_available_set(Eina_Bool *avail) +{ + if (!memcmp(avail, &wireless_type_available, sizeof(wireless_type_available))) return; + memcpy(&wireless_type_available, avail, WIRELESS_SERVICE_TYPE_LAST * sizeof(Eina_Bool)); + E_LIST_FOREACH(instances, _wireless_gadget_refresh); +} + +EINTERN void +wireless_service_type_enabled_set(Eina_Bool *avail) +{ + if (!memcmp(avail, &wireless_type_enabled, sizeof(wireless_type_enabled))) return; + memcpy(&wireless_type_enabled, avail, WIRELESS_SERVICE_TYPE_LAST * sizeof(Eina_Bool)); + E_LIST_FOREACH(instances, _wireless_gadget_refresh); +} + +EINTERN void +wireless_wifi_current_networks_set(Wireless_Connection **current) +{ + Eina_List *l; + Instance *inst; + Wireless_Connection *prev[WIRELESS_SERVICE_TYPE_LAST] = {NULL}; + int type; + + memcpy(&prev, &wireless_current, WIRELESS_SERVICE_TYPE_LAST * sizeof(void*)); + memcpy(&wireless_current, current, WIRELESS_SERVICE_TYPE_LAST * sizeof(void*)); + type = wireless_popup.type; + if ((type > -1) && wireless_popup.items) + { + Elm_Object_Item *it; + Evas_Object *icon; + + if (wireless_current[type]) + { + it = eina_hash_find(wireless_popup.items, &wireless_current[type]->wn); + icon = elm_object_item_content_get(it); + _wifi_icon_init(icon, wireless_current[type]->wn, type); + } + if (prev[type]) + { + it = eina_hash_find(wireless_popup.items, &prev[type]->wn); + if (it) + { + icon = elm_object_item_content_get(it); + _wifi_icon_init(icon, prev[type]->wn, type); + } + } + } + else if ((type > -1) && wireless_popup.popup && (!wireless_current[type])) + { + evas_object_hide(wireless_popup.popup); + evas_object_del(wireless_popup.popup); + } + EINA_LIST_FOREACH(instances, l, inst) + { + _wireless_gadget_refresh(inst); + type = inst->tooltip.type; + if (type < 0) continue; + if (prev[type] && + ((!wireless_current[type]) || + ((wireless_current[type] != prev[type]) && (!eina_streq(wireless_current[type]->wn->name, prev[type]->wn->name))))) + { + elm_object_tooltip_hide(inst->icon[type]); + continue; + } + if (inst->tooltip.method) + elm_object_text_set(inst->tooltip.method, _wireless_tooltip_method_name()); + if (inst->tooltip.address) + elm_object_text_set(inst->tooltip.address, wireless_current[type]->address); + if (inst->tooltip.signal) + { + char buf[32]; + + snprintf(buf, sizeof(buf), "%u%%", wireless_current[type]->wn->strength); + elm_object_text_set(inst->tooltip.signal, buf); + } + } +} + +static Eina_Bool +_wireless_networks_count(const void *cont EINA_UNUSED, void *data, void *fdata EINA_UNUSED) +{ + Wireless_Network *wn = data; + + wireless_network_count[wn->type]++; + return EINA_TRUE; +} + +EINTERN Eina_Array * +wireless_networks_set(Eina_Array *networks) +{ + Eina_Array *prev = wireless_networks; + + wireless_networks = networks; + memset(&wireless_network_count, 0, sizeof(wireless_network_count)); + eina_array_foreach(networks, _wireless_networks_count, NULL); + if (wireless_popup.popup && wireless_popup.items) + { + elm_list_clear(wireless_popup.content); + eina_hash_free_buckets(wireless_popup.items); + _wireless_popup_list_populate(); + } + + return prev; +} + +EINTERN void +wireless_airplane_mode_set(Eina_Bool enabled) +{ + wireless_offline = enabled; + if (enabled == e_config->mode.offline) return; + e_config->mode.offline = !!enabled; + e_config_mode_changed(); + e_config_save_queue(); +} + +static void +_wireless_auth_del(void *data, Evas_Object *popup) +{ + Wireless_Auth_Popup *p = data; + + if (!p->sent) + p->cb(p->data, NULL); + free(p); + wireless_auth_popup = NULL; + evas_object_hide(popup); + evas_object_del(popup); + if (!wireless_auth_pending) return; + wireless_auth_popup = eina_list_data_get(wireless_auth_pending); + wireless_auth_pending = eina_list_remove_list(wireless_auth_pending, wireless_auth_pending); + evas_object_show(wireless_auth_popup->popup); + e_comp_object_util_autoclose(wireless_auth_popup->popup, + _wireless_auth_del, e_comp_object_util_autoclose_on_escape, wireless_auth_popup); +} + +static void +_wireless_auth_send(void *data, Evas_Object *obj, void *event_info EINA_UNUSED) +{ + Wireless_Auth_Popup *p = data; + Eina_Array *arr = NULL; + Evas_Object *tb, *o; + unsigned int row = 1; + + tb = evas_object_data_get(obj, "table"); + do + { + const char *txt; + + o = elm_table_child_get(tb, 0, row); + if (!o) break; + if (!arr) arr = eina_array_new(2); + txt = elm_object_text_get(o); + eina_array_push(arr, txt); + o = elm_object_content_get(o); + /* skip checkboxes */ + if (!strncmp(txt, "Pass", 4)) row++; + eina_array_push(arr, elm_object_text_get(o)); + row += 2; + } while (1); + p->cb(p->data, arr); + p->sent = 1; + eina_array_free(arr); + e_comp_object_util_autoclose(NULL, NULL, NULL, NULL); +} + +static void +_wireless_auth_password_toggle(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + elm_entry_password_set(data, !elm_entry_password_get(data)); +} + +EINTERN void +wireless_authenticate(const Eina_Array *fields, Wireless_Auth_Cb cb, void *data) +{ + Evas_Object *popup, *tb, *lbl, *entry = NULL; + Eina_Iterator *it; + const char *f; + Wireless_Auth_Popup *p; + int row = 0; + + p = E_NEW(Wireless_Auth_Popup, 1); + p->cb = cb; + p->data = data; + if (wireless_popup.popup) + { + evas_object_hide(wireless_popup.popup); + evas_object_del(wireless_popup.popup); + } + + popup = elm_popup_add(e_comp->elm); + elm_popup_allow_events_set(popup, 1); + elm_popup_scrollable_set(popup, 1); + + tb = elm_table_add(popup); + evas_object_show(tb); + elm_object_content_set(popup, tb); + + lbl = elm_label_add(popup); + evas_object_show(lbl); + elm_object_text_set(lbl, _("Authentication Required")); + elm_table_pack(tb, lbl, 0, row++, 2, 1); + + it = eina_array_iterator_new(fields); + EINA_ITERATOR_FOREACH(it, f) + { + Evas_Object *o; + char buf[1024]; + Evas_Object *ck; + + o = _wireless_popup_table_entry_row(tb, f, _wireless_auth_send, p, &row); + if (strncmp(f, "Pass", 4)) continue; + if (!entry) entry = o; + elm_entry_password_set(o, 1); + + ck = elm_check_add(tb); + evas_object_show(ck); + E_ALIGN(ck, 0, -1); + snprintf(buf, sizeof(buf), _("Show %s"), f); + evas_object_smart_callback_add(ck, "changed", _wireless_auth_password_toggle, o); + elm_object_text_set(ck, buf); + elm_table_pack(tb, ck, 0, row++, 2, 1); + } + popup = e_comp_object_util_add(popup, E_COMP_OBJECT_TYPE_NONE); + p->popup = popup; + evas_object_resize(popup, e_zone_current_get()->w / 4, e_zone_current_get()->h / 3); + evas_object_layer_set(popup, E_LAYER_POPUP); + e_comp_object_util_center(popup); + if (wireless_auth_popup) + wireless_auth_pending = eina_list_append(wireless_auth_pending, p); + else + { + wireless_auth_popup = p; + evas_object_show(popup); + e_comp_object_util_autoclose(popup, _wireless_auth_del, + e_comp_object_util_autoclose_on_escape, p); + elm_object_focus_set(entry, 1); + } +} + +EINTERN void +wireless_authenticate_cancel(void) +{ + if (!wireless_auth_popup) return; + evas_object_hide(wireless_auth_popup->popup); + evas_object_del(wireless_auth_popup->popup); +} + +static void +_wireless_auth_external_deny(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + /* FIXME */ + free(data); + auth_popup = 0; +} + +static void +_wireless_auth_external_allow(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + char *sb, *uri = data; + const char *bindir; + size_t size = PATH_MAX, len; + + bindir = e_prefix_bin_get(); + len = strlen(bindir); + sb = malloc(size); + snprintf(sb, size, "%s/enlightenment_open", bindir); + sb = e_util_string_append_quoted(sb, &size, &len, uri); + DBG("launched command: %s", sb); + ecore_exe_run(sb, NULL); + free(sb); + free(uri); + auth_popup = 0; +} + +EINTERN void +wireless_authenticate_external(Wireless_Network *wn, const char *url) +{ + char buf[1024]; + Eina_List *l; + Instance *inst; + + EINA_LIST_FOREACH(instances, l, inst) + if (wireless_popup.popup) + { + evas_object_hide(wireless_popup.popup); + evas_object_del(wireless_popup.popup); + } + if (wn->type == WIRELESS_SERVICE_TYPE_ETHERNET) + snprintf(buf, sizeof(buf), _("Ethernet connection wants to open a url:<br>%s"), url); + else + snprintf(buf, sizeof(buf), _("Network '%s' wants to open a url:<br>%s"), wn->name, url); + EINA_LIST_FOREACH(instances, l, inst) + { + if (!inst->icon[wn->type]) continue; + e_gadget_util_allow_deny_ctxpopup(inst->box, buf, _wireless_auth_external_allow, _wireless_auth_external_deny, strdup(url)); + auth_popup = 1; + break; + } +} diff --git a/src/modules/wireless/wireless.h b/src/modules/wireless/wireless.h new file mode 100644 index 0000000..883f80f --- /dev/null +++ b/src/modules/wireless/wireless.h @@ -0,0 +1,134 @@ +#ifndef E_WIRELESS_H +# define E_WIRELESS_H + +#include "e.h" + +typedef enum +{ + WIRELESS_SERVICE_TYPE_NONE = -1, + WIRELESS_SERVICE_TYPE_ETHERNET, + WIRELESS_SERVICE_TYPE_WIFI, + WIRELESS_SERVICE_TYPE_BLUETOOTH, + WIRELESS_SERVICE_TYPE_CELLULAR, + WIRELESS_SERVICE_TYPE_LAST, +} Wireless_Service_Type; + +typedef enum +{ + WIRELESS_NETWORK_STATE_NONE, + WIRELESS_NETWORK_STATE_CONFIGURING, + WIRELESS_NETWORK_STATE_CONNECTED, + WIRELESS_NETWORK_STATE_ONLINE, + WIRELESS_NETWORK_STATE_FAILURE, +} Wireless_Network_State; + +typedef enum +{ + WIRELESS_NETWORK_SECURITY_NONE = 0, + WIRELESS_NETWORK_SECURITY_WEP = (1 << 0), + WIRELESS_NETWORK_SECURITY_PSK = (1 << 1), + WIRELESS_NETWORK_SECURITY_IEEE8021X = (1 << 2), + WIRELESS_NETWORK_SECURITY_WPS = (1 << 3), +} Wireless_Network_Security; + +typedef enum +{ + WIRELESS_NETWORK_IPV4_METHOD_OFF, + WIRELESS_NETWORK_IPV4_METHOD_MANUAL, + WIRELESS_NETWORK_IPV4_METHOD_DHCP, + WIRELESS_NETWORK_IPV4_METHOD_FIXED, +} Wireless_Network_IPv4_Method; + +typedef enum +{ + WIRELESS_NETWORK_IPV6_METHOD_OFF, + WIRELESS_NETWORK_IPV6_METHOD_MANUAL, + WIRELESS_NETWORK_IPV6_METHOD_AUTO, + WIRELESS_NETWORK_IPV6_METHOD_6TO4, + WIRELESS_NETWORK_IPV6_METHOD_FIXED, +} Wireless_Network_IPv6_Method; + +typedef enum +{ + WIRELESS_NETWORK_IPV6_PRIVACY_DISABLED, + WIRELESS_NETWORK_IPV6_PRIVACY_ENABLED, + WIRELESS_NETWORK_IPV6_PRIVACY_PREFERRED, +} Wireless_Network_IPv6_Privacy; + +typedef enum +{ + WIRELESS_PROXY_TYPE_DIRECT, + WIRELESS_PROXY_TYPE_MANUAL, + WIRELESS_PROXY_TYPE_AUTO, +} Wireless_Proxy_Type; + +typedef struct Wireless_Network Wireless_Network; + +typedef Eina_Bool (*Wireless_Network_Connect_Cb)(Wireless_Network *); + +struct Wireless_Network +{ + Eina_Stringshare *path;//dbus path + Eina_Stringshare *name; + Wireless_Network_Security security; + Wireless_Network_State state; + Wireless_Service_Type type; + uint8_t strength; + + Wireless_Network_Connect_Cb connect_cb; +}; + +typedef struct Wireless_Connection +{ + Wireless_Network *wn; + unsigned int method; + Eina_Stringshare *address; + Eina_Stringshare *gateway; + union + { + struct + { + Eina_Stringshare *netmask; + } v4; + struct + { + Eina_Stringshare *prefixlength; + Wireless_Network_IPv6_Privacy privacy; + } v6; + } ip; + + Eina_Array *domain_servers; + Eina_Array *name_servers; + Eina_Array *time_servers; + + Wireless_Proxy_Type proxy_type; + Eina_Stringshare *proxy_url; + Eina_Array *proxy_servers; + Eina_Array *proxy_excludes; + Eina_Bool ipv6 : 1; + Eina_Bool favorite : 1; +} Wireless_Connection; + +typedef void (*Wireless_Auth_Cb)(void *data, const Eina_Array *fields); + +extern Eldbus_Connection *dbus_conn; + +EINTERN void wireless_service_type_available_set(Eina_Bool *avail); +EINTERN void wireless_service_type_enabled_set(Eina_Bool *enabled); +EINTERN void wireless_wifi_current_networks_set(Wireless_Connection **current); +EINTERN Eina_Array *wireless_networks_set(Eina_Array *networks); +EINTERN void wireless_airplane_mode_set(Eina_Bool enabled); +EINTERN void wireless_authenticate(const Eina_Array *fields, Wireless_Auth_Cb cb, void *data); +EINTERN void wireless_authenticate_cancel(void); +EINTERN void wireless_authenticate_external(Wireless_Network *wn, const char *url); + +static inline void +array_clear(Eina_Array *arr) +{ + if (arr) + while (eina_array_count(arr)) + eina_stringshare_del(eina_array_pop(arr)); + eina_array_free(arr); +} + +#endif --