efl/src/lib/ecore_con/efl_net_control-connman.c

1259 lines
40 KiB
C

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "Ecore.h"
#include "Ecore_Con.h"
#include "ecore_con_private.h"
#include "efl_net-connman.h"
typedef struct
{
/* Eldbus_Proxy/Eldbus_Object keeps a list of pending calls, but
* they are reference counted singletons and will only cancel
* pending calls when everything is gone. However we operate on
* our private data, that may be gone before other refs. So
* keep the pending list.
*/
Eina_List *pending;
Eina_List *signal_handlers;
Eina_List *technologies;
Eina_List *access_points;
Eldbus_Service_Interface *agent; /* net.connman.Agent */
Eldbus_Pending *agent_pending;
struct {
Eldbus_Message *msg;
} agent_request_input;
Efl_Net_Control_State state;
Eina_Bool radios_offline;
Eina_Bool operating; /* connman exists */
Eina_Bool agent_enabled;
struct {
unsigned char radios_offline; /* 0xff = not requested */
Eldbus_Pending *radios_offline_pending;
} request;
} Efl_Net_Control_Manager_Data;
#define MY_CLASS EFL_NET_CONTROL_MANAGER_CLASS
static void _efl_net_control_manager_agent_enabled_set(Eo *o, Efl_Net_Control_Manager_Data *pd, Eina_Bool agent_enabled);
static Eo *
_efl_net_control_technology_find(const Efl_Net_Control_Manager_Data *pd, const char *path)
{
const Eina_List *n;
Eo *child;
EINA_LIST_FOREACH(pd->technologies, n, child)
{
Eina_Stringshare *cp = efl_net_connman_technology_path_get(child);
if (strcmp(path, cp) == 0)
return child;
}
WRN("technology path='%s' not found!", path);
return NULL;
}
static Eo *
_efl_net_control_access_point_find(const Efl_Net_Control_Manager_Data *pd, const char *path)
{
const Eina_List *n;
Eo *child;
EINA_LIST_FOREACH(pd->access_points, n, child)
{
Eina_Stringshare *cp = efl_net_connman_access_point_path_get(child);
if (strcmp(path, cp) == 0)
return child;
}
DBG("access_point path='%s' not found!", path);
return NULL;
}
static Eldbus_Message *
_efl_net_control_agent_release(const Eldbus_Service_Interface *service, const Eldbus_Message *msg)
{
Eo *o = eldbus_service_object_data_get(service, "efl_net");
Efl_Net_Control_Manager_Data *pd = efl_data_scope_get(o, MY_CLASS);
DBG("Agent %p is released %s", o, eldbus_message_path_get(msg));
EINA_SAFETY_ON_NULL_GOTO(o, end);
EINA_SAFETY_ON_NULL_GOTO(pd, end);
pd->agent_enabled = EINA_FALSE;
efl_event_callback_call(o, EFL_NET_CONTROL_MANAGER_EVENT_AGENT_RELEASED, NULL);
end:
return eldbus_message_method_return_new(msg);
}
static Eldbus_Message *
_efl_net_control_agent_cancel(const Eldbus_Service_Interface *service, const Eldbus_Message *msg)
{
Eo *o = eldbus_service_object_data_get(service, "efl_net");
Efl_Net_Control_Manager_Data *pd = efl_data_scope_get(o, MY_CLASS);
Eldbus_Message *reply;
DBG("Agent %p request canceled %s", o, eldbus_message_path_get(msg));
EINA_SAFETY_ON_NULL_GOTO(o, end);
EINA_SAFETY_ON_NULL_GOTO(pd, end);
if (!pd->agent_request_input.msg) goto end;
reply = eldbus_message_error_new(pd->agent_request_input.msg,
"net.connman.Error.Canceled",
"Cancel requested.");
eldbus_connection_send(efl_net_connman_connection_get(), reply, NULL, NULL, -1.0);
eldbus_message_unref(pd->agent_request_input.msg);
pd->agent_request_input.msg = NULL;
end:
return eldbus_message_method_return_new(msg);
}
static const char *
_efl_net_control_agent_entry_type_get(Eldbus_Message_Iter *dict)
{
Eldbus_Message_Iter *array, *entry;
if (!eldbus_message_iter_arguments_get(dict, "a{sv}", &array))
{
ERR("Expected dict, got %s", eldbus_message_iter_signature_get(dict));
return NULL;
}
while (eldbus_message_iter_get_and_next(array, 'e', &entry))
{
Eldbus_Message_Iter *var;
const char *key, *c;
if (!eldbus_message_iter_arguments_get(entry, "sv", &key, &var))
{
ERR("Expected dict entry, got %s", eldbus_message_iter_signature_get(entry));
continue;
}
if (!eldbus_message_iter_arguments_get(var, "s", &c))
{
ERR("Expected string, got %s", eldbus_message_iter_signature_get(var));
continue;
}
if (strcmp(key, "Type") == 0)
return c;
}
return NULL;
}
static Efl_Net_Control_Agent_Request_Input_Information *
_efl_net_control_agent_informational_get(const char *name, Eldbus_Message_Iter *dict)
{
Eldbus_Message_Iter *array, *entry;
Eina_Bool is_informational = EINA_FALSE;
const char *value = NULL;
if (!eldbus_message_iter_arguments_get(dict, "a{sv}", &array))
{
ERR("Expected dict, got %s", eldbus_message_iter_signature_get(dict));
return NULL;
}
while (eldbus_message_iter_get_and_next(array, 'e', &entry))
{
Eldbus_Message_Iter *var;
const char *key, *c;
if (!eldbus_message_iter_arguments_get(entry, "sv", &key, &var))
{
ERR("Expected dict entry, got %s", eldbus_message_iter_signature_get(entry));
continue;
}
if (!eldbus_message_iter_arguments_get(var, "s", &c))
{
ERR("Expected string, got %s", eldbus_message_iter_signature_get(var));
continue;
}
if (strcmp(key, "Requirement") == 0)
{
if (strcmp(c, "informational") == 0)
is_informational = EINA_TRUE;
}
else if (strcmp(key, "Value") == 0)
value = c;
else if (strcmp(key, "Alternates") == 0)
{ }
else if (strcmp(key, "Type") == 0)
{ }
else WRN("Unknown key '%s' for requested input entry %s", key, name);
}
if (is_informational && value)
{
Efl_Net_Control_Agent_Request_Input_Information *info;
info = calloc(1, sizeof(*info));
EINA_SAFETY_ON_NULL_RETURN_VAL(info, NULL);
info->name = name;
info->value = value;
DBG("Field %s=%s is informational", name, value);
return info;
}
DBG("Field %s=%s is not informational", name, value);
return NULL;
}
static Eldbus_Message *
_efl_net_control_agent_request_input(const Eldbus_Service_Interface *service, const Eldbus_Message *msg)
{
Eo *o = eldbus_service_object_data_get(service, "efl_net");
Efl_Net_Control_Manager_Data *pd = efl_data_scope_get(o, MY_CLASS);
Efl_Net_Control_Agent_Request_Input event = { };
Efl_Net_Control_Agent_Request_Input_Information *info;
Eldbus_Message_Iter *array, *entry;
Eina_Array infos;
const char *path;
DBG("Agent %p requested input %s", o, eldbus_message_path_get(msg));
EINA_SAFETY_ON_NULL_GOTO(o, err);
EINA_SAFETY_ON_NULL_GOTO(pd, err);
if (pd->agent_request_input.msg)
{
Eldbus_Message *reply = eldbus_message_error_new(pd->agent_request_input.msg,
"net.connman.Error.OperationAborted",
"Received new input request, aborting previous.");
eldbus_connection_send(efl_net_connman_connection_get(), reply, NULL, NULL, -1.0);
eldbus_message_unref(pd->agent_request_input.msg);
pd->agent_request_input.msg = NULL;
}
if (!eldbus_message_arguments_get(msg, "oa{sv}", &path, &array))
goto err;
eina_array_step_set(&infos, sizeof (Eina_Array), 4);
event.informational = eina_array_accessor_new(&infos);
event.access_point = _efl_net_control_access_point_find(pd, path);
while (eldbus_message_iter_get_and_next(array, 'e', &entry))
{
Eldbus_Message_Iter *var;
const char *name;
if (!eldbus_message_iter_arguments_get(entry, "sv", &name, &var))
goto err;
if (strcmp(name, "Name") == 0)
event.fields |= EFL_NET_CONTROL_AGENT_REQUEST_INPUT_FIELD_NAME;
else if (strcmp(name, "SSID") == 0)
event.fields |= EFL_NET_CONTROL_AGENT_REQUEST_INPUT_FIELD_SSID;
else if (strcmp(name, "WPS") == 0)
event.fields |= EFL_NET_CONTROL_AGENT_REQUEST_INPUT_FIELD_WPS;
else if ((strcmp(name, "Identity") == 0) ||
(strcmp(name, "Username") == 0))
event.fields |= EFL_NET_CONTROL_AGENT_REQUEST_INPUT_FIELD_USERNAME;
else if ((strcmp(name, "Passphrase") == 0) ||
(strcmp(name, "Password") == 0))
{
const char *value = _efl_net_control_agent_entry_type_get(var);
if ((!event.passphrase_type) && (value))
event.passphrase_type = value;
event.fields |= EFL_NET_CONTROL_AGENT_REQUEST_INPUT_FIELD_PASSPHRASE;
}
else
{
info = _efl_net_control_agent_informational_get(name, var);
if (info)
eina_array_push(&infos, info);
else
WRN("Unknown field name '%s'", name);
}
}
pd->agent_request_input.msg = eldbus_message_ref((Eldbus_Message *)msg);
efl_event_callback_call(o, EFL_NET_CONTROL_MANAGER_EVENT_AGENT_REQUEST_INPUT, &event);
eina_accessor_free(event.informational);
while ((info = eina_array_pop(&infos))) free(info);
eina_array_flush(&infos);
return NULL; /* reply later */
err:
return eldbus_message_method_return_new(msg);
}
static Eldbus_Message *
_efl_net_control_agent_report_error(const Eldbus_Service_Interface *service, const Eldbus_Message *msg)
{
Eo *o = eldbus_service_object_data_get(service, "efl_net");
Efl_Net_Control_Manager_Data *pd = efl_data_scope_get(o, MY_CLASS);
const char *path, *err_msg;
Efl_Net_Control_Agent_Error event = { };
if (!eldbus_message_arguments_get(msg, "os", &path, &err_msg))
{
ERR("Invalid net.connman.ReportError signature=%s", eldbus_message_signature_get(msg));
goto end;
}
DBG("Agent %p error %s: %s", o, path, err_msg);
EINA_SAFETY_ON_NULL_GOTO(o, end);
EINA_SAFETY_ON_NULL_GOTO(pd, end);
event.access_point = _efl_net_control_access_point_find(pd, path);
event.message = err_msg;
efl_event_callback_call(o, EFL_NET_CONTROL_MANAGER_EVENT_AGENT_ERROR, &event);
end:
return eldbus_message_method_return_new(msg);
}
static Eldbus_Message *
_efl_net_control_agent_request_browser(const Eldbus_Service_Interface *service, const Eldbus_Message *msg)
{
Eo *o = eldbus_service_object_data_get(service, "efl_net");
Efl_Net_Control_Manager_Data *pd = efl_data_scope_get(o, MY_CLASS);
const char *path, *url;
Efl_Net_Control_Agent_Browser_Url event = { };
if (!eldbus_message_arguments_get(msg, "os", &path, &url))
{
ERR("Invalid net.connman.RequestBrowser signature=%s", eldbus_message_signature_get(msg));
goto end;
}
DBG("Agent %p browser %s: %s", o, path, url);
EINA_SAFETY_ON_NULL_GOTO(o, end);
EINA_SAFETY_ON_NULL_GOTO(pd, end);
event.access_point = _efl_net_control_access_point_find(pd, path);
event.url = url;
efl_event_callback_call(o, EFL_NET_CONTROL_MANAGER_EVENT_AGENT_BROWSER_URL, &event);
end:
return eldbus_message_method_return_new(msg);
}
static Eldbus_Message *
_efl_net_control_agent_report_peer_error(const Eldbus_Service_Interface *service, const Eldbus_Message *msg)
{
Eo *o = eldbus_service_object_data_get(service, "efl_net");
const char *path, *err_msg;
if (!eldbus_message_arguments_get(msg, "os", &path, &err_msg))
{
ERR("Invalid net.connman.ReportPeerError signature=%s", eldbus_message_signature_get(msg));
goto end;
}
DBG("Agent %p peer error %s: %s", o, path, err_msg);
end:
return eldbus_message_method_return_new(msg);
}
static Eldbus_Message *
_efl_net_control_agent_request_peer_authorization(const Eldbus_Service_Interface *service, const Eldbus_Message *msg)
{
Eo *o = eldbus_service_object_data_get(service, "efl_net");
DBG("Agent %p peer authorization required", o);
return eldbus_message_method_return_new(msg);
}
static const Eldbus_Service_Interface_Desc _efl_net_control_agent_desc = {
.interface = "net.connman.Agent",
.methods = (const Eldbus_Method []) {
{
.member = "Release",
.in = NULL,
.out = NULL,
.cb = _efl_net_control_agent_release,
.flags = 0
},
{
.member = "ReportError",
.in = ELDBUS_ARGS({"o", "service"}, {"s", "message"}),
.out = NULL,
.cb = _efl_net_control_agent_report_error,
.flags = 0
},
{
.member = "RequestBrowser",
.in = ELDBUS_ARGS({"o", "service"}, {"s", "url"}),
.out = NULL,
.cb = _efl_net_control_agent_request_browser,
.flags = 0
},
{
.member = "RequestInput",
.in = ELDBUS_ARGS({"o", "service"}, {"a{sv}", "fields"}),
.out = NULL,
.cb = _efl_net_control_agent_request_input,
.flags = 0
},
{
.member = "Cancel",
.in = NULL,
.out = NULL,
.cb = _efl_net_control_agent_cancel,
.flags = 0
},
{
.member = "ReportPeerError",
.in = ELDBUS_ARGS({"o", "peer"}, {"s", "message"}),
.out = NULL,
.cb = _efl_net_control_agent_report_peer_error,
.flags = 0
},
{
.member = "RequestPeerAuthorization",
.in = ELDBUS_ARGS({"o", "peer"}, {"a{sv}", "fields"}),
.out = NULL,
.cb = _efl_net_control_agent_request_peer_authorization,
.flags = 0
},
{ NULL, NULL, NULL, NULL, 0 }
},
.signals = NULL,
.properties = NULL,
.default_get = NULL,
.default_set = NULL
};
static void
_efl_net_control_technology_added_internal(Eo *o, Efl_Net_Control_Manager_Data *pd, Eldbus_Message_Iter *itr)
{
const char *path;
Eldbus_Message_Iter *array;
Eo *child;
if (!eldbus_message_iter_arguments_get(itr, "oa{sv}", &path, &array))
{
ERR("could not get technology path or properties, signature=%s", eldbus_message_iter_signature_get(itr));
return;
}
child = efl_net_connman_technology_new(o, path, array);
if (!child)
{
ERR("could not create technology for path=%s", path);
return;
}
pd->technologies = eina_list_append(pd->technologies, child);
DBG("Technology Added Path=%s", path);
efl_event_callback_call(o, EFL_NET_CONTROL_MANAGER_EVENT_TECHNOLOGY_ADD, child);
}
static void
_efl_net_control_technology_added(void *data, const Eldbus_Message *msg)
{
Eo *o = data;
Efl_Net_Control_Manager_Data *pd = efl_data_scope_get(o, MY_CLASS);
Eldbus_Message_Iter *itr = eldbus_message_iter_get(msg);
_efl_net_control_technology_added_internal(o, pd, itr);
}
static void
_efl_net_control_technology_removed(void *data, const Eldbus_Message *msg)
{
Eo *o = data;
Efl_Net_Control_Manager_Data *pd = efl_data_scope_get(o, MY_CLASS);
Eo *child;
const char *path;
if (!eldbus_message_arguments_get(msg, "o", &path))
{
ERR("Could not get removed technology's path");
return;
}
child = _efl_net_control_technology_find(pd, path);
if (!child)
{
WRN("Technology path '%s' doesn't exist", path);
return;
}
pd->technologies = eina_list_remove(pd->technologies, child);
DBG("Technology Removed Path=%s", path);
efl_event_callback_call(o, EFL_NET_CONTROL_MANAGER_EVENT_TECHNOLOGY_DEL, child);
efl_del(child);
}
static void
_efl_net_control_access_point_added(Eo *o, Efl_Net_Control_Manager_Data *pd, const char *path, Eldbus_Message_Iter *properties, unsigned int priority)
{
Eo *child;
child = efl_net_connman_access_point_new(o, path, properties, priority);
if (!child)
{
ERR("could not create access point for path=%s", path);
return;
}
pd->access_points = eina_list_append(pd->access_points, child);
DBG("Access Point Added Path=%s", path);
efl_event_callback_call(o, EFL_NET_CONTROL_MANAGER_EVENT_ACCESS_POINT_ADD, child);
}
static void
_efl_net_control_access_point_updated(Eo *o, Efl_Net_Control_Manager_Data *pd, const char *path, Eldbus_Message_Iter *properties, unsigned int priority)
{
Eo *child = _efl_net_control_access_point_find(pd, path);
if (!child)
{
_efl_net_control_access_point_added(o, pd, path, properties, priority);
return;
}
efl_net_connman_access_point_update(child, properties, priority);
}
static void
_efl_net_control_access_point_removed(Eo *o, Efl_Net_Control_Manager_Data *pd, const char *path)
{
Eo *child = _efl_net_control_access_point_find(pd, path);
if (!child)
{
WRN("Access Point path '%s' doesn't exist", path);
return;
}
pd->access_points = eina_list_remove(pd->access_points, child);
DBG("Access Point Removed Path=%s", path);
efl_event_callback_call(o, EFL_NET_CONTROL_MANAGER_EVENT_ACCESS_POINT_DEL, child);
efl_del(child);
}
static int
_efl_net_control_access_point_sort_cb(const void *d1, const void *d2)
{
const Eo *a = d1;
const Eo *b = d2;
unsigned int pa = efl_net_control_access_point_priority_get(a);
unsigned int pb = efl_net_control_access_point_priority_get(b);
if (pa < pb) return -1;
else if (pa > pb) return 1;
return 0;
}
static void
_efl_net_control_services_changed(void *data, const Eldbus_Message *msg)
{
Eo *o = data;
Efl_Net_Control_Manager_Data *pd = efl_data_scope_get(o, MY_CLASS);
Eldbus_Message_Iter *changed, *invalidated, *sub;
const char *path;
unsigned int priority;
if (!eldbus_message_arguments_get(msg, "a(oa{sv})ao", &changed, &invalidated))
{
ERR("could not get services changed, signature=%s", eldbus_message_signature_get(msg));
return;
}
efl_ref(o); /* will trigger events, which call user which may delete us */
priority = 0;
while (eldbus_message_iter_get_and_next(changed, 'r', &sub))
{
Eldbus_Message_Iter *properties;
if (!eldbus_message_iter_arguments_get(sub, "oa{sv}", &path, &properties))
{
ERR("could not get access point path or properties, signature=%s", eldbus_message_iter_signature_get(sub));
continue;
}
_efl_net_control_access_point_updated(o, pd, path, properties, priority++);
}
while (eldbus_message_iter_get_and_next(invalidated, 'o', &path))
_efl_net_control_access_point_removed(o, pd, path);
pd->access_points = eina_list_sort(pd->access_points, 0, _efl_net_control_access_point_sort_cb);
efl_event_callback_call(o, EFL_NET_CONTROL_MANAGER_EVENT_ACCESS_POINTS_CHANGED, NULL);
efl_unref(o);
}
static Efl_Net_Control_State
_efl_net_control_state_from_str(const char *str)
{
const struct {
const char *name;
Efl_Net_Control_State state;
} *itr, map[] = {
{"offline", EFL_NET_CONTROL_STATE_OFFLINE},
{"idle", EFL_NET_CONTROL_STATE_OFFLINE},
{"ready", EFL_NET_CONTROL_STATE_LOCAL},
{"online", EFL_NET_CONTROL_STATE_ONLINE},
{ }
};
for (itr = map; itr->name != NULL; itr++)
if (strcmp(itr->name, str) == 0) return itr->state;
ERR("Unknown State '%s', assume offline.", str);
return EFL_NET_CONTROL_STATE_OFFLINE;
}
static void
_efl_net_control_property_state_changed(Eo *o, Efl_Net_Control_Manager_Data *pd, Eldbus_Message_Iter *value)
{
const char *str;
Efl_Net_Control_State state;
if (!eldbus_message_iter_arguments_get(value, "s", &str))
{
ERR("Expected string, got %s", eldbus_message_iter_signature_get(value));
return;
}
state = _efl_net_control_state_from_str(str);
if (pd->state == state) return;
pd->state = state;
DBG("state=%d (%s)", state, str);
efl_event_callback_call(o, EFL_NET_CONTROL_MANAGER_EVENT_STATE_CHANGED, NULL);
}
static void
_efl_net_control_property_radios_offline_changed(Eo *o, Efl_Net_Control_Manager_Data *pd, Eldbus_Message_Iter *value)
{
Eina_Bool offline;
if (!eldbus_message_iter_arguments_get(value, "b", &offline))
{
ERR("Expected boolean, got %s", eldbus_message_iter_signature_get(value));
return;
}
if (pd->radios_offline == offline) return;
pd->radios_offline = offline;
DBG("radios offline=%hhu", offline);
efl_event_callback_call(o, EFL_NET_CONTROL_MANAGER_EVENT_RADIOS_OFFLINE_CHANGED, NULL);
}
static void
_efl_net_control_property_changed_internal(Eo *o, Efl_Net_Control_Manager_Data *pd, Eldbus_Message_Iter *itr)
{
Eldbus_Message_Iter *value;
const char *name;
if (!eldbus_message_iter_arguments_get(itr, "sv", &name, &value))
{
ERR("Unexpected signature: %s", eldbus_message_iter_signature_get(itr));
return;
}
if (strcmp(name, "State") == 0)
_efl_net_control_property_state_changed(o, pd, value);
else if (strcmp(name, "OfflineMode") == 0)
_efl_net_control_property_radios_offline_changed(o, pd, value);
else if (strcmp(name, "SessionMode") == 0)
{ }
else
WRN("Unknown property name: %s", name);
}
static void
_efl_net_control_property_changed(void *data, const Eldbus_Message *msg)
{
Eo *o = data;
Efl_Net_Control_Manager_Data *pd = efl_data_scope_get(o, MY_CLASS);
Eldbus_Message_Iter *itr;
itr = eldbus_message_iter_get(msg);
_efl_net_control_property_changed_internal(o, pd, itr);
}
static void
_efl_net_control_properties_get_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending)
{
Eo *o = data;
Efl_Net_Control_Manager_Data *pd = efl_data_scope_get(o, MY_CLASS);
Eldbus_Message_Iter *array, *entry;
const char *err_name, *err_msg;
pd->pending = eina_list_remove(pd->pending, pending);
if (eldbus_message_error_get(msg, &err_name, &err_msg))
{
ERR("Could not get properties %p: %s=%s", o, err_name, err_msg);
return;
}
if (!eldbus_message_arguments_get(msg, "a{sv}", &array))
{
ERR("Expected dict ('a{sv}'), got '%s'", eldbus_message_signature_get(msg));
return;
}
efl_ref(o); /* properties will trigger events, which call user which may delete us */
/* force changed to be emitted */
pd->radios_offline = 0xff;
pd->state = 0xff;
while (eldbus_message_iter_get_and_next(array, 'e', &entry))
_efl_net_control_property_changed_internal(o, pd, entry);
efl_unref(o);
}
static void
_efl_net_control_technologies_get_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending)
{
Eo *o = data;
Efl_Net_Control_Manager_Data *pd = efl_data_scope_get(o, MY_CLASS);
Eldbus_Message_Iter *array, *entry;
const char *err_name, *err_msg;
pd->pending = eina_list_remove(pd->pending, pending);
if (eldbus_message_error_get(msg, &err_name, &err_msg))
{
ERR("Could not get technologies %p: %s=%s", o, err_name, err_msg);
return;
}
if (!eldbus_message_arguments_get(msg, "a(oa{sv})", &array))
{
ERR("Expected 'a{oa{sv}}', got '%s'", eldbus_message_signature_get(msg));
return;
}
efl_ref(o); /* will trigger events, which call user which may delete us */
while (eldbus_message_iter_get_and_next(array, 'r', &entry))
_efl_net_control_technology_added_internal(o, pd, entry);
efl_unref(o);
}
static void
_efl_net_control_services_get_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending)
{
Eo *o = data;
Efl_Net_Control_Manager_Data *pd = efl_data_scope_get(o, MY_CLASS);
Eldbus_Message_Iter *array, *sub;
const char *err_name, *err_msg, *path;
unsigned int priority = 0;
pd->pending = eina_list_remove(pd->pending, pending);
if (eldbus_message_error_get(msg, &err_name, &err_msg))
{
ERR("Could not get services %p: %s=%s", o, err_name, err_msg);
return;
}
if (!eldbus_message_arguments_get(msg, "a(oa{sv})", &array))
{
ERR("Expected 'a{oa{sv}}', got '%s'", eldbus_message_signature_get(msg));
return;
}
efl_ref(o); /* will trigger events, which call user which may delete us */
while (eldbus_message_iter_get_and_next(array, 'r', &sub))
{
Eldbus_Message_Iter *properties;
if (!eldbus_message_iter_arguments_get(sub, "oa{sv}", &path, &properties))
{
ERR("could not get access point path or properties, signature=%s", eldbus_message_iter_signature_get(sub));
continue;
}
_efl_net_control_access_point_updated(o, pd, path, properties, priority++);
}
pd->access_points = eina_list_sort(pd->access_points, 0, _efl_net_control_access_point_sort_cb);
efl_event_callback_call(o, EFL_NET_CONTROL_MANAGER_EVENT_ACCESS_POINTS_CHANGED, NULL);
efl_unref(o);
}
static void
_efl_net_control_radios_offline_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending)
{
Eo *o = data;
Efl_Net_Control_Manager_Data *pd = efl_data_scope_get(o, MY_CLASS);
const char *err_name, *err_msg;
Eina_Bool radios_offline = pd->request.radios_offline;
if (pd->request.radios_offline_pending == pending)
{
pd->request.radios_offline_pending = NULL;
pd->request.radios_offline = 0xff;
}
pd->pending = eina_list_remove(pd->pending, pending);
if (eldbus_message_error_get(msg, &err_name, &err_msg))
{
ERR("Could not set property radios_offline=%hhu %p: %s=%s", radios_offline, o, err_name, err_msg);
return;
}
DBG("Set radios_offline=%hhu", radios_offline);
}
static void
_efl_net_control_radios_offline_apply(Eo *o, Efl_Net_Control_Manager_Data *pd)
{
Eldbus_Proxy *mgr = efl_net_connman_manager_get();
Eldbus_Message *msg = eldbus_proxy_method_call_new(mgr, "SetProperty");
Eldbus_Message_Iter *msg_itr, *var;
Eldbus_Pending *p;
if (pd->request.radios_offline_pending)
{
pd->pending = eina_list_remove(pd->pending, pd->request.radios_offline_pending);
eldbus_pending_cancel(pd->request.radios_offline_pending);
pd->request.radios_offline_pending = NULL;
}
EINA_SAFETY_ON_NULL_RETURN(msg);
msg_itr = eldbus_message_iter_get(msg);
EINA_SAFETY_ON_NULL_GOTO(msg_itr, error_send);
eldbus_message_iter_basic_append(msg_itr, 's', "OfflineMode");
var = eldbus_message_iter_container_new(msg_itr, 'v', "b");
eldbus_message_iter_basic_append(var, 'b', pd->request.radios_offline);
eldbus_message_iter_container_close(msg_itr, var);
p = eldbus_proxy_send(mgr, msg, _efl_net_control_radios_offline_cb, o, DEFAULT_TIMEOUT);
EINA_SAFETY_ON_NULL_GOTO(p, error_send);
pd->pending = eina_list_append(pd->pending, p);
pd->request.radios_offline_pending = p;
DBG("setting radios offline=%hhu", pd->request.radios_offline);
return;
error_send:
eldbus_message_unref(msg);
}
static void
_efl_net_control_clear(Eo *o, Efl_Net_Control_Manager_Data *pd)
{
Eo *child;
EINA_LIST_FREE(pd->access_points, child)
{
efl_event_callback_call(o, EFL_NET_CONTROL_MANAGER_EVENT_ACCESS_POINT_DEL, child);
efl_del(child);
}
efl_event_callback_call(o, EFL_NET_CONTROL_MANAGER_EVENT_ACCESS_POINTS_CHANGED, NULL);
EINA_LIST_FREE(pd->technologies, child)
{
efl_event_callback_call(o, EFL_NET_CONTROL_MANAGER_EVENT_TECHNOLOGY_DEL, child);
efl_del(child);
}
pd->state = EFL_NET_CONTROL_STATE_OFFLINE;
efl_event_callback_call(o, EFL_NET_CONTROL_MANAGER_EVENT_STATE_CHANGED, NULL);
pd->radios_offline = EINA_TRUE;
efl_event_callback_call(o, EFL_NET_CONTROL_MANAGER_EVENT_RADIOS_OFFLINE_CHANGED, NULL);
}
static void
_efl_net_control_connman_name_owner_changed(void *data, const char *bus, const char *old_id, const char *new_id)
{
Eo *o = data;
Efl_Net_Control_Manager_Data *pd = efl_data_scope_get(o, MY_CLASS);
Eldbus_Proxy *mgr;
DBG("Name Owner Changed %s: %s->%s", bus, old_id, new_id);
if ((!new_id) || (new_id[0] == '\0'))
{
_efl_net_control_clear(o, pd);
pd->operating = EINA_FALSE;
if (pd->agent_enabled)
{
pd->agent_enabled = EINA_FALSE;
efl_event_callback_call(o, EFL_NET_CONTROL_MANAGER_EVENT_AGENT_RELEASED, NULL);
}
return;
}
mgr = efl_net_connman_manager_get();
EINA_SAFETY_ON_NULL_RETURN(mgr);
#define SH(sig, cb) \
do { \
Eldbus_Signal_Handler *sh = eldbus_proxy_signal_handler_add(mgr, sig, cb, o); \
if (sh) pd->signal_handlers = eina_list_append(pd->signal_handlers, sh); \
else ERR("could not add DBus signal handler %s", sig); \
} while (0)
SH("TechnologyAdded", _efl_net_control_technology_added);
SH("TechnologyRemoved", _efl_net_control_technology_removed);
SH("ServicesChanged", _efl_net_control_services_changed);
SH("PropertyChanged", _efl_net_control_property_changed);
#undef SH
#define CALL(meth, cb) \
do { \
Eldbus_Pending *p = eldbus_proxy_call(mgr, meth, cb, o, DEFAULT_TIMEOUT, ""); \
if (p) pd->pending = eina_list_append(pd->pending, p); \
else ERR("could not call DBus method %s", meth); \
} while (0)
CALL("GetProperties", _efl_net_control_properties_get_cb);
CALL("GetTechnologies", _efl_net_control_technologies_get_cb);
CALL("GetServices", _efl_net_control_services_get_cb);
#undef CALL
pd->operating = EINA_TRUE;
if (pd->request.radios_offline != 0xff) _efl_net_control_radios_offline_apply(o, pd);
if (pd->agent_pending)
{
eldbus_pending_cancel(pd->agent_pending);
pd->agent_pending = NULL;
}
if (pd->agent_enabled)
{
pd->agent_enabled = EINA_FALSE;
_efl_net_control_manager_agent_enabled_set(o, pd, EINA_TRUE);
}
}
void
efl_net_connman_control_access_points_reload(Eo *o)
{
Efl_Net_Control_Manager_Data *pd = efl_data_scope_get(o, MY_CLASS);
Eldbus_Proxy *mgr;
Eldbus_Pending *p;
EINA_SAFETY_ON_NULL_RETURN(pd);
mgr = efl_net_connman_manager_get();
EINA_SAFETY_ON_NULL_RETURN(mgr);
p = eldbus_proxy_call(mgr, "GetServices", _efl_net_control_services_get_cb, o, DEFAULT_TIMEOUT, "");
if (p) pd->pending = eina_list_append(pd->pending, p);
else ERR("could not call DBus method %s", "GetServices");
}
EOLIAN static Eo *
_efl_net_control_manager_efl_object_constructor(Eo *o, Efl_Net_Control_Manager_Data *pd EINA_UNUSED)
{
pd->radios_offline = EINA_TRUE;
pd->request.radios_offline = 0xff;
if (!efl_net_connman_init())
{
ERR("could not initialize connman infrastructure");
return NULL;
}
return efl_constructor(efl_super(o, MY_CLASS));
}
EOLIAN static Eo *
_efl_net_control_manager_efl_object_finalize(Eo *o, Efl_Net_Control_Manager_Data *pd EINA_UNUSED)
{
Eldbus_Connection *conn;
char path[128];
o = efl_finalize(efl_super(o, MY_CLASS));
if (!o) return NULL;
conn = efl_net_connman_connection_get();
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL);
snprintf(path, sizeof(path), "/connman/agent_%p", o);
pd->agent = eldbus_service_interface_register(conn, path, &_efl_net_control_agent_desc);
EINA_SAFETY_ON_NULL_RETURN_VAL(pd->agent, NULL);
eldbus_service_object_data_set(pd->agent, "efl_net", o);
eldbus_name_owner_changed_callback_add(conn, "net.connman", _efl_net_control_connman_name_owner_changed, o, EINA_TRUE);
DBG("waiting for net.connman to show on the DBus system bus...");
return o;
}
EOLIAN static void
_efl_net_control_manager_efl_object_destructor(Eo *o, Efl_Net_Control_Manager_Data *pd)
{
Eldbus_Pending *p;
Eldbus_Signal_Handler *sh;
pd->operating = EINA_FALSE;
pd->request.radios_offline_pending = NULL;
EINA_LIST_FREE(pd->pending, p)
eldbus_pending_cancel(p);
EINA_LIST_FREE(pd->signal_handlers, sh)
eldbus_signal_handler_del(sh);
if (pd->agent_request_input.msg)
{
Eldbus_Message *reply = eldbus_message_error_new(pd->agent_request_input.msg,
"net.connman.Error.OperationAborted",
"Agent is gone.");
eldbus_connection_send(efl_net_connman_connection_get(), reply, NULL, NULL, -1.0);
eldbus_message_unref(pd->agent_request_input.msg);
pd->agent_request_input.msg = NULL;
}
if (pd->agent_enabled)
{
Eldbus_Proxy *mgr = efl_net_connman_manager_get();
/* UnregisterAgent is required since the manager proxy and bus
* may be alive, thus connman won't get any NameOwnerChanged
* or related to know the object is gone
*/
eldbus_proxy_call(mgr, "UnregisterAgent", NULL, NULL, DEFAULT_TIMEOUT,
"o", eldbus_service_object_path_get(pd->agent));
}
if (pd->agent)
{
eldbus_service_object_data_del(pd->agent, "efl_net");
eldbus_service_object_unregister(pd->agent);
pd->agent = NULL;
}
_efl_net_control_clear(o, pd);
efl_destructor(efl_super(o, MY_CLASS));
efl_net_connman_shutdown();
}
EOLIAN static void
_efl_net_control_manager_radios_offline_set(Eo *o, Efl_Net_Control_Manager_Data *pd, Eina_Bool radios_offline)
{
pd->request.radios_offline = radios_offline;
if (pd->operating) _efl_net_control_radios_offline_apply(o, pd);
}
EOLIAN static Eina_Bool
_efl_net_control_manager_radios_offline_get(const Eo *o EINA_UNUSED, Efl_Net_Control_Manager_Data *pd)
{
return pd->radios_offline;
}
EOLIAN static Efl_Net_Control_State
_efl_net_control_manager_state_get(const Eo *o EINA_UNUSED, Efl_Net_Control_Manager_Data *pd)
{
return pd->state;
}
EOLIAN static Eina_Iterator *
_efl_net_control_manager_access_points_get(const Eo *o EINA_UNUSED, Efl_Net_Control_Manager_Data *pd)
{
return eina_list_iterator_new(pd->access_points);
}
EOLIAN static Eina_Iterator *
_efl_net_control_manager_technologies_get(const Eo *o EINA_UNUSED, Efl_Net_Control_Manager_Data *pd)
{
return eina_list_iterator_new(pd->technologies);
}
Efl_Net_Control_Technology *
efl_net_connman_control_find_technology_by_type(Efl_Net_Control_Manager *o, const char *tech_type)
{
Efl_Net_Control_Manager_Data *pd = efl_data_scope_get(o, MY_CLASS);
Efl_Net_Control_Technology_Type desired = efl_net_connman_technology_type_from_str(tech_type);
const Eina_List *n;
Eo *child;
EINA_SAFETY_ON_NULL_RETURN_VAL(pd, NULL);
EINA_LIST_FOREACH(pd->technologies, n, child)
{
Efl_Net_Control_Technology_Type type = efl_net_control_technology_type_get(child);
if (type == desired)
return child;
}
WRN("Could not find technology type %#x '%s'", desired, tech_type);
return NULL;
}
static void
_efl_net_control_agent_register_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending)
{
Eo *o = data;
Efl_Net_Control_Manager_Data *pd = efl_data_scope_get(o, MY_CLASS);
const char *err_name, *err_msg;
pd->pending = eina_list_remove(pd->pending, pending);
if (pd->agent_pending == pending)
pd->agent_pending = NULL;
if (eldbus_message_error_get(msg, &err_name, &err_msg))
{
ERR("Could not Register Agent %p: %s=%s", o, err_name, err_msg);
return;
}
DBG("Agent registered %p: %s", o, eldbus_service_object_path_get(pd->agent));
pd->agent_enabled = EINA_TRUE;
}
static void
_efl_net_control_agent_unregister_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending)
{
Eo *o = data;
Efl_Net_Control_Manager_Data *pd = efl_data_scope_get(o, MY_CLASS);
const char *err_name, *err_msg;
pd->pending = eina_list_remove(pd->pending, pending);
if (pd->agent_pending == pending)
pd->agent_pending = NULL;
if (eldbus_message_error_get(msg, &err_name, &err_msg))
{
ERR("Could not Unregister Agent %p: %s=%s", o, err_name, err_msg);
return;
}
DBG("Agent unregistered %p: %s", o, eldbus_service_object_path_get(pd->agent));
pd->agent_enabled = EINA_FALSE;
}
EOLIAN static void
_efl_net_control_manager_agent_enabled_set(Eo *o, Efl_Net_Control_Manager_Data *pd, Eina_Bool agent_enabled)
{
Eldbus_Pending *p;
Eldbus_Proxy *mgr;
if (!pd->operating)
{
DBG("no connman yet, postpone agent_enabled=%hhu", agent_enabled);
pd->agent_enabled = agent_enabled;
return;
}
if (pd->agent_enabled == agent_enabled)
{
DBG("Already agent_enabled=%hhu", agent_enabled);
return;
}
if (pd->agent_pending)
{
eldbus_pending_cancel(pd->agent_pending);
pd->agent_pending = NULL;
}
mgr = efl_net_connman_manager_get();
EINA_SAFETY_ON_NULL_RETURN(mgr);
if (agent_enabled)
{
p = eldbus_proxy_call(mgr, "RegisterAgent",
_efl_net_control_agent_register_cb, o, DEFAULT_TIMEOUT,
"o", eldbus_service_object_path_get(pd->agent));
DBG("%p RegisterAgent=%p", o, p);
}
else
{
p = eldbus_proxy_call(mgr, "UnregisterAgent",
_efl_net_control_agent_unregister_cb, o, DEFAULT_TIMEOUT,
"o", eldbus_service_object_path_get(pd->agent));
DBG("%p UnregisterAgent=%p", o, p);
}
EINA_SAFETY_ON_NULL_RETURN(p);
pd->pending = eina_list_append(pd->pending, p);
pd->agent_pending = p;
}
EOLIAN static Eina_Bool
_efl_net_control_manager_agent_enabled_get(const Eo *o EINA_UNUSED, Efl_Net_Control_Manager_Data *pd)
{
return pd->agent_enabled;
}
static void
_append_dict_entry(Eldbus_Message_Iter *array, const char *name, const char *signature, ...)
{
Eldbus_Message_Iter *entry, *var;
va_list ap;
if (!eldbus_message_iter_arguments_append(array, "{sv}", &entry))
{
ERR("could not append dict entry");
return;
}
eldbus_message_iter_basic_append(entry, 's', name);
var = eldbus_message_iter_container_new(entry, 'v', signature);
va_start(ap, signature);
eldbus_message_iter_arguments_vappend(var, signature, ap);
va_end(ap);
eldbus_message_iter_container_close(entry, var);
eldbus_message_iter_container_close(array, entry);
}
static void
_append_dict_entry_byte_array(Eldbus_Message_Iter *array, const char *name, const Eina_Slice *slice)
{
Eldbus_Message_Iter *entry, *var, *sub;
if (!eldbus_message_iter_arguments_append(array, "{sv}", &entry))
{
ERR("could not append dict entry");
return;
}
eldbus_message_iter_basic_append(entry, 's', name);
var = eldbus_message_iter_container_new(entry, 'v', "ay");
sub = eldbus_message_iter_container_new(var, 'a', "y");
eldbus_message_iter_fixed_array_append(sub, 'y', slice->mem, slice->len);
eldbus_message_iter_container_close(var, sub);
eldbus_message_iter_container_close(entry, var);
eldbus_message_iter_container_close(array, entry);
}
EOLIAN static void
_efl_net_control_manager_agent_reply(Eo *o EINA_UNUSED, Efl_Net_Control_Manager_Data *pd, const char *name, const Eina_Slice *ssid, const char *username, const char *passphrase, const char *wps)
{
Eldbus_Message *reply;
Eldbus_Message_Iter *msg_itr, *array;
if (!pd->agent_request_input.msg)
{
ERR("no agent input was required.");
return;
}
reply = eldbus_message_method_return_new(pd->agent_request_input.msg);
EINA_SAFETY_ON_NULL_RETURN(reply);
eldbus_message_unref(pd->agent_request_input.msg);
pd->agent_request_input.msg = NULL;
msg_itr = eldbus_message_iter_get(reply);
eldbus_message_iter_arguments_append(msg_itr, "a{sv}", &array);
if (name) _append_dict_entry(array, "Name", "s", name);
if (ssid) _append_dict_entry_byte_array(array, "SSID", ssid);
if (username)
{
_append_dict_entry(array, "Username", "s", username);
_append_dict_entry(array, "Identity", "s", username);
}
if (passphrase)
{
_append_dict_entry(array, "Password", "s", passphrase);
_append_dict_entry(array, "Passphrase", "s", passphrase);
}
if (wps) _append_dict_entry(array, "WPS", "s", wps);
eldbus_message_iter_container_close(msg_itr, array);
eldbus_connection_send(efl_net_connman_connection_get(), reply, NULL, NULL, -1.0);
}
#include "efl_net_control_manager.eo.c"