Improve coverage of credentials tests
[platform/upstream/glib.git] / gio / tests / gdbus-peer.c
1 /* GLib testing framework examples and tests
2  *
3  * Copyright (C) 2008-2010 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: David Zeuthen <davidz@redhat.com>
21  */
22
23 #include <gio/gio.h>
24 #include <unistd.h>
25 #include <string.h>
26
27 /* for open(2) */
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31
32 /* for g_unlink() */
33 #include <glib/gstdio.h>
34
35 #include <gio/gunixsocketaddress.h>
36 #include <gio/gunixfdlist.h>
37
38 /* for struct ucred */
39 #ifdef __linux__
40 #define __USE_GNU
41 #include <sys/types.h>
42 #include <sys/socket.h>
43 #endif
44
45 #include "gdbus-tests.h"
46
47
48 #ifdef G_OS_UNIX
49 static gboolean is_unix = TRUE;
50 #else
51 static gboolean is_unix = FALSE;
52 #endif
53
54 static gchar *test_guid = NULL;
55 static GMainLoop *service_loop = NULL;
56 static GDBusServer *server = NULL;
57 static GMainLoop *loop = NULL;
58
59 /* ---------------------------------------------------------------------------------------------------- */
60 /* Test that peer-to-peer connections work */
61 /* ---------------------------------------------------------------------------------------------------- */
62
63
64 typedef struct
65 {
66   gboolean accept_connection;
67   gint num_connection_attempts;
68   GPtrArray *current_connections;
69   guint num_method_calls;
70   gboolean signal_received;
71 } PeerData;
72
73 static const gchar *test_interface_introspection_xml =
74   "<node>"
75   "  <interface name='org.gtk.GDBus.PeerTestInterface'>"
76   "    <method name='HelloPeer'>"
77   "      <arg type='s' name='greeting' direction='in'/>"
78   "      <arg type='s' name='response' direction='out'/>"
79   "    </method>"
80   "    <method name='EmitSignal'/>"
81   "    <method name='EmitSignalWithNameSet'/>"
82   "    <method name='OpenFile'>"
83   "      <arg type='s' name='path' direction='in'/>"
84   "    </method>"
85   "    <signal name='PeerSignal'>"
86   "      <arg type='s' name='a_string'/>"
87   "    </signal>"
88   "    <property type='s' name='PeerProperty' access='read'/>"
89   "  </interface>"
90   "</node>";
91 static GDBusInterfaceInfo *test_interface_introspection_data = NULL;
92
93 static void
94 test_interface_method_call (GDBusConnection       *connection,
95                             const gchar           *sender,
96                             const gchar           *object_path,
97                             const gchar           *interface_name,
98                             const gchar           *method_name,
99                             GVariant              *parameters,
100                             GDBusMethodInvocation *invocation,
101                             gpointer               user_data)
102 {
103   PeerData *data = user_data;
104
105   data->num_method_calls++;
106
107   g_assert_cmpstr (object_path, ==, "/org/gtk/GDBus/PeerTestObject");
108   g_assert_cmpstr (interface_name, ==, "org.gtk.GDBus.PeerTestInterface");
109
110   if (g_strcmp0 (method_name, "HelloPeer") == 0)
111     {
112       const gchar *greeting;
113       gchar *response;
114
115       g_variant_get (parameters, "(&s)", &greeting);
116
117       response = g_strdup_printf ("You greeted me with '%s'.",
118                                   greeting);
119       g_dbus_method_invocation_return_value (invocation,
120                                              g_variant_new ("(s)", response));
121       g_free (response);
122     }
123   else if (g_strcmp0 (method_name, "EmitSignal") == 0)
124     {
125       GError *error;
126
127       error = NULL;
128       g_dbus_connection_emit_signal (connection,
129                                      NULL,
130                                      "/org/gtk/GDBus/PeerTestObject",
131                                      "org.gtk.GDBus.PeerTestInterface",
132                                      "PeerSignal",
133                                      NULL,
134                                      &error);
135       g_assert_no_error (error);
136       g_dbus_method_invocation_return_value (invocation, NULL);
137     }
138   else if (g_strcmp0 (method_name, "EmitSignalWithNameSet") == 0)
139     {
140       GError *error;
141       gboolean ret;
142       GDBusMessage *message;
143
144       message = g_dbus_message_new_signal ("/org/gtk/GDBus/PeerTestObject",
145                                            "org.gtk.GDBus.PeerTestInterface",
146                                            "PeerSignalWithNameSet");
147       g_dbus_message_set_sender (message, ":1.42");
148
149       error = NULL;
150       ret = g_dbus_connection_send_message (connection, message, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, &error);
151       g_assert_no_error (error);
152       g_assert (ret);
153       g_object_unref (message);
154
155       g_dbus_method_invocation_return_value (invocation, NULL);
156     }
157   else if (g_strcmp0 (method_name, "OpenFile") == 0)
158     {
159 #ifdef G_OS_UNIX
160       const gchar *path;
161       GDBusMessage *reply;
162       GError *error;
163       gint fd;
164       GUnixFDList *fd_list;
165
166       g_variant_get (parameters, "(&s)", &path);
167
168       fd_list = g_unix_fd_list_new ();
169
170       error = NULL;
171
172       fd = open (path, O_RDONLY);
173       g_unix_fd_list_append (fd_list, fd, &error);
174       g_assert_no_error (error);
175       close (fd);
176
177       reply = g_dbus_message_new_method_reply (g_dbus_method_invocation_get_message (invocation));
178       g_dbus_message_set_unix_fd_list (reply, fd_list);
179       g_object_unref (invocation);
180
181       error = NULL;
182       g_dbus_connection_send_message (connection,
183                                       reply,
184                                       G_DBUS_SEND_MESSAGE_FLAGS_NONE,
185                                       NULL, /* out_serial */
186                                       &error);
187       g_assert_no_error (error);
188       g_object_unref (reply);
189 #else
190       g_dbus_method_invocation_return_dbus_error (invocation,
191                                                   "org.gtk.GDBus.NotOnUnix",
192                                                   "Your OS does not support file descriptor passing");
193 #endif
194     }
195   else
196     {
197       g_assert_not_reached ();
198     }
199 }
200
201 static GVariant *
202 test_interface_get_property (GDBusConnection  *connection,
203                              const gchar      *sender,
204                              const gchar      *object_path,
205                              const gchar      *interface_name,
206                              const gchar      *property_name,
207                              GError          **error,
208                              gpointer          user_data)
209 {
210   g_assert_cmpstr (object_path, ==, "/org/gtk/GDBus/PeerTestObject");
211   g_assert_cmpstr (interface_name, ==, "org.gtk.GDBus.PeerTestInterface");
212   g_assert_cmpstr (property_name, ==, "PeerProperty");
213
214   return g_variant_new_string ("ThePropertyValue");
215 }
216
217
218 static const GDBusInterfaceVTable test_interface_vtable =
219 {
220   test_interface_method_call,
221   test_interface_get_property,
222   NULL  /* set_property */
223 };
224
225 static void
226 on_proxy_signal_received (GDBusProxy *proxy,
227                           gchar      *sender_name,
228                           gchar      *signal_name,
229                           GVariant   *parameters,
230                           gpointer    user_data)
231 {
232   PeerData *data = user_data;
233
234   data->signal_received = TRUE;
235
236   g_assert (sender_name == NULL);
237   g_assert_cmpstr (signal_name, ==, "PeerSignal");
238   g_main_loop_quit (loop);
239 }
240
241 static void
242 on_proxy_signal_received_with_name_set (GDBusProxy *proxy,
243                                         gchar      *sender_name,
244                                         gchar      *signal_name,
245                                         GVariant   *parameters,
246                                         gpointer    user_data)
247 {
248   PeerData *data = user_data;
249
250   data->signal_received = TRUE;
251
252   g_assert_cmpstr (sender_name, ==, ":1.42");
253   g_assert_cmpstr (signal_name, ==, "PeerSignalWithNameSet");
254   g_main_loop_quit (loop);
255 }
256
257 /* ---------------------------------------------------------------------------------------------------- */
258
259 static gboolean
260 on_authorize_authenticated_peer (GDBusAuthObserver *observer,
261                                  GIOStream         *stream,
262                                  GCredentials      *credentials,
263                                  gpointer           user_data)
264 {
265   PeerData *data = user_data;
266   gboolean authorized;
267
268   data->num_connection_attempts++;
269
270   authorized = TRUE;
271   if (!data->accept_connection)
272     {
273       authorized = FALSE;
274       g_main_loop_quit (loop);
275     }
276
277   return authorized;
278 }
279
280 /* Runs in thread we created GDBusServer in (since we didn't pass G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) */
281 static void
282 on_new_connection (GDBusServer *server,
283                    GDBusConnection *connection,
284                    gpointer user_data)
285 {
286   PeerData *data = user_data;
287   GError *error;
288   guint reg_id;
289
290   //g_print ("Client connected.\n"
291   //         "Negotiated capabilities: unix-fd-passing=%d\n",
292   //         g_dbus_connection_get_capabilities (connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING);
293
294   g_ptr_array_add (data->current_connections, g_object_ref (connection));
295
296   /* export object on the newly established connection */
297   error = NULL;
298   reg_id = g_dbus_connection_register_object (connection,
299                                               "/org/gtk/GDBus/PeerTestObject",
300                                               test_interface_introspection_data,
301                                               &test_interface_vtable,
302                                               data,
303                                               NULL, /* GDestroyNotify for data */
304                                               &error);
305   g_assert_no_error (error);
306   g_assert (reg_id > 0);
307
308   g_main_loop_quit (loop);
309 }
310
311 static gpointer
312 service_thread_func (gpointer user_data)
313 {
314   PeerData *data = user_data;
315   GMainContext *service_context;
316   GDBusAuthObserver *observer;
317   GError *error;
318
319   service_context = g_main_context_new ();
320   g_main_context_push_thread_default (service_context);
321
322   error = NULL;
323   observer = g_dbus_auth_observer_new ();
324   server = g_dbus_server_new_sync (is_unix ? "unix:tmpdir=/tmp/gdbus-test-" : "nonce-tcp:",
325                                    G_DBUS_SERVER_FLAGS_NONE,
326                                    test_guid,
327                                    observer,
328                                    NULL, /* cancellable */
329                                    &error);
330   g_assert_no_error (error);
331
332   g_signal_connect (server,
333                     "new-connection",
334                     G_CALLBACK (on_new_connection),
335                     data);
336   g_signal_connect (observer,
337                     "authorize-authenticated-peer",
338                     G_CALLBACK (on_authorize_authenticated_peer),
339                     data);
340   g_object_unref (observer);
341
342   g_dbus_server_start (server);
343
344   service_loop = g_main_loop_new (service_context, FALSE);
345   g_main_loop_run (service_loop);
346
347   g_main_context_pop_thread_default (service_context);
348
349   g_main_loop_unref (service_loop);
350   g_main_context_unref (service_context);
351
352   /* test code specifically unrefs the server - see below */
353   g_assert (server == NULL);
354
355   return NULL;
356 }
357
358 #if 0
359 static gboolean
360 on_incoming_connection (GSocketService     *service,
361                         GSocketConnection  *socket_connection,
362                         GObject            *source_object,
363                         gpointer           user_data)
364 {
365   PeerData *data = user_data;
366
367   if (data->accept_connection)
368     {
369       GError *error;
370       guint reg_id;
371       GDBusConnection *connection;
372
373       error = NULL;
374       connection = g_dbus_connection_new_sync (G_IO_STREAM (socket_connection),
375                                                test_guid,
376                                                G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER,
377                                                NULL, /* cancellable */
378                                                &error);
379       g_assert_no_error (error);
380
381       g_ptr_array_add (data->current_connections, connection);
382
383       /* export object on the newly established connection */
384       error = NULL;
385       reg_id = g_dbus_connection_register_object (connection,
386                                                   "/org/gtk/GDBus/PeerTestObject",
387                                                   &test_interface_introspection_data,
388                                                   &test_interface_vtable,
389                                                   data,
390                                                   NULL, /* GDestroyNotify for data */
391                                                   &error);
392       g_assert_no_error (error);
393       g_assert (reg_id > 0);
394
395     }
396   else
397     {
398       /* don't do anything */
399     }
400
401   data->num_connection_attempts++;
402
403   g_main_loop_quit (loop);
404
405   /* stops other signal handlers from being invoked */
406   return TRUE;
407 }
408
409 static gpointer
410 service_thread_func (gpointer data)
411 {
412   GMainContext *service_context;
413   gchar *socket_path;
414   GSocketAddress *address;
415   GError *error;
416
417   service_context = g_main_context_new ();
418   g_main_context_push_thread_default (service_context);
419
420   socket_path = g_strdup_printf ("/tmp/gdbus-test-pid-%d", getpid ());
421   address = g_unix_socket_address_new (socket_path);
422
423   service = g_socket_service_new ();
424   error = NULL;
425   g_socket_listener_add_address (G_SOCKET_LISTENER (service),
426                                  address,
427                                  G_SOCKET_TYPE_STREAM,
428                                  G_SOCKET_PROTOCOL_DEFAULT,
429                                  NULL, /* source_object */
430                                  NULL, /* effective_address */
431                                  &error);
432   g_assert_no_error (error);
433   g_signal_connect (service,
434                     "incoming",
435                     G_CALLBACK (on_incoming_connection),
436                     data);
437   g_socket_service_start (service);
438
439   service_loop = g_main_loop_new (service_context, FALSE);
440   g_main_loop_run (service_loop);
441
442   g_main_context_pop_thread_default (service_context);
443
444   g_main_loop_unref (service_loop);
445   g_main_context_unref (service_context);
446
447   g_object_unref (address);
448   g_free (socket_path);
449   return NULL;
450 }
451 #endif
452
453 /* ---------------------------------------------------------------------------------------------------- */
454
455 #if 0
456 static gboolean
457 check_connection (gpointer user_data)
458 {
459   PeerData *data = user_data;
460   guint n;
461
462   for (n = 0; n < data->current_connections->len; n++)
463     {
464       GDBusConnection *c;
465       GIOStream *stream;
466
467       c = G_DBUS_CONNECTION (data->current_connections->pdata[n]);
468       stream = g_dbus_connection_get_stream (c);
469
470       g_debug ("In check_connection for %d: connection %p, stream %p", n, c, stream);
471       g_debug ("closed = %d", g_io_stream_is_closed (stream));
472
473       GSocket *socket;
474       socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (stream));
475       g_debug ("socket_closed = %d", g_socket_is_closed (socket));
476       g_debug ("socket_condition_check = %d", g_socket_condition_check (socket, G_IO_IN|G_IO_OUT|G_IO_ERR|G_IO_HUP));
477
478       gchar buf[128];
479       GError *error;
480       gssize num_read;
481       error = NULL;
482       num_read = g_input_stream_read (g_io_stream_get_input_stream (stream),
483                                       buf,
484                                       128,
485                                       NULL,
486                                       &error);
487       if (num_read < 0)
488         {
489           g_debug ("error: %s", error->message);
490           g_error_free (error);
491         }
492       else
493         {
494           g_debug ("no error, read %d bytes", (gint) num_read);
495         }
496     }
497
498   return FALSE;
499 }
500
501 static gboolean
502 on_do_disconnect_in_idle (gpointer data)
503 {
504   GDBusConnection *c = G_DBUS_CONNECTION (data);
505   g_debug ("GDC %p has ref_count %d", c, G_OBJECT (c)->ref_count);
506   g_dbus_connection_disconnect (c);
507   g_object_unref (c);
508   return FALSE;
509 }
510 #endif
511
512 static void
513 test_peer (void)
514 {
515   GDBusConnection *c;
516   GDBusConnection *c2;
517   GDBusProxy *proxy;
518   GError *error;
519   PeerData data;
520   GVariant *value;
521   GVariant *result;
522   const gchar *s;
523   GThread *service_thread;
524   gulong signal_handler_id;
525
526   memset (&data, '\0', sizeof (PeerData));
527   data.current_connections = g_ptr_array_new_with_free_func (g_object_unref);
528
529   /* first try to connect when there is no server */
530   error = NULL;
531   c = g_dbus_connection_new_for_address_sync (is_unix ? "unix:path=/tmp/gdbus-test-does-not-exist-pid" :
532                                               /* NOTE: Even if something is listening on port 12345 the connection
533                                                * will fail because the nonce file doesn't exist */
534                                               "nonce-tcp:host=localhost,port=12345,noncefile=this-does-not-exist-gdbus",
535                                               G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
536                                               NULL, /* GDBusAuthObserver */
537                                               NULL, /* cancellable */
538                                               &error);
539   _g_assert_error_domain (error, G_IO_ERROR);
540   g_assert (!g_dbus_error_is_remote_error (error));
541   g_clear_error (&error);
542   g_assert (c == NULL);
543
544   /* bring up a server - we run the server in a different thread to avoid deadlocks */
545   error = NULL;
546   service_thread = g_thread_create (service_thread_func,
547                                     &data,
548                                     TRUE,
549                                     &error);
550   while (service_loop == NULL)
551     g_thread_yield ();
552   g_assert (server != NULL);
553
554   /* bring up a connection and accept it */
555   data.accept_connection = TRUE;
556   error = NULL;
557   c = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server),
558                                               G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
559                                               NULL, /* GDBusAuthObserver */
560                                               NULL, /* cancellable */
561                                               &error);
562   g_assert_no_error (error);
563   g_assert (c != NULL);
564   while (data.current_connections->len < 1)
565     g_main_loop_run (loop);
566   g_assert_cmpint (data.current_connections->len, ==, 1);
567   g_assert_cmpint (data.num_connection_attempts, ==, 1);
568   g_assert (g_dbus_connection_get_unique_name (c) == NULL);
569   g_assert_cmpstr (g_dbus_connection_get_guid (c), ==, test_guid);
570
571   /* check that we create a proxy, read properties, receive signals and invoke
572    * the HelloPeer() method. Since the server runs in another thread it's fine
573    * to use synchronous blocking API here.
574    */
575   error = NULL;
576   proxy = g_dbus_proxy_new_sync (c,
577                                  G_DBUS_PROXY_FLAGS_NONE,
578                                  NULL,
579                                  NULL, /* bus_name */
580                                  "/org/gtk/GDBus/PeerTestObject",
581                                  "org.gtk.GDBus.PeerTestInterface",
582                                  NULL, /* GCancellable */
583                                  &error);
584   g_assert_no_error (error);
585   g_assert (proxy != NULL);
586   error = NULL;
587   value = g_dbus_proxy_get_cached_property (proxy, "PeerProperty");
588   g_assert_cmpstr (g_variant_get_string (value, NULL), ==, "ThePropertyValue");
589
590   /* try invoking a method */
591   error = NULL;
592   result = g_dbus_proxy_call_sync (proxy,
593                                    "HelloPeer",
594                                    g_variant_new ("(s)", "Hey Peer!"),
595                                    G_DBUS_CALL_FLAGS_NONE,
596                                    -1,
597                                    NULL,  /* GCancellable */
598                                    &error);
599   g_assert_no_error (error);
600   g_variant_get (result, "(&s)", &s);
601   g_assert_cmpstr (s, ==, "You greeted me with 'Hey Peer!'.");
602   g_variant_unref (result);
603   g_assert_cmpint (data.num_method_calls, ==, 1);
604
605   /* make the other peer emit a signal - catch it */
606   signal_handler_id = g_signal_connect (proxy,
607                                         "g-signal",
608                                         G_CALLBACK (on_proxy_signal_received),
609                                         &data);
610   g_assert (!data.signal_received);
611   g_dbus_proxy_call (proxy,
612                      "EmitSignal",
613                      NULL,  /* no arguments */
614                      G_DBUS_CALL_FLAGS_NONE,
615                      -1,
616                      NULL,  /* GCancellable */
617                      NULL,  /* GAsyncReadyCallback - we don't care about the result */
618                      NULL); /* user_data */
619   g_main_loop_run (loop);
620   g_assert (data.signal_received);
621   g_assert_cmpint (data.num_method_calls, ==, 2);
622   g_signal_handler_disconnect (proxy, signal_handler_id);
623
624   /* Also ensure that messages with the sender header-field set gets
625    * delivered to the proxy - note that this doesn't really make sense
626    * e.g. names are meaning-less in a peer-to-peer case... but we
627    * support it because it makes sense in certain bridging
628    * applications - see e.g. #623815.
629    */
630   signal_handler_id = g_signal_connect (proxy,
631                                         "g-signal",
632                                         G_CALLBACK (on_proxy_signal_received_with_name_set),
633                                         &data);
634   data.signal_received = FALSE;
635   g_dbus_proxy_call (proxy,
636                      "EmitSignalWithNameSet",
637                      NULL,  /* no arguments */
638                      G_DBUS_CALL_FLAGS_NONE,
639                      -1,
640                      NULL,  /* GCancellable */
641                      NULL,  /* GAsyncReadyCallback - we don't care about the result */
642                      NULL); /* user_data */
643   g_main_loop_run (loop);
644   g_assert (data.signal_received);
645   g_assert_cmpint (data.num_method_calls, ==, 3);
646   g_signal_handler_disconnect (proxy, signal_handler_id);
647
648   /* check for UNIX fd passing */
649 #ifdef G_OS_UNIX
650   {
651     GDBusMessage *method_call_message;
652     GDBusMessage *method_reply_message;
653     GUnixFDList *fd_list;
654     gint fd;
655     gchar buf[1024];
656     gssize len;
657     gchar *buf2;
658     gsize len2;
659
660     method_call_message = g_dbus_message_new_method_call (NULL, /* name */
661                                                           "/org/gtk/GDBus/PeerTestObject",
662                                                           "org.gtk.GDBus.PeerTestInterface",
663                                                           "OpenFile");
664     g_dbus_message_set_body (method_call_message, g_variant_new ("(s)", "/etc/hosts"));
665     error = NULL;
666     method_reply_message = g_dbus_connection_send_message_with_reply_sync (c,
667                                                                            method_call_message,
668                                                                            G_DBUS_SEND_MESSAGE_FLAGS_NONE,
669                                                                            -1,
670                                                                            NULL, /* out_serial */
671                                                                            NULL, /* cancellable */
672                                                                            &error);
673     g_assert_no_error (error);
674     g_assert (g_dbus_message_get_message_type (method_reply_message) == G_DBUS_MESSAGE_TYPE_METHOD_RETURN);
675     fd_list = g_dbus_message_get_unix_fd_list (method_reply_message);
676     g_assert (fd_list != NULL);
677     g_assert_cmpint (g_unix_fd_list_get_length (fd_list), ==, 1);
678     error = NULL;
679     fd = g_unix_fd_list_get (fd_list, 0, &error);
680     g_assert_no_error (error);
681     g_object_unref (method_call_message);
682     g_object_unref (method_reply_message);
683
684     memset (buf, '\0', sizeof (buf));
685     len = read (fd, buf, sizeof (buf) - 1);
686     close (fd);
687
688     error = NULL;
689     g_file_get_contents ("/etc/hosts",
690                          &buf2,
691                          &len2,
692                          &error);
693     g_assert_no_error (error);
694     if (len2 > sizeof (buf))
695       buf2[sizeof (buf)] = '\0';
696     g_assert_cmpstr (buf, ==, buf2);
697     g_free (buf2);
698   }
699 #else
700   error = NULL;
701   result = g_dbus_proxy_call_sync (proxy,
702                                    "OpenFile",
703                                    g_variant_new ("(s)", "boo"),
704                                    G_DBUS_CALL_FLAGS_NONE,
705                                    -1,
706                                    NULL,  /* GCancellable */
707                                    &error);
708   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR);
709   g_assert (result == NULL);
710   g_error_free (error);
711 #endif /* G_OS_UNIX */
712
713   /* Check that g_socket_get_credentials() work - this really should
714    * be in a GSocket-specific test suite but no such test suite exists
715    * right now.
716    */
717   {
718     GSocket *socket;
719     GCredentials *credentials;
720     socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (g_dbus_connection_get_stream (c)));
721     g_assert (G_IS_SOCKET (socket));
722     error = NULL;
723     credentials = g_socket_get_credentials (socket, &error);
724 #ifdef __linux__
725     {
726       struct ucred *native_creds;
727       g_assert_no_error (error);
728       g_assert (G_IS_CREDENTIALS (credentials));
729       native_creds = g_credentials_get_native (credentials, G_CREDENTIALS_TYPE_LINUX_UCRED);
730       g_assert (native_creds != NULL);
731       g_assert (native_creds->uid == getuid ());
732       g_assert (native_creds->gid == getgid ());
733       g_assert (native_creds->pid == getpid ());
734     }
735     g_object_unref (credentials);
736 #else
737     g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
738     g_assert (credentials == NULL);
739 #endif
740   }
741
742
743   /* bring up a connection - don't accept it - this should fail
744    */
745   data.accept_connection = FALSE;
746   error = NULL;
747   c2 = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server),
748                                                G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
749                                                NULL, /* GDBusAuthObserver */
750                                                NULL, /* cancellable */
751                                                &error);
752   _g_assert_error_domain (error, G_IO_ERROR);
753   g_assert (c2 == NULL);
754
755 #if 0
756   /* TODO: THIS TEST DOESN'T WORK YET */
757
758   /* bring up a connection - accept it.. then disconnect from the client side - check
759    * that the server side gets the disconnect signal.
760    */
761   error = NULL;
762   data.accept_connection = TRUE;
763   c2 = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server),
764                                                G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
765                                                NULL, /* GDBusAuthObserver */
766                                                NULL, /* cancellable */
767                                                &error);
768   g_assert_no_error (error);
769   g_assert (c2 != NULL);
770   g_assert (!g_dbus_connection_get_is_disconnected (c2));
771   while (data.num_connection_attempts < 3)
772     g_main_loop_run (loop);
773   g_assert_cmpint (data.current_connections->len, ==, 2);
774   g_assert_cmpint (data.num_connection_attempts, ==, 3);
775   g_assert (!g_dbus_connection_get_is_disconnected (G_DBUS_CONNECTION (data.current_connections->pdata[1])));
776   g_idle_add (on_do_disconnect_in_idle, c2);
777   g_debug ("==================================================");
778   g_debug ("==================================================");
779   g_debug ("==================================================");
780   g_debug ("waiting for disconnect on connection %p, stream %p",
781            data.current_connections->pdata[1],
782            g_dbus_connection_get_stream (data.current_connections->pdata[1]));
783
784   g_timeout_add (2000, check_connection, &data);
785   //_g_assert_signal_received (G_DBUS_CONNECTION (data.current_connections->pdata[1]), "closed");
786   g_main_loop_run (loop);
787   g_assert (g_dbus_connection_get_is_disconnected (G_DBUS_CONNECTION (data.current_connections->pdata[1])));
788   g_ptr_array_set_size (data.current_connections, 1); /* remove disconnected connection object */
789 #endif
790
791   /* unref the server and stop listening for new connections
792    *
793    * This won't bring down the established connections - check that c is still connected
794    * by invoking a method
795    */
796   //g_socket_service_stop (service);
797   //g_object_unref (service);
798   g_dbus_server_stop (server);
799   g_object_unref (server);
800   server = NULL;
801
802   error = NULL;
803   result = g_dbus_proxy_call_sync (proxy,
804                                    "HelloPeer",
805                                    g_variant_new ("(s)", "Hey Again Peer!"),
806                                    G_DBUS_CALL_FLAGS_NONE,
807                                    -1,
808                                    NULL,  /* GCancellable */
809                                    &error);
810   g_assert_no_error (error);
811   g_variant_get (result, "(&s)", &s);
812   g_assert_cmpstr (s, ==, "You greeted me with 'Hey Again Peer!'.");
813   g_variant_unref (result);
814   g_assert_cmpint (data.num_method_calls, ==, 5);
815
816 #if 0
817   /* TODO: THIS TEST DOESN'T WORK YET */
818
819   /* now disconnect from the server side - check that the client side gets the signal */
820   g_assert_cmpint (data.current_connections->len, ==, 1);
821   g_assert (G_DBUS_CONNECTION (data.current_connections->pdata[0]) != c);
822   g_dbus_connection_disconnect (G_DBUS_CONNECTION (data.current_connections->pdata[0]));
823   if (!g_dbus_connection_get_is_disconnected (c))
824     _g_assert_signal_received (c, "closed");
825   g_assert (g_dbus_connection_get_is_disconnected (c));
826 #endif
827
828   g_object_unref (c);
829   g_ptr_array_unref (data.current_connections);
830   g_object_unref (proxy);
831
832   g_main_loop_quit (service_loop);
833   g_thread_join (service_thread);
834 }
835
836 /* ---------------------------------------------------------------------------------------------------- */
837
838 typedef struct
839 {
840   GDBusServer *server;
841   GMainContext *context;
842   GMainLoop *loop;
843
844   GList *connections;
845 } DmpData;
846
847 static void
848 dmp_data_free (DmpData *data)
849 {
850   g_main_loop_unref (data->loop);
851   g_main_context_unref (data->context);
852   g_object_unref (data->server);
853   g_list_foreach (data->connections, (GFunc) g_object_unref, NULL);
854   g_list_free (data->connections);
855   g_free (data);
856 }
857
858 static void
859 dmp_on_method_call (GDBusConnection       *connection,
860                     const gchar           *sender,
861                     const gchar           *object_path,
862                     const gchar           *interface_name,
863                     const gchar           *method_name,
864                     GVariant              *parameters,
865                     GDBusMethodInvocation *invocation,
866                     gpointer               user_data)
867 {
868   //DmpData *data = user_data;
869   gint32 first;
870   gint32 second;
871   g_variant_get (parameters,
872                  "(ii)",
873                  &first,
874                  &second);
875   g_dbus_method_invocation_return_value (invocation,
876                                          g_variant_new ("(i)", first + second));
877 }
878
879 static const GDBusInterfaceVTable dmp_interface_vtable =
880 {
881   dmp_on_method_call,
882   NULL,  /* get_property */
883   NULL   /* set_property */
884 };
885
886
887 /* Runs in thread we created GDBusServer in (since we didn't pass G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) */
888 static void
889 dmp_on_new_connection (GDBusServer     *server,
890                        GDBusConnection *connection,
891                        gpointer         user_data)
892 {
893   DmpData *data = user_data;
894   GDBusNodeInfo *node;
895   GError *error;
896
897   /* accept the connection */
898   data->connections = g_list_prepend (data->connections, g_object_ref (connection));
899
900   error = NULL;
901   node = g_dbus_node_info_new_for_xml ("<node>"
902                                        "  <interface name='org.gtk.GDBus.DmpInterface'>"
903                                        "    <method name='AddPair'>"
904                                        "      <arg type='i' name='first' direction='in'/>"
905                                        "      <arg type='i' name='second' direction='in'/>"
906                                        "      <arg type='i' name='sum' direction='out'/>"
907                                        "    </method>"
908                                        "  </interface>"
909                                        "</node>",
910                                        &error);
911   g_assert_no_error (error);
912
913   /* sleep 100ms before exporting an object - this is to test that
914    * G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING really works
915    * (GDBusServer uses this feature).
916    */
917   usleep (100 * 1000);
918
919   /* export an object */
920   error = NULL;
921   g_dbus_connection_register_object (connection,
922                                      "/dmp/test",
923                                      node->interfaces[0],
924                                      &dmp_interface_vtable,
925                                      data,
926                                      NULL,
927                                      &error);
928   g_dbus_node_info_unref (node);
929 }
930
931 static gpointer
932 dmp_thread_func (gpointer user_data)
933 {
934   DmpData *data = user_data;
935   GError *error;
936   gchar *guid;
937
938   data->context = g_main_context_new ();
939   g_main_context_push_thread_default (data->context);
940
941   error = NULL;
942   guid = g_dbus_generate_guid ();
943   data->server = g_dbus_server_new_sync ("unix:tmpdir=/tmp/gdbus-test-",
944                                          G_DBUS_SERVER_FLAGS_NONE,
945                                          guid,
946                                          NULL, /* GDBusAuthObserver */
947                                          NULL, /* GCancellable */
948                                          &error);
949   g_assert_no_error (error);
950   g_signal_connect (data->server,
951                     "new-connection",
952                     G_CALLBACK (dmp_on_new_connection),
953                     data);
954
955   g_dbus_server_start (data->server);
956
957   data->loop = g_main_loop_new (data->context, FALSE);
958   g_main_loop_run (data->loop);
959
960   g_main_context_pop_thread_default (data->context);
961
962   g_free (guid);
963   return NULL;
964 }
965
966 static void
967 delayed_message_processing (void)
968 {
969   GError *error;
970   DmpData *data;
971   GThread *service_thread;
972   guint n;
973
974   data = g_new0 (DmpData, 1);
975
976   error = NULL;
977   service_thread = g_thread_create (dmp_thread_func,
978                                     data,
979                                     TRUE,
980                                     &error);
981   while (data->server == NULL || !g_dbus_server_is_active (data->server))
982     g_thread_yield ();
983
984   for (n = 0; n < 5; n++)
985     {
986       GDBusConnection *c;
987       GVariant *res;
988       gint32 val;
989
990       error = NULL;
991       c = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (data->server),
992                                                   G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
993                                                   NULL, /* GDBusAuthObserver */
994                                                   NULL, /* GCancellable */
995                                                   &error);
996       g_assert_no_error (error);
997
998       error = NULL;
999       res = g_dbus_connection_call_sync (c,
1000                                          NULL,    /* bus name */
1001                                          "/dmp/test",
1002                                          "org.gtk.GDBus.DmpInterface",
1003                                          "AddPair",
1004                                          g_variant_new ("(ii)", 2, n),
1005                                          G_VARIANT_TYPE ("(i)"),
1006                                          G_DBUS_CALL_FLAGS_NONE,
1007                                          -1, /* timeout_msec */
1008                                          NULL, /* GCancellable */
1009                                          &error);
1010       g_assert_no_error (error);
1011       g_variant_get (res, "(i)", &val);
1012       g_assert_cmpint (val, ==, 2 + n);
1013       g_variant_unref (res);
1014       g_object_unref (c);
1015   }
1016
1017   g_main_loop_quit (data->loop);
1018   g_thread_join (service_thread);
1019   dmp_data_free (data);
1020 }
1021
1022 /* ---------------------------------------------------------------------------------------------------- */
1023
1024 static gboolean
1025 nonce_tcp_on_authorize_authenticated_peer (GDBusAuthObserver *observer,
1026                                            GIOStream         *stream,
1027                                            GCredentials      *credentials,
1028                                            gpointer           user_data)
1029 {
1030   PeerData *data = user_data;
1031   gboolean authorized;
1032
1033   data->num_connection_attempts++;
1034
1035   authorized = TRUE;
1036   if (!data->accept_connection)
1037     {
1038       authorized = FALSE;
1039       g_main_loop_quit (loop);
1040     }
1041
1042   return authorized;
1043 }
1044
1045 /* Runs in thread we created GDBusServer in (since we didn't pass G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) */
1046 static void
1047 nonce_tcp_on_new_connection (GDBusServer *server,
1048                              GDBusConnection *connection,
1049                              gpointer user_data)
1050 {
1051   PeerData *data = user_data;
1052
1053   g_ptr_array_add (data->current_connections, g_object_ref (connection));
1054
1055   g_main_loop_quit (loop);
1056 }
1057
1058 static gpointer
1059 nonce_tcp_service_thread_func (gpointer user_data)
1060 {
1061   PeerData *data = user_data;
1062   GMainContext *service_context;
1063   GDBusAuthObserver *observer;
1064   GError *error;
1065
1066   service_context = g_main_context_new ();
1067   g_main_context_push_thread_default (service_context);
1068
1069   error = NULL;
1070   observer = g_dbus_auth_observer_new ();
1071   server = g_dbus_server_new_sync ("nonce-tcp:",
1072                                    G_DBUS_SERVER_FLAGS_NONE,
1073                                    test_guid,
1074                                    observer,
1075                                    NULL, /* cancellable */
1076                                    &error);
1077   g_assert_no_error (error);
1078
1079   g_signal_connect (server,
1080                     "new-connection",
1081                     G_CALLBACK (nonce_tcp_on_new_connection),
1082                     data);
1083   g_signal_connect (observer,
1084                     "authorize-authenticated-peer",
1085                     G_CALLBACK (nonce_tcp_on_authorize_authenticated_peer),
1086                     data);
1087   g_object_unref (observer);
1088
1089   g_dbus_server_start (server);
1090
1091   service_loop = g_main_loop_new (service_context, FALSE);
1092   g_main_loop_run (service_loop);
1093
1094   g_main_context_pop_thread_default (service_context);
1095
1096   g_main_loop_unref (service_loop);
1097   g_main_context_unref (service_context);
1098
1099   /* test code specifically unrefs the server - see below */
1100   g_assert (server == NULL);
1101
1102   return NULL;
1103 }
1104
1105 static void
1106 test_nonce_tcp (void)
1107 {
1108   PeerData data;
1109   GError *error;
1110   GThread *service_thread;
1111   GDBusConnection *c;
1112   gchar *s;
1113   gchar *nonce_file;
1114   gboolean res;
1115   const gchar *address;
1116
1117   memset (&data, '\0', sizeof (PeerData));
1118   data.current_connections = g_ptr_array_new_with_free_func (g_object_unref);
1119
1120   error = NULL;
1121   server = NULL;
1122   service_loop = NULL;
1123   service_thread = g_thread_create (nonce_tcp_service_thread_func,
1124                                     &data,
1125                                     TRUE,
1126                                     &error);
1127   while (service_loop == NULL)
1128     g_thread_yield ();
1129   g_assert (server != NULL);
1130
1131
1132   /* bring up a connection and accept it */
1133   data.accept_connection = TRUE;
1134   error = NULL;
1135   c = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server),
1136                                               G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
1137                                               NULL, /* GDBusAuthObserver */
1138                                               NULL, /* cancellable */
1139                                               &error);
1140   g_assert_no_error (error);
1141   g_assert (c != NULL);
1142   while (data.current_connections->len < 1)
1143     g_main_loop_run (loop);
1144   g_assert_cmpint (data.current_connections->len, ==, 1);
1145   g_assert_cmpint (data.num_connection_attempts, ==, 1);
1146   g_assert (g_dbus_connection_get_unique_name (c) == NULL);
1147   g_assert_cmpstr (g_dbus_connection_get_guid (c), ==, test_guid);
1148   g_object_unref (c);
1149
1150   /* now, try to subvert the nonce file (this assumes noncefile is the last key/value pair)
1151    */
1152
1153   address = g_dbus_server_get_client_address (server);
1154
1155   s = strstr (address, "noncefile=");
1156   g_assert (s != NULL);
1157   s += sizeof "noncefile=" - 1;
1158   nonce_file = g_strdup (s);
1159
1160   /* First try invalid data in the nonce file - this will actually
1161    * make the client send this and the server will reject it. The way
1162    * it works is that if the nonce doesn't match, the server will
1163    * simply close the connection. So, from the client point of view,
1164    * we can see a variety of errors.
1165    */
1166   error = NULL;
1167   res = g_file_set_contents (nonce_file,
1168                              "0123456789012345",
1169                              -1,
1170                              &error);
1171   g_assert_no_error (error);
1172   g_assert (res);
1173   c = g_dbus_connection_new_for_address_sync (address,
1174                                               G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
1175                                               NULL, /* GDBusAuthObserver */
1176                                               NULL, /* cancellable */
1177                                               &error);
1178   _g_assert_error_domain (error, G_IO_ERROR);
1179   g_assert (c == NULL);
1180
1181   /* Then try with a nonce-file of incorrect length - this will make
1182    * the client complain - we won't even try connecting to the server
1183    * for this
1184    */
1185   error = NULL;
1186   res = g_file_set_contents (nonce_file,
1187                              "0123456789012345_",
1188                              -1,
1189                              &error);
1190   g_assert_no_error (error);
1191   g_assert (res);
1192   c = g_dbus_connection_new_for_address_sync (address,
1193                                               G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
1194                                               NULL, /* GDBusAuthObserver */
1195                                               NULL, /* cancellable */
1196                                               &error);
1197   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
1198   g_assert (c == NULL);
1199
1200   /* Finally try with no nonce-file at all */
1201   g_assert_cmpint (g_unlink (nonce_file), ==, 0);
1202   error = NULL;
1203   c = g_dbus_connection_new_for_address_sync (address,
1204                                               G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
1205                                               NULL, /* GDBusAuthObserver */
1206                                               NULL, /* cancellable */
1207                                               &error);
1208   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
1209   g_assert (c == NULL);
1210
1211   g_free (nonce_file);
1212
1213   g_dbus_server_stop (server);
1214   g_object_unref (server);
1215   server = NULL;
1216
1217   g_main_loop_quit (service_loop);
1218   g_thread_join (service_thread);
1219 }
1220
1221 static void
1222 test_credentials (void)
1223 {
1224   GCredentials *c1, *c2;
1225   GError *error;
1226   gchar *desc;
1227
1228   c1 = g_credentials_new ();
1229   c2 = g_credentials_new ();
1230
1231   error = NULL;
1232   if (g_credentials_set_unix_user (c2, getuid (), &error))
1233     g_assert_no_error (error);
1234
1235   g_clear_error (&error);
1236   g_assert (g_credentials_is_same_user (c1, c2, &error));
1237   g_assert_no_error (error);
1238
1239   desc = g_credentials_to_string (c1);
1240   g_assert (desc != NULL);
1241   g_free (desc);
1242
1243   g_object_unref (c1);
1244   g_object_unref (c2);
1245 }
1246
1247 /* ---------------------------------------------------------------------------------------------------- */
1248
1249 int
1250 main (int   argc,
1251       char *argv[])
1252 {
1253   gint ret;
1254   GDBusNodeInfo *introspection_data = NULL;
1255
1256   g_type_init ();
1257   g_thread_init (NULL);
1258   g_test_init (&argc, &argv, NULL);
1259
1260   introspection_data = g_dbus_node_info_new_for_xml (test_interface_introspection_xml, NULL);
1261   g_assert (introspection_data != NULL);
1262   test_interface_introspection_data = introspection_data->interfaces[0];
1263
1264   test_guid = g_dbus_generate_guid ();
1265
1266   /* all the tests rely on a shared main loop */
1267   loop = g_main_loop_new (NULL, FALSE);
1268
1269   g_test_add_func ("/gdbus/peer-to-peer", test_peer);
1270   g_test_add_func ("/gdbus/delayed-message-processing", delayed_message_processing);
1271   g_test_add_func ("/gdbus/nonce-tcp", test_nonce_tcp);
1272   g_test_add_func ("/gdbus/credentials", test_credentials);
1273
1274   ret = g_test_run();
1275
1276   g_main_loop_unref (loop);
1277   g_free (test_guid);
1278   g_dbus_node_info_unref (introspection_data);
1279
1280   return ret;
1281 }