[gnome-flashback] polkit: initial version



commit ae1f0bfa0ad0d653c31c50f98a248efaf10a7c03
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date:   Mon Aug 24 18:30:21 2015 +0300

    polkit: initial version
    
    Origial PolicyKit-gnome:
    https://git.gnome.org/browse/PolicyKit-gnome/
    
    https://bugzilla.gnome.org/show_bug.cgi?id=739539

 configure.ac                                       |    7 +
 data/org.gnome.gnome-flashback.gschema.xml.in.in   |    5 +
 gnome-flashback/Makefile.am                        |    2 +
 gnome-flashback/flashback-application.c            |    4 +
 gnome-flashback/flashback.gresource.xml            |    1 +
 gnome-flashback/libpolkit/Makefile.am              |   40 +
 .../libpolkit/flashback-authenticator.c            |  534 ++++++++++
 .../libpolkit/flashback-authenticator.h            |   50 +
 gnome-flashback/libpolkit/flashback-listener.c     |  233 +++++
 gnome-flashback/libpolkit/flashback-listener.h     |   58 ++
 .../libpolkit/flashback-polkit-dialog.c            | 1018 ++++++++++++++++++++
 .../libpolkit/flashback-polkit-dialog.h            |   59 ++
 .../libpolkit/flashback-polkit-dialog.ui           |  253 +++++
 gnome-flashback/libpolkit/flashback-polkit.c       |  114 +++
 gnome-flashback/libpolkit/flashback-polkit.h       |   33 +
 po/POTFILES.in                                     |    4 +
 16 files changed, 2415 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index c064bd8..6e5fd25 100644
--- a/configure.ac
+++ b/configure.ac
@@ -36,6 +36,8 @@ LIBGNOME_DESKTOP_REQUIRED=3.12.0
 CANBERRA_REQUIRED=0.13
 GLIB_REQUIRED=2.44.0
 GSETTINGS_DESKTOP_SCHEMAS_REQUIRED=3.12.0
+POLKIT_AGENT_REQUIRED=0.97
+POLKIT_GOBJECT_REQUIRED=0.97
 
 PKG_CHECK_MODULES(GNOME_FLASHBACK, gtk+-3.0 >= $GTK_REQUIRED dbus-glib-1)
 AC_SUBST(GNOME_FLASHBACK_CFLAGS)
@@ -61,6 +63,10 @@ PKG_CHECK_MODULES(IDLE_MONITOR, gtk+-3.0 >= $GTK_REQUIRED x11 xext)
 AC_SUBST(IDLE_MONITOR_CFLAGS)
 AC_SUBST(IDLE_MONITOR_LIBS)
 
+PKG_CHECK_MODULES(POLKIT, gtk+-3.0 >= $GTK_REQUIRED glib-2.0 >= $GLIB_REQUIRED polkit-agent-1 >= 
$POLKIT_AGENT_REQUIRED polkit-gobject-1 >= $POLKIT_GOBJECT_REQUIRED)
+AC_SUBST(POLKIT_CFLAGS)
+AC_SUBST(POLKIT_LIBS)
+
 PKG_CHECK_MODULES(SCREENCAST, gtk+-3.0 >= $GTK_REQUIRED)
 AC_SUBST(SCREENCAST_CFLAGS)
 AC_SUBST(SCREENCAST_LIBS)
@@ -95,6 +101,7 @@ gnome-flashback/libdesktop-background/Makefile
 gnome-flashback/libdisplay-config/Makefile
 gnome-flashback/libend-session-dialog/Makefile
 gnome-flashback/libidle-monitor/Makefile
+gnome-flashback/libpolkit/Makefile
 gnome-flashback/libscreencast/Makefile
 gnome-flashback/libscreenshot/Makefile
 gnome-flashback/libshell/Makefile
diff --git a/data/org.gnome.gnome-flashback.gschema.xml.in.in 
b/data/org.gnome.gnome-flashback.gschema.xml.in.in
index 28b6bf6..da7d719 100644
--- a/data/org.gnome.gnome-flashback.gschema.xml.in.in
+++ b/data/org.gnome.gnome-flashback.gschema.xml.in.in
@@ -25,6 +25,11 @@
                        <_summary>Idle monitor</_summary>
                        <_description>If set to true, then GNOME Flashback application will be used for user 
activity monitoring.</_description>
                </key>
+               <key name="polkit" type="b">
+                       <default>true</default>
+                       <_summary>Authentication Agent for polkit</_summary>
+                       <_description>This is same authentication agent that was provided by 
PolicyKit-gnome.</_description>
+               </key>
                <key name="screencast" type="b">
                        <default>true</default>
                        <_summary>Screencasts</_summary>
diff --git a/gnome-flashback/Makefile.am b/gnome-flashback/Makefile.am
index fc00afd..f30acae 100644
--- a/gnome-flashback/Makefile.am
+++ b/gnome-flashback/Makefile.am
@@ -6,6 +6,7 @@ SUBDIRS = \
        libdisplay-config \
        libend-session-dialog \
        libidle-monitor \
+       libpolkit \
        libscreencast \
        libscreenshot \
        libshell \
@@ -46,6 +47,7 @@ gnome_flashback_LDADD = \
        $(top_builddir)/gnome-flashback/libdisplay-config/libdisplay-config.la \
        $(top_builddir)/gnome-flashback/libend-session-dialog/libend-session-dialog.la \
        $(top_builddir)/gnome-flashback/libidle-monitor/libidle-monitor.la \
+       $(top_builddir)/gnome-flashback/libpolkit/libpolkit.la \
        $(top_builddir)/gnome-flashback/libscreencast/libscreencast.la \
        $(top_builddir)/gnome-flashback/libscreenshot/libscreenshot.la \
        $(top_builddir)/gnome-flashback/libshell/libshell.la \
diff --git a/gnome-flashback/flashback-application.c b/gnome-flashback/flashback-application.c
index a6bc6ca..4c87907 100644
--- a/gnome-flashback/flashback-application.c
+++ b/gnome-flashback/flashback-application.c
@@ -24,6 +24,7 @@
 #include "libdisplay-config/flashback-display-config.h"
 #include "libend-session-dialog/flashback-end-session-dialog.h"
 #include "libidle-monitor/flashback-idle-monitor.h"
+#include "libpolkit/flashback-polkit.h"
 #include "libscreencast/flashback-screencast.h"
 #include "libscreenshot/flashback-screenshot.h"
 #include "libshell/flashback-shell.h"
@@ -45,6 +46,7 @@ struct _FlashbackApplication
   FlashbackDisplayConfig    *config;
   FlashbackEndSessionDialog *dialog;
   FlashbackIdleMonitor      *idle_monitor;
+  FlashbackPolkit           *polkit;
   FlashbackScreencast       *screencast;
   FlashbackScreenshot       *screenshot;
   FlashbackShell            *shell;
@@ -133,6 +135,7 @@ settings_changed (GSettings   *settings,
   SETTING_CHANGED (config, "display-config", flashback_display_config_new)
   SETTING_CHANGED (idle_monitor, "idle-monitor", flashback_idle_monitor_new)
   SETTING_CHANGED (dialog, "end-session-dialog", flashback_end_session_dialog_new)
+  SETTING_CHANGED (polkit, "polkit", flashback_polkit_new)
   SETTING_CHANGED (screencast, "screencast", flashback_screencast_new)
   SETTING_CHANGED (screenshot, "screenshot", flashback_screenshot_new)
   SETTING_CHANGED (shell, "shell", flashback_shell_new)
@@ -167,6 +170,7 @@ flashback_application_finalize (GObject *object)
   g_clear_object (&application->config);
   g_clear_object (&application->dialog);
   g_clear_object (&application->idle_monitor);
+  g_clear_object (&application->polkit);
   g_clear_object (&application->screencast);
   g_clear_object (&application->screenshot);
   g_clear_object (&application->shell);
diff --git a/gnome-flashback/flashback.gresource.xml b/gnome-flashback/flashback.gresource.xml
index 46520ef..0d6133a 100644
--- a/gnome-flashback/flashback.gresource.xml
+++ b/gnome-flashback/flashback.gresource.xml
@@ -5,5 +5,6 @@
     <file>HighContrast.css</file>
     <file alias="flashback-confirm-dialog.ui" 
compressed="true">libdisplay-config/flashback-confirm-dialog.ui</file>
     <file alias="flashback-inhibit-dialog.ui" 
compressed="true">libend-session-dialog/flashback-inhibit-dialog.ui</file>
+    <file alias="flashback-polkit-dialog.ui" compressed="true">libpolkit/flashback-polkit-dialog.ui</file>
   </gresource>
 </gresources>
diff --git a/gnome-flashback/libpolkit/Makefile.am b/gnome-flashback/libpolkit/Makefile.am
new file mode 100644
index 0000000..c76a750
--- /dev/null
+++ b/gnome-flashback/libpolkit/Makefile.am
@@ -0,0 +1,40 @@
+NULL =
+
+noinst_LTLIBRARIES = \
+       libpolkit.la \
+       $(NULL)
+
+libpolkit_la_CFLAGS = \
+       $(POLKIT_CFLAGS) \
+       $(WARN_CFLAGS) \
+       $(AM_CFLAGS) \
+       -I$(top_builddir)/gnome-flashback/libpolkit \
+       -DGNOMELOCALEDIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \
+       -DPOLKIT_AGENT_I_KNOW_API_IS_SUBJECT_TO_CHANGE \
+       $(NULL)
+
+libpolkit_la_SOURCES = \
+  flashback-authenticator.c \
+       flashback-authenticator.h \
+       flashback-listener.c \
+       flashback-listener.h \
+       flashback-polkit.c \
+       flashback-polkit.h \
+       flashback-polkit-dialog.c \
+       flashback-polkit-dialog.h \
+       $(NULL)
+
+libpolkit_la_LDFLAGS = \
+       $(WARN_LDFLAGS) \
+       $(AM_LDFLAGS) \
+       $(NULL)
+
+libpolkit_la_LIBADD = \
+       $(POLKIT_LIBS) \
+       $(NULL)
+
+EXTRA_DIST = \
+       flashback-polkit-dialog.ui \
+       $(NULL)
+
+-include $(top_srcdir)/git.mk
diff --git a/gnome-flashback/libpolkit/flashback-authenticator.c 
b/gnome-flashback/libpolkit/flashback-authenticator.c
new file mode 100644
index 0000000..081ab02
--- /dev/null
+++ b/gnome-flashback/libpolkit/flashback-authenticator.c
@@ -0,0 +1,534 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2015 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *     Alberts Muktupāvels <alberts muktupavels gmail com>
+ *     David Zeuthen <davidz redhat com>
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <pwd.h>
+
+#include "flashback-authenticator.h"
+#include "flashback-polkit-dialog.h"
+
+struct _FlashbackAuthenticator
+{
+  GObject                   parent;
+
+  PolkitAuthority          *authority;
+
+  gchar                    *action_id;
+  gchar                    *message;
+  gchar                    *icon_name;
+  PolkitDetails            *details;
+  gchar                    *cookie;
+  GList                    *identities;
+
+  PolkitActionDescription  *action_desc;
+  gchar                   **users;
+
+  GtkWidget                *dialog;
+
+  gboolean                  gained_authorization;
+  gboolean                  was_cancelled;
+  gboolean                  new_user_selected;
+  gchar                    *selected_user;
+
+  PolkitAgentSession       *session;
+
+  GMainLoop                *loop;
+
+  gulong                    idle_id;
+};
+
+enum
+{
+  SIGNAL_COMPLETED,
+
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (FlashbackAuthenticator, flashback_authenticator, G_TYPE_OBJECT)
+
+static void
+session_request (PolkitAgentSession *session,
+                 const char         *request,
+                 gboolean            echo_on,
+                 gpointer            user_data)
+{
+  FlashbackAuthenticator *authenticator;
+  FlashbackPolkitDialog *dialog;
+  gchar *modified_request;
+  gchar *password;
+
+  authenticator = FLASHBACK_AUTHENTICATOR (user_data);
+  dialog = FLASHBACK_POLKIT_DIALOG (authenticator->dialog);
+
+  /*g_debug ("in conversation_pam_prompt, request='%s', echo_on=%d", request, echo_on);*/
+
+  /* Fix up, and localize, password prompt if it's password auth */
+  if (g_ascii_strncasecmp (request, "password:", 9) == 0)
+    {
+      if (strcmp (g_get_user_name (), authenticator->selected_user) != 0)
+        {
+          modified_request = g_strdup_printf (_("_Password for %s:"), authenticator->selected_user);
+        }
+      else
+        {
+          modified_request = g_strdup (_("_Password:"));
+        }
+    }
+  else
+    {
+      modified_request = g_strdup (request);
+    }
+
+  gtk_widget_show_all (authenticator->dialog);
+  gtk_window_present (GTK_WINDOW (dialog));
+
+  password = flashback_polkit_dialog_run_until_response_for_prompt (dialog,
+                                                                    modified_request,
+                                                                    echo_on,
+                                                                    &authenticator->was_cancelled,
+                                                                    &authenticator->new_user_selected);
+
+  g_free (modified_request);
+
+  /* cancel auth unless user provided a password */
+  if (password == NULL)
+    {
+      flashback_authenticator_cancel (authenticator);
+      return;
+    }
+
+  polkit_agent_session_response (authenticator->session, password);
+  g_free (password);
+}
+
+static void
+session_show_info (PolkitAgentSession *session,
+                   const gchar        *msg,
+                   gpointer            user_data)
+{
+  FlashbackAuthenticator *authenticator;
+  FlashbackPolkitDialog *dialog;
+  gchar *s;
+
+  authenticator = FLASHBACK_AUTHENTICATOR (user_data);
+  dialog = FLASHBACK_POLKIT_DIALOG (authenticator->dialog);
+
+  s = g_strconcat ("<b>", msg, "</b>", NULL);
+  flashback_polkit_dialog_set_info_message (dialog, s);
+  g_free (s);
+
+  gtk_widget_show_all (authenticator->dialog);
+  gtk_window_present (GTK_WINDOW (dialog));
+}
+
+static void
+session_show_error (PolkitAgentSession *session,
+                    const gchar        *msg,
+                    gpointer            user_data)
+{
+  FlashbackAuthenticator *authenticator;
+  FlashbackPolkitDialog *dialog;
+  gchar *s;
+
+  authenticator = FLASHBACK_AUTHENTICATOR (user_data);
+  dialog = FLASHBACK_POLKIT_DIALOG (authenticator->dialog);
+
+  s = g_strconcat ("<b>", msg, "</b>", NULL);
+  flashback_polkit_dialog_set_info_message (dialog, s);
+  g_free (s);
+}
+
+static void
+session_completed (PolkitAgentSession *session,
+                   gboolean            gained_authorization,
+                   gpointer            user_data)
+{
+  FlashbackAuthenticator *authenticator;
+
+  authenticator = FLASHBACK_AUTHENTICATOR (user_data);
+
+  authenticator->gained_authorization = gained_authorization;
+
+  //g_debug ("in conversation_done gained=%d", gained_authorization);
+
+  g_main_loop_quit (authenticator->loop);
+}
+
+static gboolean
+do_initiate (gpointer user_data)
+{
+  FlashbackAuthenticator *authenticator;
+  FlashbackPolkitDialog *dialog;
+  gint num_tries;
+
+  authenticator = FLASHBACK_AUTHENTICATOR (user_data);
+  dialog = FLASHBACK_POLKIT_DIALOG (authenticator->dialog);
+
+  gtk_widget_show_all (authenticator->dialog);
+  gtk_window_present (GTK_WINDOW (dialog));
+
+  if (!flashback_polkit_dialog_run_until_user_is_selected (dialog))
+    {
+      authenticator->was_cancelled = TRUE;
+
+      g_signal_emit_by_name (authenticator, "completed",
+                             authenticator->gained_authorization,
+                             authenticator->was_cancelled);
+
+      authenticator->idle_id = 0;
+      return G_SOURCE_REMOVE;
+    }
+
+  authenticator->loop = g_main_loop_new (NULL, TRUE);
+
+  num_tries = 0;
+  while (num_tries < 3)
+    {
+      PolkitIdentity *identity;
+
+      g_free (authenticator->selected_user);
+      authenticator->selected_user = flashback_polkit_dialog_get_selected_user (dialog);
+
+      /*g_warning ("Authenticating user %s", authenticator->selected_user);*/
+      identity = polkit_unix_user_new_for_name (authenticator->selected_user, NULL);
+
+      authenticator->session = polkit_agent_session_new (identity, authenticator->cookie);
+      g_object_unref (identity);
+
+      g_signal_connect (authenticator->session, "request",
+                        G_CALLBACK (session_request), authenticator);
+      g_signal_connect (authenticator->session, "show-info",
+                        G_CALLBACK (session_show_info), authenticator);
+      g_signal_connect (authenticator->session, "show-error",
+                        G_CALLBACK (session_show_error), authenticator);
+      g_signal_connect (authenticator->session, "completed",
+                        G_CALLBACK (session_completed), authenticator);
+
+      polkit_agent_session_initiate (authenticator->session);
+      g_main_loop_run (authenticator->loop);
+
+      /*g_warning ("gained_authorization=%d was_cancelled=%d new_user_selected=%d.",
+                 authenticator->gained_authorization, authenticator->was_cancelled,
+                 authenticator->new_user_selected);*/
+
+      if (authenticator->new_user_selected)
+        {
+          /*g_warning ("New user selected");*/
+          authenticator->new_user_selected = FALSE;
+          g_clear_object (&authenticator->session);
+
+          continue;
+        }
+
+      num_tries++;
+
+      if (!authenticator->gained_authorization && !authenticator->was_cancelled)
+        {
+          if (authenticator->dialog != NULL)
+            {
+              gchar *s;
+
+              s = g_strconcat ("<b>", _("Authentication Failure"), "</b>", NULL);
+              flashback_polkit_dialog_set_info_message (dialog, s);
+              g_free (s);
+
+              gtk_widget_queue_draw (authenticator->dialog);
+
+              /* shake the dialog to indicate error */
+              flashback_polkit_dialog_indicate_error (dialog);
+
+              g_clear_object (&authenticator->session);
+
+              continue;
+            }
+        }
+
+      break;
+    }
+
+  g_signal_emit_by_name (authenticator, "completed",
+                         authenticator->gained_authorization,
+                         authenticator->was_cancelled);
+
+  authenticator->idle_id = 0;
+  return G_SOURCE_REMOVE;
+}
+
+static void
+delete_event_cb (GtkWidget *widget,
+                 GdkEvent  *event,
+                 gpointer   user_data)
+{
+  FlashbackAuthenticator *authenticator;
+
+  authenticator = FLASHBACK_AUTHENTICATOR (user_data);
+
+  flashback_authenticator_cancel (authenticator);
+}
+
+static void
+selected_user_cb (GObject    *object,
+                  GParamSpec *pspec,
+                  gpointer    user_data)
+{
+  FlashbackAuthenticator *authenticator;
+  FlashbackPolkitDialog *dialog;
+
+  authenticator = FLASHBACK_AUTHENTICATOR (user_data);
+
+  dialog = FLASHBACK_POLKIT_DIALOG (authenticator->dialog);
+  flashback_polkit_dialog_set_info_message (dialog, "");
+
+  flashback_authenticator_cancel (authenticator);
+  authenticator->new_user_selected = TRUE;
+}
+
+static PolkitActionDescription *
+get_desc_for_action (FlashbackAuthenticator *authenticator)
+{
+  PolkitActionDescription *result;
+  GList *descs;
+  GList *l;
+
+  result = NULL;
+  descs = polkit_authority_enumerate_actions_sync (authenticator->authority,
+                                                   NULL, NULL);
+
+  for (l = descs; l != NULL; l = l->next)
+    {
+      PolkitActionDescription *action_desc;
+      const gchar *action_id;
+
+      action_desc = POLKIT_ACTION_DESCRIPTION (l->data);
+      action_id = polkit_action_description_get_action_id (action_desc);
+
+      if (g_strcmp0 (action_id, authenticator->action_id) == 0)
+        {
+          result = g_object_ref (action_desc);
+          break;
+        }
+    }
+
+  g_list_foreach (descs, (GFunc) g_object_unref, NULL);
+  g_list_free (descs);
+
+  return result;
+}
+
+static void
+flashback_authenticator_dispose (GObject *object)
+{
+  FlashbackAuthenticator *authenticator;
+
+  authenticator = FLASHBACK_AUTHENTICATOR (object);
+
+  if (authenticator->idle_id > 0)
+    {
+      g_source_remove (authenticator->idle_id);
+      authenticator->idle_id = 0;
+    }
+
+  g_clear_object (&authenticator->authority);
+  g_clear_object (&authenticator->details);
+  g_clear_object (&authenticator->action_desc);
+
+  if (authenticator->dialog != NULL)
+    {
+      gtk_widget_destroy (authenticator->dialog);
+      authenticator->dialog = NULL;
+    }
+
+  g_clear_object (&authenticator->session);
+
+  if (authenticator->loop != NULL)
+    {
+      g_main_loop_unref (authenticator->loop);
+      authenticator->loop = NULL;
+    }
+
+  G_OBJECT_CLASS (flashback_authenticator_parent_class)->dispose (object);
+}
+
+static void
+flashback_authenticator_finalize (GObject *object)
+{
+  FlashbackAuthenticator *authenticator;
+
+  authenticator = FLASHBACK_AUTHENTICATOR (object);
+
+  g_free (authenticator->action_id);
+  g_free (authenticator->message);
+  g_free (authenticator->icon_name);
+  g_free (authenticator->cookie);
+
+  g_list_foreach (authenticator->identities, (GFunc) g_object_unref, NULL);
+  g_list_free (authenticator->identities);
+
+  g_strfreev (authenticator->users);
+
+  g_free (authenticator->selected_user);
+
+  G_OBJECT_CLASS (flashback_authenticator_parent_class)->finalize (object);
+}
+
+static void
+flashback_authenticator_class_init (FlashbackAuthenticatorClass *authenticator_class)
+{
+  GObjectClass *object_class;
+
+  object_class = G_OBJECT_CLASS (authenticator_class);
+
+  object_class->dispose = flashback_authenticator_dispose;
+  object_class->finalize = flashback_authenticator_finalize;
+
+  /**
+   * FlashbackAuthenticator::completed
+   * @authenticator: A #PolkitGnomeAuthenticator.
+   * @gained_authorization: Whether the authorization was gained.
+   * @dismissed: Whether the dialog was dismissed.
+   *
+   * Emitted when the authentication is completed. The user is supposed to
+   * dispose of @authenticator upon receiving this signal.
+   */
+  signals[SIGNAL_COMPLETED] =
+    g_signal_new ("completed",
+                  G_OBJECT_CLASS_TYPE (authenticator_class),
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL,
+                  g_cclosure_marshal_generic,
+                  G_TYPE_NONE, 2, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
+}
+
+static void
+flashback_authenticator_init (FlashbackAuthenticator *authenticator)
+{
+}
+
+FlashbackAuthenticator *
+flashback_authenticator_new (const gchar   *action_id,
+                             const gchar   *message,
+                             const gchar   *icon_name,
+                             PolkitDetails *details,
+                             const gchar   *cookie,
+                             GList         *identities)
+{
+  FlashbackAuthenticator *authenticator;
+  GError *error;
+  gint users;
+  gint i;
+  GList *l;
+  const gchar *vendor;
+  const gchar *vendor_url;
+
+  authenticator = g_object_new (FLASHBACK_TYPE_AUTHENTICATOR, NULL);
+
+  error = NULL;
+  authenticator->authority = polkit_authority_get_sync (NULL, &error);
+
+  if (error != NULL)
+    {
+      g_critical ("Error getting authority: %s", error->message);
+      g_error_free (error);
+
+      g_object_unref (authenticator);
+      return NULL;
+    }
+
+  authenticator->action_id = g_strdup (action_id);
+  authenticator->message = g_strdup (message);
+  authenticator->icon_name = g_strdup (icon_name);
+  authenticator->details = details != NULL ? g_object_ref (details) : NULL;
+  authenticator->cookie = g_strdup (cookie);
+  authenticator->identities = g_list_copy (identities);
+  g_list_foreach (authenticator->identities, (GFunc) g_object_ref, NULL);
+
+  authenticator->action_desc = get_desc_for_action (authenticator);
+
+  if (authenticator->action_desc == NULL)
+    {
+      g_object_unref (authenticator);
+      return NULL;
+    }
+
+  users = g_list_length (authenticator->identities);
+  authenticator->users = g_new0 (gchar *, users + 1);
+
+  for (l = authenticator->identities, i = 0; l != NULL; l = l->next, i++)
+    {
+      PolkitUnixUser *user;
+      uid_t uid;
+      struct passwd *passwd;
+
+      user = POLKIT_UNIX_USER (l->data);
+      uid = polkit_unix_user_get_uid (user);
+      passwd = getpwuid (uid);
+      authenticator->users[i] = g_strdup (passwd->pw_name);
+    }
+
+  vendor = polkit_action_description_get_vendor_name (authenticator->action_desc);
+  vendor_url = polkit_action_description_get_vendor_url (authenticator->action_desc);
+
+  authenticator->dialog = flashback_polkit_dialog_new (authenticator->action_id,
+                                                       vendor, vendor_url,
+                                                       authenticator->icon_name,
+                                                       authenticator->message,
+                                                       authenticator->details,
+                                                       authenticator->users);
+
+  g_signal_connect (authenticator->dialog, "delete-event",
+                    G_CALLBACK (delete_event_cb), authenticator);
+  g_signal_connect (authenticator->dialog, "notify::selected-user",
+                    G_CALLBACK (selected_user_cb), authenticator);
+
+  return authenticator;
+}
+
+void
+flashback_authenticator_initiate (FlashbackAuthenticator *authenticator)
+{
+  authenticator->idle_id = g_idle_add (do_initiate, authenticator);
+  g_source_set_name_by_id (authenticator->idle_id, "[gnome-flashback] do_initiate");
+}
+
+void
+flashback_authenticator_cancel (FlashbackAuthenticator *authenticator)
+{
+  FlashbackPolkitDialog *dialog;
+
+  dialog = FLASHBACK_POLKIT_DIALOG (authenticator->dialog);
+  flashback_polkit_dialog_cancel (dialog);
+
+  authenticator->was_cancelled = TRUE;
+
+  if (authenticator->session != NULL)
+    polkit_agent_session_cancel (authenticator->session);
+}
+
+const gchar *
+flashback_authenticator_get_cookie (FlashbackAuthenticator *authenticator)
+{
+  return authenticator->cookie;
+}
diff --git a/gnome-flashback/libpolkit/flashback-authenticator.h 
b/gnome-flashback/libpolkit/flashback-authenticator.h
new file mode 100644
index 0000000..359e42c
--- /dev/null
+++ b/gnome-flashback/libpolkit/flashback-authenticator.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2015 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *     Alberts Muktupāvels <alberts muktupavels gmail com>
+ *     David Zeuthen <davidz redhat com>
+ */
+
+#ifndef FLASHBACK_AUTHENTICATOR_H
+#define FLASHBACK_AUTHENTICATOR_H
+
+#include <glib-object.h>
+#include <polkitagent/polkitagent.h>
+
+G_BEGIN_DECLS
+
+#define FLASHBACK_TYPE_AUTHENTICATOR flashback_authenticator_get_type ()
+G_DECLARE_FINAL_TYPE (FlashbackAuthenticator, flashback_authenticator,
+                      FLASHBACK, AUTHENTICATOR, GObject)
+
+FlashbackAuthenticator *flashback_authenticator_new        (const gchar            *action_id,
+                                                            const gchar            *message,
+                                                            const gchar            *icon_name,
+                                                            PolkitDetails          *details,
+                                                            const gchar            *cookie,
+                                                            GList                  *identities);
+
+void                    flashback_authenticator_initiate   (FlashbackAuthenticator *authenticator);
+void                    flashback_authenticator_cancel     (FlashbackAuthenticator *authenticator);
+const gchar            *flashback_authenticator_get_cookie (FlashbackAuthenticator *authenticator);
+
+
+
+G_END_DECLS
+
+#endif
diff --git a/gnome-flashback/libpolkit/flashback-listener.c b/gnome-flashback/libpolkit/flashback-listener.c
new file mode 100644
index 0000000..65a9e65
--- /dev/null
+++ b/gnome-flashback/libpolkit/flashback-listener.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2015 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *     Alberts Muktupāvels <alberts muktupavels gmail com>
+ *     David Zeuthen <davidz redhat com>
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "flashback-authenticator.h"
+#include "flashback-listener.h"
+
+struct _FlashbackListener
+{
+  PolkitAgentListener     parent;
+
+  GList                  *authenticators;
+  FlashbackAuthenticator *active;
+};
+
+typedef struct
+{
+  FlashbackListener      *listener;
+  FlashbackAuthenticator *authenticator;
+
+  GTask                  *task;
+
+  GCancellable           *cancellable;
+  gulong                  cancel_id;
+} AuthData;
+
+G_DEFINE_TYPE (FlashbackListener, flashback_listener, POLKIT_AGENT_TYPE_LISTENER)
+
+static void
+flashback_listener_dispose (GObject *object)
+{
+  G_OBJECT_CLASS (flashback_listener_parent_class)->dispose (object);
+}
+
+static void
+maybe_initiate_next_authenticator (FlashbackListener *listener)
+{
+  FlashbackAuthenticator *authenticator;
+
+  if (listener->authenticators == NULL)
+    return;
+
+  if (listener->active != NULL)
+    return;
+
+  authenticator = FLASHBACK_AUTHENTICATOR (listener->authenticators->data);
+
+  flashback_authenticator_initiate (authenticator);
+  listener->active = authenticator;
+}
+
+static AuthData *
+auth_data_new (FlashbackListener      *listener,
+               FlashbackAuthenticator *authenticator,
+               GTask                  *task,
+               GCancellable           *cancellable)
+{
+  AuthData *data;
+
+  data = g_new0 (AuthData, 1);
+
+  data->listener = g_object_ref (listener);
+  data->authenticator = g_object_ref (authenticator);
+  data->task = g_object_ref (task);
+  data->cancellable = g_object_ref (cancellable);
+
+  return data;
+}
+
+static void
+auth_data_free (AuthData *data)
+{
+  g_object_unref (data->listener);
+  g_object_unref (data->authenticator);
+  g_object_unref (data->task);
+
+  if (data->cancellable != NULL)
+    {
+      if (data->cancel_id > 0)
+        g_signal_handler_disconnect (data->cancellable, data->cancel_id);
+      g_object_unref (data->cancellable);
+    }
+
+  g_free (data);
+}
+
+static void
+cancelled_cb (GCancellable *cancellable,
+              gpointer      user_data)
+{
+  AuthData *data;
+
+  data = (AuthData *) user_data;
+
+  flashback_authenticator_cancel (data->authenticator);
+}
+
+static void
+completed_cb (FlashbackAuthenticator *authenticator,
+              gboolean                gained_authorization,
+              gboolean                dismissed,
+              gpointer                user_data)
+{
+  AuthData *data;
+
+  data = (AuthData *) user_data;
+
+  data->listener->authenticators = g_list_remove (data->listener->authenticators, authenticator);
+  if (authenticator == data->listener->active)
+    data->listener->active = NULL;
+
+  g_object_unref (authenticator);
+
+  if (dismissed)
+    {
+      g_task_return_new_error (data->task, POLKIT_ERROR, POLKIT_ERROR_CANCELLED,
+                               _("Authentication dialog was dismissed by the user"));
+    }
+  else
+    {
+      g_task_return_boolean (data->task, TRUE);
+    }
+
+  g_object_unref (data->task);
+
+  maybe_initiate_next_authenticator (data->listener);
+  auth_data_free (data);
+}
+
+static void
+flashback_listener_initiate_authentication (PolkitAgentListener  *agent_listener,
+                                            const gchar          *action_id,
+                                            const gchar          *message,
+                                            const gchar          *icon_name,
+                                            PolkitDetails        *details,
+                                            const gchar          *cookie,
+                                            GList                *identities,
+                                            GCancellable         *cancellable,
+                                            GAsyncReadyCallback   callback,
+                                            gpointer              user_data)
+{
+  FlashbackListener *listener;
+  GTask *task;
+  FlashbackAuthenticator *authenticator;
+  AuthData *data;
+
+  listener = FLASHBACK_LISTENER (agent_listener);
+
+  authenticator = flashback_authenticator_new (action_id, message, icon_name,
+                                               details, cookie, identities);
+
+  task = g_task_new (listener, cancellable, callback, user_data);
+
+  if (authenticator == NULL)
+    {
+      g_task_return_new_error (task, POLKIT_ERROR, POLKIT_ERROR_FAILED,
+                               "Error creating authentication object");
+      g_object_unref (task);
+
+      return;
+    }
+
+  data = auth_data_new (listener, authenticator, task, cancellable);
+
+  if (cancellable != NULL)
+    data->cancel_id = g_signal_connect (cancellable, "cancelled",
+                                        G_CALLBACK (cancelled_cb), data);
+
+  g_signal_connect (authenticator, "completed",
+                    G_CALLBACK (completed_cb), data);
+
+  listener->authenticators = g_list_append (listener->authenticators, authenticator);
+
+  maybe_initiate_next_authenticator (listener);
+}
+
+static gboolean
+flashback_listener_initiate_authentication_finish (PolkitAgentListener  *listener,
+                                                   GAsyncResult         *res,
+                                                   GError              **error)
+{
+  g_return_val_if_fail (g_task_is_valid (res, listener), FALSE);
+
+  return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+flashback_listener_class_init (FlashbackListenerClass *listener_class)
+{
+  GObjectClass *object_class;
+  PolkitAgentListenerClass *agent_class;
+
+  object_class = G_OBJECT_CLASS (listener_class);
+  agent_class = POLKIT_AGENT_LISTENER_CLASS (listener_class);
+
+  object_class->dispose = flashback_listener_dispose;
+
+  agent_class->initiate_authentication = flashback_listener_initiate_authentication;
+  agent_class->initiate_authentication_finish = flashback_listener_initiate_authentication_finish;
+}
+
+static void
+flashback_listener_init (FlashbackListener *listener)
+{
+}
+
+PolkitAgentListener *
+flashback_listener_new (void)
+{
+  return g_object_new (FLASHBACK_TYPE_LISTENER, NULL);
+}
diff --git a/gnome-flashback/libpolkit/flashback-listener.h b/gnome-flashback/libpolkit/flashback-listener.h
new file mode 100644
index 0000000..1846242
--- /dev/null
+++ b/gnome-flashback/libpolkit/flashback-listener.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2015 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *     Alberts Muktupāvels <alberts muktupavels gmail com>
+ *     David Zeuthen <davidz redhat com>
+ */
+
+#ifndef FLASHBACK_LISTENER_H
+#define FLASHBACK_LISTENER_H
+
+#include <polkitagent/polkitagent.h>
+
+G_BEGIN_DECLS
+
+#define FLASHBACK_TYPE_LISTENER         (flashback_listener_get_type())
+#define FLASHBACK_LISTENER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o),      \
+                                         FLASHBACK_TYPE_LISTENER,              \
+                                         FlashbackListener))
+#define FLASHBACK_IS_LISTENER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o),      \
+                                         FLASHBACK_TYPE_LISTENER))
+#define FLASHBACK_LISTENER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k),          \
+                                         FLASHBACK_TYPE_LISTENER,              \
+                                         FlashbackListenerClass))
+#define FLASHBACK_IS_LISTENER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k),         \
+                                         FLASHBACK_TYPE_LISTENER))
+#define FLASHBACK_LISTENER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o),       \
+                                         FLASHBACK_TYPE_LISTENER,              \
+                                         FlashbackListenerClass))
+
+typedef struct _FlashbackListener      FlashbackListener;
+typedef struct _FlashbackListenerClass FlashbackListenerClass;
+
+struct _FlashbackListenerClass
+{
+  PolkitAgentListenerClass parent_class;
+};
+
+GType                flashback_listener_get_type (void) G_GNUC_CONST;
+PolkitAgentListener *flashback_listener_new      (void);
+
+G_END_DECLS
+
+#endif
diff --git a/gnome-flashback/libpolkit/flashback-polkit-dialog.c 
b/gnome-flashback/libpolkit/flashback-polkit-dialog.c
new file mode 100644
index 0000000..92b5d7c
--- /dev/null
+++ b/gnome-flashback/libpolkit/flashback-polkit-dialog.c
@@ -0,0 +1,1018 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2015 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *     Alberts Muktupāvels <alberts muktupavels gmail com>
+ *     David Zeuthen <davidz redhat com>
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <glib/gi18n-lib.h>
+#include <pwd.h>
+
+#include "flashback-polkit-dialog.h"
+
+#define RESPONSE_USER_SELECTED 1001
+
+struct _FlashbackPolkitDialog
+{
+  GtkWindow       parent;
+
+  GtkWidget      *image;
+  GtkWidget      *main_message_label;
+  GtkWidget      *secondary_message_label;
+  GtkWidget      *users_combobox;
+  GtkWidget      *entry_box;
+  GtkWidget      *prompt_label;
+  GtkWidget      *password_entry;
+  GtkWidget      *info_label;
+  GtkWidget      *details_grid;
+  GtkWidget      *auth_button;
+
+  GtkListStore   *users_store;
+
+  gchar          *action_id;
+  gchar          *vendor;
+  gchar          *vendor_url;
+  gchar          *icon_name;
+  gchar          *message;
+  PolkitDetails  *details;
+  gchar         **users;
+
+  gchar          *selected_user;
+
+  gint            response;
+
+  gboolean        is_running;
+};
+
+enum
+{
+  PROP_0,
+
+  PROP_ACTION_ID,
+  PROP_VENDOR,
+  PROP_VENDOR_URL,
+  PROP_ICON_NAME,
+  PROP_MESSAGE,
+  PROP_DETAILS,
+  PROP_USERS,
+
+  PROP_SELECTED_USER,
+
+  LAST_PROP
+};
+
+static GParamSpec *properties[LAST_PROP] = { NULL };
+
+enum
+{
+  SIGNAL_CLOSE,
+
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+enum
+{
+  COLUMN_PIXBUF,
+  COLUMN_TEXT,
+  COLUMN_USERNAME,
+
+  N_COLUMNS
+};
+
+G_DEFINE_TYPE (FlashbackPolkitDialog, flashback_polkit_dialog, GTK_TYPE_WINDOW)
+
+static void
+cancel_button_clicked_cb (GtkButton             *button,
+                          FlashbackPolkitDialog *dialog)
+{
+  dialog->response = GTK_RESPONSE_CANCEL;
+
+  if (dialog->is_running == TRUE)
+    gtk_main_quit ();
+
+  gtk_window_close (GTK_WINDOW (dialog));
+}
+
+static void
+auth_button_clicked_cb (GtkButton             *button,
+                        FlashbackPolkitDialog *dialog)
+{
+  dialog->response = GTK_RESPONSE_OK;
+
+  if (dialog->is_running == TRUE)
+    gtk_main_quit ();
+}
+
+static void
+setup_image (FlashbackPolkitDialog *dialog)
+{
+  GtkIconTheme *icon_theme;
+  GdkPixbuf *vendor;
+  GdkPixbuf *password;
+  GdkPixbuf *copy;
+
+  if (dialog->icon_name == NULL || strlen (dialog->icon_name) == 0)
+    return;
+
+  icon_theme = gtk_icon_theme_get_default ();
+  vendor = gtk_icon_theme_load_icon (icon_theme, dialog->icon_name, 48, 0, NULL);
+
+  if (vendor == NULL)
+    {
+      g_warning ("No icon for themed icon with name '%s'", dialog->icon_name);
+
+      return;
+    }
+
+  password = gtk_icon_theme_load_icon (icon_theme, "dialog-password", 48, 0, NULL);
+
+  if (password == NULL)
+    {
+      g_warning ("Unable to get a pixbuf for icon with name 'dialog-password' at size 48");
+
+      gtk_image_set_from_pixbuf (GTK_IMAGE (dialog->image), vendor);
+      g_object_unref (vendor);
+
+      return;
+    }
+
+  copy = gdk_pixbuf_copy (password);
+
+  if (copy == NULL)
+    {
+      g_object_unref (vendor);
+      g_object_unref (password);
+
+      return;
+    }
+
+  gdk_pixbuf_composite (vendor, copy, 24, 24, 24, 24, 24, 24, 0.5, 0.5,
+                        GDK_INTERP_BILINEAR, 255);
+
+  gtk_image_set_from_pixbuf (GTK_IMAGE (dialog->image), copy);
+
+  g_object_unref (vendor);
+  g_object_unref (password);
+  g_object_unref (copy);
+}
+
+static void
+setup_main_message_label (FlashbackPolkitDialog *dialog)
+{
+  gchar *message;
+
+  message = g_strdup_printf ("<big><b>%s</b></big>", dialog->message);
+
+  gtk_label_set_markup (GTK_LABEL (dialog->main_message_label), message);
+  gtk_label_set_xalign (GTK_LABEL (dialog->main_message_label), 0);
+
+  g_free (message);
+}
+
+static void
+setup_secondary_message_label (FlashbackPolkitDialog *dialog)
+{
+  const gchar *message;
+  const gchar *user_name1;
+  const gchar *user_name2;
+
+  if (dialog->users != NULL && g_strv_length (dialog->users) > 1)
+    {
+      message = _("An application is attempting to perform an action that requires privileges. "
+                  "Authentication as one of the users below is required to perform this action.");
+    }
+  else
+    {
+      user_name1 = g_get_user_name ();
+      user_name2 = dialog->users != NULL ? dialog->users[0] : NULL;
+
+      if (g_strcmp0 (user_name1, user_name2) == 0)
+        {
+          message = _("An application is attempting to perform an action that requires privileges. "
+                      "Authentication is required to perform this action.");
+        }
+      else
+        {
+          message = _("An application is attempting to perform an action that requires privileges. "
+                      "Authentication as the super user is required to perform this action.");
+        }
+    }
+
+  gtk_label_set_markup (GTK_LABEL (dialog->secondary_message_label), message);
+  gtk_label_set_xalign (GTK_LABEL (dialog->secondary_message_label), 0);
+}
+
+static void
+combobox_set_sensitive (GtkCellLayout   *cell_layout,
+                        GtkCellRenderer *cell,
+                        GtkTreeModel    *tree_model,
+                        GtkTreeIter     *iter,
+                        gpointer         user_data)
+{
+  GtkTreePath *path;
+  gint *indices;
+
+  path = gtk_tree_model_get_path (tree_model, iter);
+  indices = gtk_tree_path_get_indices (path);
+
+  g_object_set (cell, "sensitive", indices[0] == 0 ? FALSE : TRUE, NULL);
+  gtk_tree_path_free (path);
+}
+
+static void
+users_combobox_changed_cb (GtkComboBox *combobox,
+                           gpointer     user_data)
+{
+  FlashbackPolkitDialog *dialog;
+  GtkTreeIter iter;
+  gchar *user_name;
+
+  dialog = FLASHBACK_POLKIT_DIALOG (user_data);
+
+  if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combobox), &iter))
+    return;
+
+  gtk_tree_model_get (GTK_TREE_MODEL (dialog->users_store), &iter,
+                      COLUMN_USERNAME, &user_name,
+                      -1);
+
+  g_free (dialog->selected_user);
+  dialog->selected_user = user_name;
+
+  g_object_notify (G_OBJECT (dialog), "selected-user");
+
+  dialog->response = RESPONSE_USER_SELECTED;
+
+  gtk_widget_set_sensitive (dialog->prompt_label, TRUE);
+  gtk_widget_set_sensitive (dialog->password_entry, TRUE);
+  gtk_widget_set_sensitive (dialog->auth_button, TRUE);
+}
+
+static void
+setup_users_store (FlashbackPolkitDialog *dialog)
+{
+  GtkTreeIter iter;
+  gint i;
+  GtkComboBox *combobox;
+  GtkCellRenderer *renderer;
+
+  if (dialog->users_store != NULL)
+    return;
+
+  dialog->users_store = gtk_list_store_new (N_COLUMNS, GDK_TYPE_PIXBUF,
+                                            G_TYPE_STRING, G_TYPE_STRING);
+
+  gtk_list_store_append (dialog->users_store, &iter);
+  gtk_list_store_set (dialog->users_store, &iter,
+                      COLUMN_PIXBUF, NULL,
+                      COLUMN_TEXT, _("Select user..."),
+                      COLUMN_USERNAME, NULL,
+                      -1);
+
+  for (i = 0; dialog->users[i] != NULL; i++)
+    {
+      gchar *gecos;
+      gchar *real_name;
+      GdkPixbuf *pixbuf;
+      struct passwd *passwd;
+
+      errno = 0;
+      passwd = getpwnam (dialog->users[i]);
+
+      if (passwd == NULL)
+        {
+          g_warning ("Error doing getpwnam(\"%s\"): %s", dialog->users[i], strerror (errno));
+          continue;
+        }
+
+      if (passwd->pw_gecos != NULL)
+        gecos = g_locale_to_utf8 (passwd->pw_gecos, -1, NULL, NULL, NULL);
+      else
+        gecos = NULL;
+
+      if (gecos != NULL && strlen (gecos) > 0)
+        {
+          gchar *first_comma;
+
+          first_comma = strchr (gecos, ',');
+
+          if (first_comma != NULL)
+            *first_comma = '\0';
+        }
+
+      if (g_strcmp0 (gecos, dialog->users[i]) != 0)
+        real_name = g_strdup_printf (_("%s (%s)"), gecos, dialog->users[i]);
+      else
+        real_name = g_strdup (dialog->users[i]);
+
+      g_free (gecos);
+
+      pixbuf = NULL;
+      if (passwd->pw_dir != NULL)
+        {
+          gchar *path;
+
+          path = g_strdup_printf ("%s/.face", passwd->pw_dir);
+          pixbuf = gdk_pixbuf_new_from_file_at_scale (path, 16, 16, TRUE, NULL);
+
+          g_free (path);
+        }
+
+      if (pixbuf == NULL)
+        {
+          GtkIconTheme *icon_theme;
+
+          icon_theme = gtk_icon_theme_get_default ();
+          pixbuf = gtk_icon_theme_load_icon (icon_theme, "avatar-default",
+                                             GTK_ICON_SIZE_MENU, 0, NULL);
+        }
+
+      gtk_list_store_append (dialog->users_store, &iter);
+      gtk_list_store_set (dialog->users_store, &iter,
+                          COLUMN_PIXBUF, pixbuf,
+                          COLUMN_TEXT, real_name,
+                          COLUMN_USERNAME, dialog->users[i],
+                          -1);
+
+      g_free (real_name);
+      g_object_unref (pixbuf);
+    }
+
+  combobox = GTK_COMBO_BOX (dialog->users_combobox);
+  gtk_combo_box_set_model (combobox, GTK_TREE_MODEL (dialog->users_store));
+
+  renderer = gtk_cell_renderer_pixbuf_new ();
+  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combobox), renderer, FALSE);
+  gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combobox), renderer,
+                                  "pixbuf", COLUMN_PIXBUF, NULL);
+  gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combobox), renderer,
+                                      (GtkCellLayoutDataFunc) combobox_set_sensitive,
+                                      NULL, NULL);
+
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combobox), renderer, TRUE);
+  gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combobox), renderer,
+                                  "text", COLUMN_TEXT, NULL);
+  gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combobox), renderer,
+                                      (GtkCellLayoutDataFunc) combobox_set_sensitive,
+                                      NULL, NULL);
+
+  gtk_combo_box_set_active (GTK_COMBO_BOX (combobox), 0);
+
+  g_signal_connect (dialog->users_combobox, "changed",
+                    G_CALLBACK (users_combobox_changed_cb), dialog);
+
+  gtk_widget_show (dialog->users_combobox);
+}
+
+static void
+setup_users_combobox (FlashbackPolkitDialog *dialog)
+{
+  gtk_widget_hide (dialog->users_combobox);
+
+  if (dialog->users == NULL)
+    return;
+
+  if (g_strv_length (dialog->users) > 1)
+    {
+      setup_users_store (dialog);
+
+      gtk_widget_set_sensitive (dialog->prompt_label, FALSE);
+      gtk_widget_set_sensitive (dialog->password_entry, FALSE);
+      gtk_widget_set_sensitive (dialog->auth_button, FALSE);
+    }
+  else
+    {
+      dialog->selected_user = g_strdup (dialog->users[0]);
+    }
+}
+
+static void
+setup_entry_box (FlashbackPolkitDialog *dialog)
+{
+  gtk_entry_set_visibility (GTK_ENTRY (dialog->password_entry), FALSE);
+
+  gtk_widget_hide (dialog->entry_box);
+  gtk_widget_set_no_show_all (dialog->entry_box, TRUE);
+
+  gtk_widget_hide (dialog->info_label);
+}
+
+static void
+add_row (FlashbackPolkitDialog *dialog,
+         gint                   row,
+         const gchar           *text1,
+         const gchar           *text2,
+         const gchar           *tooltip)
+{
+  GtkGrid *grid;
+  GtkWidget *label1;
+  GtkWidget *label2;
+
+  grid = GTK_GRID (dialog->details_grid);
+
+  label1 = gtk_label_new_with_mnemonic (text1);
+  gtk_label_set_use_markup (GTK_LABEL (label1), TRUE);
+  gtk_label_set_xalign (GTK_LABEL (label1), 1.0);
+
+  label2 = gtk_label_new_with_mnemonic (text2);
+  gtk_label_set_use_markup (GTK_LABEL (label2), TRUE);
+  gtk_label_set_xalign (GTK_LABEL (label2), 0.0);
+
+  gtk_label_set_mnemonic_widget (GTK_LABEL (label1), label2);
+
+  gtk_grid_attach (grid, label1, 0, row, 1, 1);
+  gtk_grid_attach (grid, label2, 1, row, 1, 1);
+
+  if (tooltip != NULL)
+    gtk_widget_set_tooltip_markup (label2,tooltip);
+  gtk_widget_set_hexpand (label2, TRUE);
+}
+
+static void
+setup_details (FlashbackPolkitDialog *dialog)
+{
+  gint row;
+  gchar *s1;
+  gchar *s2;
+  gchar *tooltip;
+
+  row = 0;
+
+  if (dialog->details != NULL)
+    {
+      gchar **keys;
+      gint i;
+
+      keys = polkit_details_get_keys (dialog->details);
+
+      for (i = 0; keys[i] != NULL; i++)
+        {
+          const gchar *key;
+          const gchar *value;
+
+          key = keys[i];
+          if (g_str_has_prefix (key, "polkit."))
+            continue;
+
+          value = polkit_details_lookup (dialog->details, key);
+
+          s1 = g_strdup_printf ("<small><b>%s:</b></small>", key);
+          s2 = g_strdup_printf ("<small>%s</small>", value);
+
+          add_row (dialog, row, s1, s2, NULL);
+
+          g_free (s1);
+          g_free (s2);
+
+          row++;
+        }
+
+      g_strfreev (keys);
+    }
+
+  s1 = g_strdup_printf ("<small><b>%s</b></small>", _("Action:"));
+  s2 = g_strdup_printf ("<small><a href=\"%s\">%s</a></small>",
+                        dialog->action_id, dialog->action_id);
+  tooltip = g_strdup_printf (_("Click to edit %s"), dialog->action_id);
+
+  add_row (dialog, row++, s1, s2, tooltip);
+
+  g_free (s1);
+  g_free (s2);
+  g_free (tooltip);
+
+  s1 = g_strdup_printf ("<small><b>%s</b></small>", _("Vendor:"));
+  s2 = g_strdup_printf ("<small><a href=\"%s\">%s</a></small>",
+                        dialog->vendor_url, dialog->vendor);
+  tooltip = g_strdup_printf (_("Click to open %s"), dialog->vendor_url);
+
+  add_row (dialog, row++, s1, s2, tooltip);
+
+  g_free (s1);
+  g_free (s2);
+  g_free (tooltip);
+}
+
+static void
+flashback_polkit_dialog_constructed (GObject *object)
+{
+  FlashbackPolkitDialog *dialog;
+
+  dialog = FLASHBACK_POLKIT_DIALOG (object);
+
+  G_OBJECT_CLASS (flashback_polkit_dialog_parent_class)->constructed (object);
+
+  dialog->response = GTK_RESPONSE_OK;
+
+  setup_image (dialog);
+  setup_main_message_label (dialog);
+  setup_secondary_message_label (dialog);
+  setup_users_combobox (dialog);
+  setup_entry_box (dialog);
+  setup_details (dialog);
+}
+
+static void
+flashback_polkit_dialog_get_property (GObject    *object,
+                                      guint       property_id,
+                                      GValue     *value,
+                                      GParamSpec *pspec)
+{
+  FlashbackPolkitDialog *dialog;
+
+  dialog = FLASHBACK_POLKIT_DIALOG (object);
+
+  switch (property_id)
+    {
+      case PROP_ACTION_ID:
+        g_value_set_string (value, dialog->action_id);
+        break;
+
+      case PROP_VENDOR:
+        g_value_set_string (value, dialog->vendor);
+        break;
+
+      case PROP_VENDOR_URL:
+        g_value_set_string (value, dialog->vendor_url);
+        break;
+
+      case PROP_ICON_NAME:
+        g_value_set_string (value, dialog->icon_name);
+        break;
+
+      case PROP_MESSAGE:
+        g_value_set_string (value, dialog->message);
+        break;
+
+      case PROP_DETAILS:
+        g_value_set_object (value, dialog->details);
+        break;
+
+      case PROP_USERS:
+        g_value_set_boxed (value, dialog->users);
+        break;
+
+      case PROP_SELECTED_USER:
+        g_value_set_string (value, dialog->selected_user);
+        break;
+
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+    }
+}
+
+static void
+flashback_polkit_dialog_set_property (GObject      *object,
+                                      guint         property_id,
+                                      const GValue *value,
+                                      GParamSpec   *pspec)
+{
+  FlashbackPolkitDialog *dialog;
+
+  dialog = FLASHBACK_POLKIT_DIALOG (object);
+
+  switch (property_id)
+    {
+      case PROP_ACTION_ID:
+        dialog->action_id = g_value_dup_string (value);
+        break;
+
+      case PROP_VENDOR:
+        dialog->vendor = g_value_dup_string (value);
+        break;
+
+      case PROP_VENDOR_URL:
+        dialog->vendor_url = g_value_dup_string (value);
+        break;
+
+      case PROP_ICON_NAME:
+        dialog->icon_name = g_value_dup_string (value);
+        break;
+
+      case PROP_MESSAGE:
+        dialog->message = g_value_dup_string (value);
+        break;
+
+      case PROP_DETAILS:
+        dialog->details = g_value_dup_object (value);
+        break;
+
+      case PROP_USERS:
+        dialog->users = g_value_dup_boxed (value);
+        break;
+
+      case PROP_SELECTED_USER:
+        break;
+
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+    }
+}
+
+static void
+flashback_polkit_dialog_dispose (GObject *object)
+{
+  FlashbackPolkitDialog *dialog;
+
+  dialog = FLASHBACK_POLKIT_DIALOG (object);
+
+  g_clear_object (&dialog->details);
+  g_clear_object (&dialog->users_store);
+
+  G_OBJECT_CLASS (flashback_polkit_dialog_parent_class)->dispose (object);
+}
+
+static void
+flashback_polkit_dialog_finalize (GObject *object)
+{
+  FlashbackPolkitDialog *dialog;
+
+  dialog = FLASHBACK_POLKIT_DIALOG (object);
+
+  g_free (dialog->action_id);
+  g_free (dialog->vendor);
+  g_free (dialog->vendor_url);
+  g_free (dialog->icon_name);
+  g_free (dialog->message);
+
+  g_strfreev (dialog->users);
+
+  g_free (dialog->selected_user);
+
+  G_OBJECT_CLASS (flashback_polkit_dialog_parent_class)->finalize (object);
+}
+
+static void
+flashback_polkit_dialog_class_init (FlashbackPolkitDialogClass *dialog_class)
+{
+  GObjectClass *object_class;
+  GtkWidgetClass *widget_class;
+  GtkBindingSet *binding_set;
+  const gchar *resource;
+
+  object_class = G_OBJECT_CLASS (dialog_class);
+  widget_class = GTK_WIDGET_CLASS (dialog_class);
+
+  object_class->constructed = flashback_polkit_dialog_constructed;
+  object_class->get_property = flashback_polkit_dialog_get_property;
+  object_class->set_property = flashback_polkit_dialog_set_property;
+  object_class->dispose = flashback_polkit_dialog_dispose;
+  object_class->finalize = flashback_polkit_dialog_finalize;
+
+  /**
+   * FlashbackPolkitDialog:action-id
+   */
+  properties[PROP_ACTION_ID] =
+    g_param_spec_string ("action-id", "action-id", "action-id",
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS);
+
+  /**
+   * FlashbackPolkitDialog:vendor
+   */
+  properties[PROP_VENDOR] =
+    g_param_spec_string ("vendor", "vendor", "vendor",
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS);
+
+  /**
+   * FlashbackPolkitDialog:vendor-url
+   */
+  properties[PROP_VENDOR_URL] =
+    g_param_spec_string ("vendor-url", "vendor-url", "vendor-url",
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS);
+
+  /**
+   * FlashbackPolkitDialog:icon-name
+   */
+  properties[PROP_ICON_NAME] =
+    g_param_spec_string ("icon-name", "icon-name", "icon-name",
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS);
+
+  /**
+   * FlashbackPolkitDialog:message
+   */
+  properties[PROP_MESSAGE] =
+    g_param_spec_string ("message", "message", "message",
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS);
+
+  /**
+   * FlashbackPolkitDialog:details
+   */
+  properties[PROP_DETAILS] =
+    g_param_spec_object ("details", "details", "details",
+                         POLKIT_TYPE_DETAILS,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS);
+
+  /**
+   * FlashbackPolkitDialog:users
+   */
+  properties[PROP_USERS] =
+    g_param_spec_boxed ("users", "users", "users",
+                        G_TYPE_STRV,
+                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                        G_PARAM_STATIC_STRINGS);
+
+  /**
+   * FlashbackPolkitDialog:selected-user
+   */
+  properties[PROP_SELECTED_USER] =
+    g_param_spec_string ("selected-user", "selected-user", "selected-user",
+                         NULL,
+                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (object_class, LAST_PROP, properties);
+
+  /**
+   * FlashbackPolkitDialog::close
+   */
+  signals[SIGNAL_CLOSE] =
+    g_signal_new ("close",
+                  G_OBJECT_CLASS_TYPE (dialog_class),
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                  0, NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+
+  binding_set = gtk_binding_set_by_class (dialog_class);
+  gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, "close", 0);
+
+  resource = "/org/gnome/gnome-flashback/flashback-polkit-dialog.ui";
+  gtk_widget_class_set_template_from_resource (widget_class, resource);
+
+  gtk_widget_class_bind_template_child (widget_class, FlashbackPolkitDialog, image);
+  gtk_widget_class_bind_template_child (widget_class, FlashbackPolkitDialog, main_message_label);
+  gtk_widget_class_bind_template_child (widget_class, FlashbackPolkitDialog, secondary_message_label);
+  gtk_widget_class_bind_template_child (widget_class, FlashbackPolkitDialog, users_combobox);
+  gtk_widget_class_bind_template_child (widget_class, FlashbackPolkitDialog, entry_box);
+  gtk_widget_class_bind_template_child (widget_class, FlashbackPolkitDialog, prompt_label);
+  gtk_widget_class_bind_template_child (widget_class, FlashbackPolkitDialog, password_entry);
+  gtk_widget_class_bind_template_child (widget_class, FlashbackPolkitDialog, info_label);
+  gtk_widget_class_bind_template_child (widget_class, FlashbackPolkitDialog, details_grid);
+  gtk_widget_class_bind_template_child (widget_class, FlashbackPolkitDialog, auth_button);
+
+  gtk_widget_class_bind_template_callback (widget_class, cancel_button_clicked_cb);
+  gtk_widget_class_bind_template_callback (widget_class, auth_button_clicked_cb);
+}
+
+static void
+flashback_polkit_dialog_close (FlashbackPolkitDialog *dialog,
+                               gpointer               user_data)
+{
+  dialog->response = GTK_RESPONSE_CANCEL;
+
+  if (dialog->is_running == TRUE)
+    gtk_main_quit ();
+
+  gtk_window_close (GTK_WINDOW (dialog));
+}
+
+static void
+flashback_polkit_dialog_init (FlashbackPolkitDialog *dialog)
+{
+  GtkWindow *window;
+  GtkWidget *widget;
+
+  widget = GTK_WIDGET (dialog);
+  window = GTK_WINDOW (dialog);
+
+  gtk_widget_init_template (widget);
+
+  gtk_window_set_keep_above (window, TRUE);
+
+  g_signal_connect (dialog, "close",
+                    G_CALLBACK (flashback_polkit_dialog_close),
+                    NULL);
+}
+
+/**
+ * flashback_polkit_dialog_new:
+ * @action_id:
+ * @vendor:
+ * @vendor_url:
+ * @icon_name:
+ * @message_markup:
+ * @details:
+ * @users:
+ *
+ * Creates a new #FlashbackPolkitDialog.
+ *
+ * Returns: (transfer full): a newly created #FlashbackPolkitDialog.
+ **/
+GtkWidget *
+flashback_polkit_dialog_new (const gchar    *action_id,
+                             const gchar    *vendor,
+                             const gchar    *vendor_url,
+                             const gchar    *icon_name,
+                             const gchar    *message_markup,
+                             PolkitDetails  *details,
+                             gchar         **users)
+{
+  return g_object_new (FLASHBACK_TYPE_POLKIT_DIALOG,
+                       "action-id", action_id,
+                       "vendor", vendor,
+                       "vendor-url", vendor_url,
+                       "icon-name", icon_name,
+                       "message", message_markup,
+                       "details", details,
+                       "users", users,
+                       NULL);
+}
+
+/**
+ * flashback_polkit_dialog_get_selected_user:
+ * @dialog: a #FlashbackPolkitDialog
+ *
+ * Gets the currently selected user.
+ *
+ * Returns: The currently selected user (free with g_free()) or %NULL if no
+ *     user is currently selected.
+ **/
+gchar *
+flashback_polkit_dialog_get_selected_user (FlashbackPolkitDialog *dialog)
+{
+  return g_strdup (dialog->selected_user);
+}
+
+/**
+ * flashback_polkit_dialog_run_until_user_is_selected:
+ * @dialog: a #FlashbackPolkitDialog
+ *
+ * Runs @dialog in a recursive main loop until a user have been selected.
+ *
+ * If there is only one element in the the users array (which is set upon
+ * construction) or an user has already been selected, this function returns
+ * immediately with the return value %TRUE.
+ *
+ * Returns: %TRUE if a user is selected or %FALSE if the dialog was cancelled.
+ **/
+gboolean
+flashback_polkit_dialog_run_until_user_is_selected (FlashbackPolkitDialog *dialog)
+{
+  if (dialog->selected_user != NULL)
+    return TRUE;
+
+  dialog->is_running = TRUE;
+
+  gtk_main ();
+
+  dialog->is_running = FALSE;
+
+  if (dialog->response == RESPONSE_USER_SELECTED)
+    return TRUE;
+
+  return FALSE;
+}
+
+/**
+ * flashback_polkit_dialog_run_until_response_for_prompt:
+ * @dialog: a #FlashbackPolkitDialog
+ * @prompt: the prompt to present the user with
+ * @echo_chars: whether characters should be echoed in the password entry box
+ * @was_cancelled: set to %TRUE if the dialog was cancelled
+ * @new_user_selected: set to %TRUE if another user was selected
+ *
+ * Runs @dialog in a recursive main loop until a response to @prompt have been
+ * obtained from the user.
+ *
+ * Returns: The response (free with g_free()) or %NULL if one of
+ *     @was_cancelled or @new_user_selected has been set to %TRUE.
+ **/
+gchar *
+flashback_polkit_dialog_run_until_response_for_prompt (FlashbackPolkitDialog *dialog,
+                                                       const gchar           *prompt,
+                                                       gboolean               echo_chars,
+                                                       gboolean              *was_cancelled,
+                                                       gboolean              *new_user_selected)
+{
+  gtk_label_set_text_with_mnemonic (GTK_LABEL (dialog->prompt_label), prompt);
+  gtk_entry_set_visibility (GTK_ENTRY (dialog->password_entry), echo_chars);
+  gtk_entry_set_text (GTK_ENTRY (dialog->password_entry), "");
+  gtk_widget_grab_focus (dialog->password_entry);
+
+  if (was_cancelled != NULL)
+    *was_cancelled = FALSE;
+
+  if (new_user_selected != NULL)
+    *new_user_selected = FALSE;
+
+  dialog->is_running = TRUE;
+
+  gtk_widget_set_no_show_all (dialog->entry_box, FALSE);
+  gtk_widget_show_all (dialog->entry_box);
+
+  gtk_main ();
+
+  gtk_widget_hide (dialog->entry_box);
+  gtk_widget_set_no_show_all (dialog->entry_box, TRUE);
+
+  dialog->is_running = FALSE;
+
+  if (dialog->response == GTK_RESPONSE_OK)
+    {
+      return g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->password_entry)));
+    }
+  else if (dialog->response == RESPONSE_USER_SELECTED)
+    {
+      if (new_user_selected != NULL)
+        *new_user_selected = TRUE;
+    }
+  else
+    {
+      if (was_cancelled != NULL)
+        *was_cancelled = TRUE;
+    }
+
+  return NULL;
+}
+
+/**
+ * flashback_polkit_dialog_cancel:
+ * @dialog: a #FlashbackPolkitDialog
+ *
+ * Cancels the dialog if it is currenlty running.
+ *
+ * Returns: %TRUE if the dialog was running.
+ **/
+gboolean
+flashback_polkit_dialog_cancel (FlashbackPolkitDialog *dialog)
+{
+  if (!dialog->is_running)
+    return FALSE;
+
+  dialog->response = GTK_RESPONSE_CANCEL;
+  gtk_main_quit ();
+
+  return TRUE;
+}
+
+/**
+ * flashback_polkit_dialog_indicate_error:
+ * @dialog: a #FlashbackPolkitDialog
+ *
+ * Call this function to indicate an authentication error; typically shakes
+ * the window.
+ **/
+void
+flashback_polkit_dialog_indicate_error (FlashbackPolkitDialog *dialog)
+{
+  GtkWindow *window;
+  gint x;
+  gint y;
+  gint n;
+
+  window = GTK_WINDOW (dialog);
+
+  gtk_window_get_position (window, &x, &y);
+
+  for (n = 0; n < 10; n++)
+    {
+      gtk_window_move (window, x + (n % 2 == 0 ? -15 : 15), y);
+
+      while (gtk_events_pending ())
+        gtk_main_iteration ();
+
+      g_usleep (10000);
+    }
+
+  gtk_window_move (window, x, y);
+}
+
+void
+flashback_polkit_dialog_set_info_message (FlashbackPolkitDialog *dialog,
+                                          const gchar           *info_markup)
+{
+  gtk_label_set_markup (GTK_LABEL (dialog->info_label), info_markup);
+}
diff --git a/gnome-flashback/libpolkit/flashback-polkit-dialog.h 
b/gnome-flashback/libpolkit/flashback-polkit-dialog.h
new file mode 100644
index 0000000..1a4c87b
--- /dev/null
+++ b/gnome-flashback/libpolkit/flashback-polkit-dialog.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2015 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *     Alberts Muktupāvels <alberts muktupavels gmail com>
+ *     David Zeuthen <davidz redhat com>
+ */
+
+#ifndef FLASHBACK_POLKIT_DIALOG_H
+#define FLASHBACK_POLKIT_DIALOG_H
+
+#include <gtk/gtk.h>
+#include <polkit/polkit.h>
+
+G_BEGIN_DECLS
+
+#define FLASHBACK_TYPE_POLKIT_DIALOG flashback_polkit_dialog_get_type ()
+G_DECLARE_FINAL_TYPE (FlashbackPolkitDialog, flashback_polkit_dialog,
+                      FLASHBACK, POLKIT_DIALOG, GtkWindow)
+
+GtkWidget *flashback_polkit_dialog_new                           (const gchar            *action_id,
+                                                                  const gchar            *vendor,
+                                                                  const gchar            *vendor_url,
+                                                                  const gchar            *icon_name,
+                                                                  const gchar            *message_markup,
+                                                                  PolkitDetails          *details,
+                                                                  gchar                 **users);
+
+gchar     *flashback_polkit_dialog_get_selected_user             (FlashbackPolkitDialog  *dialog);
+
+gboolean   flashback_polkit_dialog_run_until_user_is_selected    (FlashbackPolkitDialog  *dialog);
+gchar     *flashback_polkit_dialog_run_until_response_for_prompt (FlashbackPolkitDialog  *dialog,
+                                                                  const gchar            *prompt,
+                                                                  gboolean                echo_chars,
+                                                                  gboolean               *was_cancelled,
+                                                                  gboolean               *new_user_selected);
+
+gboolean   flashback_polkit_dialog_cancel                        (FlashbackPolkitDialog  *dialog);
+void       flashback_polkit_dialog_indicate_error                (FlashbackPolkitDialog  *dialog);
+void       flashback_polkit_dialog_set_info_message              (FlashbackPolkitDialog  *dialog,
+                                                                  const gchar            *info_markup);
+
+G_END_DECLS
+
+#endif
diff --git a/gnome-flashback/libpolkit/flashback-polkit-dialog.ui 
b/gnome-flashback/libpolkit/flashback-polkit-dialog.ui
new file mode 100644
index 0000000..036ffbe
--- /dev/null
+++ b/gnome-flashback/libpolkit/flashback-polkit-dialog.ui
@@ -0,0 +1,253 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.19.0 -->
+<interface>
+  <requires lib="gtk+" version="3.16"/>
+  <template class="FlashbackPolkitDialog" parent="GtkWindow">
+    <property name="can_focus">False</property>
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">Authenticate</property>
+    <property name="resizable">False</property>
+    <property name="modal">True</property>
+    <property name="window_position">center</property>
+    <property name="icon_name">dialog-password</property>
+    <property name="type_hint">dialog</property>
+    <child>
+      <object class="GtkBox" id="dialog_box">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="border_width">2</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child>
+          <object class="GtkBox" id="content_box">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="border_width">5</property>
+            <property name="spacing">12</property>
+            <child>
+              <object class="GtkImage" id="image">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">center</property>
+                <property name="valign">start</property>
+                <property name="pixel_size">48</property>
+                <property name="icon_name">dialog-password</property>
+                <property name="icon_size">6</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox" id="main_box">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">10</property>
+                <child>
+                  <object class="GtkLabel" id="main_message_label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" 
translatable="yes">&lt;big&gt;&lt;b&gt;Title&lt;/b&gt;&lt;/big&gt;</property>
+                    <property name="use_markup">True</property>
+                    <property name="wrap">True</property>
+                    <property name="max_width_chars">70</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="secondary_message_label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">An application is attempting to perform an 
action that requires privileges. Authentication is required to perform this action.</property>
+                    <property name="use_markup">True</property>
+                    <property name="wrap">True</property>
+                    <property name="max_width_chars">70</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkComboBox" id="users_combobox">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkBox" id="entry_box">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="spacing">6</property>
+                    <child>
+                      <object class="GtkLabel" id="prompt_label">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label" translatable="yes">_Password:</property>
+                        <property name="use_underline">True</property>
+                        <property name="mnemonic_widget">password_entry</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="password_entry">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="activates_default">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="info_label">
+                    <property name="can_focus">False</property>
+                    <property name="use_markup">True</property>
+                    <property name="wrap">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">4</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkExpander" id="details_expander">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <child>
+              <object class="GtkBox" id="details_box">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">10</property>
+                <child>
+                  <object class="GtkGrid" id="details_grid">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="row_spacing">6</property>
+                    <property name="column_spacing">12</property>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+            <child type="label">
+              <object class="GtkLabel" id="expander_label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" 
translatable="yes">&lt;small&gt;&lt;b&gt;_Details&lt;/b&gt;&lt;/small&gt;</property>
+                <property name="use_markup">True</property>
+                <property name="use_underline">True</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButtonBox" id="action_box">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="border_width">5</property>
+            <property name="spacing">6</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="cancel_button">
+                <property name="label" translatable="yes">_Cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_underline">True</property>
+                <signal name="clicked" handler="cancel_button_clicked_cb" swapped="no"/>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="auth_button">
+                <property name="label" translatable="yes">_Authenticate</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="has_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_underline">True</property>
+                <signal name="clicked" handler="auth_button_clicked_cb" swapped="no"/>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/gnome-flashback/libpolkit/flashback-polkit.c b/gnome-flashback/libpolkit/flashback-polkit.c
new file mode 100644
index 0000000..654ce23
--- /dev/null
+++ b/gnome-flashback/libpolkit/flashback-polkit.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2015 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "flashback-polkit.h"
+#include "flashback-listener.h"
+
+struct _FlashbackPolkit
+{
+  GObject              parent;
+
+  PolkitSubject       *session;
+  PolkitAgentListener *listener;
+
+  gpointer             handle;
+};
+
+G_DEFINE_TYPE (FlashbackPolkit, flashback_polkit, G_TYPE_OBJECT)
+
+static void
+unix_session_cb (GObject      *source_object,
+                 GAsyncResult *res,
+                 gpointer      user_data)
+{
+  FlashbackPolkit *polkit;
+  GError *error;
+
+  polkit = FLASHBACK_POLKIT (user_data);
+
+  error = NULL;
+  polkit->session = polkit_unix_session_new_for_process_finish (res, &error);
+
+  if (error != NULL)
+    {
+      g_warning ("Unable to determine the session we are in: %s", error->message);
+      g_error_free (error);
+
+      return;
+    }
+
+  polkit->listener = flashback_listener_new ();
+
+  error = NULL;
+  polkit->handle = polkit_agent_listener_register (polkit->listener,
+                                                   POLKIT_AGENT_REGISTER_FLAGS_NONE,
+                                                   polkit->session,
+                                                   "/org/gnome/PolicyKit1/AuthenticationAgent",
+                                                   NULL, /* GCancellable */
+                                                   &error);
+
+  if (error != NULL)
+    {
+      g_warning ("Cannot register authentication agent: %s", error->message);
+      g_error_free (error);
+
+      return;
+    }
+}
+
+static void
+flashback_polkit_dispose (GObject *object)
+{
+  FlashbackPolkit *polkit;
+
+  polkit = FLASHBACK_POLKIT (object);
+
+  if (polkit->handle != NULL)
+    {
+      polkit_agent_listener_unregister (polkit->handle);
+      polkit->handle = NULL;
+    }
+
+  g_clear_object (&polkit->session);
+  g_clear_object (&polkit->listener);
+
+  G_OBJECT_CLASS (flashback_polkit_parent_class)->dispose (object);
+}
+
+static void
+flashback_polkit_class_init (FlashbackPolkitClass *polkit_class)
+{
+  GObjectClass *object_class;
+
+  object_class = G_OBJECT_CLASS (polkit_class);
+
+  object_class->dispose = flashback_polkit_dispose;
+}
+
+static void
+flashback_polkit_init (FlashbackPolkit *polkit)
+{
+  polkit_unix_session_new_for_process (getpid (), NULL, unix_session_cb, polkit);
+}
+
+FlashbackPolkit *
+flashback_polkit_new (void)
+{
+  return g_object_new (FLASHBACK_TYPE_POLKIT, NULL);
+}
diff --git a/gnome-flashback/libpolkit/flashback-polkit.h b/gnome-flashback/libpolkit/flashback-polkit.h
new file mode 100644
index 0000000..01d01c3
--- /dev/null
+++ b/gnome-flashback/libpolkit/flashback-polkit.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef FLASHBACK_POLKIT_H
+#define FLASHBACK_POLKIT_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define FLASHBACK_TYPE_POLKIT flashback_polkit_get_type ()
+G_DECLARE_FINAL_TYPE (FlashbackPolkit, flashback_polkit,
+                      FLASHBACK, POLKIT, GObject)
+
+FlashbackPolkit *flashback_polkit_new (void);
+
+G_END_DECLS
+
+#endif
diff --git a/po/POTFILES.in b/po/POTFILES.in
index c0a9703..320716b 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -15,6 +15,10 @@ gnome-flashback/libdisplay-config/flashback-display-config.c
 [type: gettext/glade]gnome-flashback/libdisplay-config/flashback-confirm-dialog.ui
 gnome-flashback/libend-session-dialog/flashback-inhibit-dialog.c
 [type: gettext/glade]gnome-flashback/libend-session-dialog/flashback-inhibit-dialog.ui
+gnome-flashback/libpolkit/flashback-authenticator.c
+gnome-flashback/libpolkit/flashback-listener.c
+gnome-flashback/libpolkit/flashback-polkit-dialog.c
+[type: gettext/glade]gnome-flashback/libpolkit/flashback-polkit-dialog.ui
 gnome-flashback/libsound-applet/gvc-applet.c
 gnome-flashback/libsound-applet/gvc-channel-bar.c
 gnome-flashback/libsound-applet/gvc/gvc-mixer-control.c


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]