X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gdbus%2Fclient.c;h=9748ae257f817182fc38dd03b341e523568ef09a;hb=a57098c65678aa6f708215133099520fcb2ef815;hp=f561564cdd014cad836b6c6fabb3c5139e58c1eb;hpb=e348a7eb11739ae00859934b37d56c016ede80a0;p=platform%2Fupstream%2Fconnman.git diff --git a/gdbus/client.c b/gdbus/client.c old mode 100644 new mode 100755 index f561564..9748ae2 --- a/gdbus/client.c +++ b/gdbus/client.c @@ -25,60 +25,870 @@ #include #endif +#include #include #include #include "gdbus.h" +#define METHOD_CALL_TIMEOUT (300 * 1000) + +#ifndef DBUS_INTERFACE_OBJECT_MANAGER +#define DBUS_INTERFACE_OBJECT_MANAGER DBUS_INTERFACE_DBUS ".ObjectManager" +#endif + struct GDBusClient { - gint ref_count; + int ref_count; DBusConnection *dbus_conn; char *service_name; - char *unique_name; char *base_path; - char *match_rules[4]; + char *root_path; + guint watch; + guint added_watch; + guint removed_watch; + GPtrArray *match_rules; DBusPendingCall *pending_call; + DBusPendingCall *get_objects_call; GDBusWatchFunction connect_func; void *connect_data; GDBusWatchFunction disconn_func; + gboolean connected; void *disconn_data; GDBusMessageFunction signal_func; void *signal_data; + GDBusProxyFunction proxy_added; + GDBusProxyFunction proxy_removed; +#if !defined TIZEN_EXT + GDBusClientFunction ready; + void *ready_data; +#endif + GDBusPropertyFunction property_changed; + void *user_data; + GList *proxy_list; +}; + +struct GDBusProxy { + int ref_count; + GDBusClient *client; + char *obj_path; + char *interface; + GHashTable *prop_list; + guint watch; + GDBusPropertyFunction prop_func; + void *prop_data; + GDBusProxyFunction removed_func; + void *removed_data; +}; + +struct prop_entry { + char *name; + int type; + DBusMessage *msg; +}; + +static void modify_match_reply(DBusPendingCall *call, void *user_data) +{ + DBusMessage *reply = dbus_pending_call_steal_reply(call); + DBusError error; + + dbus_error_init(&error); + + if (dbus_set_error_from_message(&error, reply) == TRUE) + dbus_error_free(&error); + + dbus_message_unref(reply); +} + +static gboolean modify_match(DBusConnection *conn, const char *member, + const char *rule) +{ + DBusMessage *msg; + DBusPendingCall *call; + + msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, member); + if (msg == NULL) + return FALSE; + + dbus_message_append_args(msg, DBUS_TYPE_STRING, &rule, + DBUS_TYPE_INVALID); + + if (g_dbus_send_message_with_reply(conn, msg, &call, -1) == FALSE) { + dbus_message_unref(msg); + return FALSE; + } + + dbus_pending_call_set_notify(call, modify_match_reply, NULL, NULL); + dbus_pending_call_unref(call); + + dbus_message_unref(msg); + + return TRUE; +} + +static void iter_append_iter(DBusMessageIter *base, DBusMessageIter *iter) +{ + int type; + + type = dbus_message_iter_get_arg_type(iter); + + if (dbus_type_is_basic(type)) { + const void *value; + + dbus_message_iter_get_basic(iter, &value); + dbus_message_iter_append_basic(base, type, &value); + } else if (dbus_type_is_container(type)) { + DBusMessageIter iter_sub, base_sub; + char *sig; + + dbus_message_iter_recurse(iter, &iter_sub); + + switch (type) { + case DBUS_TYPE_ARRAY: + case DBUS_TYPE_VARIANT: + sig = dbus_message_iter_get_signature(&iter_sub); + break; + default: + sig = NULL; + break; + } + + dbus_message_iter_open_container(base, type, sig, &base_sub); + + if (sig != NULL) + dbus_free(sig); + + while (dbus_message_iter_get_arg_type(&iter_sub) != + DBUS_TYPE_INVALID) { + iter_append_iter(&base_sub, &iter_sub); + dbus_message_iter_next(&iter_sub); + } + + dbus_message_iter_close_container(base, &base_sub); + } +} + +static void prop_entry_update(struct prop_entry *prop, DBusMessageIter *iter) +{ + DBusMessage *msg; + DBusMessageIter base; + + msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &base); + iter_append_iter(&base, iter); + + if (prop->msg != NULL) + dbus_message_unref(prop->msg); + + prop->msg = dbus_message_copy(msg); + dbus_message_unref(msg); +} + +static struct prop_entry *prop_entry_new(const char *name, + DBusMessageIter *iter) +{ + struct prop_entry *prop; + + prop = g_try_new0(struct prop_entry, 1); + if (prop == NULL) + return NULL; + + prop->name = g_strdup(name); + prop->type = dbus_message_iter_get_arg_type(iter); + + prop_entry_update(prop, iter); + + return prop; +} + +static void prop_entry_free(gpointer data) +{ + struct prop_entry *prop = data; + + if (prop->msg != NULL) + dbus_message_unref(prop->msg); + + g_free(prop->name); + + g_free(prop); +} + +static void add_property(GDBusProxy *proxy, const char *name, + DBusMessageIter *iter, gboolean send_changed) +{ + GDBusClient *client = proxy->client; + DBusMessageIter value; + struct prop_entry *prop; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT) + return; + + dbus_message_iter_recurse(iter, &value); + + prop = g_hash_table_lookup(proxy->prop_list, name); + if (prop != NULL) { + prop_entry_update(prop, &value); + goto done; + } + + prop = prop_entry_new(name, &value); + if (prop == NULL) + return; + + g_hash_table_replace(proxy->prop_list, prop->name, prop); + +done: + if (proxy->prop_func) + proxy->prop_func(proxy, name, &value, proxy->prop_data); + + if (client == NULL || send_changed == FALSE) + return; + + if (client->property_changed) + client->property_changed(proxy, name, &value, + client->user_data); +} + +static void update_properties(GDBusProxy *proxy, DBusMessageIter *iter, + gboolean send_changed) +{ + DBusMessageIter dict; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return; + + dbus_message_iter_recurse(iter, &dict); + + while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter entry; + const char *name; + + dbus_message_iter_recurse(&dict, &entry); + + if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) + break; + + dbus_message_iter_get_basic(&entry, &name); + dbus_message_iter_next(&entry); + + add_property(proxy, name, &entry, send_changed); + + dbus_message_iter_next(&dict); + } +} + +static void get_all_properties_reply(DBusPendingCall *call, void *user_data) +{ + GDBusProxy *proxy = user_data; + GDBusClient *client = proxy->client; + DBusMessage *reply = dbus_pending_call_steal_reply(call); + DBusMessageIter iter; + DBusError error; + + dbus_error_init(&error); + + if (dbus_set_error_from_message(&error, reply) == TRUE) { + dbus_error_free(&error); + goto done; + } + + dbus_message_iter_init(reply, &iter); + + update_properties(proxy, &iter, FALSE); + +done: + if (g_list_find(client->proxy_list, proxy) == NULL) { + if (client->proxy_added) + client->proxy_added(proxy, client->user_data); + + client->proxy_list = g_list_append(client->proxy_list, proxy); + } + + dbus_message_unref(reply); + + g_dbus_client_unref(client); +} + +static void get_all_properties(GDBusProxy *proxy) +{ + GDBusClient *client = proxy->client; + const char *service_name = client->service_name; + DBusMessage *msg; + DBusPendingCall *call; + + msg = dbus_message_new_method_call(service_name, proxy->obj_path, + DBUS_INTERFACE_PROPERTIES, "GetAll"); + if (msg == NULL) + return; + + dbus_message_append_args(msg, DBUS_TYPE_STRING, &proxy->interface, + DBUS_TYPE_INVALID); + + if (g_dbus_send_message_with_reply(client->dbus_conn, msg, + &call, -1) == FALSE) { + dbus_message_unref(msg); + return; + } + + g_dbus_client_ref(client); + + dbus_pending_call_set_notify(call, get_all_properties_reply, + proxy, NULL); + dbus_pending_call_unref(call); + + dbus_message_unref(msg); +} + +static GDBusProxy *proxy_lookup(GDBusClient *client, const char *path, + const char *interface) +{ + GList *list; + + for (list = g_list_first(client->proxy_list); list; + list = g_list_next(list)) { + GDBusProxy *proxy = list->data; + + if (g_str_equal(proxy->interface, interface) == TRUE && + g_str_equal(proxy->obj_path, path) == TRUE) + return proxy; + } + + return NULL; +} + +static gboolean properties_changed(DBusConnection *conn, DBusMessage *msg, + void *user_data) +{ + GDBusProxy *proxy = user_data; + GDBusClient *client = proxy->client; + DBusMessageIter iter, entry; + const char *interface; + + if (dbus_message_iter_init(msg, &iter) == FALSE) + return TRUE; + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + return TRUE; + + dbus_message_iter_get_basic(&iter, &interface); + dbus_message_iter_next(&iter); + + update_properties(proxy, &iter, TRUE); + + dbus_message_iter_next(&iter); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) + return TRUE; + + dbus_message_iter_recurse(&iter, &entry); + + while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) { + const char *name; + + dbus_message_iter_get_basic(&entry, &name); + + g_hash_table_remove(proxy->prop_list, name); + + if (proxy->prop_func) + proxy->prop_func(proxy, name, NULL, proxy->prop_data); + + if (client->property_changed) + client->property_changed(proxy, name, NULL, + client->user_data); + + dbus_message_iter_next(&entry); + } + + return TRUE; +} + +static GDBusProxy *proxy_new(GDBusClient *client, const char *path, + const char *interface) +{ + GDBusProxy *proxy; + + proxy = g_try_new0(GDBusProxy, 1); + if (proxy == NULL) + return NULL; + + proxy->client = client; + proxy->obj_path = g_strdup(path); + proxy->interface = g_strdup(interface); + + proxy->prop_list = g_hash_table_new_full(g_str_hash, g_str_equal, + NULL, prop_entry_free); + proxy->watch = g_dbus_add_properties_watch(client->dbus_conn, + client->service_name, + proxy->obj_path, + proxy->interface, + properties_changed, + proxy, NULL); + + return g_dbus_proxy_ref(proxy); +} + +static void proxy_free(gpointer data) +{ + GDBusProxy *proxy = data; + + if (proxy->client) { + GDBusClient *client = proxy->client; + + if (client->proxy_removed) + client->proxy_removed(proxy, client->user_data); + + g_dbus_remove_watch(client->dbus_conn, proxy->watch); + + g_hash_table_remove_all(proxy->prop_list); + + proxy->client = NULL; + } + + if (proxy->removed_func) + proxy->removed_func(proxy, proxy->removed_data); + + g_dbus_proxy_unref(proxy); +} + +static void proxy_remove(GDBusClient *client, const char *path, + const char *interface) +{ + GList *list; + + for (list = g_list_first(client->proxy_list); list; + list = g_list_next(list)) { + GDBusProxy *proxy = list->data; + + if (g_str_equal(proxy->interface, interface) == TRUE && + g_str_equal(proxy->obj_path, path) == TRUE) { + client->proxy_list = + g_list_delete_link(client->proxy_list, list); + proxy_free(proxy); + break; + } + } +} + +GDBusProxy *g_dbus_proxy_new(GDBusClient *client, const char *path, + const char *interface) +{ + GDBusProxy *proxy; + + if (client == NULL) + return NULL; + + proxy = proxy_lookup(client, path, interface); + if (proxy) + return g_dbus_proxy_ref(proxy); + + proxy = proxy_new(client, path, interface); + if (proxy == NULL) + return NULL; + + get_all_properties(proxy); + + return g_dbus_proxy_ref(proxy); +} + +GDBusProxy *g_dbus_proxy_ref(GDBusProxy *proxy) +{ + if (proxy == NULL) + return NULL; + + __sync_fetch_and_add(&proxy->ref_count, 1); + + return proxy; +} + +void g_dbus_proxy_unref(GDBusProxy *proxy) +{ + if (proxy == NULL) + return; + + if (__sync_sub_and_fetch(&proxy->ref_count, 1) > 0) + return; + + g_hash_table_destroy(proxy->prop_list); + + g_free(proxy->obj_path); + g_free(proxy->interface); + + g_free(proxy); +} + +const char *g_dbus_proxy_get_path(GDBusProxy *proxy) +{ + if (proxy == NULL) + return NULL; + + return proxy->obj_path; +} + +const char *g_dbus_proxy_get_interface(GDBusProxy *proxy) +{ + if (proxy == NULL) + return NULL; + + return proxy->interface; +} + +gboolean g_dbus_proxy_get_property(GDBusProxy *proxy, const char *name, + DBusMessageIter *iter) +{ + struct prop_entry *prop; + + if (proxy == NULL || name == NULL) + return FALSE; + + prop = g_hash_table_lookup(proxy->prop_list, name); + if (prop == NULL) + return FALSE; + + if (prop->msg == NULL) + return FALSE; + + if (dbus_message_iter_init(prop->msg, iter) == FALSE) + return FALSE; + + return TRUE; +} + +struct refresh_property_data { + GDBusProxy *proxy; + char *name; +}; + +static void refresh_property_free(gpointer user_data) +{ + struct refresh_property_data *data = user_data; + + g_free(data->name); + g_free(data); +} + +static void refresh_property_reply(DBusPendingCall *call, void *user_data) +{ + struct refresh_property_data *data = user_data; + DBusMessage *reply = dbus_pending_call_steal_reply(call); + DBusError error; + + dbus_error_init(&error); + + if (dbus_set_error_from_message(&error, reply) == FALSE) { + DBusMessageIter iter; + + dbus_message_iter_init(reply, &iter); + + add_property(data->proxy, data->name, &iter, TRUE); + } else + dbus_error_free(&error); + + dbus_message_unref(reply); +} + +gboolean g_dbus_proxy_refresh_property(GDBusProxy *proxy, const char *name) +{ + struct refresh_property_data *data; + GDBusClient *client; + DBusMessage *msg; + DBusMessageIter iter; + DBusPendingCall *call; + + if (proxy == NULL || name == NULL) + return FALSE; + + client = proxy->client; + if (client == NULL) + return FALSE; + + data = g_try_new0(struct refresh_property_data, 1); + if (data == NULL) + return FALSE; + + data->proxy = proxy; + data->name = g_strdup(name); + + msg = dbus_message_new_method_call(client->service_name, + proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Get"); + if (msg == NULL) { + refresh_property_free(data); + return FALSE; + } + + dbus_message_iter_init_append(msg, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, + &proxy->interface); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name); + + if (g_dbus_send_message_with_reply(client->dbus_conn, msg, + &call, -1) == FALSE) { + dbus_message_unref(msg); + refresh_property_free(data); + return FALSE; + } + + dbus_pending_call_set_notify(call, refresh_property_reply, + data, refresh_property_free); + dbus_pending_call_unref(call); + + dbus_message_unref(msg); + + return TRUE; +} + +struct set_property_data { + GDBusResultFunction function; + void *user_data; + GDBusDestroyFunction destroy; +}; + +static void set_property_reply(DBusPendingCall *call, void *user_data) +{ + struct set_property_data *data = user_data; + DBusMessage *reply = dbus_pending_call_steal_reply(call); + DBusError error; + + dbus_error_init(&error); + + dbus_set_error_from_message(&error, reply); + + if (data->function) + data->function(&error, data->user_data); + + if (data->destroy) + data->destroy(data->user_data); + + dbus_error_free(&error); + + dbus_message_unref(reply); +} + +gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy, + const char *name, int type, const void *value, + GDBusResultFunction function, void *user_data, + GDBusDestroyFunction destroy) +{ + struct set_property_data *data; + GDBusClient *client; + DBusMessage *msg; + DBusMessageIter iter, variant; + DBusPendingCall *call; + char type_as_str[2]; + + if (proxy == NULL || name == NULL || value == NULL) + return FALSE; + + if (dbus_type_is_basic(type) == FALSE) + return FALSE; + + client = proxy->client; + if (client == NULL) + return FALSE; + + data = g_try_new0(struct set_property_data, 1); + if (data == NULL) + return FALSE; + + data->function = function; + data->user_data = user_data; + data->destroy = destroy; + + msg = dbus_message_new_method_call(client->service_name, + proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Set"); + if (msg == NULL) { + g_free(data); + return FALSE; + } + + type_as_str[0] = (char) type; + type_as_str[1] = '\0'; + + dbus_message_iter_init_append(msg, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, + &proxy->interface); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, + type_as_str, &variant); + dbus_message_iter_append_basic(&variant, type, value); + dbus_message_iter_close_container(&iter, &variant); + + if (g_dbus_send_message_with_reply(client->dbus_conn, msg, + &call, -1) == FALSE) { + dbus_message_unref(msg); + g_free(data); + return FALSE; + } + + dbus_pending_call_set_notify(call, set_property_reply, data, g_free); + dbus_pending_call_unref(call); + + dbus_message_unref(msg); + + return TRUE; +} + +#if !defined TIZEN_EXT +gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy, + const char *name, int type, const void *value, + size_t size, GDBusResultFunction function, + void *user_data, GDBusDestroyFunction destroy) +{ + struct set_property_data *data; + GDBusClient *client; + DBusMessage *msg; + DBusMessageIter iter, variant, array; + DBusPendingCall *call; + char array_sig[3]; + char type_sig[2]; + + if (!proxy || !name || !value) + return FALSE; + + if (!dbus_type_is_basic(type)) + return FALSE; + + client = proxy->client; + if (!client) + return FALSE; + + data = g_try_new0(struct set_property_data, 1); + if (!data) + return FALSE; + + data->function = function; + data->user_data = user_data; + data->destroy = destroy; + + msg = dbus_message_new_method_call(client->service_name, + proxy->obj_path, + DBUS_INTERFACE_PROPERTIES, + "Set"); + if (!msg) { + g_free(data); + return FALSE; + } + + array_sig[0] = DBUS_TYPE_ARRAY; + array_sig[1] = (char) type; + array_sig[2] = '\0'; + + type_sig[0] = (char) type; + type_sig[1] = '\0'; + + dbus_message_iter_init_append(msg, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, + &proxy->interface); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, + array_sig, &variant); + + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, + type_sig, &array); + + if (dbus_type_is_fixed(type)) + dbus_message_iter_append_fixed_array(&array, type, &value, + size); + else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) { + const char **str = (const char **) value; + size_t i; + + for (i = 0; i < size; i++) + dbus_message_iter_append_basic(&array, type, &str[i]); + } + + dbus_message_iter_close_container(&variant, &array); + dbus_message_iter_close_container(&iter, &variant); + + if (g_dbus_send_message_with_reply(client->dbus_conn, msg, + &call, -1) == FALSE) { + dbus_message_unref(msg); + g_free(data); + return FALSE; + } + + dbus_pending_call_set_notify(call, set_property_reply, data, g_free); + dbus_pending_call_unref(call); + + dbus_message_unref(msg); + + return TRUE; +} +#endif + +struct method_call_data { + GDBusReturnFunction function; + void *user_data; + GDBusDestroyFunction destroy; }; -static void modify_match_reply(DBusPendingCall *call, void *user_data) +static void method_call_reply(DBusPendingCall *call, void *user_data) { + struct method_call_data *data = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); - DBusError error; - dbus_error_init(&error); + if (data->function) + data->function(reply, data->user_data); - if (dbus_set_error_from_message(&error, reply) == TRUE) - dbus_error_free(&error); + if (data->destroy) + data->destroy(data->user_data); dbus_message_unref(reply); } -static gboolean modify_match(DBusConnection *conn, const char *member, - const char *rule) +gboolean g_dbus_proxy_method_call(GDBusProxy *proxy, const char *method, + GDBusSetupFunction setup, + GDBusReturnFunction function, void *user_data, + GDBusDestroyFunction destroy) { + struct method_call_data *data; + GDBusClient *client; DBusMessage *msg; DBusPendingCall *call; - msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS, member); - if (!msg) + if (proxy == NULL || method == NULL) return FALSE; - dbus_message_append_args(msg, DBUS_TYPE_STRING, &rule, - DBUS_TYPE_INVALID); + client = proxy->client; + if (client == NULL) + return FALSE; + + msg = dbus_message_new_method_call(client->service_name, + proxy->obj_path, proxy->interface, method); + if (msg == NULL) + return FALSE; + + if (setup) { + DBusMessageIter iter; + + dbus_message_iter_init_append(msg, &iter); + setup(&iter, user_data); + } + + if (!function) + return g_dbus_send_message(client->dbus_conn, msg); + + data = g_try_new0(struct method_call_data, 1); + if (data == NULL) + return FALSE; + + data->function = function; + data->user_data = user_data; + data->destroy = destroy; - if (dbus_connection_send_with_reply(conn, msg, &call, -1) == FALSE) { + + if (g_dbus_send_message_with_reply(client->dbus_conn, msg, + &call, METHOD_CALL_TIMEOUT) == FALSE) { dbus_message_unref(msg); + g_free(data); return FALSE; } - dbus_pending_call_set_notify(call, modify_match_reply, NULL, NULL); + dbus_pending_call_set_notify(call, method_call_reply, data, g_free); dbus_pending_call_unref(call); dbus_message_unref(msg); @@ -86,12 +896,198 @@ static gboolean modify_match(DBusConnection *conn, const char *member, return TRUE; } -static void get_name_owner_reply(DBusPendingCall *call, void *user_data) +gboolean g_dbus_proxy_set_property_watch(GDBusProxy *proxy, + GDBusPropertyFunction function, void *user_data) +{ + if (proxy == NULL) + return FALSE; + + proxy->prop_func = function; + proxy->prop_data = user_data; + + return TRUE; +} + +gboolean g_dbus_proxy_set_removed_watch(GDBusProxy *proxy, + GDBusProxyFunction function, void *user_data) +{ + if (proxy == NULL) + return FALSE; + + proxy->removed_func = function; + proxy->removed_data = user_data; + + return TRUE; +} + +static void refresh_properties(GDBusClient *client) +{ + GList *list; + + for (list = g_list_first(client->proxy_list); list; + list = g_list_next(list)) { + GDBusProxy *proxy = list->data; + + get_all_properties(proxy); + } +} + +static void parse_properties(GDBusClient *client, const char *path, + const char *interface, DBusMessageIter *iter) +{ + GDBusProxy *proxy; + + if (g_str_equal(interface, DBUS_INTERFACE_INTROSPECTABLE) == TRUE) + return; + + if (g_str_equal(interface, DBUS_INTERFACE_PROPERTIES) == TRUE) + return; + + proxy = proxy_lookup(client, path, interface); + if (proxy) { + update_properties(proxy, iter, FALSE); + return; + } + + proxy = proxy_new(client, path, interface); + if (proxy == NULL) + return; + + update_properties(proxy, iter, FALSE); + + if (client->proxy_added) + client->proxy_added(proxy, client->user_data); + + client->proxy_list = g_list_append(client->proxy_list, proxy); +} + +static void parse_interfaces(GDBusClient *client, const char *path, + DBusMessageIter *iter) +{ + DBusMessageIter dict; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return; + + dbus_message_iter_recurse(iter, &dict); + + while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter entry; + const char *interface; + + dbus_message_iter_recurse(&dict, &entry); + + if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) + break; + + dbus_message_iter_get_basic(&entry, &interface); + dbus_message_iter_next(&entry); + + parse_properties(client, path, interface, &entry); + + dbus_message_iter_next(&dict); + } +} + +static gboolean interfaces_added(DBusConnection *conn, DBusMessage *msg, + void *user_data) +{ + GDBusClient *client = user_data; + DBusMessageIter iter; + const char *path; + + if (dbus_message_iter_init(msg, &iter) == FALSE) + return TRUE; + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH) + return TRUE; + + dbus_message_iter_get_basic(&iter, &path); + dbus_message_iter_next(&iter); + + g_dbus_client_ref(client); + + parse_interfaces(client, path, &iter); + + g_dbus_client_unref(client); + + return TRUE; +} + +static gboolean interfaces_removed(DBusConnection *conn, DBusMessage *msg, + void *user_data) +{ + GDBusClient *client = user_data; + DBusMessageIter iter, entry; + const char *path; + + if (dbus_message_iter_init(msg, &iter) == FALSE) + return TRUE; + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH) + return TRUE; + + dbus_message_iter_get_basic(&iter, &path); + dbus_message_iter_next(&iter); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) + return TRUE; + + dbus_message_iter_recurse(&iter, &entry); + + g_dbus_client_ref(client); + + while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) { + const char *interface; + + dbus_message_iter_get_basic(&entry, &interface); + proxy_remove(client, path, interface); + dbus_message_iter_next(&entry); + } + + g_dbus_client_unref(client); + + return TRUE; +} + +static void parse_managed_objects(GDBusClient *client, DBusMessage *msg) +{ + DBusMessageIter iter, dict; + + if (dbus_message_iter_init(msg, &iter) == FALSE) + return; + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) + return; + + dbus_message_iter_recurse(&iter, &dict); + + while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter entry; + const char *path; + + dbus_message_iter_recurse(&dict, &entry); + + if (dbus_message_iter_get_arg_type(&entry) != + DBUS_TYPE_OBJECT_PATH) + break; + + dbus_message_iter_get_basic(&entry, &path); + dbus_message_iter_next(&entry); + + parse_interfaces(client, path, &entry); + + dbus_message_iter_next(&dict); + } +} + +static void get_managed_objects_reply(DBusPendingCall *call, void *user_data) { GDBusClient *client = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError error; - const char *name; + + g_dbus_client_ref(client); dbus_error_init(&error); @@ -100,107 +1096,113 @@ static void get_name_owner_reply(DBusPendingCall *call, void *user_data) goto done; } - if (dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID) == FALSE) - goto done; - - g_free(client->unique_name); - client->unique_name = g_strdup(name); - - if (client->connect_func) - client->connect_func(client->dbus_conn, client->connect_data); + parse_managed_objects(client, reply); done: +#if !defined TIZEN_EXT + if (client->ready) + client->ready(client, client->ready_data); +#endif + dbus_message_unref(reply); - dbus_pending_call_unref(client->pending_call); - client->pending_call = NULL; + dbus_pending_call_unref(client->get_objects_call); + client->get_objects_call = NULL; + + g_dbus_client_unref(client); } -static void get_name_owner(GDBusClient *client, const char *name) +static void get_managed_objects(GDBusClient *client) { DBusMessage *msg; - msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS, "GetNameOwner"); - if (!msg) + if (!client->connected) return; - dbus_message_append_args(msg, DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID); + if ((!client->proxy_added && !client->proxy_removed) || + !client->root_path) { + refresh_properties(client); + return; + } - if (dbus_connection_send_with_reply(client->dbus_conn, msg, - &client->pending_call, -1) == FALSE) { + if (client->get_objects_call != NULL) + return; + + msg = dbus_message_new_method_call(client->service_name, + client->root_path, + DBUS_INTERFACE_OBJECT_MANAGER, + "GetManagedObjects"); + if (msg == NULL) + return; + + dbus_message_append_args(msg, DBUS_TYPE_INVALID); + + if (g_dbus_send_message_with_reply(client->dbus_conn, msg, + &client->get_objects_call, -1) == FALSE) { dbus_message_unref(msg); return; } - dbus_pending_call_set_notify(client->pending_call, - get_name_owner_reply, client, NULL); + dbus_pending_call_set_notify(client->get_objects_call, + get_managed_objects_reply, + client, NULL); dbus_message_unref(msg); } +static void service_connect(DBusConnection *conn, void *user_data) +{ + GDBusClient *client = user_data; + + g_dbus_client_ref(client); + + client->connected = TRUE; + + if (client->connect_func) + client->connect_func(conn, client->connect_data); + + get_managed_objects(client); + + g_dbus_client_unref(client); +} + +static void service_disconnect(DBusConnection *conn, void *user_data) +{ + GDBusClient *client = user_data; + + client->connected = FALSE; + + g_list_free_full(client->proxy_list, proxy_free); + client->proxy_list = NULL; + + if (client->disconn_func) + client->disconn_func(conn, client->disconn_data); +} + static DBusHandlerResult message_filter(DBusConnection *connection, DBusMessage *message, void *user_data) { GDBusClient *client = user_data; - const char *sender; + const char *sender, *path, *interface; if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; sender = dbus_message_get_sender(message); + if (sender == NULL) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - if (g_str_equal(sender, DBUS_SERVICE_DBUS) == TRUE) { - const char *interface, *member; - const char *name, *old, *new; - - interface = dbus_message_get_interface(message); - - if (g_str_equal(interface, DBUS_INTERFACE_DBUS) == FALSE) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - member = dbus_message_get_member(message); - - if (g_str_equal(member, "NameOwnerChanged") == FALSE) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - if (dbus_message_get_args(message, NULL, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_STRING, &old, - DBUS_TYPE_STRING, &new, - DBUS_TYPE_INVALID) == FALSE) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - if (g_str_equal(name, client->service_name) == FALSE) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - if (*new == '\0') { - if (client->disconn_func) - client->disconn_func(client->dbus_conn, - client->disconn_data); - g_free(client->unique_name); - client->unique_name = NULL; - } else if (*old == '\0') { - if (client->connect_func) - client->connect_func(client->dbus_conn, - client->connect_data); - g_free(client->unique_name); - client->unique_name = g_strdup(new); - } + path = dbus_message_get_path(message); + interface = dbus_message_get_interface(message); + if (g_str_has_prefix(path, client->base_path) == FALSE) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - if (client->unique_name == NULL) + if (g_str_equal(interface, DBUS_INTERFACE_PROPERTIES) == TRUE) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - if (g_str_equal(sender, client->unique_name) == TRUE) { - if (client->signal_func) - client->signal_func(client->dbus_conn, - message, client->signal_data); - } + if (client->signal_func) + client->signal_func(connection, message, client->signal_data); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } @@ -208,10 +1210,18 @@ static DBusHandlerResult message_filter(DBusConnection *connection, GDBusClient *g_dbus_client_new(DBusConnection *connection, const char *service, const char *path) { + return g_dbus_client_new_full(connection, service, path, "/"); +} + +GDBusClient *g_dbus_client_new_full(DBusConnection *connection, + const char *service, + const char *path, + const char *root_path) +{ GDBusClient *client; - int i; + unsigned int i; - if (connection == NULL) + if (!connection || !service) return NULL; client = g_try_new0(GDBusClient, 1); @@ -227,29 +1237,40 @@ GDBusClient *g_dbus_client_new(DBusConnection *connection, client->dbus_conn = dbus_connection_ref(connection); client->service_name = g_strdup(service); client->base_path = g_strdup(path); - - get_name_owner(client, client->service_name); - - client->match_rules[0] = g_strdup_printf("type='signal',sender='%s'," - "path='%s',interface='%s'," - "member='NameOwnerChanged',arg0='%s'", - DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS, client->service_name); - client->match_rules[1] = g_strdup_printf("type='signal',sender='%s'," - "path='/',interface='%s.ObjectManager'," - "member='InterfacesAdded'", - client->service_name, DBUS_INTERFACE_DBUS); - client->match_rules[2] = g_strdup_printf("type='signal',sender='%s'," - "path='/',interface='%s.ObjectManager'," - "member='InterfacesRemoved'", - client->service_name, DBUS_INTERFACE_DBUS); - client->match_rules[3] = g_strdup_printf("type='signal',sender='%s'," - "path_namespace='%s'", - client->service_name, client->base_path); - - for (i = 0; i < 4; i++) + client->root_path = g_strdup(root_path); + client->connected = FALSE; + + client->match_rules = g_ptr_array_sized_new(1); + g_ptr_array_set_free_func(client->match_rules, g_free); + + client->watch = g_dbus_add_service_watch(connection, service, + service_connect, + service_disconnect, + client, NULL); + + if (!root_path) + return g_dbus_client_ref(client); + + client->added_watch = g_dbus_add_signal_watch(connection, service, + client->root_path, + DBUS_INTERFACE_OBJECT_MANAGER, + "InterfacesAdded", + interfaces_added, + client, NULL); + client->removed_watch = g_dbus_add_signal_watch(connection, service, + client->root_path, + DBUS_INTERFACE_OBJECT_MANAGER, + "InterfacesRemoved", + interfaces_removed, + client, NULL); + g_ptr_array_add(client->match_rules, g_strdup_printf("type='signal'," + "sender='%s',path_namespace='%s'", + client->service_name, client->base_path)); + + for (i = 0; i < client->match_rules->len; i++) { modify_match(client->dbus_conn, "AddMatch", - client->match_rules[i]); + g_ptr_array_index(client->match_rules, i)); + } return g_dbus_client_ref(client); } @@ -259,19 +1280,19 @@ GDBusClient *g_dbus_client_ref(GDBusClient *client) if (client == NULL) return NULL; - g_atomic_int_inc(&client->ref_count); + __sync_fetch_and_add(&client->ref_count, 1); return client; } void g_dbus_client_unref(GDBusClient *client) { - int i; + unsigned int i; if (client == NULL) return; - if (g_atomic_int_dec_and_test(&client->ref_count) == FALSE) + if (__sync_sub_and_fetch(&client->ref_count, 1) > 0) return; if (client->pending_call != NULL) { @@ -279,23 +1300,39 @@ void g_dbus_client_unref(GDBusClient *client) dbus_pending_call_unref(client->pending_call); } - for (i = 0; i < 4; i++) { + if (client->get_objects_call != NULL) { + dbus_pending_call_cancel(client->get_objects_call); + dbus_pending_call_unref(client->get_objects_call); + } + + for (i = 0; i < client->match_rules->len; i++) { modify_match(client->dbus_conn, "RemoveMatch", - client->match_rules[i]); - g_free(client->match_rules[i]); + g_ptr_array_index(client->match_rules, i)); } + g_ptr_array_free(client->match_rules, TRUE); + dbus_connection_remove_filter(client->dbus_conn, message_filter, client); - if (client->disconn_func) + g_list_free_full(client->proxy_list, proxy_free); + + /* + * Don't call disconn_func twice if disconnection + * was previously reported. + */ + if (client->disconn_func && client->connected) client->disconn_func(client->dbus_conn, client->disconn_data); + g_dbus_remove_watch(client->dbus_conn, client->watch); + g_dbus_remove_watch(client->dbus_conn, client->added_watch); + g_dbus_remove_watch(client->dbus_conn, client->removed_watch); + dbus_connection_unref(client->dbus_conn); g_free(client->service_name); - g_free(client->unique_name); g_free(client->base_path); + g_free(client->root_path); g_free(client); } @@ -335,3 +1372,37 @@ gboolean g_dbus_client_set_signal_watch(GDBusClient *client, return TRUE; } + +#if !defined TIZEN_EXT +gboolean g_dbus_client_set_ready_watch(GDBusClient *client, + GDBusClientFunction ready, void *user_data) +{ + if (client == NULL) + return FALSE; + + client->ready = ready; + client->ready_data = user_data; + + return TRUE; +} +#endif + +gboolean g_dbus_client_set_proxy_handlers(GDBusClient *client, + GDBusProxyFunction proxy_added, + GDBusProxyFunction proxy_removed, + GDBusPropertyFunction property_changed, + void *user_data) +{ + if (client == NULL) + return FALSE; + + client->proxy_added = proxy_added; + client->proxy_removed = proxy_removed; + client->property_changed = property_changed; + client->user_data = user_data; + + if (proxy_added || proxy_removed || property_changed) + get_managed_objects(client); + + return TRUE; +}