[gnome-software/wip/williamhua/reviews] WIP: Use login dialog to get OAuth credentials



commit dd2c774b566e88db347776ef36f22963c2175cb1
Author: William Hua <william hua canonical com>
Date:   Sun Feb 7 01:34:06 2016 -0500

    WIP: Use login dialog to get OAuth credentials

 src/gnome-software.gresource.xml       |    1 +
 src/plugins/gs-plugin-ubuntu-reviews.c |  305 +++++++++++++++++++++++++++++++-
 2 files changed, 300 insertions(+), 6 deletions(-)
---
diff --git a/src/gnome-software.gresource.xml b/src/gnome-software.gresource.xml
index 710d8da..22928a3 100644
--- a/src/gnome-software.gresource.xml
+++ b/src/gnome-software.gresource.xml
@@ -27,6 +27,7 @@
   <file preprocess="xml-stripblanks">gs-star-widget.ui</file>
   <file preprocess="xml-stripblanks">gs-update-dialog.ui</file>
   <file preprocess="xml-stripblanks">gs-upgrade-banner.ui</file>
+  <file preprocess="xml-stripblanks">plugins/ubuntu-one.ui</file>
   <file preprocess="xml-stripblanks">org.freedesktop.PackageKit.xml</file>
   <file>gtk-style.css</file>
   <file>gtk-style-hc.css</file>
diff --git a/src/plugins/gs-plugin-ubuntu-reviews.c b/src/plugins/gs-plugin-ubuntu-reviews.c
index 6a2d49e..01afd12 100644
--- a/src/plugins/gs-plugin-ubuntu-reviews.c
+++ b/src/plugins/gs-plugin-ubuntu-reviews.c
@@ -22,6 +22,7 @@
 #include <config.h>
 
 #include <math.h>
+#include <glib/gi18n.h>
 #include <libsoup/soup.h>
 #include <json-glib/json-glib.h>
 #include <oauth.h>
@@ -35,6 +36,10 @@ struct GsPluginPrivate {
        sqlite3         *db;
        gsize            db_loaded;
        SoupSession     *session;
+       gchar           *consumer_key;
+       gchar           *consumer_secret;
+       gchar           *token_key;
+       gchar           *token_secret;
 };
 
 typedef struct {
@@ -51,6 +56,7 @@ gs_plugin_get_name (void)
        return "ubuntu-reviews";
 }
 
+#define UBUNTU_LOGIN_SERVER            "https://login.ubuntu.com";
 #define UBUNTU_REVIEWS_SERVER          "https://reviews.ubuntu.com/reviews";
 
 /* Download new stats every three months */
@@ -86,10 +92,14 @@ gs_plugin_get_deps (GsPlugin *plugin)
 void
 gs_plugin_destroy (GsPlugin *plugin)
 {
-       if (plugin->priv->db != NULL)
-               sqlite3_close (plugin->priv->db);
-       if (plugin->priv->session != NULL)
-               g_object_unref (plugin->priv->session);
+       GsPluginPrivate *priv = plugin->priv;
+
+       g_clear_pointer (&priv->token_secret, g_free);
+       g_clear_pointer (&priv->token_key, g_free);
+       g_clear_pointer (&priv->consumer_secret, g_free);
+       g_clear_pointer (&priv->consumer_key, g_free);
+       g_clear_pointer (&priv->db, sqlite3_close);
+       g_clear_object (&priv->session);
 }
 
 static gboolean
@@ -790,8 +800,6 @@ set_package_review (GsPlugin *plugin,
 
        /* Write review into database so we can easily access it */
 
-       /* Load OAuth token */
-
        /*
        result = send_review (plugin,
                              review,
@@ -806,6 +814,288 @@ set_package_review (GsPlugin *plugin,
        return result;
 }
 
+typedef struct
+{
+       GsPlugin *plugin;
+       GtkBuilder *builder;
+       GtkWidget *dialog;
+       GtkWidget *cancel;
+       GtkWidget *back;
+       GtkWidget *next;
+       GtkWidget *stack;
+       GtkWidget *email;
+       GtkWidget *password;
+       GtkWidget *passcode;
+       GtkWidget *stack0;
+       GtkWidget *status00;
+       GtkWidget *box0;
+       GtkWidget *status01;
+       GtkWidget *stack1;
+       GtkWidget *status10;
+       GtkWidget *box1;
+       GtkWidget *status11;
+} LoginContext;
+
+static gboolean
+is_email_address (const gchar *text)
+{
+       text = g_utf8_strchr (text, -1, '@');
+
+       if (text == NULL)
+               return FALSE;
+
+       text = g_utf8_strchr (text + 1, -1, '.');
+
+       if (text == NULL)
+               return FALSE;
+
+       return text[1] != '\0';
+}
+
+static void
+update_dialog (LoginContext *context)
+{
+       g_print ("%s %p '%s'\n", G_STRFUNC, context->stack, gtk_stack_get_visible_child_name (GTK_STACK 
(context->stack)));
+
+       if (g_str_equal (gtk_stack_get_visible_child_name (GTK_STACK (context->stack)), "page0")) {
+               gtk_widget_set_visible (context->cancel, TRUE);
+               gtk_widget_set_visible (context->back, FALSE);
+               gtk_widget_set_sensitive (context->next, is_email_address (gtk_entry_get_text (GTK_ENTRY 
(context->email))) &&
+                                                        gtk_entry_get_text_length (GTK_ENTRY 
(context->password)) > 0);
+               gtk_button_set_label (GTK_BUTTON (context->next), _("_Next"));
+       } else if (g_str_equal (gtk_stack_get_visible_child_name (GTK_STACK (context->stack)), "page1")) {
+               gtk_widget_set_visible (context->cancel, TRUE);
+               gtk_widget_set_visible (context->back, TRUE);
+               gtk_widget_set_sensitive (context->next, gtk_entry_get_text_length (GTK_ENTRY 
(context->passcode)) > 0);
+               gtk_button_set_label (GTK_BUTTON (context->next), _("_Next"));
+       } else if (g_str_equal (gtk_stack_get_visible_child_name (GTK_STACK (context->stack)), "page2")) {
+               gtk_widget_set_visible (context->cancel, FALSE);
+               gtk_widget_set_visible (context->back, FALSE);
+               gtk_widget_set_sensitive (context->next, TRUE);
+               gtk_button_set_label (GTK_BUTTON (context->next), _("_Close"));
+       }
+}
+
+static void
+dialog_show_cb (GtkWidget *widget,
+                gpointer   user_data)
+{
+       g_print ("%s\n", G_STRFUNC);
+
+       update_dialog (user_data);
+}
+
+static void
+cancel_clicked_cb (GtkButton *button,
+                   gpointer   user_data)
+{
+       LoginContext *context = user_data;
+
+       g_print ("%s\n", G_STRFUNC);
+
+       gtk_dialog_response (GTK_DIALOG (context->dialog), GTK_RESPONSE_CANCEL);
+}
+
+static void
+back_clicked_cb (GtkButton *button,
+                 gpointer   user_data)
+{
+       LoginContext *context = user_data;
+
+       g_print ("%s\n", G_STRFUNC);
+
+       if (g_str_equal (gtk_stack_get_visible_child_name (GTK_STACK (context->stack)), "page1")) {
+               gtk_stack_set_visible_child_name (GTK_STACK (context->stack), "page0");
+               update_dialog (context);
+       }
+}
+
+static guint
+send_variant_request (SoupSession  *session,
+                      const gchar  *method,
+                      const gchar  *host,
+                      const gchar  *uri,
+                      GVariant     *request,
+                      GVariant    **response,
+                      GError      **error)
+{
+       gchar *data;
+       gsize length;
+       gchar *url;
+       SoupMessage *message;
+       guint response_code;
+       GBytes *bytes;
+
+       g_return_val_if_fail (session != NULL, 0);
+       g_return_val_if_fail (method != NULL, 0);
+       g_return_val_if_fail (host != NULL, 0);
+       g_return_val_if_fail (uri != NULL, 0);
+       g_return_val_if_fail (request != NULL, 0);
+
+       g_variant_ref_sink (request);
+       data = json_gvariant_serialize_data (request, &length);
+       g_variant_unref (request);
+
+       url = g_strdup_printf ("%s%s", host, uri);
+       message = soup_message_new (method, url);
+       g_free (url);
+
+       soup_message_set_request (message, "application/json", SOUP_MEMORY_TAKE, data, length);
+       response_code = soup_session_send_message (session, message);
+       g_object_get (message, SOUP_MESSAGE_RESPONSE_BODY_DATA, &bytes, NULL);
+       g_object_unref (message);
+
+       data = g_bytes_unref_to_data (bytes, &length);
+
+       if (response)
+               *response = json_gvariant_deserialize_data (data, length, NULL, error);
+
+       g_free (data);
+
+       return response_code;
+}
+
+static void
+send_first_factor (LoginContext *context)
+{
+       GVariant *request;
+       GVariant *response;
+       guint response_code;
+       const gchar *code;
+       GError *error = NULL;
+
+       request = g_variant_new_parsed ("{ 'token_name' : 'GNOME Software', 'email' : %s, 'password' : %s }",
+                                       gtk_entry_get_text (GTK_ENTRY (context->email)),
+                                       gtk_entry_get_text (GTK_ENTRY (context->password)));
+
+       response_code = send_variant_request (context->plugin->priv->session,
+                                             SOUP_METHOD_POST,
+                                             UBUNTU_LOGIN_SERVER,
+                                             "/api/v2/tokens/oauth",
+                                             request,
+                                             &response,
+                                             &error);
+
+       if (response) {
+               g_variant_ref_sink (response);
+
+               switch (response_code) {
+               case 401:
+                       g_variant_lookup (response, "code", "&s", &code);
+
+                       g_print ("%s\n", code);
+
+                       if (g_str_equal (code, "INVALID_CREDENTIALS")) {
+                       } else if (g_str_equal (code, "TWOFACTOR_REQUIRED")) {
+                       }
+
+                       break;
+               }
+
+               g_variant_unref (response);
+       }
+}
+
+static void
+send_second_factor (LoginContext *context)
+{
+}
+
+static void
+next_clicked_cb (GtkButton *button,
+                 gpointer   user_data)
+{
+       LoginContext *context = user_data;
+
+       g_print ("%s %p '%s'\n", G_STRFUNC, context->stack, gtk_stack_get_visible_child_name (GTK_STACK 
(context->stack)));
+
+       if (g_str_equal (gtk_stack_get_visible_child_name (GTK_STACK (context->stack)), "page0")) {
+               send_first_factor (context);
+       } else if (g_str_equal (gtk_stack_get_visible_child_name (GTK_STACK (context->stack)), "page1")) {
+               send_second_factor (context);
+       } else if (g_str_equal (gtk_stack_get_visible_child_name (GTK_STACK (context->stack)), "page2")) {
+               gtk_dialog_response (GTK_DIALOG (context->dialog), GTK_RESPONSE_CLOSE);
+       }
+}
+
+static void
+email_notify_cb (GObject    *object,
+                 GParamSpec *pspec,
+                 gpointer    user_data)
+{
+       g_print ("%s\n", G_STRFUNC);
+
+       update_dialog (user_data);
+}
+
+static void
+password_notify_cb (GObject    *object,
+                    GParamSpec *pspec,
+                    gpointer    user_data)
+{
+       g_print ("%s\n", G_STRFUNC);
+
+       update_dialog (user_data);
+}
+
+static void
+passcode_notify_cb (GObject    *object,
+                    GParamSpec *pspec,
+                    gpointer    user_data)
+{
+       g_print ("%s\n", G_STRFUNC);
+
+       update_dialog (user_data);
+}
+
+static gboolean
+sign_into_ubuntu_one (GsPlugin  *plugin,
+                      GError   **error)
+{
+       GsPluginPrivate *priv = plugin->priv;
+       LoginContext context = { NULL };
+
+       if (priv->consumer_key && priv->consumer_secret && priv->token_key && priv->token_secret)
+               return TRUE;
+
+       g_clear_pointer (&priv->token_secret, g_free);
+       g_clear_pointer (&priv->token_key, g_free);
+       g_clear_pointer (&priv->consumer_secret, g_free);
+       g_clear_pointer (&priv->consumer_key, g_free);
+
+       context.plugin = plugin;
+       context.builder = gtk_builder_new_from_resource ("/org/gnome/Software/plugins/ubuntu-one.ui");
+       context.dialog = GTK_WIDGET (gtk_builder_get_object (context.builder, "dialog"));
+       context.cancel = GTK_WIDGET (gtk_builder_get_object (context.builder, "cancel"));
+       context.back = GTK_WIDGET (gtk_builder_get_object (context.builder, "back"));
+       context.next = GTK_WIDGET (gtk_builder_get_object (context.builder, "next"));
+       context.stack = GTK_WIDGET (gtk_builder_get_object (context.builder, "stack"));
+       context.email = GTK_WIDGET (gtk_builder_get_object (context.builder, "email"));
+       context.password = GTK_WIDGET (gtk_builder_get_object (context.builder, "password"));
+       context.passcode = GTK_WIDGET (gtk_builder_get_object (context.builder, "passcode"));
+       context.stack0 = GTK_WIDGET (gtk_builder_get_object (context.builder, "stack0"));
+       context.status00 = GTK_WIDGET (gtk_builder_get_object (context.builder, "status00"));
+       context.box0 = GTK_WIDGET (gtk_builder_get_object (context.builder, "box0"));
+       context.status01 = GTK_WIDGET (gtk_builder_get_object (context.builder, "status01"));
+       context.stack1 = GTK_WIDGET (gtk_builder_get_object (context.builder, "stack1"));
+       context.status10 = GTK_WIDGET (gtk_builder_get_object (context.builder, "status10"));
+       context.box1 = GTK_WIDGET (gtk_builder_get_object (context.builder, "box1"));
+       context.status11 = GTK_WIDGET (gtk_builder_get_object (context.builder, "status11"));
+
+       g_signal_connect (context.dialog, "show", G_CALLBACK (dialog_show_cb), &context);
+       g_signal_connect (context.cancel, "clicked", G_CALLBACK (cancel_clicked_cb), &context);
+       g_signal_connect (context.back, "clicked", G_CALLBACK (back_clicked_cb), &context);
+       g_signal_connect (context.next, "clicked", G_CALLBACK (next_clicked_cb), &context);
+       g_signal_connect (context.email, "notify::text", G_CALLBACK (email_notify_cb), &context);
+       g_signal_connect (context.password, "notify::text", G_CALLBACK (password_notify_cb), &context);
+       g_signal_connect (context.passcode, "notify::text", G_CALLBACK (passcode_notify_cb), &context);
+
+       gtk_dialog_run (GTK_DIALOG (context.dialog));
+
+       gtk_widget_destroy (context.dialog);
+       g_object_unref (context.builder);
+}
+
 gboolean
 gs_plugin_app_set_review (GsPlugin *plugin,
                          GsApp *app,
@@ -840,6 +1130,9 @@ gs_plugin_app_set_review (GsPlugin *plugin,
        if (!setup_networking (plugin, error))
                return FALSE;
 
+       if (!sign_into_ubuntu_one (plugin, error))
+               return FALSE;
+
        /* set rating for each package */
        for (i = 0; i < sources->len; i++) {
                package_name = g_ptr_array_index (sources, i);


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