Bug 621945 – Filter outgoing messages in GDBusConnection
[platform/upstream/glib.git] / gio / gdbusprivate.c
index 64ea7d9..3a48d1d 100644 (file)
@@ -1,6 +1,6 @@
 /* GDBus - GLib D-Bus Library
  *
- * Copyright (C) 2008-2009 Red Hat, Inc.
+ * Copyright (C) 2008-2010 Red Hat, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
 
 #include <stdlib.h>
 #include <string.h>
-
-#ifdef G_OS_UNIX
-#include <gio/gunixconnection.h>
-#include <gio/gunixfdmessage.h>
-#include "gunixcredentialsmessage.h"
+#ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
 
 #include "ginputstream.h"
 #include "giostream.h"
 #include "gsocketcontrolmessage.h"
+#include "gsocketconnection.h"
 
 #ifdef G_OS_UNIX
-#include <unistd.h>
 #include "gunixfdmessage.h"
 #include "gunixconnection.h"
+#include "gunixcredentialsmessage.h"
+#endif
+
+#ifdef G_OS_WIN32
+#include <windows.h>
 #endif
 
 #include "glibintl.h"
+#include "gioalias.h"
 
 /* ---------------------------------------------------------------------------------------------------- */
 
-static gchar *
-hexdump (const gchar *data, gsize len, guint indent)
+gchar *
+_g_dbus_hexdump (const gchar *data, gsize len, guint indent)
 {
  guint n, m;
  GString *ret;
@@ -353,6 +355,7 @@ struct GDBusWorker
   GDBusCapabilityFlags                capabilities;
   GCancellable                       *cancellable;
   GDBusWorkerMessageReceivedCallback  message_received_callback;
+  GDBusWorkerMessageAboutToBeSentCallback message_about_to_be_sent_callback;
   GDBusWorkerDisconnectedCallback     disconnected_callback;
   gpointer                            user_data;
 
@@ -422,13 +425,24 @@ _g_dbus_worker_emit_disconnected (GDBusWorker  *worker,
 }
 
 static void
-_g_dbus_worker_emit_message (GDBusWorker  *worker,
-                             GDBusMessage *message)
+_g_dbus_worker_emit_message_received (GDBusWorker  *worker,
+                                      GDBusMessage *message)
 {
   if (!worker->stopped)
     worker->message_received_callback (worker, message, worker->user_data);
 }
 
+static gboolean
+_g_dbus_worker_emit_message_about_to_be_sent (GDBusWorker  *worker,
+                                              GDBusMessage *message)
+{
+  gboolean ret;
+  ret = FALSE;
+  if (!worker->stopped)
+    ret = worker->message_about_to_be_sent_callback (worker, message, worker->user_data);
+  return ret;
+}
+
 static void _g_dbus_worker_do_read_unlocked (GDBusWorker *worker);
 
 /* called in private thread shared by all GDBusConnection instances (without read-lock held) */
@@ -582,19 +596,32 @@ _g_dbus_worker_do_read_cb (GInputStream  *input_stream,
 
           message = g_dbus_message_new_from_blob ((guchar *) worker->read_buffer,
                                                   worker->read_buffer_cur_size,
+                                                  worker->capabilities,
                                                   &error);
           if (message == NULL)
             {
+              gchar *s;
+              s = _g_dbus_hexdump (worker->read_buffer, worker->read_buffer_cur_size, 2);
+              g_warning ("Error decoding D-Bus message of %" G_GSIZE_FORMAT " bytes\n"
+                         "The error is: %s\n"
+                         "The payload is as follows:\n"
+                         "%s\n",
+                         worker->read_buffer_cur_size,
+                         error->message,
+                         s);
+              g_free (s);
               _g_dbus_worker_emit_disconnected (worker, FALSE, error);
               g_error_free (error);
               goto out;
             }
 
+#ifdef G_OS_UNIX
           if (worker->read_fd_list != NULL)
             {
               g_dbus_message_set_unix_fd_list (message, worker->read_fd_list);
               worker->read_fd_list = NULL;
             }
+#endif
 
           if (G_UNLIKELY (_g_dbus_debug_message ()))
             {
@@ -606,13 +633,13 @@ _g_dbus_worker_do_read_cb (GInputStream  *input_stream,
               s = g_dbus_message_print (message, 2);
               g_print ("%s", s);
               g_free (s);
-              s = hexdump (worker->read_buffer, worker->read_buffer_cur_size, 2);
+              s = _g_dbus_hexdump (worker->read_buffer, worker->read_buffer_cur_size, 2);
               g_print ("%s\n", s);
               g_free (s);
             }
 
           /* yay, got a message, go deliver it */
-          _g_dbus_worker_emit_message (worker, message);
+          _g_dbus_worker_emit_message_received (worker, message);
           g_object_unref (message);
 
           /* start reading another message! */
@@ -705,7 +732,7 @@ message_to_write_data_free (MessageToWriteData *data)
 
 /* ---------------------------------------------------------------------------------------------------- */
 
-/* called in private thread shared by all GDBusConnection instances (with write-lock held) */
+/* called in private thread shared by all GDBusConnection instances (without write-lock held) */
 static gboolean
 write_message (GDBusWorker         *worker,
                MessageToWriteData  *data,
@@ -815,7 +842,7 @@ write_message (GDBusWorker         *worker,
       s = g_dbus_message_print (data->message, 2);
       g_print ("%s", s);
       g_free (s);
-      s = hexdump (data->blob, data->blob_size, 2);
+      s = _g_dbus_hexdump (data->blob, data->blob_size, 2);
       g_print ("%s\n", s);
       g_free (s);
     }
@@ -833,29 +860,39 @@ write_message_in_idle_cb (gpointer user_data)
   GDBusWorker *worker = user_data;
   gboolean more_writes_are_pending;
   MessageToWriteData *data;
+  gboolean message_was_dropped;
   GError *error;
 
   g_mutex_lock (worker->write_lock);
-
   data = g_queue_pop_head (worker->write_queue);
   g_assert (data != NULL);
+  more_writes_are_pending = (g_queue_get_length (worker->write_queue) > 0);
+  worker->write_is_pending = more_writes_are_pending;
+  g_mutex_unlock (worker->write_lock);
 
-  error = NULL;
-  if (!write_message (worker,
-                      data,
-                      &error))
+  /* Note that write_lock is only used for protecting the @write_queue
+   * and @write_is_pending fields of the GDBusWorker struct ... which we
+   * need to modify from arbitrary threads in _g_dbus_worker_send_message().
+   *
+   * Therefore, it's fine to drop it here when calling back into user
+   * code and then writing the message out onto the GIOStream since this
+   * function only runs on the worker thread.
+   */
+  message_was_dropped = _g_dbus_worker_emit_message_about_to_be_sent (worker, data->message);
+  if (G_LIKELY (!message_was_dropped))
     {
-      /* TODO: handle */
-      _g_dbus_worker_emit_disconnected (worker, TRUE, error);
-      g_error_free (error);
+      error = NULL;
+      if (!write_message (worker,
+                          data,
+                          &error))
+        {
+          /* TODO: handle */
+          _g_dbus_worker_emit_disconnected (worker, TRUE, error);
+          g_error_free (error);
+        }
     }
   message_to_write_data_free (data);
 
-  more_writes_are_pending = (g_queue_get_length (worker->write_queue) > 0);
-
-  worker->write_is_pending = more_writes_are_pending;
-  g_mutex_unlock (worker->write_lock);
-
   return more_writes_are_pending;
 }
 
@@ -913,16 +950,18 @@ _g_dbus_worker_thread_begin_func (gpointer user_data)
 }
 
 GDBusWorker *
-_g_dbus_worker_new (GIOStream                          *stream,
-                    GDBusCapabilityFlags                capabilities,
-                    GDBusWorkerMessageReceivedCallback  message_received_callback,
-                    GDBusWorkerDisconnectedCallback     disconnected_callback,
-                    gpointer                            user_data)
+_g_dbus_worker_new (GIOStream                              *stream,
+                    GDBusCapabilityFlags                    capabilities,
+                    GDBusWorkerMessageReceivedCallback      message_received_callback,
+                    GDBusWorkerMessageAboutToBeSentCallback message_about_to_be_sent_callback,
+                    GDBusWorkerDisconnectedCallback         disconnected_callback,
+                    gpointer                                user_data)
 {
   GDBusWorker *worker;
 
   g_return_val_if_fail (G_IS_IO_STREAM (stream), NULL);
   g_return_val_if_fail (message_received_callback != NULL, NULL);
+  g_return_val_if_fail (message_about_to_be_sent_callback != NULL, NULL);
   g_return_val_if_fail (disconnected_callback != NULL, NULL);
 
   worker = g_new0 (GDBusWorker, 1);
@@ -930,6 +969,7 @@ _g_dbus_worker_new (GIOStream                          *stream,
 
   worker->read_lock = g_mutex_new ();
   worker->message_received_callback = message_received_callback;
+  worker->message_about_to_be_sent_callback = message_about_to_be_sent_callback;
   worker->disconnected_callback = disconnected_callback;
   worker->user_data = user_data;
   worker->stream = g_object_ref (stream);
@@ -1030,23 +1070,99 @@ _g_dbus_initialize (void)
 
 /* ---------------------------------------------------------------------------------------------------- */
 
-gchar *
-_g_dbus_compute_complete_signature (GDBusArgInfo **args,
-                                    gboolean       include_parentheses)
+GVariantType *
+_g_dbus_compute_complete_signature (GDBusArgInfo **args)
 {
-  GString *s;
+  const GVariantType *arg_types[256];
   guint n;
 
-  if (include_parentheses)
-    s = g_string_new ("(");
-  else
-    s = g_string_new ("");
-  if (args != NULL)
+  if (args)
     for (n = 0; args[n] != NULL; n++)
-      g_string_append (s, args[n]->signature);
+      {
+        /* DBus places a hard limit of 255 on signature length.
+         * therefore number of args must be less than 256.
+         */
+        g_assert (n < 256);
+
+        arg_types[n] = G_VARIANT_TYPE (args[n]->signature);
+
+        if G_UNLIKELY (arg_types[n] == NULL)
+          return NULL;
+      }
+  else
+    n = 0;
+
+  return g_variant_type_new_tuple (arg_types, n);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+#ifdef G_OS_WIN32
+
+extern BOOL WINAPI ConvertSidToStringSidA (PSID Sid, LPSTR *StringSid);
+
+gchar *
+_g_dbus_win32_get_user_sid (void)
+{
+  HANDLE h;
+  TOKEN_USER *user;
+  DWORD token_information_len;
+  PSID psid;
+  gchar *sid;
+  gchar *ret;
+
+  ret = NULL;
+  user = NULL;
+  h = INVALID_HANDLE_VALUE;
+
+  if (!OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY, &h))
+    {
+      g_warning ("OpenProcessToken failed with error code %d", (gint) GetLastError ());
+      goto out;
+    }
+
+  /* Get length of buffer */
+  token_information_len = 0;
+  if (!GetTokenInformation (h, TokenUser, NULL, 0, &token_information_len))
+    {
+      if (GetLastError () != ERROR_INSUFFICIENT_BUFFER)
+        {
+          g_warning ("GetTokenInformation() failed with error code %d", (gint) GetLastError ());
+          goto out;
+        }
+    }
+  user = g_malloc (token_information_len);
+  if (!GetTokenInformation (h, TokenUser, user, token_information_len, &token_information_len))
+    {
+      g_warning ("GetTokenInformation() failed with error code %d", (gint) GetLastError ());
+      goto out;
+    }
 
-  if (include_parentheses)
-    g_string_append_c (s, ')');
+  psid = user->User.Sid;
+  if (!IsValidSid (psid))
+    {
+      g_warning ("Invalid SID");
+      goto out;
+    }
 
-  return g_string_free (s, FALSE);
+  if (!ConvertSidToStringSidA (psid, &sid))
+    {
+      g_warning ("Invalid SID");
+      goto out;
+    }
+
+  ret = g_strdup (sid);
+  LocalFree (sid);
+
+out:
+  g_free (user);
+  if (h != INVALID_HANDLE_VALUE)
+    CloseHandle (h);
+  return ret;
 }
+#endif
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+#define __G_DBUS_PRIVATE_C__
+#include "gioaliasdef.c"