[gnome-software/wip/kalev/flatpak-auth-support: 63/65] Add basic auth support to flatpak plugin



commit f52394e809ab8a52b5ea47fb6e9b83895aca95b7
Author: Kalev Lember <klember redhat com>
Date:   Mon May 18 14:45:35 2020 +0200

    Add basic auth support to flatpak plugin
    
    This is useful for e.g. OCI remotes that can use basic auth.
    
    All user visible strings in the basic auth dialog are taken from the
    flatpak CLI client.

 lib/gs-plugin-loader.c              |  29 +++++-
 lib/gs-plugin-loader.h              |   7 +-
 lib/gs-plugin.c                     |  68 +++++++++++-
 lib/gs-plugin.h                     |  13 ++-
 plugins/flatpak/gs-plugin-flatpak.c |  55 +++++++++-
 po/POTFILES.in                      |   2 +
 src/gnome-software.gresource.xml    |   1 +
 src/gs-basic-auth-dialog.c          | 130 +++++++++++++++++++++++
 src/gs-basic-auth-dialog.h          |  28 +++++
 src/gs-basic-auth-dialog.ui         | 203 ++++++++++++++++++++++++++++++++++++
 src/gs-shell.c                      |  25 ++++-
 src/meson.build                     |   1 +
 12 files changed, 556 insertions(+), 6 deletions(-)
---
diff --git a/lib/gs-plugin-loader.c b/lib/gs-plugin-loader.c
index d7de62c8..dbe35aca 100644
--- a/lib/gs-plugin-loader.c
+++ b/lib/gs-plugin-loader.c
@@ -1,7 +1,7 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  *
  * Copyright (C) 2007-2018 Richard Hughes <richard hughsie com>
- * Copyright (C) 2014-2018 Kalev Lember <klember redhat com>
+ * Copyright (C) 2014-2020 Kalev Lember <klember redhat com>
  *
  * SPDX-License-Identifier: GPL-2.0+
  */
@@ -74,6 +74,7 @@ enum {
        SIGNAL_PENDING_APPS_CHANGED,
        SIGNAL_UPDATES_CHANGED,
        SIGNAL_RELOAD,
+       SIGNAL_BASIC_AUTH_START,
        SIGNAL_LAST
 };
 
@@ -2016,6 +2017,23 @@ gs_plugin_loader_status_changed_cb (GsPlugin *plugin,
                       0, app, status);
 }
 
+static void
+gs_plugin_loader_basic_auth_start_cb (GsPlugin *plugin,
+                                      const gchar *remote,
+                                      const gchar *realm,
+                                      GCallback callback,
+                                      gpointer user_data,
+                                      GsPluginLoader *plugin_loader)
+{
+       g_debug ("emitting basic-auth-start %s", realm);
+       g_signal_emit (plugin_loader,
+                      signals[SIGNAL_BASIC_AUTH_START], 0,
+                      remote,
+                      realm,
+                      callback,
+                      user_data);
+}
+
 static gboolean
 gs_plugin_loader_job_actions_changed_delay_cb (gpointer user_data)
 {
@@ -2102,6 +2120,9 @@ gs_plugin_loader_open_plugin (GsPluginLoader *plugin_loader,
        g_signal_connect (plugin, "status-changed",
                          G_CALLBACK (gs_plugin_loader_status_changed_cb),
                          plugin_loader);
+       g_signal_connect (plugin, "basic-auth-start",
+                         G_CALLBACK (gs_plugin_loader_basic_auth_start_cb),
+                         plugin_loader);
        g_signal_connect (plugin, "report-event",
                          G_CALLBACK (gs_plugin_loader_report_event_cb),
                          plugin_loader);
@@ -2712,6 +2733,12 @@ gs_plugin_loader_class_init (GsPluginLoaderClass *klass)
                              G_STRUCT_OFFSET (GsPluginLoaderClass, reload),
                              NULL, NULL, g_cclosure_marshal_VOID__VOID,
                              G_TYPE_NONE, 0);
+       signals [SIGNAL_BASIC_AUTH_START] =
+               g_signal_new ("basic-auth-start",
+                             G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
+                             G_STRUCT_OFFSET (GsPluginLoaderClass, basic_auth_start),
+                             NULL, NULL, g_cclosure_marshal_generic,
+                             G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER);
 }
 
 static void
diff --git a/lib/gs-plugin-loader.h b/lib/gs-plugin-loader.h
index 51e27f9b..665eab67 100644
--- a/lib/gs-plugin-loader.h
+++ b/lib/gs-plugin-loader.h
@@ -1,7 +1,7 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  *
  * Copyright (C) 2007-2017 Richard Hughes <richard hughsie com>
- * Copyright (C) 2015 Kalev Lember <klember redhat com>
+ * Copyright (C) 2015-2020 Kalev Lember <klember redhat com>
  *
  * SPDX-License-Identifier: GPL-2.0+
  */
@@ -31,6 +31,11 @@ struct _GsPluginLoaderClass
        void                    (*pending_apps_changed) (GsPluginLoader *plugin_loader);
        void                    (*updates_changed)      (GsPluginLoader *plugin_loader);
        void                    (*reload)               (GsPluginLoader *plugin_loader);
+       void                    (*basic_auth_start)     (GsPluginLoader *plugin_loader,
+                                                        const gchar    *remote,
+                                                        const gchar    *realm,
+                                                        GCallback       callback,
+                                                        gpointer        user_data);
 };
 
 GsPluginLoader *gs_plugin_loader_new                   (void);
diff --git a/lib/gs-plugin.c b/lib/gs-plugin.c
index 5aed1058..3f63fa97 100644
--- a/lib/gs-plugin.c
+++ b/lib/gs-plugin.c
@@ -1,7 +1,7 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  *
  * Copyright (C) 2013-2016 Richard Hughes <richard hughsie com>
- * Copyright (C) 2014-2018 Kalev Lember <klember redhat com>
+ * Copyright (C) 2014-2020 Kalev Lember <klember redhat com>
  *
  * SPDX-License-Identifier: GPL-2.0+
  */
@@ -87,6 +87,7 @@ enum {
        SIGNAL_RELOAD,
        SIGNAL_REPORT_EVENT,
        SIGNAL_ALLOW_UPDATES,
+       SIGNAL_BASIC_AUTH_START,
        SIGNAL_LAST
 };
 
@@ -851,6 +852,64 @@ gs_plugin_status_update (GsPlugin *plugin, GsApp *app, GsPluginStatus status)
        g_source_attach (idle_source, NULL);
 }
 
+typedef struct {
+       GsPlugin        *plugin;
+       gchar           *remote;
+       gchar           *realm;
+       GCallback        callback;
+       gpointer         user_data;
+} GsPluginBasicAuthHelper;
+
+static gboolean
+gs_plugin_basic_auth_start_cb (gpointer user_data)
+{
+       GsPluginBasicAuthHelper *helper = user_data;
+       g_signal_emit (helper->plugin,
+                      signals[SIGNAL_BASIC_AUTH_START], 0,
+                      helper->remote,
+                      helper->realm,
+                      helper->callback,
+                      helper->user_data);
+       g_free (helper->remote);
+       g_free (helper->realm);
+       g_slice_free (GsPluginBasicAuthHelper, helper);
+       return FALSE;
+}
+
+/**
+ * gs_plugin_basic_auth_start:
+ * @plugin: a #GsPlugin
+ * @remote: a string
+ * @realm: a string
+ * @callback: callback to invoke to submit the user/password
+ * @user_data: callback data to pass to the callback
+ *
+ * Emit the basic-auth-start signal in the main thread.
+ *
+ * Since: 3.38
+ **/
+void
+gs_plugin_basic_auth_start (GsPlugin *plugin,
+                            const gchar *remote,
+                            const gchar *realm,
+                            GCallback callback,
+                            gpointer user_data)
+{
+       GsPluginBasicAuthHelper *helper;
+       g_autoptr(GSource) idle_source = NULL;
+
+       helper = g_slice_new0 (GsPluginBasicAuthHelper);
+       helper->plugin = plugin;
+       helper->remote = g_strdup (remote);
+       helper->realm = g_strdup (realm);
+       helper->callback = callback;
+       helper->user_data = user_data;
+
+       idle_source = g_idle_source_new ();
+       g_source_set_callback (idle_source, gs_plugin_basic_auth_start_cb, helper, NULL);
+       g_source_attach (idle_source, NULL);
+}
+
 static gboolean
 gs_plugin_app_launch_cb (gpointer user_data)
 {
@@ -1959,6 +2018,13 @@ gs_plugin_class_init (GsPluginClass *klass)
                              G_STRUCT_OFFSET (GsPluginClass, allow_updates),
                              NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN,
                              G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+
+       signals [SIGNAL_BASIC_AUTH_START] =
+               g_signal_new ("basic-auth-start",
+                             G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
+                             G_STRUCT_OFFSET (GsPluginClass, basic_auth_start),
+                             NULL, NULL, g_cclosure_marshal_generic,
+                             G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER);
 }
 
 static void
diff --git a/lib/gs-plugin.h b/lib/gs-plugin.h
index 7dd2d864..d07afd3b 100644
--- a/lib/gs-plugin.h
+++ b/lib/gs-plugin.h
@@ -1,6 +1,7 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  *
  * Copyright (C) 2012-2016 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2020 Kalev Lember <klember redhat com>
  *
  * SPDX-License-Identifier: GPL-2.0+
  */
@@ -37,7 +38,12 @@ struct _GsPluginClass
                                                         GsPluginEvent  *event);
        void                    (*allow_updates)        (GsPlugin       *plugin,
                                                         gboolean        allow_updates);
-       gpointer                 padding[26];
+       void                    (*basic_auth_start)     (GsPlugin       *plugin,
+                                                        const gchar    *remote,
+                                                        const gchar    *realm,
+                                                        GCallback       callback,
+                                                        gpointer        user_data);
+       gpointer                 padding[25];
 };
 
 typedef struct GsPluginData    GsPluginData;
@@ -116,5 +122,10 @@ void                gs_plugin_report_event                 (GsPlugin       *plugin,
 void            gs_plugin_set_allow_updates            (GsPlugin       *plugin,
                                                         gboolean        allow_updates);
 gboolean        gs_plugin_get_network_available        (GsPlugin       *plugin);
+void            gs_plugin_basic_auth_start             (GsPlugin       *plugin,
+                                                        const gchar    *remote,
+                                                        const gchar    *realm,
+                                                        GCallback       callback,
+                                                        gpointer        user_data);
 
 G_END_DECLS
diff --git a/plugins/flatpak/gs-plugin-flatpak.c b/plugins/flatpak/gs-plugin-flatpak.c
index ae026d5d..56a3367b 100644
--- a/plugins/flatpak/gs-plugin-flatpak.c
+++ b/plugins/flatpak/gs-plugin-flatpak.c
@@ -2,7 +2,7 @@
  *
  * Copyright (C) 2016 Joaquim Rocha <jrocha endlessm com>
  * Copyright (C) 2016-2018 Richard Hughes <richard hughsie com>
- * Copyright (C) 2017-2018 Kalev Lember <klember redhat com>
+ * Copyright (C) 2017-2020 Kalev Lember <klember redhat com>
  *
  * SPDX-License-Identifier: GPL-2.0+
  */
@@ -456,6 +456,55 @@ _group_apps_by_installation (GsPlugin *plugin,
        return g_steal_pointer (&applist_by_flatpaks);
 }
 
+#if FLATPAK_CHECK_VERSION(1,6,0)
+typedef struct {
+       FlatpakTransaction *transaction;
+       guint id;
+} BasicAuthData;
+
+static void
+basic_auth_data_free (BasicAuthData *data)
+{
+       g_object_unref (data->transaction);
+       g_slice_free (BasicAuthData, data);
+}
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(BasicAuthData, basic_auth_data_free)
+
+static void
+_basic_auth_cb (const gchar *user, const gchar *password, gpointer user_data)
+{
+       g_autoptr(BasicAuthData) data = user_data;
+
+       g_debug ("Submitting basic auth data");
+
+       /* NULL user aborts the basic auth request */
+       flatpak_transaction_complete_basic_auth (data->transaction, data->id, user, password, NULL /* options 
*/);
+}
+
+static gboolean
+_basic_auth_start (FlatpakTransaction *transaction,
+                   const char *remote,
+                   const char *realm,
+                   GVariant *options,
+                   guint id,
+                   GsPlugin *plugin)
+{
+       BasicAuthData *data;
+
+       if (!gs_plugin_has_flags (plugin, GS_PLUGIN_FLAGS_INTERACTIVE))
+               return FALSE;
+
+       data = g_slice_new0 (BasicAuthData);
+       data->transaction = g_object_ref (transaction);
+       data->id = id;
+
+       g_debug ("Login required remote %s (realm %s)\n", remote, realm);
+       gs_plugin_basic_auth_start (plugin, remote, realm, G_CALLBACK (_basic_auth_cb), data);
+       return TRUE;
+}
+#endif
+
 static FlatpakTransaction *
 _build_transaction (GsPlugin *plugin, GsFlatpak *flatpak,
                    GCancellable *cancellable, GError **error)
@@ -503,6 +552,10 @@ _build_transaction (GsPlugin *plugin, GsFlatpak *flatpak,
        /* connect up signals */
        g_signal_connect (transaction, "ref-to-app",
                          G_CALLBACK (_ref_to_app), plugin);
+#if FLATPAK_CHECK_VERSION(1,6,0)
+       g_signal_connect (transaction, "basic-auth-start",
+                         G_CALLBACK (_basic_auth_start), plugin);
+#endif
 
        /* use system installations as dependency sources for user installations */
        flatpak_transaction_add_default_dependency_sources (transaction);
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 20721c4a..a44a6ad3 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -10,6 +10,8 @@ src/gs-app-row.c
 src/gs-app-row.ui
 src/gs-app-tile.c
 src/gs-app-tile.ui
+src/gs-basic-auth-dialog.c
+src/gs-basic-auth-dialog.ui
 lib/gs-category.c
 src/gs-category-page.c
 src/gs-category-page.ui
diff --git a/src/gnome-software.gresource.xml b/src/gnome-software.gresource.xml
index 3eaabca2..459ecf82 100644
--- a/src/gnome-software.gresource.xml
+++ b/src/gnome-software.gresource.xml
@@ -4,6 +4,7 @@
   <file preprocess="xml-stripblanks">gnome-software.ui</file>
   <file preprocess="xml-stripblanks">gs-app-addon-row.ui</file>
   <file preprocess="xml-stripblanks">gs-app-row.ui</file>
+  <file preprocess="xml-stripblanks">gs-basic-auth-dialog.ui</file>
   <file preprocess="xml-stripblanks">gs-category-page.ui</file>
   <file preprocess="xml-stripblanks">gs-category-tile.ui</file>
   <file preprocess="xml-stripblanks">gs-details-page.ui</file>
diff --git a/src/gs-basic-auth-dialog.c b/src/gs-basic-auth-dialog.c
new file mode 100644
index 00000000..c690a327
--- /dev/null
+++ b/src/gs-basic-auth-dialog.c
@@ -0,0 +1,130 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2020 Kalev Lember <klember redhat com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include "config.h"
+
+#include "gs-basic-auth-dialog.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+struct _GsBasicAuthDialog
+{
+       GtkDialog                parent_instance;
+
+       GsBasicAuthCallback      callback;
+       gpointer                 callback_data;
+
+       /* template widgets */
+       GtkButton               *login_button;
+       GtkLabel                *description_label;
+       GtkEntry                *user_entry;
+       GtkEntry                *password_entry;
+};
+
+G_DEFINE_TYPE (GsBasicAuthDialog, gs_basic_auth_dialog, GTK_TYPE_DIALOG)
+
+static void
+cancel_button_clicked_cb (GsBasicAuthDialog *dialog)
+{
+       /* abort the basic auth request */
+       dialog->callback (NULL, NULL, dialog->callback_data);
+
+       gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
+}
+
+static void
+login_button_clicked_cb (GsBasicAuthDialog *dialog)
+{
+       const gchar *user;
+       const gchar *password;
+
+       user = gtk_entry_get_text (dialog->user_entry);
+       password = gtk_entry_get_text (dialog->password_entry);
+
+       /* submit the user/password to basic auth */
+       dialog->callback (user, password, dialog->callback_data);
+
+       gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
+}
+
+static void
+dialog_validate (GsBasicAuthDialog *dialog)
+{
+       const gchar *user;
+       const gchar *password;
+       gboolean valid_user;
+       gboolean valid_password;
+
+       /* require user */
+       user = gtk_entry_get_text (dialog->user_entry);
+       valid_user = user != NULL && strlen (user) != 0;
+
+       /* require password */
+       password = gtk_entry_get_text (dialog->password_entry);
+       valid_password = password != NULL && strlen (password) != 0;
+
+       gtk_widget_set_sensitive (GTK_WIDGET (dialog->login_button), valid_user && valid_password);
+}
+
+static void
+update_description (GsBasicAuthDialog *dialog, const gchar *remote, const gchar *realm)
+{
+       g_autofree gchar *description = NULL;
+
+       /* TRANSLATORS: This is a description for entering user/password */
+       description = g_strdup_printf (_("Login required remote %s (realm %s)"),
+                                      remote, realm);
+       gtk_label_set_text (dialog->description_label, description);
+}
+
+static void
+gs_basic_auth_dialog_init (GsBasicAuthDialog *dialog)
+{
+       gtk_widget_init_template (GTK_WIDGET (dialog));
+}
+
+static void
+gs_basic_auth_dialog_class_init (GsBasicAuthDialogClass *klass)
+{
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+       gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/Software/gs-basic-auth-dialog.ui");
+
+       gtk_widget_class_bind_template_child (widget_class, GsBasicAuthDialog, login_button);
+       gtk_widget_class_bind_template_child (widget_class, GsBasicAuthDialog, description_label);
+       gtk_widget_class_bind_template_child (widget_class, GsBasicAuthDialog, user_entry);
+       gtk_widget_class_bind_template_child (widget_class, GsBasicAuthDialog, password_entry);
+
+       gtk_widget_class_bind_template_callback (widget_class, dialog_validate);
+       gtk_widget_class_bind_template_callback (widget_class, cancel_button_clicked_cb);
+       gtk_widget_class_bind_template_callback (widget_class, login_button_clicked_cb);
+}
+
+GtkWidget *
+gs_basic_auth_dialog_new (GtkWindow *parent,
+                          const gchar *remote,
+                          const gchar *realm,
+                          GsBasicAuthCallback callback,
+                          gpointer callback_data)
+{
+       GsBasicAuthDialog *dialog;
+
+       dialog = g_object_new (GS_TYPE_BASIC_AUTH_DIALOG,
+                              "use-header-bar", TRUE,
+                              "transient-for", parent,
+                              "modal", TRUE,
+                              NULL);
+       dialog->callback = callback;
+       dialog->callback_data = callback_data;
+
+       update_description (dialog, remote, realm);
+       dialog_validate (dialog);
+
+       return GTK_WIDGET (dialog);
+}
diff --git a/src/gs-basic-auth-dialog.h b/src/gs-basic-auth-dialog.h
new file mode 100644
index 00000000..ec5f1d03
--- /dev/null
+++ b/src/gs-basic-auth-dialog.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2020 Kalev Lember <klember redhat com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+#include "gnome-software-private.h"
+
+G_BEGIN_DECLS
+
+typedef void (*GsBasicAuthCallback) (const gchar *user, const gchar *password, gpointer callback_data);
+
+#define GS_TYPE_BASIC_AUTH_DIALOG (gs_basic_auth_dialog_get_type ())
+
+G_DECLARE_FINAL_TYPE (GsBasicAuthDialog, gs_basic_auth_dialog, GS, BASIC_AUTH_DIALOG, GtkDialog)
+
+GtkWidget      *gs_basic_auth_dialog_new               (GtkWindow              *parent,
+                                                        const gchar            *remote,
+                                                        const gchar            *realm,
+                                                        GsBasicAuthCallback     callback,
+                                                        gpointer                callback_data);
+
+G_END_DECLS
diff --git a/src/gs-basic-auth-dialog.ui b/src/gs-basic-auth-dialog.ui
new file mode 100644
index 00000000..339e831d
--- /dev/null
+++ b/src/gs-basic-auth-dialog.ui
@@ -0,0 +1,203 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="GsBasicAuthDialog" parent="GtkDialog">
+    <property name="can_focus">False</property>
+    <property name="border_width">5</property>
+    <property name="resizable">False</property>
+    <property name="modal">True</property>
+    <property name="destroy_with_parent">True</property>
+    <property name="type_hint">dialog</property>
+    <property name="title" translatable="yes">Login Required</property>
+    <property name="use_header_bar">1</property>
+    <child internal-child="headerbar">
+      <object class="GtkHeaderBar">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="show_close_button">False</property>
+        <child>
+          <object class="GtkButton" id="cancel_button">
+            <property name="label" translatable="yes">_Cancel</property>
+            <property name="visible">True</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="can_default">True</property>
+            <property name="receives_default">True</property>
+            <property name="use_action_appearance">False</property>
+            <property name="use_underline">True</property>
+            <property name="valign">center</property>
+            <signal name="clicked" handler="cancel_button_clicked_cb" object="GsBasicAuthDialog" 
swapped="yes"/>
+            <style>
+              <class name="text-button"/>
+            </style>
+          </object>
+          <packing>
+            <property name="pack_type">start</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="login_button">
+            <property name="label" translatable="yes">_Login</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">True</property>
+            <property name="use_action_appearance">False</property>
+            <property name="use_underline">True</property>
+            <property name="valign">center</property>
+            <signal name="clicked" handler="login_button_clicked_cb" object="GsBasicAuthDialog" 
swapped="yes"/>
+            <style>
+              <class name="text-button"/>
+              <class name="suggested-action"/>
+            </style>
+          </object>
+          <packing>
+            <property name="pack_type">end</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <child internal-child="vbox">
+      <object class="GtkBox">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkGrid">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="hexpand">True</property>
+            <property name="row_spacing">8</property>
+            <property name="column_spacing">6</property>
+            <property name="border_width">20</property>
+            <property name="margin_end">20</property>
+            <child>
+              <object class="GtkLabel" id="description_label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="wrap">True</property>
+                <property name="wrap_mode">word-char</property>
+                <property name="margin_bottom">20</property>
+                <property name="max_width_chars">55</property>
+                <property name="xalign">0</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">0</property>
+                <property name="width">2</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="user_label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">1</property>
+                <property name="label" translatable="yes">_User</property>
+                <property name="use_underline">True</property>
+                <property name="mnemonic_widget">user_entry</property>
+                <property name="margin_start">20</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">3</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="password_label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">1</property>
+                <property name="label" translatable="yes">_Password</property>
+                <property name="use_underline">True</property>
+                <property name="mnemonic_widget">password_entry</property>
+                <property name="margin_start">20</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">4</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkEntry" id="user_entry">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="has_focus">True</property>
+                <property name="hexpand">True</property>
+                <property name="invisible_char">●</property>
+                <property name="activates_default">True</property>
+                <property name="invisible_char_set">True</property>
+                <property name="input_purpose">password</property>
+                <signal name="changed" handler="dialog_validate" object="GsBasicAuthDialog" swapped="yes"/>
+                <signal name="activate" handler="dialog_validate" object="GsBasicAuthDialog" swapped="yes"/>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">3</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkEntry" id="password_entry">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="hexpand">True</property>
+                <property name="visibility">False</property>
+                <property name="invisible_char">●</property>
+                <property name="activates_default">True</property>
+                <property name="invisible_char_set">True</property>
+                <property name="input_purpose">password</property>
+                <signal name="changed" handler="dialog_validate" object="GsBasicAuthDialog" swapped="yes"/>
+                <signal name="activate" handler="dialog_validate" object="GsBasicAuthDialog" swapped="yes"/>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">4</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
+  <object class="GtkSizeGroup">
+    <widgets>
+      <widget name="user_label"/>
+      <widget name="password_label"/>
+    </widgets>
+  </object>
+  <object class="GtkSizeGroup">
+    <widgets>
+      <widget name="user_entry"/>
+      <widget name="password_entry"/>
+    </widgets>
+  </object>
+  <object class="GtkSizeGroup">
+    <property name="mode">horizontal</property>
+    <widgets>
+      <widget name="login_button"/>
+      <widget name="cancel_button"/>
+    </widgets>
+  </object>
+</interface>
diff --git a/src/gs-shell.c b/src/gs-shell.c
index 009776ad..41503cf8 100644
--- a/src/gs-shell.c
+++ b/src/gs-shell.c
@@ -2,7 +2,7 @@
  *
  * Copyright (C) 2013-2017 Richard Hughes <richard hughsie com>
  * Copyright (C) 2013 Matthias Clasen <mclasen redhat com>
- * Copyright (C) 2014-2018 Kalev Lember <klember redhat com>
+ * Copyright (C) 2014-2020 Kalev Lember <klember redhat com>
  *
  * SPDX-License-Identifier: GPL-2.0+
  */
@@ -18,6 +18,7 @@
 
 #include "gs-common.h"
 #include "gs-shell.h"
+#include "gs-basic-auth-dialog.h"
 #include "gs-details-page.h"
 #include "gs-installed-page.h"
 #include "gs-metered-data-dialog.h"
@@ -362,6 +363,25 @@ scheduler_ready_cb (GObject *source_object,
 }
 #endif  /* HAVE_MOGWAI */
 
+static void
+gs_shell_basic_auth_start_cb (GsPluginLoader *plugin_loader,
+                              const gchar *remote,
+                              const gchar *realm,
+                              GsBasicAuthCallback callback,
+                              gpointer callback_data,
+                              GsShell *shell)
+{
+       GsShellPrivate *priv = gs_shell_get_instance_private (shell);
+       GtkWidget *dialog;
+
+       dialog = gs_basic_auth_dialog_new (priv->main_window, remote, realm, callback, callback_data);
+       gs_shell_modal_dialog_present (shell, GTK_DIALOG (dialog));
+
+       /* just destroy */
+       g_signal_connect_swapped (dialog, "response",
+                                 G_CALLBACK (gtk_widget_destroy), dialog);
+}
+
 static void
 free_back_entry (BackEntry *entry)
 {
@@ -2126,6 +2146,9 @@ gs_shell_setup (GsShell *shell, GsPluginLoader *plugin_loader, GCancellable *can
        g_signal_connect_object (priv->plugin_loader, "notify::network-metered",
                                 G_CALLBACK (gs_shell_network_metered_notify_cb),
                                 shell, 0);
+       g_signal_connect_object (priv->plugin_loader, "basic-auth-start",
+                                G_CALLBACK (gs_shell_basic_auth_start_cb),
+                                shell, 0);
        priv->cancellable = g_object_ref (cancellable);
 
        priv->settings = g_settings_new ("org.gnome.software");
diff --git a/src/meson.build b/src/meson.build
index cbd0a511..6581e77c 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -20,6 +20,7 @@ gnome_software_sources = [
   'gs-application.c',
   'gs-app-row.c',
   'gs-app-tile.c',
+  'gs-basic-auth-dialog.c',
   'gs-category-page.c',
   'gs-category-tile.c',
   'gs-common.c',


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