[epiphany] sync: Fix incorrect device name on accounts.firefox.com



commit 6a6e448176af7b7597c8ef99aaed9d6f7cd75b4e
Author: Gabriel Ivascu <gabrielivascu gnome org>
Date:   Sun Nov 26 02:11:35 2017 +0200

    sync: Fix incorrect device name on accounts.firefox.com
    
    This also fixes Epiphany's synced tabs not being viewable in Firefox.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=790370

 lib/ephy-profile-utils.h                     |    3 +-
 lib/ephy-sync-utils.c                        |   75 ++++++-
 lib/ephy-sync-utils.h                        |    6 +
 lib/sync/debug/ephy-sync-debug.c             |  200 ++++++++++++++++-
 lib/sync/debug/ephy-sync-debug.h             |   44 ++--
 lib/sync/ephy-open-tabs-manager.c            |   23 +-
 lib/sync/ephy-sync-service.c                 |  307 ++++++++++++++++----------
 lib/sync/ephy-sync-service.h                 |    7 +-
 src/prefs-dialog.c                           |    4 +-
 src/profile-migrator/ephy-profile-migrator.c |   52 +++++-
 10 files changed, 546 insertions(+), 175 deletions(-)
---
diff --git a/lib/ephy-profile-utils.h b/lib/ephy-profile-utils.h
index 3c215a4..9b97968 100644
--- a/lib/ephy-profile-utils.h
+++ b/lib/ephy-profile-utils.h
@@ -24,11 +24,12 @@
 
 G_BEGIN_DECLS
 
-#define EPHY_PROFILE_MIGRATION_VERSION 22
+#define EPHY_PROFILE_MIGRATION_VERSION 23
 #define EPHY_INSECURE_PASSWORDS_MIGRATION_VERSION 11
 #define EPHY_SETTINGS_MIGRATION_VERSION 16
 #define EPHY_FIREFOX_SYNC_PASSWORDS_MIGRATION_VERSION 19
 #define EPHY_TARGET_ORIGIN_MIGRATION_VERSION 21
+#define EPHY_SYNC_DEVICE_ID_MIGRATION_VERSION 23
 
 #define EPHY_BOOKMARKS_FILE     "bookmarks.gvdb"
 #define EPHY_HISTORY_FILE       "ephy-history.db"
diff --git a/lib/ephy-sync-utils.c b/lib/ephy-sync-utils.c
index 97936b7..6cbcd02 100644
--- a/lib/ephy-sync-utils.c
+++ b/lib/ephy-sync-utils.c
@@ -23,6 +23,8 @@
 
 #include "ephy-settings.h"
 
+#include <inttypes.h>
+#include <json-glib/json-glib.h>
 #include <libsoup/soup.h>
 #include <stdio.h>
 #include <string.h>
@@ -230,6 +232,46 @@ ephy_sync_utils_get_random_sync_id (void)
   return id;
 }
 
+char *
+ephy_sync_utils_make_client_record (const char *device_bso_id,
+                                    const char *device_id,
+                                    const char *device_name)
+{
+  JsonNode *node;
+  JsonObject *object;
+  JsonArray *array;
+  char *protocol;
+  char *retval;
+
+  g_assert (device_bso_id);
+  g_assert (device_id);
+  g_assert (device_name);
+
+  array = json_array_new ();
+  protocol = g_strdup_printf ("1.%"PRIu32, EPHY_SYNC_STORAGE_VERSION);
+  json_array_add_string_element (array, protocol);
+
+  object = json_object_new ();
+  json_object_set_string_member (object, "id", device_bso_id);
+  json_object_set_string_member (object, "fxaDeviceId", device_id);
+  json_object_set_string_member (object, "name", device_name);
+  json_object_set_string_member (object, "type", "desktop");
+  json_object_set_string_member (object, "version", VERSION);
+  json_object_set_array_member (object, "protocols", array);
+  json_object_set_string_member (object, "os", "Linux");
+  json_object_set_string_member (object, "appPackage", "org.gnome.epiphany");
+  json_object_set_string_member (object, "application", "Epiphany");
+
+  node = json_node_new (JSON_NODE_OBJECT);
+  json_node_take_object (node, object);
+  retval = json_to_string (node, FALSE);
+
+  g_free (protocol);
+  json_node_unref (node);
+
+  return retval;
+}
+
 void
 ephy_sync_utils_set_device_id (const char *id)
 {
@@ -240,15 +282,25 @@ ephy_sync_utils_set_device_id (const char *id)
 char *
 ephy_sync_utils_get_device_id (void)
 {
-  char *id;
+  return g_settings_get_string (EPHY_SETTINGS_SYNC, EPHY_PREFS_SYNC_DEVICE_ID);
+}
+
+char *
+ephy_sync_utils_get_device_bso_id (void)
+{
+  char *device_bso_id;
+  char *device_id;
 
-  id = g_settings_get_string (EPHY_SETTINGS_SYNC, EPHY_PREFS_SYNC_DEVICE_ID);
-  if (!g_strcmp0 (id, "")) {
-    g_free (id);
-    id = ephy_sync_utils_get_random_sync_id ();
+  device_id = g_settings_get_string (EPHY_SETTINGS_SYNC, EPHY_PREFS_SYNC_DEVICE_ID);
+  if (!g_strcmp0 (device_id, "")) {
+    /* This should never be reached. */
+    return g_strnfill (EPHY_SYNC_BSO_ID_LEN, '0');
   }
 
-  return id;
+  device_bso_id = g_strndup (device_id, EPHY_SYNC_BSO_ID_LEN);
+  g_free (device_id);
+
+  return device_bso_id;
 }
 
 void
@@ -264,11 +316,12 @@ ephy_sync_utils_get_device_name (void)
   char *name;
 
   name = g_settings_get_string (EPHY_SETTINGS_SYNC, EPHY_PREFS_SYNC_DEVICE_NAME);
-  if (!g_strcmp0 (name, "")) {
-    g_free (name);
-    name = g_strdup_printf ("%s's GNOME Web on %s",
-                            g_get_user_name (), g_get_host_name ());
-  }
+  if (g_strcmp0 (name, ""))
+    return name;
+
+  g_free (name);
+  name = g_strdup_printf ("%s’s GNOME Web on %s", g_get_user_name (), g_get_host_name ());
+  g_settings_set_string (EPHY_SETTINGS_SYNC, EPHY_PREFS_SYNC_DEVICE_NAME, name);
 
   return name;
 }
diff --git a/lib/ephy-sync-utils.h b/lib/ephy-sync-utils.h
index 4b2a582..5110068 100644
--- a/lib/ephy-sync-utils.h
+++ b/lib/ephy-sync-utils.h
@@ -34,6 +34,7 @@ const SecretSchema *ephy_sync_utils_get_secret_schema (void) G_GNUC_CONST;
 #define EPHY_SYNC_FX_ACCOUNTS_SERVER_URL  "https://api.accounts.firefox.com/v1";
 
 #define EPHY_SYNC_STORAGE_VERSION 5
+#define EPHY_SYNC_DEVICE_ID_LEN   32
 #define EPHY_SYNC_BSO_ID_LEN      12
 
 char     *ephy_sync_utils_encode_hex                    (const guint8 *data,
@@ -53,8 +54,13 @@ void      ephy_sync_utils_generate_random_bytes         (void   *random_ctx,
 char     *ephy_sync_utils_get_audience                  (const char *url);
 char     *ephy_sync_utils_get_random_sync_id            (void);
 
+char     *ephy_sync_utils_make_client_record            (const char *sync_id,
+                                                         const char *device_id,
+                                                         const char *device_name);
+
 void      ephy_sync_utils_set_device_id                 (const char *id);
 char     *ephy_sync_utils_get_device_id                 (void);
+char     *ephy_sync_utils_get_device_bso_id             (void);
 
 void      ephy_sync_utils_set_device_name               (const char *name);
 char     *ephy_sync_utils_get_device_name               (void);
diff --git a/lib/sync/debug/ephy-sync-debug.c b/lib/sync/debug/ephy-sync-debug.c
index 7cb7875..8a5a46c 100644
--- a/lib/sync/debug/ephy-sync-debug.c
+++ b/lib/sync/debug/ephy-sync-debug.c
@@ -26,7 +26,6 @@
 #include "ephy-sync-crypto.h"
 #include "ephy-sync-utils.h"
 
-#include <json-glib/json-glib.h>
 #include <libsoup/soup.h>
 #include <string.h>
 
@@ -125,8 +124,37 @@ free_secrets:
 }
 
 static char *
-ephy_sync_debug_get_body_for_delete (const char          *id,
-                                     SyncCryptoKeyBundle *bundle)
+ephy_sync_debug_make_upload_body (const char          *id,
+                                  const char          *record,
+                                  SyncCryptoKeyBundle *bundle)
+{
+  JsonNode *node;
+  JsonObject *json;
+  char *payload;
+  char *body;
+
+  g_assert (id);
+  g_assert (record);
+  g_assert (bundle);
+
+  payload = ephy_sync_crypto_encrypt_record (record, bundle);
+  json = json_object_new ();
+  json_object_set_string_member (json, "id", id);
+  json_object_set_string_member (json, "payload", payload);
+  node = json_node_new (JSON_NODE_OBJECT);
+  json_node_set_object (node, json);
+  body = json_to_string (node, FALSE);
+
+  g_free (payload);
+  json_object_unref (json);
+  json_node_unref (node);
+
+  return body;
+}
+
+static char *
+ephy_sync_debug_make_delete_body (const char          *id,
+                                  SyncCryptoKeyBundle *bundle)
 {
   JsonNode *node;
   JsonObject *json;
@@ -613,6 +641,47 @@ free_endpoint:
 }
 
 /**
+ * ephy_sync_debug_upload_record:
+ * @collection: the collection name
+ * @id: the record id
+ * @record: record's JSON representation
+ *
+ * Upload record with id @id to collection @collection.
+ **/
+void
+ephy_sync_debug_upload_record (const char *collection,
+                               const char *id,
+                               const char *record)
+{
+  SyncCryptoKeyBundle *bundle;
+  char *id_safe;
+  char *endpoint;
+  char *body;
+  char *response;
+
+  g_assert (collection);
+  g_assert (id);
+  g_assert (record);
+
+  bundle = ephy_sync_debug_get_bundle_for_collection (collection);
+  if (!bundle)
+    return;
+
+  id_safe = soup_uri_encode (id, NULL);
+  endpoint = g_strdup_printf ("storage/%s/%s", collection, id_safe);
+  body = ephy_sync_debug_make_upload_body (id, record, bundle);
+  response = ephy_sync_debug_send_request (endpoint, "PUT", body);
+
+  LOG ("%s", response);
+
+  g_free (id_safe);
+  g_free (endpoint);
+  g_free (body);
+  g_free (response);
+  ephy_sync_crypto_key_bundle_free (bundle);
+}
+
+/**
  * ephy_sync_debug_delete_collection:
  * @collection: the collection name
  *
@@ -652,7 +721,7 @@ ephy_sync_debug_delete_collection (const char *collection)
   for (guint i = 0; i < json_array_get_length (array); i++) {
     const char *id = json_array_get_string_element (array, i);
     char *id_safe = soup_uri_encode (id, NULL);
-    char *body = ephy_sync_debug_get_body_for_delete (id, bundle);
+    char *body = ephy_sync_debug_make_delete_body (id, bundle);
     char *to = g_strdup_printf ("storage/%s/%s", collection, id_safe);
     char *resp = ephy_sync_debug_send_request (to, "PUT", body);
 
@@ -701,7 +770,7 @@ ephy_sync_debug_delete_record (const char *collection,
 
   id_safe = soup_uri_encode (id, NULL);
   endpoint = g_strdup_printf ("storage/%s/%s", collection, id_safe);
-  body = ephy_sync_debug_get_body_for_delete (id, bundle);
+  body = ephy_sync_debug_make_delete_body (id, bundle);
   response = ephy_sync_debug_send_request (endpoint, "PUT", body);
 
   LOG ("%s", response);
@@ -935,3 +1004,124 @@ free_response:
 free_secrets:
   json_object_unref (secrets);
 }
+
+/**
+ * ephy_sync_debug_view_connected_devices:
+ *
+ * Displays the current devices connected to Firefox Sync for the signed in user.
+ *
+ * https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md#get-accountdevices
+ **/
+void
+ephy_sync_debug_view_connected_devices (void)
+{
+  JsonObject *secrets;
+  SoupSession *session;
+  SoupMessage *msg;
+  guint8 *id;
+  guint8 *key;
+  guint8 *tmp;
+  char *id_hex;
+  char *url;
+  const char *session_token;
+
+  secrets = ephy_sync_debug_load_secrets ();
+  if (!secrets)
+    return;
+
+  session_token = json_object_get_string_member (secrets, "session_token");
+  ephy_sync_crypto_derive_session_token (session_token, &id, &key, &tmp);
+
+  url = g_strdup_printf ("%s/account/devices", EPHY_SYNC_FX_ACCOUNTS_SERVER_URL);
+  id_hex = ephy_sync_utils_encode_hex (id, 32);
+  msg = ephy_sync_debug_prepare_soup_message (url, "GET", NULL, id_hex, key, 32);
+  session = soup_session_new ();
+  soup_session_send_message (session, msg);
+
+  LOG ("%s", msg->response_body->data);
+
+  g_object_unref (session);
+  g_object_unref (msg);
+  g_free (id_hex);
+  g_free (url);
+  g_free (id);
+  g_free (key);
+  g_free (tmp);
+  json_object_unref (secrets);
+}
+
+/**
+ * ephy_sync_debug_get_current_device:
+ *
+ * Gets the current device connected to Firefox Sync for the signed in user.
+ *
+ * https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md#get-accountdevices
+ *
+ * Return value: (transfer full): the current device as a #JsonObject
+ **/
+JsonObject *
+ephy_sync_debug_get_current_device (void)
+{
+  JsonObject *retval = NULL;
+  JsonObject *secrets;
+  JsonNode *response;
+  JsonArray *array;
+  SoupSession *session;
+  SoupMessage *msg;
+  GError *error = NULL;
+  guint8 *id;
+  guint8 *key;
+  guint8 *tmp;
+  char *id_hex;
+  char *url;
+  const char *session_token;
+  guint status_code;
+
+  secrets = ephy_sync_debug_load_secrets ();
+  if (!secrets)
+    return NULL;
+
+  session_token = json_object_get_string_member (secrets, "session_token");
+  ephy_sync_crypto_derive_session_token (session_token, &id, &key, &tmp);
+
+  url = g_strdup_printf ("%s/account/devices", EPHY_SYNC_FX_ACCOUNTS_SERVER_URL);
+  id_hex = ephy_sync_utils_encode_hex (id, 32);
+  msg = ephy_sync_debug_prepare_soup_message (url, "GET", NULL, id_hex, key, 32);
+  session = soup_session_new ();
+  status_code = soup_session_send_message (session, msg);
+
+  if (status_code != 200) {
+    LOG ("Failed to GET account devices: %s", msg->response_body->data);
+    goto free_session;
+  }
+
+  response = json_from_string (msg->response_body->data, &error);
+  if (error) {
+    LOG ("Response is not a valid JSON: %s", error->message);
+    g_error_free (error);
+    goto free_session;
+  }
+
+  array = json_node_get_array (response);
+  for (guint i = 0; i < json_array_get_length (array); i++) {
+    JsonObject *device = json_array_get_object_element (array, i);
+
+    if (json_object_get_boolean_member (device, "isCurrentDevice")) {
+      retval = json_object_ref (device);
+      break;
+    }
+  }
+
+  json_node_unref (response);
+free_session:
+  g_object_unref (session);
+  g_object_unref (msg);
+  g_free (id_hex);
+  g_free (url);
+  g_free (id);
+  g_free (key);
+  g_free (tmp);
+  json_object_unref (secrets);
+
+  return retval;
+}
diff --git a/lib/sync/debug/ephy-sync-debug.h b/lib/sync/debug/ephy-sync-debug.h
index dc35b48..bff0c1f 100644
--- a/lib/sync/debug/ephy-sync-debug.h
+++ b/lib/sync/debug/ephy-sync-debug.h
@@ -21,27 +21,33 @@
 #pragma once
 
 #include <glib.h>
+#include <json-glib/json-glib.h>
 
 G_BEGIN_DECLS
 
-void ephy_sync_debug_view_secrets             (void);
-void ephy_sync_debug_view_collection          (const char *collection,
-                                               gboolean    decrypt);
-void ephy_sync_debug_view_record              (const char *collection,
-                                               const char *id,
-                                               gboolean    decrypt);
-void ephy_sync_debug_delete_collection        (const char *collection);
-void ephy_sync_debug_delete_record            (const char *collection,
-                                               const char *id);
-void ephy_sync_debug_erase_collection         (const char *collection);
-void ephy_sync_debug_erase_record             (const char *collection,
-                                               const char *id);
-void ephy_sync_debug_view_collection_info     (void);
-void ephy_sync_debug_view_quota_info          (void);
-void ephy_sync_debug_view_collection_usage    (void);
-void ephy_sync_debug_view_collection_counts   (void);
-void ephy_sync_debug_view_configuration_info  (void);
-void ephy_sync_debug_view_meta_global_record  (void);
-void ephy_sync_debug_view_crypto_keys_record  (void);
+void        ephy_sync_debug_view_secrets            (void);
+void        ephy_sync_debug_view_collection         (const char *collection,
+                                                     gboolean    decrypt);
+void        ephy_sync_debug_view_record             (const char *collection,
+                                                     const char *id,
+                                                     gboolean    decrypt);
+void        ephy_sync_debug_upload_record           (const char *collection,
+                                                     const char *id,
+                                                     const char *body);
+void        ephy_sync_debug_delete_collection       (const char *collection);
+void        ephy_sync_debug_delete_record           (const char *collection,
+                                                     const char *id);
+void        ephy_sync_debug_erase_collection        (const char *collection);
+void        ephy_sync_debug_erase_record            (const char *collection,
+                                                     const char *id);
+void        ephy_sync_debug_view_collection_info    (void);
+void        ephy_sync_debug_view_quota_info         (void);
+void        ephy_sync_debug_view_collection_usage   (void);
+void        ephy_sync_debug_view_collection_counts  (void);
+void        ephy_sync_debug_view_configuration_info (void);
+void        ephy_sync_debug_view_meta_global_record (void);
+void        ephy_sync_debug_view_crypto_keys_record (void);
+void        ephy_sync_debug_view_connected_devices  (void);
+JsonObject *ephy_sync_debug_get_current_device      (void);
 
 G_END_DECLS
diff --git a/lib/sync/ephy-open-tabs-manager.c b/lib/sync/ephy-open-tabs-manager.c
index 32ee9a6..76c888b 100644
--- a/lib/sync/ephy-open-tabs-manager.c
+++ b/lib/sync/ephy-open-tabs-manager.c
@@ -144,23 +144,24 @@ ephy_open_tabs_manager_get_local_tabs (EphyOpenTabsManager *self)
   EphyOpenTabsRecord *local_tabs;
   EphyTabInfo *info;
   GList *tabs_info;
-  char *id;
-  char *name;
+  char *device_bso_id;
+  char *device_name;
 
   g_assert (EPHY_IS_OPEN_TABS_MANAGER (self));
 
-  id = ephy_sync_utils_get_device_id ();
-  name = ephy_sync_utils_get_device_name ();
-  local_tabs = ephy_open_tabs_record_new (id, name);
+  device_bso_id = ephy_sync_utils_get_device_bso_id ();
+  device_name = ephy_sync_utils_get_device_name ();
 
+  local_tabs = ephy_open_tabs_record_new (device_bso_id, device_name);
   tabs_info = ephy_tabs_catalog_get_tabs_info (self->catalog);
+
   for (GList *l = tabs_info; l && l->data; l = l->next) {
     info = (EphyTabInfo *)l->data;
     ephy_open_tabs_record_add_tab (local_tabs, info->title, info->url, info->favicon);
   }
 
-  g_free (id);
-  g_free (name);
+  g_free (device_bso_id);
+  g_free (device_name);
   g_list_free_full (tabs_info, (GDestroyNotify)ephy_tab_info_free);
 
   return local_tabs;
@@ -258,15 +259,15 @@ synchronizable_manager_merge (EphySynchronizableManager              *manager,
   EphyOpenTabsManager *self = EPHY_OPEN_TABS_MANAGER (manager);
   EphyOpenTabsRecord *local_tabs;
   GList *to_upload = NULL;
-  char *id;
+  char *device_bso_id;
 
-  id = ephy_sync_utils_get_device_id ();
+  device_bso_id = ephy_sync_utils_get_device_bso_id ();
   g_list_free_full (self->remote_records, g_object_unref);
   self->remote_records = NULL;
 
   for (GList *l = remotes_updated; l && l->data; l = l->next) {
     /* Exclude the record which describes the local open tabs. */
-    if (!g_strcmp0 (id, ephy_open_tabs_record_get_id (l->data)))
+    if (!g_strcmp0 (device_bso_id, ephy_open_tabs_record_get_id (l->data)))
       continue;
 
     self->remote_records = g_list_prepend (self->remote_records, g_object_ref (l->data));
@@ -278,7 +279,7 @@ synchronizable_manager_merge (EphySynchronizableManager              *manager,
   local_tabs = ephy_open_tabs_manager_get_local_tabs (self);
   to_upload = g_list_prepend (to_upload, local_tabs);
 
-  g_free (id);
+  g_free (device_bso_id);
 
   callback (to_upload, TRUE, user_data);
 }
diff --git a/lib/sync/ephy-sync-service.c b/lib/sync/ephy-sync-service.c
index 7df8c7c..b66fb97 100644
--- a/lib/sync/ephy-sync-service.c
+++ b/lib/sync/ephy-sync-service.c
@@ -686,6 +686,7 @@ ephy_sync_service_destroy_session (EphySyncService *self,
     session_token = ephy_sync_service_get_secret (self, secrets[SESSION_TOKEN]);
   g_assert (session_token);
 
+  /* This also destroys the device associated with the session token. */
   url = g_strdup_printf ("%s/session/destroy", EPHY_SYNC_FX_ACCOUNTS_SERVER_URL);
   ephy_sync_crypto_derive_session_token (session_token, &token_id,
                                          &req_hmac_key, &tmp);
@@ -889,7 +890,7 @@ get_signed_certificate_cb (SoupSession *session,
   if (json_object_get_int_member (json, "errno") == 110) {
     message = _("The password of your Firefox account seems to have been changed.");
     suggestion = _("Please visit Preferences and sign in with the new password to continue syncing.");
-    ephy_sync_service_sign_out (self, FALSE);
+    ephy_sync_service_sign_out (self);
   }
 
   g_warning ("Failed to sign certificate. Status code: %u, response: %s",
@@ -1598,6 +1599,79 @@ ephy_sync_service_store_secrets (EphySyncService *self)
 }
 
 static void
+upload_client_record_cb (SoupSession *session,
+                         SoupMessage *msg,
+                         gpointer     user_data)
+{
+  EphySyncService *self = EPHY_SYNC_SERVICE (user_data);
+
+  if (msg->status_code != 200) {
+    g_warning ("Failed to upload client record. Status code: %u, response: %s",
+               msg->status_code, msg->response_body->data);
+    if (self->is_signing_in)
+      ephy_sync_service_report_sign_in_error (self, _("Failed to upload client record."), NULL, TRUE);
+  } else {
+    LOG ("Successfully uploaded client record");
+    if (self->is_signing_in)
+      ephy_sync_service_store_secrets (self);
+  }
+}
+
+static void
+ephy_sync_service_upload_client_record (EphySyncService *self)
+{
+  SyncCryptoKeyBundle *bundle;
+  JsonNode *node;
+  JsonObject *bso;
+  char *device_bso_id;
+  char *device_id;
+  char *device_name;
+  char *record;
+  char *encrypted;
+  char *body;
+  char *endpoint;
+
+  g_assert (EPHY_IS_SYNC_SERVICE (self));
+
+  /* Make device ID and name. */
+  device_bso_id = ephy_sync_utils_get_device_bso_id ();
+  device_id = ephy_sync_utils_get_device_id ();
+  device_name = ephy_sync_utils_get_device_name ();
+
+  /* Make BSO as string. */
+  record = ephy_sync_utils_make_client_record (device_bso_id, device_id, device_name);
+  bundle = ephy_sync_service_get_key_bundle (self, "clients");
+  encrypted = ephy_sync_crypto_encrypt_record (record, bundle);
+
+  bso = json_object_new ();
+  json_object_set_string_member (bso, "id", device_bso_id);
+  json_object_set_string_member (bso, "payload", encrypted);
+
+  node = json_node_new (JSON_NODE_OBJECT);
+  json_node_set_object (node, bso);
+  body = json_to_string (node, FALSE);
+
+  /* Upload BSO and store the new device ID and name. */
+  LOG ("Uploading client record, device_bso_id=%s, device_id=%s, device_name=%s",
+       device_bso_id, device_id, device_name);
+  endpoint = g_strdup_printf ("storage/clients/%s", device_bso_id);
+  ephy_sync_service_queue_storage_request (self, endpoint,
+                                           SOUP_METHOD_PUT, body, -1, -1,
+                                           upload_client_record_cb, self);
+
+  g_free (device_bso_id);
+  g_free (device_id);
+  g_free (device_name);
+  g_free (record);
+  g_free (encrypted);
+  g_free (endpoint);
+  g_free (body);
+  json_object_unref (bso);
+  json_node_unref (node);
+  ephy_sync_crypto_key_bundle_free (bundle);
+}
+
+static void
 ephy_sync_service_finalize (GObject *object)
 {
   EphySyncService *self = EPHY_SYNC_SERVICE (object);
@@ -1730,7 +1804,7 @@ upload_crypto_keys_cb (SoupSession *session,
   } else {
     LOG ("Successfully uploaded crypto/keys record");
     ephy_sync_service_set_secret (self, secrets[CRYPTO_KEYS], self->crypto_keys);
-    ephy_sync_service_register_device (self, NULL);
+    ephy_sync_service_upload_client_record (self);
   }
 
   g_clear_pointer (&self->crypto_keys, g_free);
@@ -1828,7 +1902,7 @@ get_crypto_keys_cb (SoupSession *session,
   }
 
   ephy_sync_service_set_secret (self, secrets[CRYPTO_KEYS], crypto_keys);
-  ephy_sync_service_register_device (self, NULL);
+  ephy_sync_service_upload_client_record (self);
   goto out_no_error;
 
 out_error:
@@ -2034,6 +2108,94 @@ ephy_sync_service_verify_storage_version (EphySyncService *self)
 }
 
 static void
+upload_fxa_device_cb (SoupSession *session,
+                      SoupMessage *msg,
+                      gpointer     user_data)
+{
+  EphySyncService *self = user_data;
+  JsonNode *node;
+  JsonObject *object;
+  GError *error = NULL;
+
+  if (msg->status_code != 200) {
+    g_warning ("Failed to upload device info on FxA Server. Status code: %u, response: %s",
+               msg->status_code, msg->response_body->data);
+    goto out_error;
+  }
+
+  node = json_from_string (msg->response_body->data, &error);
+  if (error) {
+    g_warning ("Response is not a valid JSON: %s", error->message);
+    g_error_free (error);
+    goto out_error;
+  }
+
+  object = json_node_get_object (node);
+  ephy_sync_utils_set_device_id (json_object_get_string_member (object, "id"));
+  json_node_unref (node);
+
+  LOG ("Successfully uploaded device info on FxA Server");
+  if (self->is_signing_in)
+    ephy_sync_service_verify_storage_version (self);
+  return;
+
+out_error:
+  if (self->is_signing_in)
+    ephy_sync_service_report_sign_in_error (self, _("Failed to upload device info"), NULL, TRUE);
+}
+
+static void
+ephy_sync_service_upload_fxa_device (EphySyncService *self)
+{
+  JsonNode *node;
+  JsonObject *object;
+  const char *session_token;
+  char *body;
+  char *device_name;
+  char *token_id_hex;
+  guint8 *token_id;
+  guint8 *req_hmac_key;
+  guint8 *tmp;
+
+  g_assert (EPHY_IS_SYNC_SERVICE (self));
+
+  object = json_object_new ();
+  device_name = ephy_sync_utils_get_device_name ();
+  json_object_set_string_member (object, "name", device_name);
+  json_object_set_string_member (object, "type", "desktop");
+
+  /* If we are signing in, the ID for the newly registered device will be returned
+   * by the FxA server in the response. Otherwise, we are updating the current
+   * device (i.e. setting its name), so we use the previously obtained ID. */
+  if (!self->is_signing_in) {
+    char *device_id = ephy_sync_utils_get_device_id ();
+    json_object_set_string_member (object, "id", device_id);
+    g_free (device_id);
+  }
+
+  node = json_node_new (JSON_NODE_OBJECT);
+  json_node_take_object (node, object);
+  body = json_to_string (node, FALSE);
+
+  session_token = ephy_sync_service_get_secret (self, secrets[SESSION_TOKEN]);
+  ephy_sync_crypto_derive_session_token (session_token, &token_id, &req_hmac_key, &tmp);
+  token_id_hex = ephy_sync_utils_encode_hex (token_id, 32);
+
+  LOG ("Uploading device info on FxA Server...");
+  ephy_sync_service_fxa_hawk_post (self, "account/device", token_id_hex,
+                                   req_hmac_key, 32, body,
+                                   upload_fxa_device_cb, self);
+
+  g_free (body);
+  g_free (device_name);
+  g_free (token_id_hex);
+  g_free (token_id);
+  g_free (req_hmac_key);
+  g_free (tmp);
+  json_node_unref (node);
+}
+
+static void
 ephy_sync_service_sign_in_finish (EphySyncService *self,
                                   SignInAsyncData *data,
                                   const char      *bundle)
@@ -2071,7 +2233,7 @@ ephy_sync_service_sign_in_finish (EphySyncService *self,
   kb_hex = ephy_sync_utils_encode_hex (kb, 32);
   ephy_sync_service_set_secret (self, secrets[MASTER_KEY], kb_hex);
 
-  ephy_sync_service_verify_storage_version (self);
+  ephy_sync_service_upload_fxa_device (self);
 
   g_free (kb_hex);
   g_free (kb);
@@ -2265,104 +2427,16 @@ ephy_sync_service_unregister_manager (EphySyncService           *self,
   g_signal_handlers_disconnect_by_func (manager, synchronizable_modified_cb, self);
 }
 
-static void
-register_device_cb (SoupSession *session,
-                    SoupMessage *msg,
-                    gpointer     user_data)
-{
-  EphySyncService *self = EPHY_SYNC_SERVICE (user_data);
-
-  if (msg->status_code != 200) {
-    g_warning ("Failed to register device. Status code: %u, response: %s",
-               msg->status_code, msg->response_body->data);
-    if (self->is_signing_in)
-      ephy_sync_service_report_sign_in_error (self, _("Failed to register device."), NULL, TRUE);
-  } else {
-    LOG ("Successfully registered device");
-    if (self->is_signing_in)
-      ephy_sync_service_store_secrets (self);
-  }
-}
-
 void
-ephy_sync_service_register_device (EphySyncService *self,
-                                   const char      *device_name)
+ephy_sync_service_update_device_name (EphySyncService *self,
+                                      const char      *name)
 {
-  SyncCryptoKeyBundle *bundle;
-  JsonNode *node;
-  JsonObject *bso;
-  JsonObject *record;
-  JsonArray *array;
-  char *id;
-  char *name;
-  char *protocol;
-  char *cleartext;
-  char *encrypted;
-  char *body;
-  char *endpoint;
-
   g_assert (EPHY_IS_SYNC_SERVICE (self));
+  g_assert (name);
 
-  /* Make protocol. */
-  protocol = g_strdup_printf ("1.%d", EPHY_SYNC_STORAGE_VERSION);
-  array = json_array_new ();
-  json_array_add_string_element (array, protocol);
-
-  /* Make device ID and name. */
-  id = ephy_sync_utils_get_device_id ();
-  if (device_name)
-    name = g_strdup (device_name);
-  else
-    name = ephy_sync_utils_get_device_name ();
-
-  /* Set record members. */
-  record = json_object_new ();
-  json_object_set_string_member (record, "id", id);
-  json_object_set_string_member (record, "fxaDeviceId",
-                                 ephy_sync_service_get_secret (self, secrets[UID]));
-  json_object_set_string_member (record, "name", name);
-  json_object_set_string_member (record, "type", "desktop");
-  json_object_set_string_member (record, "version", VERSION);
-  json_object_set_array_member (record, "protocols", array);
-  json_object_set_string_member (record, "os", "Linux");
-  json_object_set_string_member (record, "appPackage", "org.gnome.epiphany");
-  json_object_set_string_member (record, "application", "Epiphany");
-
-  /* Get record's string representation. */
-  node = json_node_new (JSON_NODE_OBJECT);
-  json_node_set_object (node, record);
-  cleartext = json_to_string (node, FALSE);
-
-  /* Make BSO as string. */
-  bundle = ephy_sync_service_get_key_bundle (self, "clients");
-  encrypted = ephy_sync_crypto_encrypt_record (cleartext, bundle);
-  bso = json_object_new ();
-  json_object_set_string_member (bso, "id", id);
-  json_object_set_string_member (bso, "payload", encrypted);
-  json_node_set_object (node, bso);
-  body = json_to_string (node, FALSE);
-
-  /* Upload BSO and store the new device ID and name. */
-  LOG ("Registering device with name '%s'...", name);
-  endpoint = g_strdup_printf ("storage/clients/%s", id);
-  ephy_sync_service_queue_storage_request (self, endpoint,
-                                           SOUP_METHOD_PUT, body, -1, -1,
-                                           register_device_cb, self);
-
-  ephy_sync_utils_set_device_id (id);
   ephy_sync_utils_set_device_name (name);
-
-  g_free (endpoint);
-  g_free (body);
-  g_free (encrypted);
-  ephy_sync_crypto_key_bundle_free (bundle);
-  g_free (cleartext);
-  g_free (name);
-  g_free (id);
-  g_free (protocol);
-  json_object_unref (record);
-  json_object_unref (bso);
-  json_node_unref (node);
+  ephy_sync_service_upload_fxa_device (self);
+  ephy_sync_service_upload_client_record (self);
 }
 
 static void
@@ -2379,62 +2453,52 @@ delete_open_tabs_record_cb (SoupSession *session,
 }
 
 static void
-unregister_device_cb (SoupSession *session,
-                      SoupMessage *msg,
-                      gpointer     user_data)
+delete_client_record_cb (SoupSession *session,
+                         SoupMessage *msg,
+                         gpointer     user_data)
 {
   if (msg->status_code != 200) {
-    g_warning ("Failed to unregister device. Status code: %u, response: %s",
+    g_warning ("Failed to delete client record. Status code: %u, response: %s",
                msg->status_code, msg->response_body->data);
   } else {
-    LOG ("Successfully unregistered device");
+    LOG ("Successfully deleted client record");
   }
 }
 
 static void
-ephy_sync_service_unregister_device (EphySyncService *self)
+ephy_sync_service_cleanup_storage_singletons (EphySyncService *self)
 {
   char *endpoint;
-  char *id;
+  char *device_bso_id;
 
   g_assert (EPHY_IS_SYNC_SERVICE (self));
 
-  id = ephy_sync_utils_get_device_id ();
+  device_bso_id = ephy_sync_utils_get_device_bso_id ();
   /* Delete the client record associated to this device. */
-  endpoint = g_strdup_printf ("storage/clients/%s", id);
+  endpoint = g_strdup_printf ("storage/clients/%s", device_bso_id);
   ephy_sync_service_queue_storage_request (self, endpoint,
                                            SOUP_METHOD_DELETE,
                                            NULL, -1, -1,
-                                           unregister_device_cb, NULL);
+                                           delete_client_record_cb, NULL);
   g_free (endpoint);
 
   /* Delete the open tabs record associated to this device. */
-  endpoint = g_strdup_printf ("storage/tabs/%s", id);
+  endpoint = g_strdup_printf ("storage/tabs/%s", device_bso_id);
   ephy_sync_service_queue_storage_request (self, endpoint,
                                            SOUP_METHOD_DELETE,
                                            NULL, -1, -1,
                                            delete_open_tabs_record_cb, NULL);
   g_free (endpoint);
-  g_free (id);
+  g_free (device_bso_id);
 }
 
 void
-ephy_sync_service_sign_out (EphySyncService *self,
-                            gboolean         unregister_device)
+ephy_sync_service_sign_out (EphySyncService *self)
 {
   g_assert (EPHY_IS_SYNC_SERVICE (self));
 
-  /* If we sign out without unregistering the device, then the current id of
-   * the device should not be cleared, but preserved for further use (Ephy will
-   * use the same id next time the user signs in). This way we prevent a zombie
-   * record in the clients collection on the Sync Storage Server.
-   */
-  if (unregister_device) {
-    ephy_sync_service_unregister_device (self);
-    ephy_sync_utils_set_device_id (NULL);
-  }
-
   ephy_sync_service_stop_periodical_sync (self);
+  ephy_sync_service_cleanup_storage_singletons (self);
   ephy_sync_service_destroy_session (self, NULL);
   ephy_sync_service_forget_secrets (self);
   ephy_sync_service_clear_storage_queue (self);
@@ -2451,6 +2515,7 @@ ephy_sync_service_sign_out (EphySyncService *self,
   ephy_sync_utils_set_passwords_sync_is_initial (TRUE);
   ephy_sync_utils_set_history_sync_is_initial (TRUE);
 
+  ephy_sync_utils_set_device_id (NULL);
   ephy_sync_utils_set_sync_time (0);
   ephy_sync_utils_set_sync_user (NULL);
 }
diff --git a/lib/sync/ephy-sync-service.h b/lib/sync/ephy-sync-service.h
index 815d649..d50e417 100644
--- a/lib/sync/ephy-sync-service.h
+++ b/lib/sync/ephy-sync-service.h
@@ -37,12 +37,11 @@ void             ephy_sync_service_sign_in            (EphySyncService *self,
                                                        const char      *session_token,
                                                        const char      *key_fetch_token,
                                                        const char      *unwrap_kb);
-void             ephy_sync_service_sign_out           (EphySyncService *self,
-                                                       gboolean         unregister_device);
+void             ephy_sync_service_sign_out           (EphySyncService *self);
 void             ephy_sync_service_sync               (EphySyncService *self);
 void             ephy_sync_service_start_sync         (EphySyncService *self);
-void             ephy_sync_service_register_device    (EphySyncService *self,
-                                                       const char      *device_name);
+void             ephy_sync_service_update_device_name (EphySyncService *self,
+                                                       const char      *name);
 void             ephy_sync_service_register_manager   (EphySyncService           *self,
                                                        EphySynchronizableManager *manager);
 void             ephy_sync_service_unregister_manager (EphySyncService           *self,
diff --git a/src/prefs-dialog.c b/src/prefs-dialog.c
index 4490018..0931b46 100644
--- a/src/prefs-dialog.c
+++ b/src/prefs-dialog.c
@@ -583,7 +583,7 @@ on_sync_sign_out_button_clicked (GtkWidget   *button,
 {
   EphySyncService *service = ephy_shell_get_sync_service (ephy_shell_get_default ());
 
-  ephy_sync_service_sign_out (service, TRUE);
+  ephy_sync_service_sign_out (service);
 
   /* Show Firefox Accounts iframe. */
   sync_setup_firefox_iframe (dialog);
@@ -644,7 +644,7 @@ on_sync_device_name_save_button_clicked (GtkWidget   *button,
     gtk_entry_set_text (GTK_ENTRY (dialog->sync_device_name_entry), name);
     g_free (name);
   } else {
-    ephy_sync_service_register_device (service, text);
+    ephy_sync_service_update_device_name (service, text);
   }
 
   gtk_widget_set_sensitive (GTK_WIDGET (dialog->sync_device_name_entry), FALSE);
diff --git a/src/profile-migrator/ephy-profile-migrator.c b/src/profile-migrator/ephy-profile-migrator.c
index 0f7d621..227b545 100644
--- a/src/profile-migrator/ephy-profile-migrator.c
+++ b/src/profile-migrator/ephy-profile-migrator.c
@@ -30,6 +30,7 @@
 #include "ephy-search-engine-manager.h"
 #include "ephy-settings.h"
 #include "ephy-sqlite-connection.h"
+#include "ephy-sync-debug.h"
 #include "ephy-sync-utils.h"
 #include "ephy-uri-tester-shared.h"
 #include "ephy-web-app-utils.h"
@@ -922,6 +923,54 @@ migrate_sync_settings_path (void)
 }
 
 static void
+migrate_sync_device_info (void)
+{
+  JsonObject *device;
+  const char *device_id;
+  const char *device_name;
+  char *prev_device_id;
+  char *device_bso_id;
+  char *record;
+  int default_profile_migration_version;
+
+  default_profile_migration_version = ephy_profile_utils_get_migration_version_for_profile_dir 
(ephy_default_dot_dir ());
+  if (default_profile_migration_version >= EPHY_SYNC_DEVICE_ID_MIGRATION_VERSION)
+    return;
+
+  if (!ephy_sync_utils_user_is_signed_in ())
+    return;
+
+  /* Fetch the device info from the Firefox Accounts Server. */
+  device = ephy_sync_debug_get_current_device ();
+  if (!device) {
+    g_warning ("Failed to migrate sync device info. Sign in again to Sync "
+               "to have your device re-registered and continue syncing.");
+    return;
+  }
+
+  /* Erase previous records from the Sync Storage Server. */
+  prev_device_id = ephy_sync_utils_get_device_id ();
+  ephy_sync_debug_erase_record ("clients", prev_device_id);
+  ephy_sync_debug_erase_record ("tabs", prev_device_id);
+
+  /* Use the device id and name assigned by the Firefox Accounts Server at sign in.
+   * The user can change later the device name in the Preferences dialog. */
+  device_id = json_object_get_string_member (device, "id");
+  ephy_sync_utils_set_device_id (device_id);
+  device_name = json_object_get_string_member (device, "name");
+  ephy_sync_utils_set_device_name (device_name);
+
+  device_bso_id = ephy_sync_utils_get_device_bso_id ();
+  record = ephy_sync_utils_make_client_record (device_bso_id, device_id, device_name);
+  ephy_sync_debug_upload_record ("clients", device_bso_id, record);
+
+  g_free (record);
+  g_free (device_bso_id);
+  g_free (prev_device_id);
+  json_object_unref (device);
+}
+
+static void
 migrate_nothing (void)
 {
   /* Used to replace migrators that have been removed. Only remove migrators
@@ -956,7 +1005,8 @@ const EphyProfileMigrator migrators[] = {
   /* 19 */ migrate_passwords_to_firefox_sync_passwords,
   /* 20 */ migrate_history_to_firefox_sync_history,
   /* 21 */ migrate_passwords_add_target_origin,
-  /* 22 */ migrate_sync_settings_path
+  /* 22 */ migrate_sync_settings_path,
+  /* 23 */ migrate_sync_device_info,
 };
 
 static gboolean



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