Add note about building against QtWebKit in Makefile help
[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   if (type == DBUS_TYPE_DOUBLE)
96   {
97     g_debug("Transferring double arg %f", (double)value);
98   }
99   dbus_message_iter_append_basic(to, type, &value);
100 }
101
102 static gboolean
103 u_transfer_variant(DBusMessageIter *to_iter, DBusMessageIter *from_iter)
104 {
105   DBusMessageIter to;
106   DBusMessageIter from;
107   const char *sig = NULL;
108   
109   dbus_message_iter_recurse(from_iter, &from);
110   sig = dbus_message_iter_get_signature(&from);
111
112   if (sig == NULL)
113     return FALSE;
114
115   dbus_message_iter_open_container(to_iter, DBUS_TYPE_VARIANT, sig, &to);
116
117   do {
118     u_transfer_arg(&to, &from);
119   } while (dbus_message_iter_next(&from));
120   
121   dbus_message_iter_close_container(to_iter, &to);
122   
123   return TRUE;
124 }
125
126 static gboolean
127 u_transfer_array(DBusMessageIter *to_iter, DBusMessageIter *from_iter)
128 {
129   DBusMessageIter to;
130   DBusMessageIter from;
131   int elem_type;
132   char *sig = NULL;
133   
134   elem_type = dbus_message_iter_get_element_type(from_iter);
135   switch (elem_type)
136   {
137     case DBUS_TYPE_ARRAY:
138       {
139         /* Fetch the element type for this too */
140         sig = g_strdup(dbus_message_iter_get_signature(from_iter)+1);
141         break;
142       }
143     case DBUS_TYPE_DICT_ENTRY:
144       /* Oh, this was actually a dict */
145       return u_transfer_dict(to_iter, from_iter);
146       break;
147     case DBUS_TYPE_STRUCT:
148       u_transfer_struct(to_iter, from_iter);
149       break;
150     default:
151       sig = g_strdup_printf("%c", dbus_message_iter_get_element_type(from_iter));
152       break;
153   }
154
155   if (sig == NULL)
156     return FALSE;
157
158   dbus_message_iter_recurse(from_iter, &from);
159
160   dbus_message_iter_open_container(to_iter, DBUS_TYPE_ARRAY, sig, &to);
161
162   do {
163     u_transfer_arg(&to, &from);
164   } while (dbus_message_iter_next(&from));
165   
166   dbus_message_iter_close_container(to_iter, &to);
167
168   return TRUE;
169 }
170
171 static gboolean
172 u_transfer_dict(DBusMessageIter *to_iter, DBusMessageIter *from_iter)
173 {
174   DBusMessageIter to;
175   DBusMessageIter from;
176   char *sig = NULL;
177
178   dbus_message_iter_recurse(from_iter, &from);
179   sig = g_strdup(dbus_message_iter_get_signature(&from));
180
181   if (sig == NULL)
182     return FALSE;
183
184   dbus_message_iter_open_container(to_iter, DBUS_TYPE_ARRAY, sig, &to);
185   do {
186     DBusMessageIter subfrom;
187     DBusMessageIter dictiter;
188     dbus_message_iter_open_container(&to, DBUS_TYPE_DICT_ENTRY,
189                                                     NULL, &dictiter);
190     dbus_message_iter_recurse(&from, &subfrom);
191
192     u_transfer_arg(&dictiter, &subfrom);
193     if (dbus_message_iter_next(&subfrom)) {
194       u_transfer_arg(&dictiter, &subfrom);
195     }
196     dbus_message_iter_close_container(&to, &dictiter);
197   } while (dbus_message_iter_next(&from));
198   
199   dbus_message_iter_close_container(to_iter, &to);
200   
201   return TRUE;
202 }
203
204 static gboolean
205 u_transfer_struct(DBusMessageIter *to_iter, DBusMessageIter *from_iter)
206 {
207   DBusMessageIter to;
208   DBusMessageIter from;
209
210   dbus_message_iter_recurse(from_iter, &from);
211   dbus_message_iter_open_container(to_iter, DBUS_TYPE_STRUCT, NULL, &to);
212
213   do {
214     u_transfer_arg(&to, &from);
215   } while (dbus_message_iter_next(&from));
216   
217   dbus_message_iter_close_container(to_iter, &to);
218   
219   return TRUE;
220 }
221
222 static DBusHandlerResult
223 u_message(DBusConnection *connection, DBusMessage *message, void *user_data)
224 {
225 #ifdef DEBUG  
226   const char *path = dbus_message_get_path(message);
227   const char *interface = dbus_message_get_interface(message);
228   const char *member = dbus_message_get_member(message);
229   const char *signature = dbus_message_get_signature(message);
230
231   g_debug("Message received, path %s, interface %s, member %s, signature %s",
232           path == NULL ? "not set" : path,
233           interface == NULL ? "not set" : interface,
234           member == NULL ? "not set" : member,
235           signature == NULL ? "not set" : signature);
236 #endif
237   
238   if (!dbus_message_has_path(message, OBJECT_PATH))
239     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
240
241   if (dbus_message_is_method_call(message, SERVICE_NAME, "start"))
242   {
243     DBusMessage *reply;
244
245     /* TODO: start logging */
246     
247     reply = dbus_message_new_method_return(message);
248     dbus_connection_send(connection, reply, NULL);
249     dbus_message_unref(reply);
250     
251     return DBUS_HANDLER_RESULT_HANDLED;
252   }
253
254   if (dbus_message_is_method_call(message, SERVICE_NAME, "end"))
255   {
256     DBusMessage *reply;
257
258     /* TODO: stop logging */
259     
260     reply = dbus_message_new_method_return(message);
261     dbus_connection_send(connection, reply, NULL);
262     dbus_message_unref(reply);
263     
264     return DBUS_HANDLER_RESULT_HANDLED;
265   }
266
267   if (strlen(dbus_message_get_signature(message)) == 0)
268   {
269     DBusMessage *reply = dbus_message_new_error(message,
270                                                 SERVICE_NAME ".ArgError",
271                                                 "Empty signature");
272     dbus_connection_send(connection, reply, NULL);
273     dbus_message_unref(reply);
274     return DBUS_HANDLER_RESULT_HANDLED;
275   }
276
277 #define BASIC_TYPE(method_name, dtype) \
278   if (dbus_message_is_method_call(message, SERVICE_NAME, method_name)) \
279   { \
280     dbus_uint64_t value = 0; \
281     DBusError err; \
282     DBusMessage *reply = NULL; \
283     DBusMessage *signal = NULL; \
284     dbus_error_init(&err); \
285     if (!dbus_message_get_args(message, &err, \
286                                DBUS_TYPE_ ## dtype, &value, \
287                                DBUS_TYPE_INVALID)) \
288     { \
289       reply = dbus_message_new_error(message, \
290                                      SERVICE_NAME ".ArgError", \
291                                      err.message); \
292       g_debug(err.message); \
293       g_assert(reply != NULL); \
294     } else { \
295       reply = dbus_message_new_method_return(message); \
296       g_assert(reply != NULL); \
297       dbus_message_append_args(reply, \
298                                DBUS_TYPE_ ## dtype, &value, \
299                                DBUS_TYPE_INVALID); \
300       signal = dbus_message_new_signal(OBJECT_PATH, SERVICE_NAME, method_name); \
301       g_debug("Appending %llu to signal", value); \
302       dbus_message_append_args(signal, \
303                                DBUS_TYPE_ ## dtype, &value, \
304                                DBUS_TYPE_INVALID); \
305     } \
306     dbus_connection_send(connection, reply, NULL); \
307     dbus_message_unref(reply); \
308     if (signal != NULL) \
309     { \
310       dbus_connection_send(connection, signal, NULL); \
311       dbus_message_unref(signal); \
312     } \
313     return DBUS_HANDLER_RESULT_HANDLED; \
314   }
315   
316   BASIC_TYPE("Boolean", BOOLEAN);
317   BASIC_TYPE("Byte", BYTE);
318   BASIC_TYPE("Int16", INT16);
319   BASIC_TYPE("Int32", INT32);
320   BASIC_TYPE("Int64", INT64);
321   BASIC_TYPE("UInt16", UINT16);
322   BASIC_TYPE("UInt32", UINT32);
323   BASIC_TYPE("UInt64", UINT64);
324   BASIC_TYPE("Double", DOUBLE);
325   BASIC_TYPE("String", STRING);
326   BASIC_TYPE("ObjectPath", OBJECT_PATH);
327   BASIC_TYPE("Signature", SIGNATURE);
328   
329   if (dbus_message_is_method_call(message, SERVICE_NAME, "Array"))
330   {
331     DBusMessageIter iter;
332     DBusMessageIter reply_iter;
333     DBusMessage *reply;
334     DBusMessage *signal;
335
336     dbus_message_iter_init(message, &iter);
337
338     if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
339     {
340       reply = dbus_message_new_error(message,
341                                      SERVICE_NAME ".ArgError",
342                                      "Signature mismatch");
343       dbus_connection_send(connection, reply, NULL);
344       dbus_message_unref(reply);
345       return DBUS_HANDLER_RESULT_HANDLED;
346     }
347
348     reply = dbus_message_new_method_return(message);
349
350     dbus_message_iter_init_append(reply, &reply_iter);
351     u_transfer_array(&reply_iter, &iter);
352
353     dbus_message_iter_init(message, &iter);
354     signal = dbus_message_new_signal(OBJECT_PATH, SERVICE_NAME, "Array");
355     dbus_message_iter_init_append(signal, &reply_iter);
356     u_transfer_array(&reply_iter, &iter);
357
358     dbus_connection_send(connection, reply, NULL);
359     dbus_message_unref(reply);
360     dbus_connection_send(connection, signal, NULL);
361     dbus_message_unref(signal);
362     return DBUS_HANDLER_RESULT_HANDLED;
363   }
364
365   if (dbus_message_is_method_call(message, SERVICE_NAME, "Variant"))
366   {
367     DBusMessageIter iter;
368     DBusMessageIter reply_iter;
369     DBusMessage *reply;
370     DBusMessage *signal = NULL;
371
372     if (!g_str_equal(dbus_message_get_signature(message), "v"))
373     {
374       reply = dbus_message_new_error(message,
375                                      SERVICE_NAME ".ArgError",
376                                      "Signature mismatch");
377       dbus_connection_send(connection, reply, NULL);
378       dbus_message_unref(reply);
379       return DBUS_HANDLER_RESULT_HANDLED;
380     }
381     dbus_message_iter_init(message, &iter);
382
383     reply = dbus_message_new_method_return(message);
384     dbus_message_iter_init_append(reply, &reply_iter);
385     u_transfer_variant(&reply_iter, &iter);
386
387     dbus_message_iter_init(message, &iter);
388     signal = dbus_message_new_signal(OBJECT_PATH, SERVICE_NAME, "Variant");
389     dbus_message_iter_init_append(signal, &reply_iter);
390     u_transfer_variant(&reply_iter, &iter);
391
392     dbus_connection_send(connection, reply, NULL);
393     dbus_message_unref(reply);
394     dbus_connection_send(connection, signal, NULL);
395     dbus_message_unref(signal);
396     return DBUS_HANDLER_RESULT_HANDLED;
397   }
398
399   if (dbus_message_is_method_call(message, SERVICE_NAME, "Dict"))
400   {
401     DBusMessageIter iter;
402     DBusMessageIter reply_iter;
403     DBusMessage *reply;
404     DBusMessage *signal;
405
406     dbus_message_iter_init(message, &iter);
407
408     reply = dbus_message_new_method_return(message);
409     dbus_message_iter_init_append(reply, &reply_iter);
410
411     u_transfer_dict(&reply_iter, &iter);
412
413     dbus_message_iter_init(message, &iter);
414     signal = dbus_message_new_signal(OBJECT_PATH, SERVICE_NAME, "Dict");
415     dbus_message_iter_init_append(signal, &reply_iter);
416     u_transfer_dict(&reply_iter, &iter);
417
418     dbus_connection_send(connection, reply, NULL);
419     dbus_message_unref(reply);
420     dbus_connection_send(connection, signal, NULL);
421     dbus_message_unref(signal);
422     
423     return DBUS_HANDLER_RESULT_HANDLED;
424   }
425
426   if (dbus_message_is_method_call(message, SERVICE_NAME, "Struct"))
427   {
428     DBusMessageIter iter;
429     DBusMessageIter reply_iter;
430     DBusMessage *reply;
431     DBusMessage *signal;
432     const char *signature = dbus_message_get_signature(message);
433     
434     if (signature[0] != '(')
435         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
436
437     dbus_message_iter_init(message, &iter);
438
439     reply = dbus_message_new_method_return(message);
440     dbus_message_iter_init_append(reply, &reply_iter);
441
442     u_transfer_struct(&reply_iter, &iter);
443
444     dbus_message_iter_init(message, &iter);
445     signal = dbus_message_new_signal(OBJECT_PATH, SERVICE_NAME, "Struct");
446     dbus_message_iter_init_append(signal, &reply_iter);
447     u_transfer_struct(&reply_iter, &iter);
448
449     dbus_connection_send(connection, reply, NULL);
450     dbus_message_unref(reply);
451     dbus_connection_send(connection, signal, NULL);
452     dbus_message_unref(signal);
453     
454     return DBUS_HANDLER_RESULT_HANDLED;
455   }
456
457 #ifdef DEBUG
458   g_debug("Didn't handle the message");
459 #endif
460
461   
462   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
463 }
464
465 int main(int argc, char **argv)
466 {
467   DBusError err;
468   DBusConnection* conn;
469   DBusObjectPathVTable vtable;
470   int ret;
471
472   dbus_error_init(&err);
473   conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
474   if (dbus_error_is_set(&err)) { 
475     g_warning("Connection Error (%s)\n", err.message); 
476     dbus_error_free(&err); 
477   }
478   g_assert(conn != NULL);
479
480   vtable.unregister_function = u_unregister;
481   vtable.message_function = u_message;
482
483   if (!dbus_connection_register_object_path(conn, OBJECT_PATH,
484                                             &vtable, NULL))
485   {
486     g_error("Could not register object path");
487   }
488
489   ret = dbus_bus_request_name(conn, SERVICE_NAME, 
490                               DBUS_NAME_FLAG_REPLACE_EXISTING, &err);
491   if (dbus_error_is_set(&err)) { 
492     g_warning("Requesting service name failed (%s)\n", err.message); 
493     dbus_error_free(&err); 
494   }
495   g_assert(DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER == ret);
496
497   dbus_bus_add_match(conn, "type='method_call'", &err);
498   if (dbus_error_is_set(&err)) { 
499     g_warning("Add match failed (%s)\n", err.message); 
500     dbus_error_free(&err);
501     exit(1);
502   }
503
504   loop = g_main_loop_new(NULL, FALSE);
505   dbus_connection_setup_with_g_main(conn, NULL);
506   
507         g_print("Unit ready to accept method calls\n");
508   g_main_loop_run(loop);
509   
510   return 0;
511 }