[epiphany/wip/ephy-sync: 126/126] Implement the sync logic



commit d7a55ebcb4fd6906f7082c6f27a05c1c80f6bcf0
Author: Gabriel Ivascu <ivascu gabriel59 gmail com>
Date:   Fri Aug 19 20:31:46 2016 +0300

    Implement the sync logic

 src/ephy-bookmark-properties-grid.c |   66 ++++-
 src/ephy-bookmark.c                 |   25 ++-
 src/ephy-bookmark.h                 |    4 +
 src/ephy-bookmarks-manager.c        |    7 +-
 src/ephy-shell.c                    |   18 +-
 src/ephy-shell.h                    |    2 +-
 src/ephy-sync-crypto.c              |   29 +--
 src/ephy-sync-service.c             |  570 +++++++++++++++++++++--------------
 src/ephy-sync-service.h             |   89 +++---
 src/prefs-dialog.c                  |   15 +-
 10 files changed, 524 insertions(+), 301 deletions(-)
---
diff --git a/src/ephy-bookmark-properties-grid.c b/src/ephy-bookmark-properties-grid.c
index d3ec69f..8f311ce 100644
--- a/src/ephy-bookmark-properties-grid.c
+++ b/src/ephy-bookmark-properties-grid.c
@@ -24,6 +24,7 @@
 #include "ephy-bookmarks-manager.h"
 #include "ephy-debug.h"
 #include "ephy-shell.h"
+#include "ephy-sync-service.h"
 #include "ephy-type-builtins.h"
 
 #include <libsoup/soup.h>
@@ -46,6 +47,10 @@ struct _EphyBookmarkPropertiesGrid {
   GtkWidget                      *add_tag_entry;
   GtkWidget                      *add_tag_button;
   GtkWidget                      *remove_bookmark_button;
+
+  char                           *prev_name;
+  char                           *prev_address;
+  GSequence                      *prev_tags;
 };
 
 G_DEFINE_TYPE (EphyBookmarkPropertiesGrid, ephy_bookmark_properties_grid, GTK_TYPE_GRID)
@@ -234,10 +239,13 @@ ephy_bookmarks_properties_grid_actions_remove_bookmark (GSimpleAction *action,
                                                         GVariant      *value,
                                                         gpointer       user_data)
 {
+  EphySyncService *service;
   EphyBookmarkPropertiesGrid *self = user_data;
 
   g_assert (EPHY_IS_BOOKMARK_PROPERTIES_GRID (self));
 
+  service = ephy_shell_get_sync_service (ephy_shell_get_default ());
+  ephy_sync_service_delete_bookmark (service, self->bookmark, FALSE);
   ephy_bookmarks_manager_remove_bookmark (self->manager,  self->bookmark);
 
   gtk_widget_destroy (self->parent);
@@ -328,6 +336,7 @@ ephy_bookmark_properties_grid_constructed (GObject *object)
   /* Set text for name entry */
   gtk_entry_set_text (GTK_ENTRY (self->name_entry),
                       ephy_bookmark_get_title (self->bookmark));
+  self->prev_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (self->name_entry)));
 
   g_object_bind_property (GTK_ENTRY (self->name_entry), "text",
                           self->bookmark, "title",
@@ -337,6 +346,7 @@ ephy_bookmark_properties_grid_constructed (GObject *object)
   if (self->type == EPHY_BOOKMARK_PROPERTIES_GRID_TYPE_DIALOG) {
     address = get_address (ephy_bookmark_get_url (self->bookmark));
     gtk_entry_set_text (GTK_ENTRY (self->address_entry), address);
+    self->prev_address = g_strdup (gtk_entry_get_text (GTK_ENTRY (self->address_entry)));
     g_free (address);
 
     g_object_bind_property (GTK_ENTRY (self->address_entry), "text",
@@ -349,6 +359,7 @@ ephy_bookmark_properties_grid_constructed (GObject *object)
                           G_BINDING_DEFAULT);
 
   /* Create tag widgets */
+  self->prev_tags = g_sequence_new (g_free);
   tags = ephy_bookmarks_manager_get_tags (self->manager);
   bookmark_tags = ephy_bookmark_get_tags (self->bookmark);
   for (iter = g_sequence_get_begin_iter (tags);
@@ -361,8 +372,11 @@ ephy_bookmark_properties_grid_constructed (GObject *object)
     if (g_sequence_lookup (bookmark_tags,
                            (gpointer)tag,
                            (GCompareDataFunc)ephy_bookmark_tags_compare,
-                           NULL))
+                           NULL)) {
       selected = TRUE;
+      g_sequence_insert_sorted (self->prev_tags, g_strdup (tag),
+                                (GCompareDataFunc)ephy_bookmark_tags_compare, NULL);
+    }
 
     widget = ephy_bookmark_properties_grid_create_tag_widget (self, tag, selected);
     gtk_flow_box_insert (GTK_FLOW_BOX (self->tags_box), widget, -1);
@@ -380,8 +394,58 @@ ephy_bookmark_properties_grid_destroy (GtkWidget *widget)
 {
   EphyBookmarkPropertiesGrid *self = EPHY_BOOKMARK_PROPERTIES_GRID (widget);
 
+  if (ephy_bookmark_is_uploaded (self->bookmark) == FALSE)
+    goto out;
+
+  /* Check if any actual changes were made to the name, address or tags. If yes,
+   * set the uploaded flag to FALSE. */
+
+  if (self->prev_name != NULL) {
+    if (g_strcmp0 (self->prev_name, ephy_bookmark_get_title (self->bookmark)) != 0) {
+      ephy_bookmark_set_uploaded (self->bookmark, FALSE);
+      goto out;
+    }
+  }
+
+  if (self->prev_address != NULL) {
+    if (g_strcmp0 (self->prev_address, ephy_bookmark_get_url (self->bookmark)) != 0) {
+      ephy_bookmark_set_uploaded (self->bookmark, FALSE);
+      goto out;
+    }
+  }
+
+  if (self->prev_tags != NULL) {
+    GSequence *tags = ephy_bookmark_get_tags (self->bookmark);
+    GSequenceIter *iter;
+
+    /* Check for added tags. */
+    for (iter = g_sequence_get_begin_iter (tags);
+         !g_sequence_iter_is_end (iter); iter = g_sequence_iter_next (iter)) {
+      if (!g_sequence_lookup (self->prev_tags, g_sequence_get (iter),
+                              (GCompareDataFunc)ephy_bookmark_tags_compare, NULL)) {
+        ephy_bookmark_set_uploaded (self->bookmark, FALSE);
+        goto out;
+      }
+    }
+
+    /* Check for deleted tags. */
+    for (iter = g_sequence_get_begin_iter (self->prev_tags);
+         !g_sequence_iter_is_end (iter); iter = g_sequence_iter_next (iter)) {
+      if (!g_sequence_lookup (tags, g_sequence_get (iter),
+                              (GCompareDataFunc)ephy_bookmark_tags_compare, NULL)) {
+        ephy_bookmark_set_uploaded (self->bookmark, FALSE);
+        goto out;
+      }
+    }
+  }
+
+out:
   ephy_bookmarks_manager_save_to_file_async (self->manager, NULL, NULL, NULL);
 
+  g_clear_pointer (&self->prev_name, g_free);
+  g_clear_pointer (&self->prev_address, g_free);
+  g_clear_pointer (&self->prev_tags, g_sequence_free);
+
   GTK_WIDGET_CLASS (ephy_bookmark_properties_grid_parent_class)->destroy (widget);
 }
 
diff --git a/src/ephy-bookmark.c b/src/ephy-bookmark.c
index 51f1a41..d737e34 100644
--- a/src/ephy-bookmark.c
+++ b/src/ephy-bookmark.c
@@ -35,6 +35,7 @@ struct _EphyBookmark {
 
   char        *id;
   double       modified;
+  gboolean     uploaded;
 };
 
 static JsonSerializableIface *serializable_iface = NULL;
@@ -365,6 +366,23 @@ ephy_bookmark_get_modified (EphyBookmark *self)
 }
 
 void
+ephy_bookmark_set_uploaded (EphyBookmark *self,
+                            gboolean      uploaded)
+{
+  g_return_if_fail (EPHY_IS_BOOKMARK (self));
+
+  self->uploaded = uploaded;
+}
+
+gboolean
+ephy_bookmark_is_uploaded (EphyBookmark *self)
+{
+  g_return_val_if_fail (EPHY_IS_BOOKMARK (self), FALSE);
+
+  return self->uploaded;
+}
+
+void
 ephy_bookmark_add_tag (EphyBookmark *self,
                        const char   *tag)
 {
@@ -481,7 +499,7 @@ ephy_bookmark_to_bso (EphyBookmark *self)
 
   g_return_val_if_fail (EPHY_IS_BOOKMARK (self), NULL);
 
-  service = ephy_shell_get_global_sync_service (ephy_shell_get_default ());
+  service = ephy_shell_get_sync_service (ephy_shell_get_default ());
   sync_key = ephy_sync_crypto_decode_hex (ephy_sync_service_get_token (service, TOKEN_KB));
   serialized = json_gobject_to_data (G_OBJECT (self), NULL);
   encrypted = ephy_sync_crypto_aes_256 (AES_256_MODE_ENCRYPT, sync_key,
@@ -511,13 +529,13 @@ ephy_bookmark_from_bso (JsonObject *bso)
 
   g_return_val_if_fail (bso != NULL, NULL);
 
-  service = ephy_shell_get_global_sync_service (ephy_shell_get_default ());
+  service = ephy_shell_get_sync_service (ephy_shell_get_default ());
   sync_key = ephy_sync_crypto_decode_hex (ephy_sync_service_get_token (service, TOKEN_KB));
   decoded = ephy_sync_crypto_base64_urlsafe_decode (json_object_get_string_member (bso, "payload"),
                                                     &decoded_len, FALSE);
   decrypted = (char *) ephy_sync_crypto_aes_256 (AES_256_MODE_DECRYPT, sync_key,
                                                  decoded, decoded_len, NULL);
-  object = json_gobject_from_data (EPHY_TYPE_BOOKMARK, decrypted, -1, &error);
+  object = json_gobject_from_data (EPHY_TYPE_BOOKMARK, decrypted, strlen (decrypted), &error);
 
   if (object == NULL) {
     g_warning ("Failed to create GObject from data: %s", error->message);
@@ -528,6 +546,7 @@ ephy_bookmark_from_bso (JsonObject *bso)
   bookmark = EPHY_BOOKMARK (object);
   ephy_bookmark_set_id (bookmark, json_object_get_string_member (bso, "id"));
   ephy_bookmark_set_modified (bookmark, json_object_get_double_member (bso, "modified"));
+  ephy_bookmark_set_uploaded (bookmark, TRUE);
 
 out:
   g_free (decoded);
diff --git a/src/ephy-bookmark.h b/src/ephy-bookmark.h
index c76dffe..5d2c797 100644
--- a/src/ephy-bookmark.h
+++ b/src/ephy-bookmark.h
@@ -51,6 +51,10 @@ void                 ephy_bookmark_set_modified        (EphyBookmark *self,
                                                         double        modified);
 double               ephy_bookmark_get_modified        (EphyBookmark *self);
 
+void                 ephy_bookmark_set_uploaded        (EphyBookmark *self,
+                                                        gboolean      uploaded);
+gboolean             ephy_bookmark_is_uploaded         (EphyBookmark *self);
+
 void                 ephy_bookmark_add_tag             (EphyBookmark *self,
                                                         const char   *tag);
 void                 ephy_bookmark_remove_tag          (EphyBookmark *self,
diff --git a/src/ephy-bookmarks-manager.c b/src/ephy-bookmarks-manager.c
index 5d801c0..cfef26e 100644
--- a/src/ephy-bookmarks-manager.c
+++ b/src/ephy-bookmarks-manager.c
@@ -68,12 +68,13 @@ build_variant (EphyBookmark *bookmark)
   GSequence *tags;
   GSequenceIter *iter;
 
-  g_variant_builder_init (&builder, G_VARIANT_TYPE ("(xssdas)"));
+  g_variant_builder_init (&builder, G_VARIANT_TYPE ("(xssdbas)"));
 
   g_variant_builder_add (&builder, "x", ephy_bookmark_get_time_added (bookmark));
   g_variant_builder_add (&builder, "s", ephy_bookmark_get_title (bookmark));
   g_variant_builder_add (&builder, "s", ephy_bookmark_get_id (bookmark));
   g_variant_builder_add (&builder, "d", ephy_bookmark_get_modified (bookmark));
+  g_variant_builder_add (&builder, "b", ephy_bookmark_is_uploaded (bookmark));
 
   g_variant_builder_open (&builder, G_VARIANT_TYPE ("as"));
   tags = ephy_bookmark_get_tags (bookmark);
@@ -561,11 +562,12 @@ ephy_bookmarks_manager_load_from_file (EphyBookmarksManager *self)
     gint64 time_added;
     char *id;
     double modified;
+    gboolean uploaded;
 
     /* Obtain the correspoding GVariant. */
     value = gvdb_table_get_value (table, list[i]);
 
-    g_variant_get (value, "(x&s&sdas)", &time_added, &title, &id, &modified, &iter);
+    g_variant_get (value, "(x&s&sdbas)", &time_added, &title, &id, &modified, &uploaded, &iter);
 
     /* Add all stored tags in a GSequence. */
     tags = g_sequence_new (g_free);
@@ -581,6 +583,7 @@ ephy_bookmarks_manager_load_from_file (EphyBookmarksManager *self)
     ephy_bookmark_set_time_added (bookmark, time_added);
     ephy_bookmark_set_id (bookmark, id);
     ephy_bookmark_set_modified (bookmark, modified);
+    ephy_bookmark_set_uploaded (bookmark, uploaded);
     g_sequence_prepend (bookmarks, bookmark);
   }
   ephy_bookmarks_manager_add_bookmarks (self, bookmarks);
diff --git a/src/ephy-shell.c b/src/ephy-shell.c
index eca6273..910b170 100644
--- a/src/ephy-shell.c
+++ b/src/ephy-shell.c
@@ -561,6 +561,15 @@ ephy_shell_init (EphyShell *shell)
   ephy_shell = shell;
   g_object_add_weak_pointer (G_OBJECT (ephy_shell),
                              (gpointer *)ptr);
+
+  ephy_shell->sync_service = ephy_sync_service_new ();
+  /* Do a start up sync and set a periodical sync afterwards. */
+  if (ephy_sync_service_is_signed_in (ephy_shell->sync_service) == TRUE) {
+    ephy_sync_service_do_periodical_sync (ephy_shell->sync_service);
+    g_timeout_add_seconds (ephy_sync_service_get_sync_frequency (ephy_shell->sync_service),
+                           (GSourceFunc)ephy_sync_service_do_periodical_sync,
+                           ephy_shell->sync_service);
+  }
 }
 
 static void
@@ -605,20 +614,17 @@ ephy_shell_finalize (GObject *object)
 }
 
 /**
- * ephy_shell_get_global_sync_service:
+ * ephy_shell_get_sync_service:
  *
  * Retrieve the default #EphySyncService object
  *
- * Return value: (transfer none):
+ * Return value: (transfer none): the default #EphySyncService
  **/
 EphySyncService *
-ephy_shell_get_global_sync_service (EphyShell *shell)
+ephy_shell_get_sync_service (EphyShell *shell)
 {
   g_return_val_if_fail (EPHY_IS_SHELL (shell), NULL);
 
-  if (shell->sync_service == NULL)
-    shell->sync_service = ephy_sync_service_new ();
-
   return shell->sync_service;
 }
 
diff --git a/src/ephy-shell.h b/src/ephy-shell.h
index 6dafee6..84f1eda 100644
--- a/src/ephy-shell.h
+++ b/src/ephy-shell.h
@@ -106,7 +106,7 @@ GtkWidget       *ephy_shell_get_bookmarks_editor         (EphyShell *shell);
 
 EphyBookmarksManager *ephy_shell_get_bookmarks_manager   (EphyShell *shell);
 
-EphySyncService *ephy_shell_get_global_sync_service      (EphyShell *shell);
+EphySyncService *ephy_shell_get_sync_service             (EphyShell *shell);
 
 GtkWidget       *ephy_shell_get_history_window           (EphyShell *shell);
 
diff --git a/src/ephy-sync-crypto.c b/src/ephy-sync-crypto.c
index 3e4a0fb..73a2f30 100644
--- a/src/ephy-sync-crypto.c
+++ b/src/ephy-sync-crypto.c
@@ -879,35 +879,28 @@ ephy_sync_crypto_aes_256 (EphySyncCryptoAES256Mode  mode,
                           gsize                    *out_len)
 {
   struct aes256_ctx aes;
-  gsize padded_len;
+  gsize padded_len = data_len;
   guint8 *padded_data;
   guint8 *out;
 
   g_return_val_if_fail (key != NULL, NULL);
   g_return_val_if_fail (data != NULL, NULL);
 
-  if (mode == AES_256_MODE_DECRYPT)
-    g_assert (data_len % AES_BLOCK_SIZE == 0);
-
-  padded_len = data_len;
-  if (data_len % AES_BLOCK_SIZE != 0)
+  if (mode == AES_256_MODE_ENCRYPT)
     padded_len = data_len + (AES_BLOCK_SIZE - data_len % AES_BLOCK_SIZE);
+  else if (mode == AES_256_MODE_DECRYPT)
+    g_assert (data_len % AES_BLOCK_SIZE == 0);
 
-  out = g_malloc (padded_len);
+  out = g_malloc0 (padded_len);
   padded_data = g_malloc0 (padded_len);
   memcpy (padded_data, data, data_len);
 
-  switch (mode) {
-    case AES_256_MODE_ENCRYPT:
-      aes256_set_encrypt_key (&aes, key);
-      aes256_encrypt (&aes, padded_len, out, padded_data);
-      break;
-    case AES_256_MODE_DECRYPT:
-      aes256_set_decrypt_key (&aes, key);
-      aes256_decrypt (&aes, padded_len, out, padded_data);
-      break;
-    default:
-      g_assert_not_reached ();
+  if (mode == AES_256_MODE_ENCRYPT) {
+    aes256_set_encrypt_key (&aes, key);
+    aes256_encrypt (&aes, padded_len, out, padded_data);
+  } else if (mode == AES_256_MODE_DECRYPT) {
+    aes256_set_decrypt_key (&aes, key);
+    aes256_decrypt (&aes, padded_len, out, padded_data);
   }
 
   if (out_len != NULL)
diff --git a/src/ephy-sync-service.c b/src/ephy-sync-service.c
index 6c9fc2e..2e9a366 100644
--- a/src/ephy-sync-service.c
+++ b/src/ephy-sync-service.c
@@ -32,16 +32,14 @@
 
 #define MOZILLA_TOKEN_SERVER_URL  "https://token.services.mozilla.com/1.0/sync/1.5";
 #define MOZILLA_FXA_SERVER_URL    "https://api.accounts.firefox.com/v1/";
-
-#define DUMMY_BOOKMARK_ID         "00000000000000000000000000000000"
 #define EPHY_BOOKMARKS_COLLECTION "ephy-bookmarks"
-
 #define EMAIL_REGEX               "^[a-zA-Z0-9_]([a-zA-Z0-9._]+[a-zA-Z0-9_])?@[a-z0-9.-]+$"
 
 struct _EphySyncService {
   GObject      parent_instance;
 
   SoupSession *session;
+  guint        sync_frequency;
 
   gchar       *uid;
   gchar       *sessionToken;
@@ -54,7 +52,7 @@ struct _EphySyncService {
   double       sync_time;
   gint64       auth_at;
 
-  gboolean     is_locked;
+  gboolean     locked;
   gchar       *storage_endpoint;
   gchar       *storage_credentials_id;
   gchar       *storage_credentials_key;
@@ -596,6 +594,7 @@ ephy_sync_service_init (EphySyncService *self)
 
   self->session = soup_session_new ();
   self->storage_queue = g_queue_new ();
+  self->sync_frequency = 15 * 60;
 
   email = g_settings_get_string (EPHY_SETTINGS_MAIN, EPHY_PREFS_SYNC_USER);
 
@@ -664,6 +663,23 @@ ephy_sync_service_set_sync_time (EphySyncService *self,
   g_settings_set_double (EPHY_SETTINGS_MAIN, EPHY_PREFS_SYNC_TIME, time);
 }
 
+guint
+ephy_sync_service_get_sync_frequency (EphySyncService *self)
+{
+  g_return_val_if_fail (EPHY_IS_SYNC_SERVICE (self), G_MAXUINT);
+
+  return self->sync_frequency;
+}
+
+void
+ephy_sync_service_set_sync_frequency (EphySyncService *self,
+                                      guint            sync_frequency)
+{
+  g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
+
+  self->sync_frequency = sync_frequency;
+}
+
 gchar *
 ephy_sync_service_get_token (EphySyncService   *self,
                              EphySyncTokenType  type)
@@ -914,8 +930,8 @@ ephy_sync_service_send_storage_message (EphySyncService     *self,
   /* If there is currently another message being transmitted, then the new
    * message has to wait in the queue, otherwise, it is free to go.
    */
-  if (self->is_locked == FALSE) {
-    self->is_locked = TRUE;
+  if (self->locked == FALSE) {
+    self->locked = TRUE;
     ephy_sync_service_issue_storage_request (self, data);
   } else {
     g_queue_push_tail (self->storage_queue, data);
@@ -927,7 +943,7 @@ ephy_sync_service_release_next_storage_message (EphySyncService *self)
 {
   g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
   /* We should never reach this with the service not being locked. */
-  g_assert (self->is_locked == TRUE);
+  g_assert (self->locked == TRUE);
 
   /* If there are other messages waiting in the queue, we release the next one
    * and keep the service locked, else, we mark the service as not locked.
@@ -935,107 +951,179 @@ ephy_sync_service_release_next_storage_message (EphySyncService *self)
   if (g_queue_is_empty (self->storage_queue) == FALSE)
     ephy_sync_service_issue_storage_request (self, g_queue_pop_head (self->storage_queue));
   else
-    self->is_locked = FALSE;
+    self->locked = FALSE;
 }
 
 static void
-log_all_message_info (const gchar *text,
-                      SoupMessage *message)
+upload_bookmark_response_cb (SoupSession *session,
+                             SoupMessage *msg,
+                             gpointer     user_data)
 {
-  LOG ("%s:", text);
-  LOG ("status_code: %u", message->status_code);
-  LOG ("response_body: %s", message->response_body->data);
-  LOG ("Retry-After: %s", soup_message_headers_get_one (message->response_headers, "Retry-After"));
-  LOG ("X-Weave-Backoff: %s", soup_message_headers_get_one (message->response_headers, "X-Weave-Backoff"));
-  LOG ("X-Last-Modified: %s", soup_message_headers_get_one (message->response_headers, "X-Last-Modified"));
-  LOG ("X-Weave-Timestamp: %s", soup_message_headers_get_one (message->response_headers, 
"X-Weave-Timestamp"));
-  LOG ("X-Weave-Records: %s", soup_message_headers_get_one (message->response_headers, "X-Weave-Records"));
-  LOG ("X-Weave-Next-Offset: %s", soup_message_headers_get_one (message->response_headers, 
"X-Weave-Next-Offset"));
-  LOG ("X-Weave-Quota-Remaining: %s", soup_message_headers_get_one (message->response_headers, 
"X-Weave-Quota-Remaining"));
-  LOG ("X-Weave-Alert: %s", soup_message_headers_get_one (message->response_headers, "X-Weave-Alert"));
+  EphySyncService *service;
+  EphyBookmarksManager *manager;
+  EphyBookmark *bookmark;
+  double last_modified;
+
+  service = ephy_shell_get_sync_service (ephy_shell_get_default ());
+  manager = ephy_shell_get_bookmarks_manager (ephy_shell_get_default ());
+  bookmark = EPHY_BOOKMARK (user_data);
+
+  if (msg->status_code == 200) {
+    last_modified = g_ascii_strtod (msg->response_body->data, NULL);
+    ephy_bookmark_set_modified (bookmark, last_modified);
+    ephy_bookmark_set_uploaded (bookmark, TRUE);
+    ephy_bookmarks_manager_save_to_file_async (manager, NULL, NULL, NULL);
+
+    LOG ("Successfully uploaded to server");
+  } else if (msg->status_code == 412) {
+    ephy_sync_service_download_bookmark (service, bookmark);
+  } else {
+    LOG ("Failed to upload to server. Status code: %u, response: %s",
+         msg->status_code, msg->response_body->data);
+  }
+
+  ephy_sync_service_release_next_storage_message (service);
+}
+
+void
+ephy_sync_service_upload_bookmark (EphySyncService *self,
+                                   EphyBookmark    *bookmark,
+                                   gboolean         force)
+{
+  gchar *endpoint;
+  gchar *bso;
+  double modified;
+
+  g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
+  g_return_if_fail (ephy_sync_service_is_signed_in (self));
+  g_return_if_fail (EPHY_IS_BOOKMARK (bookmark));
+
+  endpoint = g_strdup_printf ("storage/%s/%s",
+                              EPHY_BOOKMARKS_COLLECTION,
+                              ephy_bookmark_get_id (bookmark));
+  bso = ephy_bookmark_to_bso (bookmark);
+  modified = ephy_bookmark_get_modified (bookmark);
+  ephy_sync_service_send_storage_message (self, endpoint,
+                                          SOUP_METHOD_PUT, bso, -1,
+                                          force ? -1 : modified,
+                                          upload_bookmark_response_cb,
+                                          bookmark);
+
+  g_free (endpoint);
+  g_free (bso);
 }
 
 static void
-create_bookmarks_storage_collection_response_cb (SoupSession *session,
-                                                 SoupMessage *message,
-                                                 gpointer     user_data)
+download_bookmark_response_cb (SoupSession *session,
+                               SoupMessage *msg,
+                               gpointer     user_data)
 {
   EphySyncService *service;
+  EphyBookmarksManager *manager;
+  EphyBookmark *bookmark;
+  GSequenceIter *iter;
+  JsonParser *parser;
+  JsonObject *bso;
+  const gchar *id;
 
-  /* Code 412 means that the BSO already exists. Don't treat this as an error. */
-  if (message->status_code != 200 && message->status_code != 412)
-    LOG ("Failed to add the dummy BSO. Status code: %u, response: %s",
-         message->status_code, message->response_body->data);
+  if (msg->status_code != 200) {
+    LOG ("Failed to download from server. Status code: %u, response: %s",
+         msg->status_code, msg->response_body->data);
+    goto out;
+  }
+
+  parser = json_parser_new ();
+  json_parser_load_from_data (parser, msg->response_body->data, -1, NULL);
+  bso = json_node_get_object (json_parser_get_root (parser));
+  bookmark = ephy_bookmark_from_bso (bso);
+  id = ephy_bookmark_get_id (bookmark);
 
-  service = ephy_shell_get_global_sync_service (ephy_shell_get_default ());
+  /* Overwrite any local bookmark. */
+  manager = ephy_shell_get_bookmarks_manager (ephy_shell_get_default ());
+  ephy_bookmarks_manager_remove_bookmark (manager,
+                                          ephy_bookmarks_manager_get_bookmark_by_id (manager, id));
+  ephy_bookmarks_manager_add_bookmark (manager, bookmark);
+
+  /* We have to manually add the tags to the bookmarks manager. */
+  for (iter = g_sequence_get_begin_iter (ephy_bookmark_get_tags (bookmark));
+       !g_sequence_iter_is_end (iter); iter = g_sequence_iter_next (iter))
+    ephy_bookmarks_manager_add_tag (manager, g_sequence_get (iter));
+
+  g_object_unref (parser);
+
+out:
+  service = ephy_shell_get_sync_service (ephy_shell_get_default ());
   ephy_sync_service_release_next_storage_message (service);
 }
 
 void
-ephy_sync_service_create_bookmarks_storage_collection (EphySyncService *self)
+ephy_sync_service_download_bookmark (EphySyncService *self,
+                                     EphyBookmark    *bookmark)
 {
-  EphyBookmark *dummy;
   gchar *endpoint;
-  gchar *bso;
 
   g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
   g_return_if_fail (ephy_sync_service_is_signed_in (self));
+  g_return_if_fail (EPHY_IS_BOOKMARK (bookmark));
 
-  endpoint = g_strdup_printf ("storage/%s/%s", EPHY_BOOKMARKS_COLLECTION, DUMMY_BOOKMARK_ID);
-  dummy = ephy_bookmark_new (g_strdup (DUMMY_BOOKMARK_ID),
-                             g_strdup (DUMMY_BOOKMARK_ID),
-                             g_sequence_new (g_free));
-  ephy_bookmark_set_id (dummy, DUMMY_BOOKMARK_ID);
-  bso = ephy_bookmark_to_bso (dummy);
-
-  /* Send a dummy BSO to the Storage Server so it will create the
-   * EPHY_BOOKMARKS_COLLECTION collection if it doesn't exist already.
-   */
-  ephy_sync_service_send_storage_message (self, endpoint, SOUP_METHOD_PUT,
-                                          bso, -1, 0,
-                                          create_bookmarks_storage_collection_response_cb, NULL);
+  endpoint = g_strdup_printf ("storage/%s/%s",
+                              EPHY_BOOKMARKS_COLLECTION,
+                              ephy_bookmark_get_id (bookmark));
+  ephy_sync_service_send_storage_message (self, endpoint,
+                                          SOUP_METHOD_GET, NULL, -1, -1,
+                                          download_bookmark_response_cb, NULL);
 
   g_free (endpoint);
-  g_free (bso);
 }
 
 static void
-upload_bookmark_to_server_response_cb (SoupSession *session,
-                                       SoupMessage *message,
-                                       gpointer     user_data)
+delete_bookmark_conditional_response_cb (SoupSession *session,
+                                         SoupMessage *msg,
+                                         gpointer     user_data)
 {
   EphySyncService *service;
-  EphyBookmarksManager *manager;
   EphyBookmark *bookmark;
-  double last_modified;
+  EphyBookmarksManager *manager;
 
-  if (message->status_code == 200) {
-    bookmark = EPHY_BOOKMARK (user_data);
-    last_modified = g_ascii_strtod (message->response_body->data, NULL);
-    ephy_bookmark_set_modified (bookmark, last_modified);
-    manager = ephy_shell_get_bookmarks_manager (ephy_shell_get_default ());
-    ephy_bookmarks_manager_save_to_file_async (manager, NULL, NULL, NULL);
+  bookmark = EPHY_BOOKMARK (user_data);
+  manager = ephy_shell_get_bookmarks_manager (ephy_shell_get_default ());
 
-    LOG ("Successfully uploaded bookmark to server.");
-  } else if (message->status_code == 412) {
-    /* FIXME: A more recent value is on the server. See how to handle this. */
+  if (msg->status_code == 404) {
+    ephy_bookmarks_manager_remove_bookmark (manager, bookmark);
+  } else if (msg->status_code == 200) {
+    LOG ("The bookmark still exists on the server, don't delete it");
   } else {
-    LOG ("Failed to upload bookmark to the server. Status code: %u, response: %s",
-         message->status_code, message->response_body->data);
+    LOG ("Failed to delete conditionally. Status code: %u, response: %s",
+         msg->status_code, msg->response_body->data);
   }
 
-  service = ephy_shell_get_global_sync_service (ephy_shell_get_default ());
+  service = ephy_shell_get_sync_service (ephy_shell_get_default ());
+  ephy_sync_service_release_next_storage_message (service);
+}
+
+static void
+delete_bookmark_response_cb (SoupSession *session,
+                             SoupMessage *msg,
+                             gpointer     user_data)
+{
+  EphySyncService *service;
+
+  if (msg->status_code == 200)
+    LOG ("Successfully deleted the bookmark from the server");
+  else
+    LOG ("Failed to delete. Status code: %u, response: %s",
+         msg->status_code, msg->response_body->data);
+
+  service = ephy_shell_get_sync_service (ephy_shell_get_default ());
   ephy_sync_service_release_next_storage_message (service);
 }
 
 void
-ephy_sync_service_upload_bookmark_to_server (EphySyncService *self,
-                                             EphyBookmark    *bookmark,
-                                             gboolean         force)
+ephy_sync_service_delete_bookmark (EphySyncService *self,
+                                   EphyBookmark    *bookmark,
+                                   gboolean         conditional)
 {
   gchar *endpoint;
-  gchar *bso;
-  double modified;
 
   g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
   g_return_if_fail (ephy_sync_service_is_signed_in (self));
@@ -1044,230 +1132,264 @@ ephy_sync_service_upload_bookmark_to_server (EphySyncService *self,
   endpoint = g_strdup_printf ("storage/%s/%s",
                               EPHY_BOOKMARKS_COLLECTION,
                               ephy_bookmark_get_id (bookmark));
-  bso = ephy_bookmark_to_bso (bookmark);
-  modified = ephy_bookmark_get_modified (bookmark);
-  ephy_sync_service_send_storage_message (self, endpoint,
-                                          SOUP_METHOD_PUT, bso, -1,
-                                          force ? -1 : modified,
-                                          upload_bookmark_to_server_response_cb,
-                                          bookmark);
+
+  /* If the bookmark does not exist on the server, delete it from the local
+   * instance too. */
+  if (conditional == TRUE) {
+    ephy_sync_service_send_storage_message (self, endpoint,
+                                            SOUP_METHOD_GET, NULL, -1, -1,
+                                            delete_bookmark_conditional_response_cb,
+                                            bookmark);
+  } else {
+    ephy_sync_service_send_storage_message (self, endpoint,
+                                            SOUP_METHOD_DELETE, NULL, -1, -1,
+                                            delete_bookmark_response_cb, NULL);
+  }
 
   g_free (endpoint);
-  g_free (bso);
 }
 
 static void
-merge_local_and_remote_bookmarks_response_cb (SoupSession *session,
-                                              SoupMessage *message,
-                                              gpointer     user_data)
+sync_bookmarks_first_time_response_cb (SoupSession *session,
+                                       SoupMessage *msg,
+                                       gpointer     user_data)
 {
   EphySyncService *service;
   EphyBookmarksManager *manager;
   GSequence *bookmarks;
+  GSequenceIter *iter;
+  GHashTable *marked;
   JsonParser *parser;
   JsonArray *array;
-  GList *remotes = NULL;
-  GList *locals = NULL;
   const gchar *timestamp;
-  gboolean *marked_locals = NULL;
-  gboolean *marked_remotes = NULL;
   double server_time;
-  gsize i;
 
-  service = ephy_shell_get_global_sync_service (ephy_shell_get_default ());
+  service = ephy_shell_get_sync_service (ephy_shell_get_default ());
   manager = ephy_shell_get_bookmarks_manager (ephy_shell_get_default ());
   bookmarks = ephy_bookmarks_manager_get_bookmarks (manager);
+  marked = g_hash_table_new (g_direct_hash, g_direct_equal);
   parser = json_parser_new ();
-  json_parser_load_from_data (parser, message->response_body->data, -1, NULL);
+  json_parser_load_from_data (parser, msg->response_body->data, -1, NULL);
 
-  if (message->status_code != 200) {
-    LOG ("Failed to merge local and remote bookmarks. Status code: %u, response: %s",
-         message->status_code, message->response_body->data);
+  if (msg->status_code != 200) {
+    LOG ("Failed to do a first time sync. Status code: %u, response: %s",
+         msg->status_code, msg->response_body->data);
     goto out;
   }
 
-  /* Convert all BSOs to EphyBookmark objects. */
   array = json_node_get_array (json_parser_get_root (parser));
-  for (i = 0; i < json_array_get_length (array); i++) {
+  for (gsize i = 0; i < json_array_get_length (array); i++) {
     JsonObject *bso = json_array_get_object_element (array, i);
-    EphyBookmark *bookmark = ephy_bookmark_from_bso (bso);
+    EphyBookmark *remote = ephy_bookmark_from_bso (bso);
+    EphyBookmark *local;
 
-    if (g_strcmp0 (ephy_bookmark_get_id (bookmark), DUMMY_BOOKMARK_ID) == 0)
-      g_object_unref (bookmark);
-    else
-      remotes = g_list_append (remotes, bookmark);
-  }
+    if (remote == NULL)
+      continue;
 
-  /* If we have no remote bookmarks, then we directly upload all the local
-   * bookmarks to the server.
-   */
-  if (g_list_length (remotes) == 0) {
-    for (GSequenceIter *iter = g_sequence_get_begin_iter (bookmarks);
-         !g_sequence_iter_is_end (iter); iter = g_sequence_iter_next (iter))
-      ephy_sync_service_upload_bookmark_to_server (service, g_sequence_get (iter), TRUE);
-    goto out;
-  }
+    local = ephy_bookmarks_manager_get_bookmark_by_id (manager, ephy_bookmark_get_id (remote));
 
-  /* If we have no local bookmarks, then we add all the remote bookmarks to the
-   * local instance.
-   */
-  if (g_sequence_is_empty (bookmarks) == TRUE) {
-    for (GList *r = remotes; r != NULL; r = r->next)
-      ephy_bookmarks_manager_add_bookmark (manager, EPHY_BOOKMARK (r->data));
-    goto out;
-  }
+    if (local == NULL) {
+      local = ephy_bookmarks_manager_get_bookmark_by_url (manager, ephy_bookmark_get_url (remote));
 
-  /* If we have both local and remote bookmarks, then we merge them in the
-   * nicest way possible (see the two cases below).
-   */
-  for (GSequenceIter *iter = g_sequence_get_begin_iter (bookmarks);
-       !g_sequence_iter_is_end (iter); iter = g_sequence_iter_next (iter))
-    locals = g_list_append (locals, g_sequence_get (iter));
-
-  marked_locals = g_malloc0 (g_list_length (locals) * sizeof (gboolean));
-  marked_remotes = g_malloc0 (g_list_length (remotes) * sizeof (gboolean));
-
-  i = 0;
-  for (GList *l = locals; l != NULL; l = l->next, i++) {
-    EphyBookmark *local = EPHY_BOOKMARK (l->data);
-    const gchar *local_id = ephy_bookmark_get_id (local);
-    double local_modified = ephy_bookmark_get_modified (local);
-    const gchar *local_url = ephy_bookmark_get_url (local);
-    gsize j = 0;
-
-    for (GList *r = remotes; r != NULL; r = r->next, j++) {
-      if (marked_remotes[j] == FALSE) {
-        EphyBookmark *remote = EPHY_BOOKMARK (r->data);
-        const gchar *remote_id = ephy_bookmark_get_id (remote);
-        double remote_modified = ephy_bookmark_get_modified (remote);
-        const gchar *remote_url = ephy_bookmark_get_url (remote);
-
-        /* Case 1: same ids.
-         * Since ids are globally unique, having a bookmark on the server with
-         * the same id means that the bookmark has been synced before in the
-         * past. Keep the one with most recent modified timestamp.
-         */
-        if (g_strcmp0 (local_id, remote_id) == 0) {
-          if (local_modified > remote_modified) {
-            ephy_sync_service_upload_bookmark_to_server (service, local, TRUE);
-          } else if (remote_modified > local_modified) {
-            ephy_bookmarks_manager_remove_bookmark (manager, local);
-            ephy_bookmarks_manager_add_bookmark (manager, remote);
-          } else {
-            g_object_unref (remote);
-          }
-          marked_locals[i] = marked_remotes[j] = TRUE;
-          break;
-        }
+      /* If there is no local equivalent of the remote bookmark, then add it to
+       * the local instance together with its tags. */
+      if (local == NULL) {
+        ephy_bookmarks_manager_add_bookmark (manager, remote);
+
+        /* We have to manually add the tags to the bookmarks manager. */
+        for (iter = g_sequence_get_begin_iter (ephy_bookmark_get_tags (remote));
+             !g_sequence_iter_is_end (iter); iter = g_sequence_iter_next (iter))
+          ephy_bookmarks_manager_add_tag (manager, g_sequence_get (iter));
 
-        /* Case 2: different ids, same urls.
-         * Merge tags into the local bookmark, keep the remote id and upload it
-         * to server. The add_tag functions will ignore the duplicates themselves.
-         */
-        if (g_strcmp0 (local_url, remote_url) == 0) {
-          GSequence *tags = ephy_bookmark_get_tags (remote);
-
-          for (GSequenceIter *it = g_sequence_get_begin_iter (tags);
-               !g_sequence_iter_is_end (it); it = g_sequence_iter_next (it)) {
-            const gchar *tag = g_sequence_get (it);
-            ephy_bookmark_add_tag (local, tag);
-            ephy_bookmarks_manager_add_tag (manager, tag);
-          }
-
-          ephy_bookmark_set_id (local, remote_id);
-          ephy_sync_service_upload_bookmark_to_server (service, local, TRUE);
-          g_object_unref (remote);
-          marked_locals[i] = marked_remotes[j] = TRUE;
-          break;
+        g_hash_table_add (marked, remote);
+      }
+      /* If there is a local bookmark with the same url as the remote one, then
+       * merge tags into the local one, keep the remote id and upload it to the
+       * server. */
+      else {
+        for (iter = g_sequence_get_begin_iter (ephy_bookmark_get_tags (remote));
+             !g_sequence_iter_is_end (iter); iter = g_sequence_iter_next (iter)) {
+          ephy_bookmark_add_tag (local, g_sequence_get (iter));
+          ephy_bookmarks_manager_add_tag (manager, g_sequence_get (iter));
         }
+
+        ephy_bookmark_set_id (local, ephy_bookmark_get_id (remote));
+        ephy_sync_service_upload_bookmark (service, local, TRUE);
+        g_object_unref (remote);
+        g_hash_table_add (marked, local);
+      }
+    }
+    /* Having a local bookmark with the same id as the remote one means that the
+     * bookmark has been synced before in the past. Keep the one with the most
+     * recent modified timestamp. */
+    else {
+      if (ephy_bookmark_get_modified (remote) > ephy_bookmark_get_modified (local)) {
+        ephy_bookmarks_manager_remove_bookmark (manager, local);
+        ephy_bookmarks_manager_add_bookmark (manager, remote);
+
+        /* We have to manually add the tags to the bookmarks manager. */
+        for (iter = g_sequence_get_begin_iter (ephy_bookmark_get_tags (remote));
+             !g_sequence_iter_is_end (iter); iter = g_sequence_iter_next (iter))
+          ephy_bookmarks_manager_add_tag (manager, g_sequence_get (iter));
+
+        g_hash_table_add (marked, remote);
+      } else {
+        if (ephy_bookmark_get_modified (local) > ephy_bookmark_get_modified (remote))
+          ephy_sync_service_upload_bookmark (service, local, TRUE);
+
+        g_hash_table_add (marked, local);
+        g_object_unref (remote);
       }
     }
   }
 
-  /* The remaining unmarked local bookmarks will be directly uploaded to the server. */
-  i = 0;
-  for (GList *l = locals; l != NULL; l = l->next, i++)
-    if (marked_locals[i] == FALSE)
-      ephy_sync_service_upload_bookmark_to_server (service, EPHY_BOOKMARK (l->data), TRUE);
+  /* Upload the remaining local bookmarks to the server. */
+  for (iter = g_sequence_get_begin_iter (bookmarks);
+       !g_sequence_iter_is_end (iter); iter = g_sequence_iter_next (iter)) {
+    EphyBookmark *bookmark = g_sequence_get (iter);
 
-  /* The remaining unmarked remote bookmarks will be added to the local instance. */
-  i = 0;
-  for (GList *r = remotes; r != NULL; r = r->next, i++)
-    if (marked_remotes[i] == FALSE)
-      ephy_bookmarks_manager_add_bookmark (manager, EPHY_BOOKMARK (r->data));
+    if (g_hash_table_contains (marked, bookmark) == FALSE)
+      ephy_sync_service_upload_bookmark (service, bookmark, TRUE);
+  }
 
   /* Save changes to file. */
   ephy_bookmarks_manager_save_to_file_async (manager, NULL, NULL, NULL);
 
   /* Set the sync time. */
-  timestamp = soup_message_headers_get_one (message->response_headers, "X-Weave-Timestamp");
+  timestamp = soup_message_headers_get_one (msg->response_headers, "X-Weave-Timestamp");
   server_time = g_ascii_strtod (timestamp, NULL);
   ephy_sync_service_set_sync_time (service, server_time);
 
 out:
   g_object_unref (parser);
-  g_list_free (locals);
-  g_list_free (remotes);
-  g_free (marked_locals);
-  g_free (marked_remotes);
+  g_hash_table_unref (marked);
 
   ephy_sync_service_release_next_storage_message (service);
 }
 
-void
-ephy_sync_service_merge_local_and_remote_bookmarks (EphySyncService *self)
+static void
+sync_bookmarks_response_cb (SoupSession *session,
+                            SoupMessage *msg,
+                            gpointer     user_data)
 {
-  gchar *endpoint;
+  EphySyncService *service;
+  EphyBookmarksManager *manager;
+  GSequence *bookmarks;
+  GSequenceIter *iter;
+  JsonParser *parser;
+  JsonArray *array;
+  const gchar *timestamp;
+  double server_time;
 
-  g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
-  g_return_if_fail (ephy_sync_service_is_signed_in (self));
+  service = ephy_shell_get_sync_service (ephy_shell_get_default ());
+  manager = ephy_shell_get_bookmarks_manager (ephy_shell_get_default ());
+  bookmarks = ephy_bookmarks_manager_get_bookmarks (manager);
+  parser = json_parser_new ();
+  json_parser_load_from_data (parser, msg->response_body->data, -1, NULL);
 
-  endpoint = g_strdup_printf ("storage/%s?full=true", EPHY_BOOKMARKS_COLLECTION);
-  ephy_sync_service_send_storage_message (self, endpoint,
-                                          SOUP_METHOD_GET, NULL, -1, -1,
-                                          merge_local_and_remote_bookmarks_response_cb, NULL);
-  g_free (endpoint);
-}
+  /* Code 304 indicates that the resource has not been modifiedf. Therefore,
+   * only upload the local bookmarks that were not uploaded. */
+  if (msg->status_code == 304)
+    goto handle_local_bookmarks;
 
-static void
-storage_server_response_default_cb (SoupSession *session,
-                                    SoupMessage *message,
-                                    gpointer     user_data)
-{
-  EphySyncService *service;
+  if (msg->status_code != 200) {
+    LOG ("Failed to sync bookmarks. Status code: %u, response: %s",
+         msg->status_code, msg->response_body->data);
+    goto out;
+  }
+
+  array = json_node_get_array (json_parser_get_root (parser));
+  for (gsize i = 0; i < json_array_get_length (array); i++) {
+    JsonObject *bso = json_array_get_object_element (array, i);
+    EphyBookmark *remote = ephy_bookmark_from_bso (bso);
+    EphyBookmark *local;
+
+    if (remote == NULL)
+      continue;
+
+    local = ephy_bookmarks_manager_get_bookmark_by_id (manager, ephy_bookmark_get_id (remote));
+
+    if (local == NULL) {
+      ephy_bookmarks_manager_add_bookmark (manager, remote);
+
+      /* We have to manually add the tags to the bookmarks manager. */
+      for (iter = g_sequence_get_begin_iter (ephy_bookmark_get_tags (remote));
+           !g_sequence_iter_is_end (iter); iter = g_sequence_iter_next (iter))
+        ephy_bookmarks_manager_add_tag (manager, g_sequence_get (iter));
+    } else {
+      if (ephy_bookmark_get_modified (remote) > ephy_bookmark_get_modified (local)) {
+        ephy_bookmarks_manager_remove_bookmark (manager, local);
+        ephy_bookmarks_manager_add_bookmark (manager, remote);
+
+        /* We have to manually add the tags to the bookmarks manager. */
+        for (iter = g_sequence_get_begin_iter (ephy_bookmark_get_tags (remote));
+             !g_sequence_iter_is_end (iter); iter = g_sequence_iter_next (iter))
+          ephy_bookmarks_manager_add_tag (manager, g_sequence_get (iter));
+      } else {
+        if (ephy_bookmark_get_modified (local) > ephy_bookmark_get_modified (remote))
+          ephy_sync_service_upload_bookmark (service, local, TRUE);
+
+        g_object_unref (remote);
+      }
+    }
+  }
+
+handle_local_bookmarks:
+  for (iter = g_sequence_get_begin_iter (bookmarks);
+       !g_sequence_iter_is_end (iter); iter = g_sequence_iter_next (iter)) {
+    EphyBookmark *bookmark = EPHY_BOOKMARK (g_sequence_get (iter));
+
+    if (ephy_bookmark_is_uploaded (bookmark) == TRUE)
+      ephy_sync_service_delete_bookmark (service, bookmark, TRUE);
+    else
+      ephy_sync_service_upload_bookmark (service, bookmark, FALSE);
+  }
+
+  /* Save changes to file. */
+  ephy_bookmarks_manager_save_to_file_async (manager, NULL, NULL, NULL);
+
+  /* Set the sync time. */
+  timestamp = soup_message_headers_get_one (msg->response_headers, "X-Weave-Timestamp");
+  server_time = g_ascii_strtod (timestamp, NULL);
+  ephy_sync_service_set_sync_time (service, server_time);
 
-  log_all_message_info ("storage_server_response_default_cb", message);
+out:
+  g_object_unref (parser);
 
-  service = ephy_shell_get_global_sync_service (ephy_shell_get_default ());
   ephy_sync_service_release_next_storage_message (service);
 }
 
 void
-ephy_sync_service_check_bookmarks_storage_collection (EphySyncService *self)
+ephy_sync_service_sync_bookmarks (EphySyncService *self,
+                                  gboolean         first)
 {
   gchar *endpoint;
 
   g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
   g_return_if_fail (ephy_sync_service_is_signed_in (self));
 
-  endpoint = g_strdup_printf ("storage/%s", EPHY_BOOKMARKS_COLLECTION);
-  ephy_sync_service_send_storage_message (self, endpoint,
-                                          SOUP_METHOD_GET, NULL, -1, -1,
-                                          storage_server_response_default_cb, NULL);
+  endpoint = g_strdup_printf ("storage/%s?full=true", EPHY_BOOKMARKS_COLLECTION);
+
+  if (first == TRUE) {
+    ephy_sync_service_send_storage_message (self, endpoint,
+                                            SOUP_METHOD_GET, NULL, -1, -1,
+                                            sync_bookmarks_first_time_response_cb, NULL);
+  } else {
+    ephy_sync_service_send_storage_message (self, endpoint,
+                                            SOUP_METHOD_GET, NULL,
+                                            ephy_sync_service_get_sync_time (self), -1,
+                                            sync_bookmarks_response_cb, NULL);
+  }
+
   g_free (endpoint);
 }
 
-void
-ephy_sync_service_delete_bookmarks_storage_collection (EphySyncService *self)
+gboolean
+ephy_sync_service_do_periodical_sync (EphySyncService *self)
 {
-  gchar *endpoint;
+  g_return_val_if_fail (EPHY_IS_SYNC_SERVICE (self), G_SOURCE_REMOVE);
 
-  g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
-  g_return_if_fail (ephy_sync_service_is_signed_in (self));
+  ephy_sync_service_sync_bookmarks (self, FALSE);
 
-  endpoint = g_strdup_printf ("storage/%s", EPHY_BOOKMARKS_COLLECTION);
-  ephy_sync_service_send_storage_message (self, endpoint,
-                                          SOUP_METHOD_DELETE, NULL, -1, -1,
-                                          storage_server_response_default_cb, NULL);
-  g_free (endpoint);
+  return G_SOURCE_CONTINUE;
 }
diff --git a/src/ephy-sync-service.h b/src/ephy-sync-service.h
index 7d794e7..2bb0561 100644
--- a/src/ephy-sync-service.h
+++ b/src/ephy-sync-service.h
@@ -31,47 +31,54 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (EphySyncService, ephy_sync_service, EPHY, SYNC_SERVICE, GObject)
 
-EphySyncService *ephy_sync_service_new                                 (void);
-gboolean         ephy_sync_service_is_signed_in                        (EphySyncService *self);
-gchar           *ephy_sync_service_get_user_email                      (EphySyncService *self);
-void             ephy_sync_service_set_user_email                      (EphySyncService *self,
-                                                                        const gchar     *email);
-double           ephy_sync_service_get_sync_time                       (EphySyncService *self);
-void             ephy_sync_service_set_sync_time                       (EphySyncService *self,
-                                                                        double           time);
-gchar           *ephy_sync_service_get_token                           (EphySyncService   *self,
-                                                                        EphySyncTokenType  type);
-void             ephy_sync_service_set_token                           (EphySyncService   *self,
-                                                                        gchar             *value,
-                                                                        EphySyncTokenType  type);
-void             ephy_sync_service_set_and_store_tokens                (EphySyncService   *self,
-                                                                        gchar             *value,
-                                                                        EphySyncTokenType  type,
-                                                                        ...) G_GNUC_NULL_TERMINATED;
-void             ephy_sync_service_clear_storage_credentials           (EphySyncService *self);
-void             ephy_sync_service_clear_tokens                        (EphySyncService *self);
-void             ephy_sync_service_destroy_session                     (EphySyncService *self,
-                                                                        const gchar     *sessionToken);
-gboolean         ephy_sync_service_fetch_sync_keys                     (EphySyncService *self,
-                                                                        const gchar     *email,
-                                                                        const gchar     *keyFetchToken,
-                                                                        const gchar     *unwrapBKey);
-void             ephy_sync_service_send_storage_message                (EphySyncService     *self,
-                                                                        gchar               *endpoint,
-                                                                        const gchar         *method,
-                                                                        gchar               *request_body,
-                                                                        double               modified_since,
-                                                                        double               
unmodified_since,
-                                                                        SoupSessionCallback  callback,
-                                                                        gpointer             user_data);
-void             ephy_sync_service_release_next_storage_message        (EphySyncService *self);
-void             ephy_sync_service_create_bookmarks_storage_collection (EphySyncService *self);
-void             ephy_sync_service_upload_bookmark_to_server           (EphySyncService *self,
-                                                                        EphyBookmark    *bookmark,
-                                                                        gboolean         force);
-void             ephy_sync_service_merge_local_and_remote_bookmarks    (EphySyncService *self);
-void             ephy_sync_service_check_bookmarks_storage_collection  (EphySyncService *self);
-void             ephy_sync_service_delete_bookmarks_storage_collection (EphySyncService *self);
+EphySyncService *ephy_sync_service_new                          (void);
+gboolean         ephy_sync_service_is_signed_in                 (EphySyncService *self);
+gchar           *ephy_sync_service_get_user_email               (EphySyncService *self);
+void             ephy_sync_service_set_user_email               (EphySyncService *self,
+                                                                 const gchar     *email);
+double           ephy_sync_service_get_sync_time                (EphySyncService *self);
+void             ephy_sync_service_set_sync_time                (EphySyncService *self,
+                                                                 double           time);
+guint            ephy_sync_service_get_sync_frequency           (EphySyncService *self);
+void             ephy_sync_service_set_sync_frequency           (EphySyncService *self,
+                                                                 guint            sync_frequency);
+gchar           *ephy_sync_service_get_token                    (EphySyncService   *self,
+                                                                 EphySyncTokenType  type);
+void             ephy_sync_service_set_token                    (EphySyncService   *self,
+                                                                 gchar             *value,
+                                                                 EphySyncTokenType  type);
+void             ephy_sync_service_set_and_store_tokens         (EphySyncService   *self,
+                                                                 gchar             *value,
+                                                                 EphySyncTokenType  type,
+                                                                 ...) G_GNUC_NULL_TERMINATED;
+void             ephy_sync_service_clear_storage_credentials    (EphySyncService *self);
+void             ephy_sync_service_clear_tokens                 (EphySyncService *self);
+void             ephy_sync_service_destroy_session              (EphySyncService *self,
+                                                                 const gchar     *sessionToken);
+gboolean         ephy_sync_service_fetch_sync_keys              (EphySyncService *self,
+                                                                 const gchar     *email,
+                                                                 const gchar     *keyFetchToken,
+                                                                 const gchar     *unwrapBKey);
+void             ephy_sync_service_send_storage_message         (EphySyncService     *self,
+                                                                 gchar               *endpoint,
+                                                                 const gchar         *method,
+                                                                 gchar               *request_body,
+                                                                 double               modified_since,
+                                                                 double               unmodified_since,
+                                                                 SoupSessionCallback  callback,
+                                                                 gpointer             user_data);
+void             ephy_sync_service_release_next_storage_message (EphySyncService *self);
+void             ephy_sync_service_upload_bookmark              (EphySyncService *self,
+                                                                 EphyBookmark    *bookmark,
+                                                                 gboolean         force);
+void             ephy_sync_service_download_bookmark            (EphySyncService *self,
+                                                                 EphyBookmark    *bookmark);
+void             ephy_sync_service_delete_bookmark              (EphySyncService *self,
+                                                                 EphyBookmark    *bookmark,
+                                                                 gboolean         conditional);
+void             ephy_sync_service_sync_bookmarks               (EphySyncService *self,
+                                                                 gboolean         first);
+gboolean         ephy_sync_service_do_periodical_sync           (EphySyncService *self);
 
 G_END_DECLS
 
diff --git a/src/prefs-dialog.c b/src/prefs-dialog.c
index d473129..50b06e8 100644
--- a/src/prefs-dialog.c
+++ b/src/prefs-dialog.c
@@ -243,7 +243,7 @@ server_message_received_cb (WebKitUserContentManager *manager,
     inject_data_to_server (dialog, "message", "login", NULL);
     gtk_widget_set_visible (dialog->sync_sign_in_details, FALSE);
 
-    service = ephy_shell_get_global_sync_service (ephy_shell_get_default ());
+    service = ephy_shell_get_sync_service (ephy_shell_get_default ());
 
     /* Extract tokens. */
     data = json_object_get_object_member (detail, "data");
@@ -295,8 +295,13 @@ server_message_received_cb (WebKitUserContentManager *manager,
                                             g_strdup (sessionToken), TOKEN_SESSIONTOKEN,
                                             NULL);
 
-    /* Create our own bookmarks BSO collection on the Storage Server. */
-    ephy_sync_service_create_bookmarks_storage_collection (service);
+    /* Do a first time sync. */
+    ephy_sync_service_sync_bookmarks (service, TRUE);
+
+    /* Set a periodical sync. */
+    g_timeout_add_seconds (ephy_sync_service_get_sync_frequency (service),
+                           (GSourceFunc) ephy_sync_service_do_periodical_sync,
+                           service);
 
     /* Translators: the %s refers to the email of the currently logged in user. */
     gtk_label_set_markup (GTK_LABEL (dialog->sync_sign_out_details),
@@ -351,7 +356,7 @@ on_sync_sign_out_button_clicked (GtkWidget   *button,
 {
   EphySyncService *service;
 
-  service = ephy_shell_get_global_sync_service (ephy_shell_get_default ());
+  service = ephy_shell_get_sync_service (ephy_shell_get_default ());
 
   /* Destroy session and delete tokens. */
   ephy_sync_service_destroy_session (service, NULL);
@@ -1490,7 +1495,7 @@ setup_sync_page (PrefsDialog *dialog)
 {
   EphySyncService *service;
 
-  service = ephy_shell_get_global_sync_service (ephy_shell_get_default ());
+  service = ephy_shell_get_sync_service (ephy_shell_get_default ());
 
   if (ephy_sync_service_is_signed_in (service) == FALSE) {
     setup_fxa_sign_in_view (dialog);


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