d2d70a32390b43c35fae5093874acaffc5777eea
[browser-dbus-bridge.git] / tests / unit.c
1 /**
2  * Unit, a D-Bus tester for argument marshalling
3  *
4  * Copyright © 2008 Movial Creative Technologies Inc
5  *  Contact: Movial Creative Technologies Inc, <info@movial.com>
6  *  Authors: Kalle Vahlman, <kalle.vahlman@movial.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later
12  *
13  * This library 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 Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
20  *
21  */
22
23 /*
24  * The Unit interface has methods that take a certain argument types and
25  * send a reply and a subsequent signal with the received arguments.
26  *
27  * To build, execute this:
28  *
29  *   gcc -o unit unit.c $(pkg-config --cflags --libs dbus-glib-1)
30  *
31  */
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include <dbus/dbus.h>
38 #include <dbus/dbus-glib-lowlevel.h>
39
40 #define OBJECT_PATH "/org/movial/Unit"
41 #define SERVICE_NAME "org.movial.Unit"
42
43 GMainLoop *loop;
44
45 static gboolean u_transfer_array(DBusMessageIter *to_iter, DBusMessageIter *from_iter);
46 static gboolean u_transfer_variant(DBusMessageIter *to_iter, DBusMessageIter *from_iter);
47 static gboolean u_transfer_dict(DBusMessageIter *to_iter, DBusMessageIter *from_iter);
48 static gboolean u_transfer_struct(DBusMessageIter *to_iter, DBusMessageIter *from_iter);
49
50 static void
51 u_unregister(DBusConnection *connection, void *user_data)
52 {
53   g_debug("Object path unregistered");
54   g_main_loop_quit(loop);
55 }
56
57 static void
58 u_transfer_arg(DBusMessageIter *to, DBusMessageIter *from)
59 {
60   dbus_uint64_t value;
61   int type = dbus_message_iter_get_arg_type(from);
62   
63   if (!dbus_type_is_basic(type))
64   {
65     switch (type)
66     {
67       case DBUS_TYPE_ARRAY:
68         u_transfer_array(to, from);
69         break;
70       case DBUS_TYPE_VARIANT:
71         u_transfer_variant(to, from);
72         break;
73       case DBUS_TYPE_DICT_ENTRY:
74         u_transfer_dict(to, from);
75         break;
76       case DBUS_TYPE_STRUCT:
77         u_transfer_struct(to, from);
78         break;
79       default:
80         g_warning("Non-basic type '%c' in variants not yet handled", type);
81         break;
82     }
83     return;
84   }
85
86   dbus_message_iter_get_basic(from, &value);
87   if (type == DBUS_TYPE_STRING)
88   {
89     g_debug("Transferring string arg %s", value);
90   }
91   if (type == DBUS_TYPE_INT32)
92   {
93     g_debug("Transferring int arg %i", value);
94   }
95   dbus_message_iter_append_basic(to, type, &value);
96 }
97
98 static gboolean
99 u_transfer_variant(DBusMessageIter *to_iter, DBusMessageIter *from_iter)
100 {
101   DBusMessageIter to;
102   DBusMessageIter from;
103   const char *sig = NULL;
104   
105   dbus_message_iter_recurse(from_iter, &from);
106   sig = dbus_message_iter_get_signature(&from);
107
108   if (sig == NULL)
109     return FALSE;
110
111   dbus_message_iter_open_container(to_iter, DBUS_TYPE_VARIANT, sig, &to);
112
113   do {
114     u_transfer_arg(&to, &from);
115   } while (dbus_message_iter_next(&from));
116   
117   dbus_message_iter_close_container(to_iter, &to);
118   
119   return TRUE;
120 }
121
122 static gboolean
123 u_transfer_array(DBusMessageIter *to_iter, DBusMessageIter *from_iter)
124 {
125   DBusMessageIter to;
126   DBusMessageIter from;
127   int elem_type;
128   char *sig = NULL;
129   
130   dbus_message_iter_recurse(from_iter, &from);
131
132   elem_type = dbus_message_iter_get_element_type(from_iter);
133   switch (elem_type)
134   {
135     case DBUS_TYPE_ARRAY:
136       {
137         /* Fetch the element type for this too */
138         sig = g_strdup(dbus_message_iter_get_signature(from_iter)+1);
139         break;
140       }
141     case DBUS_TYPE_DICT_ENTRY:
142     case DBUS_TYPE_STRUCT:
143       g_warning("Element type '%c' in arrays not supported", elem_type);
144       break;
145     default:
146       sig = g_strdup_printf("%c", dbus_message_iter_get_element_type(from_iter));
147       break;
148   }
149
150   if (sig == NULL)
151     return FALSE;
152
153   dbus_message_iter_open_container(to_iter, DBUS_TYPE_ARRAY, sig, &to);
154
155   do {
156     u_transfer_arg(&to, &from);
157   } while (dbus_message_iter_next(&from));
158   
159   dbus_message_iter_close_container(to_iter, &to);
160
161   return TRUE;
162 }
163
164 static gboolean
165 u_transfer_dict(DBusMessageIter *to_iter, DBusMessageIter *from_iter)
166 {
167   DBusMessageIter to;
168   DBusMessageIter from;
169   char *sig = NULL;
170
171   dbus_message_iter_recurse(from_iter, &from);
172   sig = g_strdup(dbus_message_iter_get_signature(&from));
173
174   if (sig == NULL)
175     return FALSE;
176
177   dbus_message_iter_open_container(to_iter, DBUS_TYPE_ARRAY, sig, &to);
178   do {
179     DBusMessageIter subfrom;
180     DBusMessageIter dictiter;
181     dbus_message_iter_open_container(&to, DBUS_TYPE_DICT_ENTRY,
182                                                     NULL, &dictiter);
183     dbus_message_iter_recurse(&from, &subfrom);
184
185     u_transfer_arg(&dictiter, &subfrom);
186     if (dbus_message_iter_next(&subfrom)) {
187       u_transfer_arg(&dictiter, &subfrom);
188     }
189     dbus_message_iter_close_container(&to, &dictiter);
190   } while (dbus_message_iter_next(&from));
191   
192   dbus_message_iter_close_container(to_iter, &to);
193   
194   return TRUE;
195 }
196
197 static gboolean
198 u_transfer_struct(DBusMessageIter *to_iter, DBusMessageIter *from_iter)
199 {
200   DBusMessageIter to;
201   DBusMessageIter from;
202
203   dbus_message_iter_recurse(from_iter, &from);
204   dbus_message_iter_open_container(to_iter, DBUS_TYPE_STRUCT, NULL, &to);
205
206   do {
207     u_transfer_arg(&to, &from);
208   } while (dbus_message_iter_next(&from));
209   
210   dbus_message_iter_close_container(to_iter, &to);
211   
212   return TRUE;
213 }
214
215 static DBusHandlerResult
216 u_message(DBusConnection *connection, DBusMessage *message, void *user_data)
217 {
218 #ifdef DEBUG  
219   const char *path = dbus_message_get_path(message);
220   const char *interface = dbus_message_get_interface(message);
221   const char *member = dbus_message_get_member(message);
222   const char *signature = dbus_message_get_signature(message);
223
224   g_debug("Message received, path %s, interface %s, member %s, signature %s",
225           path == NULL ? "not set" : path,
226           interface == NULL ? "not set" : interface,
227           member == NULL ? "not set" : member,
228           signature == NULL ? "not set" : signature);
229 #endif
230   
231   if (!dbus_message_has_path(message, OBJECT_PATH))
232     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
233
234   if (dbus_message_is_method_call(message, SERVICE_NAME, "start"))
235   {
236     DBusMessage *reply;
237
238     /* TODO: start logging */
239     
240     reply = dbus_message_new_method_return(message);
241     dbus_connection_send(connection, reply, NULL);
242     dbus_message_unref(reply);
243     
244     return DBUS_HANDLER_RESULT_HANDLED;
245   }
246
247   if (dbus_message_is_method_call(message, SERVICE_NAME, "end"))
248   {
249     DBusMessage *reply;
250
251     /* TODO: stop logging */
252     
253     reply = dbus_message_new_method_return(message);
254     dbus_connection_send(connection, reply, NULL);
255     dbus_message_unref(reply);
256     
257     return DBUS_HANDLER_RESULT_HANDLED;
258   }
259
260   if (strlen(dbus_message_get_signature(message)) == 0)
261   {
262     DBusMessage *reply = dbus_message_new_error(message,
263                                                 SERVICE_NAME ".ArgError",
264                                                 "Empty signature");
265     dbus_connection_send(connection, reply, NULL);
266     dbus_message_unref(reply);
267     return DBUS_HANDLER_RESULT_HANDLED;
268   }
269
270 #define BASIC_TYPE(method_name, dtype) \
271   if (dbus_message_is_method_call(message, SERVICE_NAME, method_name)) \
272   { \
273     dbus_uint64_t value = 0; \
274     DBusError err; \
275     DBusMessage *reply = NULL; \
276     DBusMessage *signal = NULL; \
277     dbus_error_init(&err); \
278     if (!dbus_message_get_args(message, &err, \
279                                DBUS_TYPE_ ## dtype, &value, \
280                                DBUS_TYPE_INVALID)) \
281     { \
282       reply = dbus_message_new_error(message, \
283                                      SERVICE_NAME ".ArgError", \
284                                      err.message); \
285       g_debug(err.message); \
286       g_assert(reply != NULL); \
287     } else { \
288       reply = dbus_message_new_method_return(message); \
289       g_assert(reply != NULL); \
290       dbus_message_append_args(reply, \
291                                DBUS_TYPE_ ## dtype, &value, \
292                                DBUS_TYPE_INVALID); \
293       signal = dbus_message_new_signal(OBJECT_PATH, SERVICE_NAME, method_name); \
294       g_debug("Appending %llu to signal", value); \
295       dbus_message_append_args(signal, \
296                                DBUS_TYPE_ ## dtype, &value, \
297                                DBUS_TYPE_INVALID); \
298     } \
299     dbus_connection_send(connection, reply, NULL); \
300     dbus_message_unref(reply); \
301     if (signal != NULL) \
302     { \
303       dbus_connection_send(connection, signal, NULL); \
304       dbus_message_unref(signal); \
305     } \
306     return DBUS_HANDLER_RESULT_HANDLED; \
307   }
308   
309   BASIC_TYPE("Boolean", BOOLEAN);
310   BASIC_TYPE("Byte", BYTE);
311   BASIC_TYPE("Int16", INT16);
312   BASIC_TYPE("Int32", INT32);
313   BASIC_TYPE("Int64", INT64);
314   BASIC_TYPE("UInt16", UINT16);
315   BASIC_TYPE("UInt32", UINT32);
316   BASIC_TYPE("UInt64", UINT64);
317   BASIC_TYPE("Double", DOUBLE);
318   BASIC_TYPE("String", STRING);
319   BASIC_TYPE("ObjectPath", OBJECT_PATH);
320   BASIC_TYPE("Signature", SIGNATURE);
321   
322   if (dbus_message_is_method_call(message, SERVICE_NAME, "Array"))
323   {
324     DBusMessageIter iter;
325     DBusMessageIter reply_iter;
326     DBusMessage *reply;
327     DBusMessage *signal;
328
329     dbus_message_iter_init(message, &iter);
330
331     reply = dbus_message_new_method_return(message);
332
333     dbus_message_iter_init_append(reply, &reply_iter);
334     u_transfer_array(&reply_iter, &iter);
335
336     dbus_message_iter_init(message, &iter);
337     signal = dbus_message_new_signal(OBJECT_PATH, SERVICE_NAME, "Array");
338     dbus_message_iter_init_append(signal, &reply_iter);
339     u_transfer_array(&reply_iter, &iter);
340
341     dbus_connection_send(connection, reply, NULL);
342     dbus_message_unref(reply);
343     dbus_connection_send(connection, signal, NULL);
344     dbus_message_unref(signal);
345     return DBUS_HANDLER_RESULT_HANDLED;
346   }
347
348   if (dbus_message_is_method_call(message, SERVICE_NAME, "Variant"))
349   {
350     DBusMessageIter iter;
351     DBusMessageIter reply_iter;
352     DBusMessage *reply;
353     DBusMessage *signal = NULL;
354
355     if (!g_str_equal(dbus_message_get_signature(message), "v"))
356     {
357       reply = dbus_message_new_error(message,
358                                      SERVICE_NAME ".ArgError",
359                                      "Signature mismatch");
360       dbus_connection_send(connection, reply, NULL);
361       dbus_message_unref(reply);
362       return DBUS_HANDLER_RESULT_HANDLED;
363     }
364     dbus_message_iter_init(message, &iter);
365
366     reply = dbus_message_new_method_return(message);
367     dbus_message_iter_init_append(reply, &reply_iter);
368     u_transfer_variant(&reply_iter, &iter);
369
370     dbus_message_iter_init(message, &iter);
371     signal = dbus_message_new_signal(OBJECT_PATH, SERVICE_NAME, "Variant");
372     dbus_message_iter_init_append(signal, &reply_iter);
373     u_transfer_variant(&reply_iter, &iter);
374
375     dbus_connection_send(connection, reply, NULL);
376     dbus_message_unref(reply);
377     dbus_connection_send(connection, signal, NULL);
378     dbus_message_unref(signal);
379     return DBUS_HANDLER_RESULT_HANDLED;
380   }
381
382   if (dbus_message_is_method_call(message, SERVICE_NAME, "Dict"))
383   {
384     DBusMessageIter iter;
385     DBusMessageIter reply_iter;
386     DBusMessage *reply;
387     DBusMessage *signal;
388
389     dbus_message_iter_init(message, &iter);
390
391     reply = dbus_message_new_method_return(message);
392     dbus_message_iter_init_append(reply, &reply_iter);
393
394     u_transfer_dict(&reply_iter, &iter);
395
396     dbus_message_iter_init(message, &iter);
397     signal = dbus_message_new_signal(OBJECT_PATH, SERVICE_NAME, "Dict");
398     dbus_message_iter_init_append(signal, &reply_iter);
399     u_transfer_dict(&reply_iter, &iter);
400
401     dbus_connection_send(connection, reply, NULL);
402     dbus_message_unref(reply);
403     dbus_connection_send(connection, signal, NULL);
404     dbus_message_unref(signal);
405     
406     return DBUS_HANDLER_RESULT_HANDLED;
407   }
408
409   if (dbus_message_is_method_call(message, SERVICE_NAME, "Struct"))
410   {
411     DBusMessageIter iter;
412     DBusMessageIter reply_iter;
413     DBusMessage *reply;
414     DBusMessage *signal;
415     const char *signature = dbus_message_get_signature(message);
416     
417     if (signature[0] != '(')
418         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
419
420     dbus_message_iter_init(message, &iter);
421
422     reply = dbus_message_new_method_return(message);
423     dbus_message_iter_init_append(reply, &reply_iter);
424
425     u_transfer_struct(&reply_iter, &iter);
426
427     dbus_message_iter_init(message, &iter);
428     signal = dbus_message_new_signal(OBJECT_PATH, SERVICE_NAME, "Struct");
429     dbus_message_iter_init_append(signal, &reply_iter);
430     u_transfer_struct(&reply_iter, &iter);
431
432     dbus_connection_send(connection, reply, NULL);
433     dbus_message_unref(reply);
434     dbus_connection_send(connection, signal, NULL);
435     dbus_message_unref(signal);
436     
437     return DBUS_HANDLER_RESULT_HANDLED;
438   }
439
440 #ifdef DEBUG
441   g_debug("Didn't handle the message");
442 #endif
443
444   
445   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
446 }
447
448 int main(int argc, char **argv)
449 {
450   DBusError err;
451   DBusConnection* conn;
452   DBusObjectPathVTable vtable;
453   int ret;
454
455   dbus_error_init(&err);
456   conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
457   if (dbus_error_is_set(&err)) { 
458     g_warning("Connection Error (%s)\n", err.message); 
459     dbus_error_free(&err); 
460   }
461   g_assert(conn != NULL);
462
463   vtable.unregister_function = u_unregister;
464   vtable.message_function = u_message;
465
466   if (!dbus_connection_register_object_path(conn, OBJECT_PATH,
467                                             &vtable, NULL))
468   {
469     g_error("Could not register object path");
470   }
471
472   ret = dbus_bus_request_name(conn, SERVICE_NAME, 
473                               DBUS_NAME_FLAG_REPLACE_EXISTING, &err);
474   if (dbus_error_is_set(&err)) { 
475     g_warning("Requesting service name failed (%s)\n", err.message); 
476     dbus_error_free(&err); 
477   }
478   g_assert(DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER == ret);
479
480   dbus_bus_add_match(conn, "type='method_call'", &err);
481   if (dbus_error_is_set(&err)) { 
482     g_warning("Add match failed (%s)\n", err.message); 
483     dbus_error_free(&err);
484     exit(1);
485   }
486
487   loop = g_main_loop_new(NULL, FALSE);
488   dbus_connection_setup_with_g_main(conn, NULL);
489   
490         g_print("Unit ready to accept method calls\n");
491   g_main_loop_run(loop);
492   
493   return 0;
494 }