5 * Copyright (C) 2004-2011 Marcel Holtmann <marcel@holtmann.org>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
29 #include <dbus/dbus.h>
35 DBusConnection *dbus_conn;
40 DBusPendingCall *pending_call;
41 GDBusWatchFunction connect_func;
43 GDBusWatchFunction disconn_func;
45 GDBusMessageFunction signal_func;
49 static void modify_match_reply(DBusPendingCall *call, void *user_data)
51 DBusMessage *reply = dbus_pending_call_steal_reply(call);
54 dbus_error_init(&error);
56 if (dbus_set_error_from_message(&error, reply) == TRUE)
57 dbus_error_free(&error);
59 dbus_message_unref(reply);
62 static gboolean modify_match(DBusConnection *conn, const char *member,
66 DBusPendingCall *call;
68 msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
69 DBUS_INTERFACE_DBUS, member);
73 dbus_message_append_args(msg, DBUS_TYPE_STRING, &rule,
76 if (dbus_connection_send_with_reply(conn, msg, &call, -1) == FALSE) {
77 dbus_message_unref(msg);
81 dbus_pending_call_set_notify(call, modify_match_reply, NULL, NULL);
82 dbus_pending_call_unref(call);
84 dbus_message_unref(msg);
89 static void get_name_owner_reply(DBusPendingCall *call, void *user_data)
91 GDBusClient *client = user_data;
92 DBusMessage *reply = dbus_pending_call_steal_reply(call);
96 dbus_error_init(&error);
98 if (dbus_set_error_from_message(&error, reply) == TRUE) {
99 dbus_error_free(&error);
103 if (dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING, &name,
104 DBUS_TYPE_INVALID) == FALSE)
107 g_free(client->unique_name);
108 client->unique_name = g_strdup(name);
110 if (client->connect_func)
111 client->connect_func(client->dbus_conn, client->connect_data);
114 dbus_message_unref(reply);
116 dbus_pending_call_unref(client->pending_call);
117 client->pending_call = NULL;
120 static void get_name_owner(GDBusClient *client, const char *name)
124 msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
125 DBUS_INTERFACE_DBUS, "GetNameOwner");
129 dbus_message_append_args(msg, DBUS_TYPE_STRING, &name,
132 if (dbus_connection_send_with_reply(client->dbus_conn, msg,
133 &client->pending_call, -1) == FALSE) {
134 dbus_message_unref(msg);
138 dbus_pending_call_set_notify(client->pending_call,
139 get_name_owner_reply, client, NULL);
141 dbus_message_unref(msg);
144 static DBusHandlerResult message_filter(DBusConnection *connection,
145 DBusMessage *message, void *user_data)
147 GDBusClient *client = user_data;
150 if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL)
151 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
153 sender = dbus_message_get_sender(message);
155 if (g_str_equal(sender, DBUS_SERVICE_DBUS) == TRUE) {
156 const char *interface, *member;
157 const char *name, *old, *new;
159 interface = dbus_message_get_interface(message);
161 if (g_str_equal(interface, DBUS_INTERFACE_DBUS) == FALSE)
162 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
164 member = dbus_message_get_member(message);
166 if (g_str_equal(member, "NameOwnerChanged") == FALSE)
167 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
169 if (dbus_message_get_args(message, NULL,
170 DBUS_TYPE_STRING, &name,
171 DBUS_TYPE_STRING, &old,
172 DBUS_TYPE_STRING, &new,
173 DBUS_TYPE_INVALID) == FALSE)
174 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
176 if (g_str_equal(name, client->service_name) == FALSE)
177 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
180 if (client->disconn_func)
181 client->disconn_func(client->dbus_conn,
182 client->disconn_data);
183 g_free(client->unique_name);
184 client->unique_name = NULL;
185 } else if (*old == '\0') {
186 if (client->connect_func)
187 client->connect_func(client->dbus_conn,
188 client->connect_data);
189 g_free(client->unique_name);
190 client->unique_name = g_strdup(new);
193 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
196 if (client->unique_name == NULL)
197 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
199 if (g_str_equal(sender, client->unique_name) == TRUE) {
200 if (client->signal_func)
201 client->signal_func(client->dbus_conn,
202 message, client->signal_data);
205 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
208 GDBusClient *g_dbus_client_new(DBusConnection *connection,
209 const char *service, const char *path)
214 if (connection == NULL)
217 client = g_try_new0(GDBusClient, 1);
221 if (dbus_connection_add_filter(connection, message_filter,
222 client, NULL) == FALSE) {
227 client->dbus_conn = dbus_connection_ref(connection);
228 client->service_name = g_strdup(service);
229 client->base_path = g_strdup(path);
231 get_name_owner(client, client->service_name);
233 client->match_rules[0] = g_strdup_printf("type='signal',sender='%s',"
234 "path='%s',interface='%s',"
235 "member='NameOwnerChanged',arg0='%s'",
236 DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
237 DBUS_INTERFACE_DBUS, client->service_name);
238 client->match_rules[1] = g_strdup_printf("type='signal',sender='%s',"
239 "path='/',interface='%s.ObjectManager',"
240 "member='InterfacesAdded'",
241 client->service_name, DBUS_INTERFACE_DBUS);
242 client->match_rules[2] = g_strdup_printf("type='signal',sender='%s',"
243 "path='/',interface='%s.ObjectManager',"
244 "member='InterfacesRemoved'",
245 client->service_name, DBUS_INTERFACE_DBUS);
246 client->match_rules[3] = g_strdup_printf("type='signal',sender='%s',"
247 "path_namespace='%s'",
248 client->service_name, client->base_path);
250 for (i = 0; i < 4; i++)
251 modify_match(client->dbus_conn, "AddMatch",
252 client->match_rules[i]);
254 return g_dbus_client_ref(client);
257 GDBusClient *g_dbus_client_ref(GDBusClient *client)
262 g_atomic_int_inc(&client->ref_count);
267 void g_dbus_client_unref(GDBusClient *client)
274 if (g_atomic_int_dec_and_test(&client->ref_count) == FALSE)
277 if (client->pending_call != NULL) {
278 dbus_pending_call_cancel(client->pending_call);
279 dbus_pending_call_unref(client->pending_call);
282 for (i = 0; i < 4; i++) {
283 modify_match(client->dbus_conn, "RemoveMatch",
284 client->match_rules[i]);
285 g_free(client->match_rules[i]);
288 dbus_connection_remove_filter(client->dbus_conn,
289 message_filter, client);
291 if (client->disconn_func)
292 client->disconn_func(client->dbus_conn, client->disconn_data);
294 dbus_connection_unref(client->dbus_conn);
296 g_free(client->service_name);
297 g_free(client->unique_name);
298 g_free(client->base_path);
303 gboolean g_dbus_client_set_connect_watch(GDBusClient *client,
304 GDBusWatchFunction function, void *user_data)
309 client->connect_func = function;
310 client->connect_data = user_data;
315 gboolean g_dbus_client_set_disconnect_watch(GDBusClient *client,
316 GDBusWatchFunction function, void *user_data)
321 client->disconn_func = function;
322 client->disconn_data = user_data;
327 gboolean g_dbus_client_set_signal_watch(GDBusClient *client,
328 GDBusMessageFunction function, void *user_data)
333 client->signal_func = function;
334 client->signal_data = user_data;