Back to index

bamf  0.2.120
bamf-indicator-source.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2010-2011 Canonical Ltd
00003  *
00004  * This program is free software: you can redistribute it and/or modify
00005  * it under the terms of the GNU General Public License version 3 as
00006  * published by the Free Software Foundation.
00007  *
00008  * This program is distributed in the hope that it will be useful,
00009  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00010  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011  * GNU General Public License for more details.
00012  *
00013  * You should have received a copy of the GNU General Public License
00014  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00015  *
00016  * Authored by: Jason Smith <jason.smith@canonical.com>
00017  *              Marco Trevisan (TreviƱo) <3v1n0@ubuntu.com>
00018  *
00019  */
00020 
00021 #include "bamf-indicator-source.h"
00022 
00023 #define BAMF_INDICATOR_SOURCE_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), \
00024                         BAMF_TYPE_INDICATOR_SOURCE, BamfIndicatorSourcePrivate))
00025 
00026 enum
00027 {
00028   INDICATOR_OPENED,
00029   INDICATOR_CLOSED,
00030 
00031   LAST_SIGNAL,
00032 };
00033 
00034 static guint indicator_source_signals[LAST_SIGNAL] = { 0 };
00035 
00036 struct _BamfIndicatorSourcePrivate
00037 {
00038   GList *indicators;
00039   GDBusProxy *proxy;
00040   int behavior;
00041 };
00042 
00043 G_DEFINE_TYPE (BamfIndicatorSource, bamf_indicator_source, STATUS_NOTIFIER_APPROVER_TYPE__SKELETON)
00044 
00045 GList *
00046 bamf_indicator_source_get_indicators (BamfIndicatorSource *self)
00047 {
00048   g_return_val_if_fail (BAMF_IS_INDICATOR_SOURCE (self), NULL);
00049 
00050   return self->priv->indicators;
00051 }
00052 
00053 static void
00054 on_indicator_closed (BamfView *view, BamfIndicatorSource *self)
00055 {
00056   BamfIndicator *indicator;
00057 
00058   g_return_if_fail (BAMF_IS_INDICATOR (view));
00059   g_return_if_fail (BAMF_IS_INDICATOR_SOURCE (self));
00060 
00061   indicator = BAMF_INDICATOR (view);
00062 
00063   self->priv->indicators = g_list_remove (self->priv->indicators, indicator);
00064   g_signal_emit (self, indicator_source_signals[INDICATOR_CLOSED], 0, indicator);
00065 
00066   g_object_unref (G_OBJECT (indicator));
00067 }
00068 
00069 void
00070 rejudge_approval (BamfIndicator *indicator, BamfIndicatorSource *self)
00071 {
00072   g_return_if_fail (BAMF_IS_INDICATOR (indicator));
00073   g_return_if_fail (BAMF_IS_INDICATOR_SOURCE (self));
00074 
00075   const char *address = bamf_indicator_get_address (indicator);
00076   const char *path = bamf_indicator_get_path (indicator);
00077   GList *parents = bamf_view_get_parents (BAMF_VIEW (indicator));
00078   gboolean approve = TRUE;
00079 
00080   switch (self->priv->behavior)
00081     {
00082       case BAMF_INDICATOR_SOURCE_APPROVE_NONE:
00083         approve = FALSE;
00084         break;
00085       case BAMF_INDICATOR_SOURCE_APPROVE_MATCHED:
00086         approve = (parents == NULL);
00087         break;
00088       case BAMF_INDICATOR_SOURCE_APPROVE_ALL:
00089         approve = TRUE;
00090         break;
00091     }
00092 
00093   g_signal_emit_by_name (self, "revise-judgement", approve, address, path);
00094 }
00095 
00096 void 
00097 bamf_indicator_source_set_behavior (BamfIndicatorSource *self,
00098                                     gint32 behavior)
00099 {
00100   BamfIndicatorSourcePrivate *priv;
00101 
00102   g_return_if_fail (BAMF_IS_INDICATOR_SOURCE (self));
00103   
00104   priv = self->priv;
00105   
00106   if (priv->behavior == behavior)
00107     return;
00108 
00109   priv->behavior = behavior;
00110 
00111   g_list_foreach (priv->indicators, (GFunc) rejudge_approval, self);
00112 }
00113 
00114 gboolean 
00115 bamf_indicator_source_approve_item (BamfIndicatorSource *self,
00116                                     const char *id,
00117                                     const char *category,
00118                                     guint32 pid,
00119                                     const char *address,
00120                                     const char *path)
00121 {
00122   GList *l;
00123   GError *error = NULL;
00124   BamfIndicator *indicator = NULL;
00125 
00126   g_return_val_if_fail (category, TRUE);
00127   g_return_val_if_fail (path, TRUE);
00128   g_return_val_if_fail (address, TRUE);
00129   g_return_val_if_fail (BAMF_IS_INDICATOR_SOURCE (self), TRUE);
00130 
00131   if (g_strcmp0 (category, "ApplicationStatus") != 0 || 
00132       g_strcmp0 (path, "/com/canonical/NotificationItem/dropbox_client") == 0)
00133     return TRUE;
00134 
00135   if (pid == 0)
00136     {
00137       GDBusProxy *gproxy;
00138       GVariant *remote_pid;
00139 
00140       g_warning ("Remote service passed indicator with pid of 0, manually fetching pid");
00141 
00142       gproxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
00143                                               G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES|
00144                                               G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
00145                                               NULL,
00146                                               "org.freedesktop.DBus",
00147                                               "/org/freedesktop/DBus",
00148                                               "org.freedesktop.DBus",
00149                                               NULL,
00150                                               &error);
00151 
00152       if (error)
00153         {
00154           g_warning ("Could not get indicator process id: %s", error->message);
00155           g_clear_error (&error);
00156         }
00157 
00158       g_return_val_if_fail (G_IS_DBUS_PROXY (gproxy), TRUE);
00159 
00160       remote_pid = g_dbus_proxy_call_sync (gproxy, "GetConnectionUnixProcessID",
00161                                            g_variant_new ("(s)", address),
00162                                            G_DBUS_CALL_FLAGS_NONE,
00163                                            -1,
00164                                            NULL,
00165                                            &error);
00166       if (error)
00167         {
00168           g_warning ("Could not get indicator process id: %s", error->message);
00169           g_clear_error (&error);
00170           return TRUE;
00171         }
00172 
00173       g_return_val_if_fail (remote_pid, TRUE);
00174 
00175       pid = g_variant_get_uint32 (g_variant_get_child_value (remote_pid, 0));
00176 
00177       g_object_unref (gproxy);
00178       g_variant_unref (remote_pid);
00179 
00180       if (pid == 0)
00181         return TRUE;
00182     }
00183 
00184   for (l = self->priv->indicators; l; l = l->next)
00185     {
00186       if (bamf_indicator_matches_signature (BAMF_INDICATOR (l->data), pid, address, path))
00187         {
00188           g_debug ("NotifierWatcher service passed already known indicator, ignoring");
00189           indicator = BAMF_INDICATOR (l->data);
00190           break;
00191         }
00192     }
00193 
00194   if (!indicator)
00195     {
00196       indicator = bamf_indicator_new (id, path, address,  pid);
00197       self->priv->indicators = g_list_prepend (self->priv->indicators, indicator);
00198   
00199       g_signal_connect (G_OBJECT (indicator), "closed", (GCallback) on_indicator_closed, self);
00200       g_signal_emit (self, indicator_source_signals[INDICATOR_OPENED], 0, indicator);
00201     }
00202 
00203   GList *parents = bamf_view_get_parents (BAMF_VIEW (indicator));
00204 
00205   switch (self->priv->behavior)
00206     {
00207       case BAMF_INDICATOR_SOURCE_APPROVE_NONE:
00208         return FALSE;
00209 
00210       case BAMF_INDICATOR_SOURCE_APPROVE_MATCHED:
00211         return !(indicator && parents);
00212 
00213       case BAMF_INDICATOR_SOURCE_APPROVE_ALL:
00214         return TRUE;
00215     }
00216 
00217   return TRUE;
00218 }
00219 
00220 static void status_notifier_proxy_owner_changed (GObject *object, GParamSpec *pspec, BamfIndicatorSource *self);
00221 
00222 static gboolean
00223 bamf_indicator_source_register_notification_approver (BamfIndicatorSource *self)
00224 {
00225   static int retry_count = 0;
00226   GDBusProxy *gproxy;
00227   GError *error = NULL;
00228   gchar *owner;
00229   GVariant* result;
00230 
00231   gproxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
00232                                           G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES|
00233                                           G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS|
00234                                           G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
00235                                           NULL,
00236                                           "org.kde.StatusNotifierWatcher",//"com.canonical.indicator.application",
00237                                           "/StatusNotifierWatcher",
00238                                           "org.kde.StatusNotifierWatcher",
00239                                           NULL,
00240                                           &error);
00241 
00242   if (error)
00243     {
00244        g_debug ("Failed to get notification approver proxy: %s", error->message);
00245        g_error_free (error);
00246     }
00247   else
00248     {
00249       owner = g_dbus_proxy_get_name_owner (gproxy);
00250 
00251       if (owner)
00252         {
00253           g_free (owner);
00254 
00255           GVariantBuilder tuple, array;
00256           g_variant_builder_init (&tuple, G_VARIANT_TYPE_TUPLE);
00257           g_variant_builder_add_value (&tuple, g_variant_new_object_path (BAMF_INDICATOR_SOURCE_PATH));
00258           g_variant_builder_init (&array, G_VARIANT_TYPE ("as"));
00259           g_variant_builder_add_value (&tuple, g_variant_builder_end (&array));
00260 
00261           result = g_dbus_proxy_call_sync (gproxy, "XAyatanaRegisterNotificationApprover",
00262                                            g_variant_builder_end (&tuple),
00263                                            G_DBUS_CALL_FLAGS_NONE,
00264                                            -1,
00265                                            NULL,
00266                                            &error);
00267 
00268           if (!error)
00269             {
00270               g_debug ("Remote notification approver registered");
00271               g_signal_connect (G_OBJECT (gproxy), "notify::g-name-owner",
00272                                 (GCallback) status_notifier_proxy_owner_changed,
00273                                 self);
00274 
00275               g_variant_unref (result);
00276               if (self->priv->proxy)
00277                 g_object_unref (self->priv->proxy);
00278 
00279               self->priv->proxy = gproxy;
00280               retry_count = 0;
00281               return FALSE;
00282             }
00283           else
00284             {
00285               g_debug ("Failed to register as approver: %s", error->message);
00286               g_error_free (error);
00287             }
00288         }
00289       else
00290         {
00291            g_debug ("Failed to get notification approver proxy: no owner available");
00292         }
00293 
00294       g_object_unref (gproxy);
00295     }
00296 
00297   retry_count++;
00298 
00299   if (retry_count > 10)
00300     {
00301       g_debug ("Retry count expired");
00302       return FALSE;
00303     }
00304 
00305   g_debug ("Retrying registration in 2 seconds");
00306   return TRUE;
00307 }
00308 
00309 static void
00310 status_notifier_proxy_owner_changed (GObject *object, GParamSpec *pspec,
00311                                      BamfIndicatorSource *self)
00312 {
00313   g_debug ("Remote approver service died, re-establishing connection");
00314   g_object_unref (self->priv->proxy);
00315   self->priv->proxy = NULL;
00316 
00317   if (bamf_indicator_source_register_notification_approver (self))
00318     g_timeout_add_seconds (2, (GSourceFunc) bamf_indicator_source_register_notification_approver, self);
00319 }
00320 
00321 static gboolean
00322 on_dbus_handle_approve_item (BamfDBusMatcher *interface,
00323                              GDBusMethodInvocation *invocation,
00324                              const gchar *id,
00325                              const gchar *category,
00326                              guint pid,
00327                              const gchar *address,
00328                              const gchar *path,
00329                              BamfIndicatorSource *self)
00330 {
00331   gboolean approved = bamf_indicator_source_approve_item (self, id, category,
00332                                                           pid, address, path);
00333   g_dbus_method_invocation_return_value (invocation,
00334                                          g_variant_new ("(b)", approved));
00335 
00336   return TRUE;
00337 }
00338 
00339 static void
00340 bamf_indicator_source_constructed (GObject *object)
00341 {
00342   BamfIndicatorSource *self;
00343 
00344   self = BAMF_INDICATOR_SOURCE (object);
00345 
00346   if (G_OBJECT_CLASS (bamf_indicator_source_parent_class)->constructed)
00347     G_OBJECT_CLASS (bamf_indicator_source_parent_class)->constructed (object);
00348 
00349   self->priv->behavior = BAMF_INDICATOR_SOURCE_APPROVE_ALL;
00350   
00351   if (bamf_indicator_source_register_notification_approver (self))
00352     g_timeout_add_seconds (2, (GSourceFunc) bamf_indicator_source_register_notification_approver, self);
00353 }
00354 
00355 static void
00356 bamf_indicator_source_dispose (GObject *object)
00357 {
00358   BamfIndicatorSource *self = BAMF_INDICATOR_SOURCE (object);
00359 
00360   if (self->priv->indicators)
00361     {
00362       g_list_free_full (self->priv->indicators, g_object_unref);
00363       self->priv->indicators = NULL;
00364     }
00365 
00366   if (self->priv->proxy)
00367     {
00368       g_object_unref (self->priv->proxy);
00369       self->priv->proxy = NULL;
00370     }
00371 
00372   G_OBJECT_CLASS (bamf_indicator_source_parent_class)->dispose (object);
00373 }
00374 
00375 static void
00376 bamf_indicator_source_class_init (BamfIndicatorSourceClass *klass)
00377 {
00378   GObjectClass *object_class = G_OBJECT_CLASS (klass);
00379   
00380   object_class->dispose = bamf_indicator_source_dispose;
00381   object_class->constructed = bamf_indicator_source_constructed;
00382 
00383   g_type_class_add_private (object_class, sizeof (BamfIndicatorSourcePrivate));
00384 
00385   indicator_source_signals [INDICATOR_OPENED] =
00386     g_signal_new ("indicator-opened",
00387                   G_OBJECT_CLASS_TYPE (klass),
00388                   G_SIGNAL_RUN_FIRST,
00389                   G_STRUCT_OFFSET (BamfIndicatorSourceClass, indicator_opened),
00390                   NULL, NULL,
00391                   g_cclosure_marshal_VOID__OBJECT,
00392                   G_TYPE_NONE, 1,
00393                   BAMF_TYPE_INDICATOR);
00394 
00395   indicator_source_signals [INDICATOR_CLOSED] =
00396     g_signal_new ("indicator-closed",
00397                   G_OBJECT_CLASS_TYPE (klass),
00398                   G_SIGNAL_RUN_FIRST,
00399                   G_STRUCT_OFFSET (BamfIndicatorSourceClass, indicator_closed),
00400                   NULL, NULL,
00401                   g_cclosure_marshal_VOID__OBJECT,
00402                   G_TYPE_NONE, 1,
00403                   BAMF_TYPE_INDICATOR);
00404 }
00405 
00406 static void
00407 bamf_indicator_source_init (BamfIndicatorSource *self)
00408 {
00409   self->priv = BAMF_INDICATOR_SOURCE_GET_PRIVATE (self);
00410 
00411   /* Registering signal callbacks to reply to dbus method calls */
00412   g_signal_connect (self, "handle-approve-item",
00413                     G_CALLBACK (on_dbus_handle_approve_item), self);
00414 }
00415 
00416 static BamfIndicatorSource *approver = NULL;
00417 
00418 BamfIndicatorSource *
00419 bamf_indicator_source_get_default ()
00420 {
00421   if (!approver)
00422     approver = g_object_new (BAMF_TYPE_INDICATOR_SOURCE, NULL);
00423 
00424   return approver;
00425 }