[gnome-online-accounts/wip/jfelder/owncloud-music: 5/14] owncloud: Generate music password on add_account



commit 3d2a4d60a91d67b4bba05f50bb19b6872eee0749
Author: Gaurav Narula <gnarula94 gmail com>
Date:   Tue May 24 22:32:17 2016 +0530

    owncloud: Generate music password on add_account
    
    It uses the "settings/userkey/generate" endpoint from the music
    application api to generate a password.
    
    See https://github.com/owncloud/music#authentication for the
    documentation of the music api.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=753415

 src/goabackend/goaowncloudprovider.c | 258 +++++++++++++++++++++++++++++++++++
 1 file changed, 258 insertions(+)
---
diff --git a/src/goabackend/goaowncloudprovider.c b/src/goabackend/goaowncloudprovider.c
index 1808d595..e22731ff 100644
--- a/src/goabackend/goaowncloudprovider.c
+++ b/src/goabackend/goaowncloudprovider.c
@@ -22,7 +22,9 @@
 
 #include <glib/gi18n-lib.h>
 
+#include <json-glib/json-glib.h>
 #include <libsoup/soup.h>
+#include <rest/rest-proxy.h>
 
 #include "goahttpclient.h"
 #include "goaprovider.h"
@@ -46,6 +48,7 @@ G_DEFINE_TYPE_WITH_CODE (GoaOwncloudProvider, goa_owncloud_provider, GOA_TYPE_PR
 
 static const gchar *CALDAV_ENDPOINT = "remote.php/caldav/";
 static const gchar *CARDDAV_ENDPOINT = "remote.php/carddav/";
+static const gchar *MUSIC_PASSWORD_GENERATE_ENDPOINT = "index.php/apps/music/api/settings/userkey/generate";
 static const gchar *WEBDAV_ENDPOINT = "remote.php/webdav/";
 
 static const gchar *
@@ -344,6 +347,19 @@ typedef struct
   GError *error;
 } AddAccountData;
 
+typedef struct
+{
+  GCancellable *cancellable;
+
+  GMainLoop *loop;
+
+  const gchar *username;
+  const gchar *password;
+  gchar *music_password;
+
+  GError *error;
+} MusicData;
+
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
@@ -616,6 +632,62 @@ check_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
   show_progress_ui (GTK_CONTAINER (data->progress_grid), FALSE);
 }
 
+static void
+generate_password_cb    (RestProxyCall              *call,
+                         const GError               *error,
+                         GObject                    *weak_object,
+                         gpointer                    user_data)
+{
+  MusicData *data = user_data;
+  GError *error_;
+  JsonParser *parser;
+  JsonObject *json_obj;
+  const gchar *payload;
+
+  parser = NULL;
+
+  if (error != NULL)
+    {
+      error_ = g_error_copy (error);
+      g_propagate_error (&data->error, error_);
+      goto out;
+    }
+
+  parser = json_parser_new ();
+  payload = rest_proxy_call_get_payload (call);
+
+  if (payload == NULL)
+    {
+      g_set_error (&data->error, GOA_ERROR, GOA_ERROR_FAILED, _("Could not parse response"));
+      goto out;
+    }
+
+  if (!json_parser_load_from_data (parser,
+                                   payload,
+                                   rest_proxy_call_get_payload_length (call),
+                                   &data->error))
+    {
+      g_clear_error (&data->error);
+      data->error = NULL;
+      g_set_error (&data->error,
+                   GOA_ERROR, GOA_ERROR_FAILED,
+                   _("Nextcloud Music application is not installed or enabled"));
+      goto out;
+    }
+
+  json_obj = json_node_get_object (json_parser_get_root (parser));
+  data->music_password = g_strdup (json_object_get_string_member (json_obj, "password"));
+  if (data->music_password == NULL)
+    {
+      g_set_error (&data->error, GOA_ERROR, GOA_ERROR_FAILED, _("Could not parse response"));
+      goto out;
+    }
+
+ out:
+  g_main_loop_quit (data->loop);
+  g_clear_object (&parser);
+}
+
 static void
 dialog_response_cb (GtkDialog *dialog, gint response_id, gpointer user_data)
 {
@@ -625,6 +697,50 @@ dialog_response_cb (GtkDialog *dialog, gint response_id, gpointer user_data)
     g_cancellable_cancel (data->cancellable);
 }
 
+static void
+on_rest_proxy_call_cancelled_cb (GCancellable *cancellable, RestProxyCall *call)
+{
+  rest_proxy_call_cancel (call);
+}
+
+static void
+music_generate_password (GoaProvider                *provider,
+                         gchar                      *uri,
+                         GCancellable               *cancellable,
+                         RestProxyCallAsyncCallback  callback,
+                         gpointer                    user_data)
+{
+  MusicData *data = user_data;
+  RestProxyCall *call;
+  RestProxy *proxy;
+
+  /* Add the authorization header manually to save a request */
+  gchar *credentials = g_strconcat (data->username, ":", data->password, NULL);
+  gchar *base64 = g_base64_encode ((guchar *)credentials, strlen (credentials));
+  gchar *auth_header = g_strconcat ("Basic ", base64, NULL);
+
+  proxy = rest_proxy_new (uri, FALSE);
+
+  call = rest_proxy_new_call (proxy);
+
+  rest_proxy_call_set_method (call, "POST");
+  rest_proxy_call_add_header (call,
+                              "Content-Type",
+                              "application/x-www-form-urlencoded");
+  rest_proxy_call_add_header (call, "Authorization", auth_header);
+  rest_proxy_call_add_param (call, "description", "GNOME Online Accounts");
+
+  rest_proxy_call_async (call, callback, NULL, data, &data->error);
+
+  g_signal_connect (cancellable, "cancelled", G_CALLBACK (on_rest_proxy_call_cancelled_cb), call);
+
+  g_object_unref (proxy);
+  g_object_unref (call);
+  g_free (credentials);
+  g_free (base64);
+  g_free (auth_header);
+}
+
 /* ---------------------------------------------------------------------------------------------------- */
 
 static GoaObject *
@@ -635,6 +751,7 @@ add_account (GoaProvider    *provider,
              GError        **error)
 {
   AddAccountData data;
+  MusicData music_data;
   GVariantBuilder credentials;
   GVariantBuilder details;
   GoaHttpClient *http_client = NULL;
@@ -647,6 +764,7 @@ add_account (GoaProvider    *provider,
   gchar *presentation_identity = NULL;
   gchar *server = NULL;
   gchar *uri = NULL;
+  gchar *uri_music_password = NULL;
   gchar *uri_webdav;
   gint response;
 
@@ -656,6 +774,13 @@ add_account (GoaProvider    *provider,
   data.dialog = dialog;
   data.error = NULL;
 
+  memset (&music_data, 0, sizeof (MusicData));
+  music_data.cancellable = g_cancellable_new ();
+  music_data.loop = g_main_loop_new (NULL, FALSE);
+  music_data.error = NULL;
+  music_data.username = NULL;
+  music_data.password = NULL;
+
   create_account_details_ui (provider, dialog, vbox, TRUE, FALSE, &data);
   gtk_widget_show_all (GTK_WIDGET (vbox));
   g_signal_connect (dialog, "response", G_CALLBACK (dialog_response_cb), &data);
@@ -678,6 +803,7 @@ add_account (GoaProvider    *provider,
   password = gtk_entry_get_text (GTK_ENTRY (data.password));
 
   uri = normalize_uri (uri_text, &server);
+  uri_music_password = g_strconcat (uri, MUSIC_PASSWORD_GENERATE_ENDPOINT, NULL);
   presentation_identity = g_strconcat (username, "@", server, NULL);
 
   /* See if there's already an account of this type with the
@@ -753,10 +879,27 @@ add_account (GoaProvider    *provider,
       goto http_again;
     }
 
+  music_data.username = username;
+  music_data.password = password;
+
+  music_generate_password (provider,
+                           uri_music_password,
+                           music_data.cancellable,
+                           (RestProxyCallAsyncCallback) generate_password_cb,
+                           &music_data);
+
+  g_main_loop_run (music_data.loop);
+
+  g_free (uri_music_password);
   gtk_widget_hide (GTK_WIDGET (dialog));
 
   g_variant_builder_init (&credentials, G_VARIANT_TYPE_VARDICT);
   g_variant_builder_add (&credentials, "{sv}", "password", g_variant_new_string (password));
+  if (music_data.music_password)
+    g_variant_builder_add (&credentials,
+                           "{sv}",
+                           "music_password",
+                           g_variant_new_string (music_data.music_password));
 
   g_variant_builder_init (&details, G_VARIANT_TYPE ("a{ss}"));
   g_variant_builder_add (&details, "{ss}", "CalendarEnabled", "true");
@@ -767,6 +910,9 @@ add_account (GoaProvider    *provider,
   g_variant_builder_add (&details, "{ss}", "Uri", uri);
   g_variant_builder_add (&details, "{ss}", "AcceptSslErrors", (accept_ssl_errors) ? "true" : "false");
 
+  if (music_data.error != NULL)
+    g_variant_builder_add (&details, "{ss}", "MusicError",
+                           music_data.error->message);
   /* OK, everything is dandy, add the account */
   /* we want the GoaClient to update before this method returns (so it
    * can create a proxy for the new object) so run the mainloop while
@@ -805,6 +951,9 @@ add_account (GoaProvider    *provider,
   g_free (data.account_object_path);
   g_clear_pointer (&data.loop, g_main_loop_unref);
   g_clear_object (&data.cancellable);
+  g_clear_pointer (&music_data.loop, (GDestroyNotify) g_main_loop_unref);
+  g_clear_object (&music_data.cancellable);
+  g_clear_error (&music_data.error);
   g_clear_object (&http_client);
   return ret;
 }
@@ -1013,6 +1162,114 @@ refresh_account (GoaProvider    *provider,
   return ret;
 }
 
+/* ---------------------------------------------------------------------------------------------------- */
+
+static struct {
+  GoaProviderFeatures feature;
+  const gchar *property;
+  const gchar *blurb;
+} provider_features_info[] = {
+  /* The order in which the features are listed is
+   * important because it affects the order in which they are
+   * displayed in the show_account() UI
+   */
+  {
+    .feature = GOA_PROVIDER_FEATURE_CALENDAR,
+    .property = "calendar-disabled",
+    .blurb = N_("Cale_ndar"),
+  },
+  {
+    .feature = GOA_PROVIDER_FEATURE_CONTACTS,
+    .property = "contacts-disabled",
+    .blurb = N_("_Contacts"),
+  },
+  {
+    .feature = GOA_PROVIDER_FEATURE_DOCUMENTS,
+    .property = "documents-disabled",
+    .blurb = N_("_Documents"),
+  },
+  {
+    .feature = GOA_PROVIDER_FEATURE_MUSIC,
+    .property = "music-disabled",
+    .blurb = N_("M_usic"),
+  },
+  {
+    .feature = GOA_PROVIDER_FEATURE_FILES,
+    .property = "files-disabled",
+    .blurb = N_("_Files"),
+  },
+  {
+    .feature = GOA_PROVIDER_FEATURE_INVALID,
+    .property = NULL,
+    .blurb = NULL,
+  }
+};
+
+static void
+show_account (GoaProvider    *provider,
+              GoaClient      *client,
+              GoaObject      *object,
+              GtkBox         *vbox,
+              G_GNUC_UNUSED GtkGrid *dummy1,
+              G_GNUC_UNUSED GtkGrid *dummy2)
+{
+  GtkWidget *grid;
+  GoaProviderFeatures features;
+  GoaAccount *account;
+  gint row;
+  guint i;
+  gchar *error_message;
+  const char *label;
+
+  row = 0;
+  error_message = NULL;
+  account = NULL;
+
+  error_message = goa_util_lookup_keyfile_string (object, "MusicError");
+  if (error_message)
+    {
+      account = goa_object_get_account (object);
+      goa_account_set_music_disabled (account, TRUE);
+    }
+
+  grid = gtk_grid_new ();
+  gtk_widget_set_halign (grid, GTK_ALIGN_CENTER);
+  gtk_widget_set_hexpand (grid, TRUE);
+  gtk_widget_set_margin_end (grid, 72);
+  gtk_widget_set_margin_start (grid, 72);
+  gtk_widget_set_margin_top (grid, 24);
+  gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
+  gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
+  gtk_container_add (GTK_CONTAINER (vbox), grid);
+
+  goa_utils_account_add_header (object, GTK_GRID (grid), row++);
+
+  features = goa_provider_get_provider_features (provider);
+  /* Translators: This is a label for a series of
+   * options switches. For example: “Use for Mail”. */
+  label = _("Use for");
+
+  for (i = 0; provider_features_info[i].property != NULL; i++)
+    {
+      if ((features & provider_features_info[i].feature) != 0)
+        {
+          GtkWidget *switch_;
+          switch_ = goa_util_add_row_switch_from_keyfile_with_blurb (GTK_GRID (grid), row++, object,
+                                                                     label,
+                                                                     provider_features_info[i].property,
+                                                                     _(provider_features_info[i].blurb));
+          if (provider_features_info[i].feature == GOA_PROVIDER_FEATURE_MUSIC && error_message)
+            {
+              gtk_widget_set_sensitive (switch_, FALSE);
+            }
+          label = NULL;
+        }
+    }
+
+  g_free (error_message);
+}
+
+
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
@@ -1033,6 +1290,7 @@ goa_owncloud_provider_class_init (GoaOwncloudProviderClass *klass)
   provider_class->add_account                = add_account;
   provider_class->refresh_account            = refresh_account;
   provider_class->build_object               = build_object;
+  provider_class->show_account               = show_account;
   provider_class->ensure_credentials_sync    = ensure_credentials_sync;
 }
 


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