[epiphany/wip/sync-rebase: 54/86] ephy-sync: Implement the sync logic



commit 6606f4f5bab9d3f15c5f88a1a521d6e510a386cd
Author: Gabriel Ivascu <ivascu gabriel59 gmail com>
Date:   Mon Aug 8 21:10:40 2016 +0300

    ephy-sync: Implement the sync logic

 data/org.gnome.epiphany.gschema.xml           |    5 +
 lib/ephy-prefs.h                              |    1 +
 src/Makefile.am                               |    2 -
 src/bookmarks/ephy-bookmark-properties-grid.c |   66 ++-
 src/bookmarks/ephy-bookmark.c                 |   74 ++-
 src/bookmarks/ephy-bookmark.h                 |    8 +
 src/bookmarks/ephy-bookmarks-manager.c        |   34 +-
 src/bookmarks/ephy-bookmarks-manager.h        |    2 +
 src/ephy-shell.c                              |   18 +-
 src/ephy-shell.h                              |    2 +-
 src/ephy-sync-bookmarks.c                     |  124 ---
 src/ephy-sync-bookmarks.h                     |   36 -
 src/ephy-sync-crypto.c                        |  927 +++++++++------------
 src/ephy-sync-crypto.h                        |  136 ++--
 src/ephy-sync-secret.c                        |   65 +-
 src/ephy-sync-secret.h                        |   18 +-
 src/ephy-sync-service.c                       | 1099 +++++++++++++++++--------
 src/ephy-sync-service.h                       |   61 +-
 src/ephy-sync-utils.c                         |  135 +++-
 src/ephy-sync-utils.h                         |   29 +-
 src/prefs-dialog.c                            |   25 +-
 21 files changed, 1616 insertions(+), 1251 deletions(-)
---
diff --git a/data/org.gnome.epiphany.gschema.xml b/data/org.gnome.epiphany.gschema.xml
index db77a1c..212f2be 100644
--- a/data/org.gnome.epiphany.gschema.xml
+++ b/data/org.gnome.epiphany.gschema.xml
@@ -82,6 +82,11 @@
                        <summary>The sync user currently logged in</summary>
                        <description>The email linked to the Firefox Account used to sync data with Mozilla's 
servers.</description>
                </key>
+               <key type="d" name="sync-time">
+                       <default>0</default>
+                       <summary>Sync timestamp</summary>
+                       <description>The timestamp at which last we had the last sync</description>
+               </key>
        </schema>
        <schema path="/org/gnome/epiphany/ui/" id="org.gnome.Epiphany.ui">
                <key type="b" name="always-show-tabs-bar">
diff --git a/lib/ephy-prefs.h b/lib/ephy-prefs.h
index cae64b5..d9b1643 100644
--- a/lib/ephy-prefs.h
+++ b/lib/ephy-prefs.h
@@ -111,6 +111,7 @@ typedef enum
 #define EPHY_PREFS_PROCESS_MODEL                  "process-model"
 #define EPHY_PREFS_MAX_PROCESSES                  "max-processes"
 #define EPHY_PREFS_SYNC_USER                      "sync-user"
+#define EPHY_PREFS_SYNC_TIME                      "sync-time"
 
 #define EPHY_PREFS_LOCKDOWN_SCHEMA            "org.gnome.Epiphany.lockdown"
 #define EPHY_PREFS_LOCKDOWN_FULLSCREEN        "disable-fullscreen"
diff --git a/src/Makefile.am b/src/Makefile.am
index 2341708..1be6894 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -44,8 +44,6 @@ libephymain_la_SOURCES = \
        ephy-session.h                          \
        ephy-shell.c                            \
        ephy-shell.h                            \
-       ephy-sync-bookmarks.c                   \
-       ephy-sync-bookmarks.h                   \
        ephy-sync-crypto.c                      \
        ephy-sync-crypto.h                      \
        ephy-sync-secret.c                      \
diff --git a/src/bookmarks/ephy-bookmark-properties-grid.c b/src/bookmarks/ephy-bookmark-properties-grid.c
index 73356a0..11f65ac 100644
--- a/src/bookmarks/ephy-bookmark-properties-grid.c
+++ b/src/bookmarks/ephy-bookmark-properties-grid.c
@@ -23,6 +23,7 @@
 #include "ephy-bookmarks-manager.h"
 #include "ephy-debug.h"
 #include "ephy-shell.h"
+#include "ephy-sync-service.h"
 #include "ephy-bookmarks-type-builtins.h"
 
 #include <libsoup/soup.h>
@@ -45,6 +46,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)
@@ -235,10 +240,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);
 
   if (self->type == EPHY_BOOKMARK_PROPERTIES_GRID_TYPE_DIALOG)
@@ -312,6 +320,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",
@@ -321,6 +330,7 @@ ephy_bookmark_properties_grid_constructed (GObject *object)
   if (self->type == EPHY_BOOKMARK_PROPERTIES_GRID_TYPE_DIALOG) {
     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_object_bind_property (GTK_ENTRY (self->address_entry), "text",
                             self->bookmark, "url",
@@ -328,6 +338,7 @@ ephy_bookmark_properties_grid_constructed (GObject *object)
   }
 
   /* 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);
@@ -340,8 +351,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);
@@ -359,10 +373,60 @@ ephy_bookmark_properties_grid_finalize (GObject *object)
 {
   EphyBookmarkPropertiesGrid *self = EPHY_BOOKMARK_PROPERTIES_GRID (object);
 
+  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,
                                              ephy_bookmarks_manager_save_to_file_warn_on_error_cb,
                                              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);
+
   G_OBJECT_CLASS (ephy_bookmark_properties_grid_parent_class)->finalize (object);
 }
 
diff --git a/src/bookmarks/ephy-bookmark.c b/src/bookmarks/ephy-bookmark.c
index dbcc956..945566c 100644
--- a/src/bookmarks/ephy-bookmark.c
+++ b/src/bookmarks/ephy-bookmark.c
@@ -24,7 +24,6 @@
 #include "ephy-sync-crypto.h"
 #include "ephy-sync-utils.h"
 
-#include <json-glib/json-glib.h>
 #include <string.h>
 
 struct _EphyBookmark {
@@ -37,6 +36,7 @@ struct _EphyBookmark {
 
   char        *id;
   double       modified;
+  gboolean     uploaded;
 };
 
 static JsonSerializableIface *serializable_iface = NULL;
@@ -192,7 +192,7 @@ ephy_bookmark_class_init (EphyBookmarkClass *klass)
 static void
 ephy_bookmark_init (EphyBookmark *self)
 {
-  self->id = ephy_sync_crypto_generate_random_string (12);
+  self->id = ephy_sync_crypto_generate_random_hex (32);
 }
 
 static JsonNode *
@@ -334,6 +334,17 @@ ephy_bookmark_get_title (EphyBookmark *bookmark)
   return bookmark->title;
 }
 
+void
+ephy_bookmark_set_id (EphyBookmark *self,
+                      const char   *id)
+{
+  g_return_if_fail (EPHY_IS_BOOKMARK (self));
+  g_return_if_fail (id != NULL);
+
+  g_free (self->id);
+  self->id = g_strdup (id);
+}
+
 const char *
 ephy_bookmark_get_id (EphyBookmark *self)
 {
@@ -360,6 +371,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)
 {
@@ -476,7 +504,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,
@@ -491,3 +519,43 @@ ephy_bookmark_to_bso (EphyBookmark *self)
 
   return bso;
 }
+
+EphyBookmark *
+ephy_bookmark_from_bso (JsonObject *bso)
+{
+  EphySyncService *service;
+  EphyBookmark *bookmark = NULL;
+  GObject *object;
+  GError *error = NULL;
+  guint8 *sync_key;
+  guint8 *decoded;
+  gsize decoded_len;
+  char *decrypted;
+
+  g_return_val_if_fail (bso != NULL, NULL);
+
+  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, strlen (decrypted), &error);
+
+  if (object == NULL) {
+    g_warning ("Failed to create GObject from data: %s", error->message);
+    g_error_free (error);
+    goto out;
+  }
+
+  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);
+  g_free (decrypted);
+
+  return bookmark;
+}
diff --git a/src/bookmarks/ephy-bookmark.h b/src/bookmarks/ephy-bookmark.h
index 76508bd..2bfc6e2 100644
--- a/src/bookmarks/ephy-bookmark.h
+++ b/src/bookmarks/ephy-bookmark.h
@@ -19,6 +19,7 @@
 #pragma once
 
 #include <glib-object.h>
+#include <json-glib/json-glib.h>
 
 G_BEGIN_DECLS
 
@@ -42,12 +43,18 @@ void                 ephy_bookmark_set_title           (EphyBookmark *self,
                                                         const char   *title);
 const char          *ephy_bookmark_get_title           (EphyBookmark *self);
 
+void                 ephy_bookmark_set_id              (EphyBookmark *self,
+                                                        const char   *id);
 const char          *ephy_bookmark_get_id              (EphyBookmark *self);
 
 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,
@@ -62,5 +69,6 @@ int                  ephy_bookmark_tags_compare        (const char *tag1,
                                                         const char *tag2);
 
 char                *ephy_bookmark_to_bso              (EphyBookmark *self);
+EphyBookmark        *ephy_bookmark_from_bso            (JsonObject *bso);
 
 G_END_DECLS
diff --git a/src/bookmarks/ephy-bookmarks-manager.c b/src/bookmarks/ephy-bookmarks-manager.c
index 02d89f4..40d4617 100644
--- a/src/bookmarks/ephy-bookmarks-manager.c
+++ b/src/bookmarks/ephy-bookmarks-manager.c
@@ -69,10 +69,13 @@ build_variant (EphyBookmark *bookmark)
   GSequence *tags;
   GSequenceIter *iter;
 
-  g_variant_builder_init (&builder, G_VARIANT_TYPE ("(xsas)"));
+  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);
@@ -349,6 +352,27 @@ ephy_bookmarks_manager_get_bookmark_by_url (EphyBookmarksManager *self,
   return NULL;
 }
 
+EphyBookmark *
+ephy_bookmarks_manager_get_bookmark_by_id (EphyBookmarksManager *self,
+                                           const char           *id)
+{
+  GSequenceIter *iter;
+
+  g_return_val_if_fail (EPHY_IS_BOOKMARKS_MANAGER (self), FALSE);
+  g_return_val_if_fail (id != NULL, FALSE);
+
+  for (iter = g_sequence_get_begin_iter (self->bookmarks);
+       !g_sequence_iter_is_end (iter);
+       iter = g_sequence_iter_next (iter)) {
+    EphyBookmark *bookmark = g_sequence_get (iter);
+
+    if (g_strcmp0 (ephy_bookmark_get_id (bookmark), id) == 0)
+      return bookmark;
+  }
+
+  return NULL;
+}
+
 void
 ephy_bookmarks_manager_create_tag (EphyBookmarksManager *self, const char *tag)
 {
@@ -535,11 +559,14 @@ ephy_bookmarks_manager_load_from_file (EphyBookmarksManager *self)
     char *tag;
     const char *title;
     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&sas)", &time_added, &title, &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);
@@ -553,6 +580,9 @@ ephy_bookmarks_manager_load_from_file (EphyBookmarksManager *self)
     /* Create the new bookmark. */
     bookmark = ephy_bookmark_new (list[i], title, tags);
     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/bookmarks/ephy-bookmarks-manager.h b/src/bookmarks/ephy-bookmarks-manager.h
index bf906d1..ab79df3 100644
--- a/src/bookmarks/ephy-bookmarks-manager.h
+++ b/src/bookmarks/ephy-bookmarks-manager.h
@@ -36,6 +36,8 @@ void         ephy_bookmarks_manager_remove_bookmark               (EphyBookmarks
                                                                    EphyBookmark         *bookmark);
 EphyBookmark *ephy_bookmarks_manager_get_bookmark_by_url          (EphyBookmarksManager *self,
                                                                    const char           *url);
+EphyBookmark *ephy_bookmarks_manager_get_bookmark_by_id           (EphyBookmarksManager *self,
+                                                                   const char           *id);
 
 void         ephy_bookmarks_manager_create_tag                    (EphyBookmarksManager *self,
                                                                    const char           *tag);
diff --git a/src/ephy-shell.c b/src/ephy-shell.c
index 6f4e7e7..24dffae 100644
--- a/src/ephy-shell.c
+++ b/src/ephy-shell.c
@@ -563,6 +563,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 23dfe89..3ec9e60 100644
--- a/src/ephy-shell.h
+++ b/src/ephy-shell.h
@@ -100,7 +100,7 @@ GNetworkMonitor *ephy_shell_get_net_monitor              (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 947a69e..73a2f30 100644
--- a/src/ephy-sync-crypto.c
+++ b/src/ephy-sync-crypto.c
@@ -19,6 +19,8 @@
 #include "config.h"
 #include "ephy-sync-crypto.h"
 
+#include "ephy-sync-utils.h"
+
 #include <glib/gstdio.h>
 #include <libsoup/soup.h>
 #include <nettle/aes.h>
@@ -28,125 +30,74 @@
 
 static const gchar hex_digits[] = "0123456789abcdef";
 
-static guint8 *
-concatenate_bytes (guint8 *first_data,
-                   gsize   first_length,
-                   ...) G_GNUC_NULL_TERMINATED;
-
 EphySyncCryptoHawkOptions *
-ephy_sync_crypto_hawk_options_new (gchar *app,
-                                   gchar *dlg,
-                                   gchar *ext,
-                                   gchar *content_type,
-                                   gchar *hash,
-                                   gchar *local_time_offset,
-                                   gchar *nonce,
-                                   gchar *payload,
-                                   gchar *timestamp)
-{
-  EphySyncCryptoHawkOptions *hawk_options;
-
-  hawk_options = g_slice_new (EphySyncCryptoHawkOptions);
-  hawk_options->app = app;
-  hawk_options->dlg = dlg;
-  hawk_options->ext = ext;
-  hawk_options->content_type = content_type;
-  hawk_options->hash = hash;
-  hawk_options->local_time_offset = local_time_offset;
-  hawk_options->nonce = nonce;
-  hawk_options->payload = payload;
-  hawk_options->timestamp = timestamp;
-
-  return hawk_options;
+ephy_sync_crypto_hawk_options_new (const gchar *app,
+                                   const gchar *dlg,
+                                   const gchar *ext,
+                                   const gchar *content_type,
+                                   const gchar *hash,
+                                   const gchar *local_time_offset,
+                                   const gchar *nonce,
+                                   const gchar *payload,
+                                   const gchar *timestamp)
+{
+  EphySyncCryptoHawkOptions *options;
+
+  options = g_slice_new (EphySyncCryptoHawkOptions);
+  options->app = g_strdup (app);
+  options->dlg = g_strdup (dlg);
+  options->ext = g_strdup (ext);
+  options->content_type = g_strdup (content_type);
+  options->hash = g_strdup (hash);
+  options->local_time_offset = g_strdup (local_time_offset);
+  options->nonce = g_strdup (nonce);
+  options->payload = g_strdup (payload);
+  options->timestamp = g_strdup (timestamp);
+
+  return options;
 }
 
 static EphySyncCryptoHawkArtifacts *
-ephy_sync_crypto_hawk_artifacts_new (gchar *app,
-                                     gchar *dlg,
-                                     gchar *ext,
-                                     gchar *hash,
-                                     gchar *host,
-                                     gchar *method,
-                                     gchar *nonce,
-                                     gchar *port,
-                                     gchar *resource,
-                                     gchar *ts)
-{
-  EphySyncCryptoHawkArtifacts *hawk_artifacts;
-
-  hawk_artifacts = g_slice_new (EphySyncCryptoHawkArtifacts);
-  hawk_artifacts->app = app;
-  hawk_artifacts->dlg = dlg;
-  hawk_artifacts->ext = ext;
-  hawk_artifacts->hash = hash;
-  hawk_artifacts->host = host;
-  hawk_artifacts->method = method;
-  hawk_artifacts->nonce = nonce;
-  hawk_artifacts->port = port;
-  hawk_artifacts->resource = resource;
-  hawk_artifacts->ts = ts;
-
-  return hawk_artifacts;
-}
-
-static EphySyncCryptoHawkHeader *
-ephy_sync_crypto_hawk_header_new (gchar                       *header,
-                                  EphySyncCryptoHawkArtifacts *artifacts)
-{
-  EphySyncCryptoHawkHeader *hawk_header;
-
-  hawk_header = g_slice_new (EphySyncCryptoHawkHeader);
-  hawk_header->header = header;
-  hawk_header->artifacts = artifacts;
-
-  return hawk_header;
-}
-
-static EphySyncCryptoProcessedKFT *
-ephy_sync_crypto_processed_kft_new (guint8 *tokenID,
-                                    guint8 *reqHMACkey,
-                                    guint8 *respHMACkey,
-                                    guint8 *respXORkey)
-{
-  EphySyncCryptoProcessedKFT *processed_kft;
-
-  processed_kft = g_slice_new (EphySyncCryptoProcessedKFT);
-  processed_kft->tokenID = tokenID;
-  processed_kft->reqHMACkey = reqHMACkey;
-  processed_kft->respHMACkey = respHMACkey;
-  processed_kft->respXORkey = respXORkey;
-
-  return processed_kft;
-}
 
-static EphySyncCryptoProcessedST *
-ephy_sync_crypto_processed_st_new (guint8 *tokenID,
-                                   guint8 *reqHMACkey,
-                                   guint8 *requestKey)
+ephy_sync_crypto_hawk_artifacts_new (const gchar *app,
+                                     const gchar *dlg,
+                                     const gchar *ext,
+                                     const gchar *hash,
+                                     const gchar *host,
+                                     const gchar *method,
+                                     const gchar *nonce,
+                                     guint        port,
+                                     const gchar *resource,
+                                     gint64       ts)
 {
-  EphySyncCryptoProcessedST *processed_st;
+  EphySyncCryptoHawkArtifacts *artifacts;
 
-  processed_st = g_slice_new (EphySyncCryptoProcessedST);
-  processed_st->tokenID = tokenID;
-  processed_st->reqHMACkey = reqHMACkey;
-  processed_st->requestKey = requestKey;
+  artifacts = g_slice_new (EphySyncCryptoHawkArtifacts);
+  artifacts->app = g_strdup (app);
+  artifacts->dlg = g_strdup (dlg);
+  artifacts->ext = g_strdup (ext);
+  artifacts->hash = g_strdup (hash);
+  artifacts->host = g_strdup (host);
+  artifacts->method = g_strdup (method);
+  artifacts->nonce = g_strdup (nonce);
+  artifacts->port = g_strdup_printf ("%u", port);
+  artifacts->resource = g_strdup (resource);
+  artifacts->ts = g_strdup_printf ("%ld", ts);
 
-  return processed_st;
+  return artifacts;
 }
 
-static EphySyncCryptoSyncKeys *
-ephy_sync_crypto_sync_keys_new (guint8 *kA,
-                                guint8 *kB,
-                                guint8 *wrapKB)
+static EphySyncCryptoHawkHeader *
+ephy_sync_crypto_hawk_header_new (gchar                       *header,
+                                  EphySyncCryptoHawkArtifacts *artifacts)
 {
-  EphySyncCryptoSyncKeys *sync_keys;
+  EphySyncCryptoHawkHeader *hheader;
 
-  sync_keys = g_slice_new (EphySyncCryptoSyncKeys);
-  sync_keys->kA = kA;
-  sync_keys->kB = kB;
-  sync_keys->wrapKB = wrapKB;
+  hheader = g_slice_new (EphySyncCryptoHawkHeader);
+  hheader->header = header;
+  hheader->artifacts = artifacts;
 
-  return sync_keys;
+  return hheader;
 }
 
 static EphySyncCryptoRSAKeyPair *
@@ -163,88 +114,51 @@ ephy_sync_crypto_rsa_key_pair_new (struct rsa_public_key  public,
 }
 
 void
-ephy_sync_crypto_hawk_options_free (EphySyncCryptoHawkOptions *hawk_options)
-{
-  g_return_if_fail (hawk_options != NULL);
-
-  g_free (hawk_options->app);
-  g_free (hawk_options->dlg);
-  g_free (hawk_options->ext);
-  g_free (hawk_options->content_type);
-  g_free (hawk_options->hash);
-  g_free (hawk_options->local_time_offset);
-  g_free (hawk_options->nonce);
-  g_free (hawk_options->payload);
-  g_free (hawk_options->timestamp);
-
-  g_slice_free (EphySyncCryptoHawkOptions, hawk_options);
-}
-
-static void
-ephy_sync_crypto_hawk_artifacts_free (EphySyncCryptoHawkArtifacts *hawk_artifacts)
-{
-  g_return_if_fail (hawk_artifacts != NULL);
-
-  g_free (hawk_artifacts->app);
-  g_free (hawk_artifacts->dlg);
-  g_free (hawk_artifacts->ext);
-  g_free (hawk_artifacts->hash);
-  g_free (hawk_artifacts->host);
-  g_free (hawk_artifacts->method);
-  g_free (hawk_artifacts->nonce);
-  g_free (hawk_artifacts->port);
-  g_free (hawk_artifacts->resource);
-  g_free (hawk_artifacts->ts);
-
-  g_slice_free (EphySyncCryptoHawkArtifacts, hawk_artifacts);
-}
-
-void
-ephy_sync_crypto_hawk_header_free (EphySyncCryptoHawkHeader *hawk_header)
-{
-  g_return_if_fail (hawk_header != NULL);
-
-  g_free (hawk_header->header);
-  ephy_sync_crypto_hawk_artifacts_free (hawk_header->artifacts);
-
-  g_slice_free (EphySyncCryptoHawkHeader, hawk_header);
-}
-
-void
-ephy_sync_crypto_processed_kft_free (EphySyncCryptoProcessedKFT *processed_kft)
+ephy_sync_crypto_hawk_options_free (EphySyncCryptoHawkOptions *options)
 {
-  g_return_if_fail (processed_kft != NULL);
+  g_return_if_fail (options != NULL);
 
-  g_free (processed_kft->tokenID);
-  g_free (processed_kft->reqHMACkey);
-  g_free (processed_kft->respHMACkey);
-  g_free (processed_kft->respXORkey);
+  g_free (options->app);
+  g_free (options->dlg);
+  g_free (options->ext);
+  g_free (options->content_type);
+  g_free (options->hash);
+  g_free (options->local_time_offset);
+  g_free (options->nonce);
+  g_free (options->payload);
+  g_free (options->timestamp);
 
-  g_slice_free (EphySyncCryptoProcessedKFT, processed_kft);
+  g_slice_free (EphySyncCryptoHawkOptions, options);
 }
 
-void
-ephy_sync_crypto_processed_st_free (EphySyncCryptoProcessedST *processed_st)
+static void
+ephy_sync_crypto_hawk_artifacts_free (EphySyncCryptoHawkArtifacts *artifacts)
 {
-  g_return_if_fail (processed_st != NULL);
+  g_return_if_fail (artifacts != NULL);
 
-  g_free (processed_st->tokenID);
-  g_free (processed_st->reqHMACkey);
-  g_free (processed_st->requestKey);
+  g_free (artifacts->app);
+  g_free (artifacts->dlg);
+  g_free (artifacts->ext);
+  g_free (artifacts->hash);
+  g_free (artifacts->host);
+  g_free (artifacts->method);
+  g_free (artifacts->nonce);
+  g_free (artifacts->port);
+  g_free (artifacts->resource);
+  g_free (artifacts->ts);
 
-  g_slice_free (EphySyncCryptoProcessedST, processed_st);
+  g_slice_free (EphySyncCryptoHawkArtifacts, artifacts);
 }
 
 void
-ephy_sync_crypto_sync_keys_free (EphySyncCryptoSyncKeys *sync_keys)
+ephy_sync_crypto_hawk_header_free (EphySyncCryptoHawkHeader *hheader)
 {
-  g_return_if_fail (sync_keys != NULL);
+  g_return_if_fail (hheader != NULL);
 
-  g_free (sync_keys->kA);
-  g_free (sync_keys->kB);
-  g_free (sync_keys->wrapKB);
+  g_free (hheader->header);
+  ephy_sync_crypto_hawk_artifacts_free (hheader->artifacts);
 
-  g_slice_free (EphySyncCryptoSyncKeys, sync_keys);
+  g_slice_free (EphySyncCryptoHawkHeader, hheader);
 }
 
 void
@@ -259,18 +173,23 @@ ephy_sync_crypto_rsa_key_pair_free (EphySyncCryptoRSAKeyPair *keypair)
 }
 
 static gchar *
-kw (const gchar *name)
+ephy_sync_crypto_kw (const gchar *name)
 {
+  g_return_val_if_fail (name != NULL, NULL);
+
   return g_strconcat ("identity.mozilla.com/picl/v1/", name, NULL);
 }
 
 static guint8 *
-xor (guint8 *a,
-     guint8 *b,
-     gsize   length)
+ephy_sync_crypto_xor (guint8 *a,
+                      guint8 *b,
+                      gsize   length)
 {
   guint8 *xored;
 
+  g_return_val_if_fail (a != NULL, NULL);
+  g_return_val_if_fail (b != NULL, NULL);
+
   xored = g_malloc (length);
   for (gsize i = 0; i < length; i++)
     xored[i] = a[i] ^ b[i];
@@ -279,54 +198,23 @@ xor (guint8 *a,
 }
 
 static gboolean
-are_equal (guint8 *a,
-           guint8 *b)
+ephy_sync_crypto_equals (guint8 *a,
+                         guint8 *b,
+                         gsize   length)
 {
-  gchar *a_hex;
-  gchar *b_hex;
-  gboolean retval;
-
-  a_hex = ephy_sync_crypto_encode_hex (a, 0);
-  b_hex = ephy_sync_crypto_encode_hex (b, 0);
-  retval = g_str_equal (a_hex, b_hex);
-
-  g_free (a_hex);
-  g_free (b_hex);
-
-  return retval;
-}
+  g_return_val_if_fail (a != NULL, FALSE);
+  g_return_val_if_fail (b != NULL, FALSE);
 
-static gchar *
-find_and_replace_string (const gchar *src,
-                         const gchar *find,
-                         const gchar *repl)
-{
-  const gchar *haystack = src;
-  const gchar *needle = NULL;
-  gsize haystack_length = strlen (src);
-  gsize find_length = strlen (find);
-  gsize repl_length = strlen (repl);
-  gsize new_length = 0;
-  gsize skip_length = 0;
-  gchar *new = g_malloc (haystack_length + 1);
-
-  while ((needle = g_strstr_len (haystack, -1, find)) != NULL) {
-    haystack_length += find_length - repl_length;
-    new = g_realloc (new, haystack_length + 1);
-    skip_length = needle - haystack;
-    memcpy (new + new_length, haystack, skip_length);
-    memcpy (new + new_length + skip_length, repl, repl_length);
-    new_length += skip_length + repl_length;
-    haystack = needle + find_length;
-  }
-  strcpy (new + new_length, haystack);
+  for (gsize i = 0; i < length; i++)
+    if (a[i] != b[i])
+      return FALSE;
 
-  return new;
+  return TRUE;
 }
 
 static gchar *
-normalize_string (const gchar                 *mac_type,
-                  EphySyncCryptoHawkArtifacts *artifacts)
+ephy_sync_crypto_normalize_string (const gchar                 *type,
+                                   EphySyncCryptoHawkArtifacts *artifacts)
 {
   gchar *host;
   gchar *info;
@@ -335,27 +223,22 @@ normalize_string (const gchar                 *mac_type,
   gchar *normalized;
   gchar *tmp;
 
-  g_return_val_if_fail (mac_type, NULL);
-  g_return_val_if_fail (artifacts, NULL);
+  g_return_val_if_fail (type != NULL, NULL);
+  g_return_val_if_fail (artifacts != NULL, NULL);
 
-  info = g_strdup_printf ("hawk.%d.%s", HAWK_VERSION, mac_type);
+  info = g_strdup_printf ("hawk.%d.%s", HAWK_VERSION, type);
   method = g_ascii_strup (artifacts->method, -1);
   host = g_ascii_strdown (artifacts->host, -1);
 
   normalized = g_strjoin ("\n",
-                          info,
-                          artifacts->ts,
-                          artifacts->nonce,
-                          method,
-                          artifacts->resource,
-                          host,
-                          artifacts->port,
-                          artifacts->hash ? artifacts->hash : "",
+                          info, artifacts->ts, artifacts->nonce,
+                          method, artifacts->resource, host,
+                          artifacts->port, artifacts->hash ? artifacts->hash : "",
                           NULL);
 
   if (artifacts->ext && strlen (artifacts->ext) > 0) {
-    tmp = find_and_replace_string (artifacts->ext, "\\", "\\\\");
-    n_ext = find_and_replace_string (tmp, "\n", "\\n");
+    tmp = ephy_sync_utils_find_and_replace (artifacts->ext, "\\", "\\\\");
+    n_ext = ephy_sync_utils_find_and_replace (tmp, "\n", "\\n");
     g_free (tmp);
   }
 
@@ -378,11 +261,13 @@ normalize_string (const gchar                 *mac_type,
 }
 
 static gchar *
-parse_content_type (const gchar *content_type)
+ephy_sync_crypto_parse_content_type (const gchar *content_type)
 {
   gchar **tokens;
   gchar *retval;
 
+  g_return_val_if_fail (content_type != NULL, NULL);
+
   tokens = g_strsplit (content_type, ";", -1);
   retval = g_ascii_strdown (g_strstrip (tokens[0]), -1);
   g_strfreev (tokens);
@@ -391,8 +276,8 @@ parse_content_type (const gchar *content_type)
 }
 
 static gchar *
-calculate_payload_hash (const gchar *payload,
-                        const gchar *content_type)
+ephy_sync_crypto_calculate_payload_hash (const gchar *payload,
+                                         const gchar *content_type)
 {
   guint8 *digest;
   gchar *digest_hex;
@@ -400,14 +285,12 @@ calculate_payload_hash (const gchar *payload,
   gchar *update;
   gchar *hash;
 
-  g_return_val_if_fail (payload, NULL);
-  g_return_val_if_fail (content_type, NULL);
+  g_return_val_if_fail (payload != NULL, NULL);
+  g_return_val_if_fail (content_type != NULL, NULL);
 
-  content = parse_content_type (content_type);
+  content = ephy_sync_crypto_parse_content_type (content_type);
   update = g_strdup_printf ("hawk.%d.payload\n%s\n%s\n",
-                            HAWK_VERSION,
-                            content,
-                            payload);
+                            HAWK_VERSION, content, payload);
 
   digest_hex = g_compute_checksum_for_string (G_CHECKSUM_SHA256, update, -1);
   digest = ephy_sync_crypto_decode_hex (digest_hex);
@@ -422,24 +305,22 @@ calculate_payload_hash (const gchar *payload,
 }
 
 static gchar *
-calculate_mac (const gchar                 *mac_type,
-               guint8                      *key,
-               gsize                        key_length,
-               EphySyncCryptoHawkArtifacts *artifacts)
+ephy_sync_crypto_calculate_mac (const gchar                 *type,
+                                guint8                      *key,
+                                gsize                        key_len,
+                                EphySyncCryptoHawkArtifacts *artifacts)
 {
   guint8 *digest;
   gchar *digest_hex;
   gchar *normalized;
   gchar *mac;
 
-  g_return_val_if_fail (mac_type, NULL);
-  g_return_val_if_fail (key, NULL);
-  g_return_val_if_fail (artifacts, NULL);
+  g_return_val_if_fail (type != NULL, NULL);
+  g_return_val_if_fail (key != NULL, NULL);
+  g_return_val_if_fail (artifacts != NULL, NULL);
 
-  normalized = normalize_string (mac_type, artifacts);
-  digest_hex = g_compute_hmac_for_string (G_CHECKSUM_SHA256,
-                                          key, key_length,
-                                          normalized, -1);
+  normalized = ephy_sync_crypto_normalize_string (type, artifacts);
+  digest_hex = g_compute_hmac_for_string (G_CHECKSUM_SHA256, key, key_len, normalized, -1);
   digest = ephy_sync_crypto_decode_hex (digest_hex);
   mac = g_base64_encode (digest, g_checksum_type_get_length (G_CHECKSUM_SHA256));
 
@@ -451,66 +332,33 @@ calculate_mac (const gchar                 *mac_type,
 }
 
 static gchar *
-append_token_to_header (gchar       *header,
-                        const gchar *token_name,
-                        gchar       *token_value)
+ephy_sync_crypto_append_to_header (gchar       *header,
+                                   const gchar *name,
+                                   gchar       *value)
 {
   gchar *new_header;
   gchar *tmp;
 
+  g_return_val_if_fail (header != NULL, NULL);
+  g_return_val_if_fail (name != NULL, NULL);
+  g_return_val_if_fail (value != NULL, NULL);
+
   tmp = header;
-  new_header = g_strconcat (header, ", ",
-                            token_name, "=\"",
-                            token_value, "\"",
-                            NULL);
+  new_header = g_strconcat (header, ", ", name, "=\"", value, "\"", NULL);
   g_free (tmp);
 
   return new_header;
 }
 
-static guint8 *
-concatenate_bytes (guint8 *first_data,
-                   gsize   first_length,
-                   ...)
-{
-  va_list args;
-  guint8 *data;
-  guint8 *out;
-  gsize length;
-  gsize out_length;
-
-  out_length = first_length;
-  out = g_malloc (out_length);
-  memcpy (out, first_data, out_length);
-
-  va_start (args, first_length);
-
-  while ((data = va_arg (args, guint8 *)) != NULL) {
-    length = va_arg (args, gsize);
-    out = g_realloc (out, out_length + length);
-    memcpy (out + out_length, data, length);
-    out_length += length;
-  }
-
-  va_end (args);
-
-  return out;
-}
-
-/*
- * HMAC-based Extract-and-Expand Key Derivation Function.
- * Uses sha256 as hash function.
- * https://tools.ietf.org/html/rfc5869
- */
 static void
-hkdf (guint8 *in,
-      gsize   in_length,
-      guint8 *salt,
-      gsize   salt_length,
-      guint8 *info,
-      gsize   info_length,
-      guint8 *out,
-      gsize   out_length)
+ephy_sync_crypto_hkdf (guint8 *in,
+                       gsize   in_len,
+                       guint8 *salt,
+                       gsize   salt_len,
+                       guint8 *info,
+                       gsize   info_len,
+                       guint8 *out,
+                       gsize   out_len)
 {
   gchar *prk_hex;
   gchar *tmp_hex;
@@ -519,56 +367,53 @@ hkdf (guint8 *in,
   guint8 *out_full;
   guint8 *data;
   guint8 counter;
-  gsize hash_length;
-  gsize data_length;
+  gsize hash_len;
+  gsize data_len;
   gsize n;
 
-  hash_length = g_checksum_type_get_length (G_CHECKSUM_SHA256);
-  g_assert (out_length <= hash_length * 255);
+  g_return_if_fail (in != NULL);
+  g_return_if_fail (info != NULL);
+  g_return_if_fail (out != NULL);
 
-  /* If salt value was not provided, use an array of hash_length zeros */
+  hash_len = g_checksum_type_get_length (G_CHECKSUM_SHA256);
+  g_assert (out_len <= hash_len * 255);
+
+  /* If salt value was not provided, use an array of hash_len zeros */
   if (salt == NULL) {
-    salt = g_malloc0 (hash_length);
-    salt_length = hash_length;
+    salt = g_malloc0 (hash_len);
+    salt_len = hash_len;
   }
 
   /* Step 1: Extract */
-  prk_hex = g_compute_hmac_for_data (G_CHECKSUM_SHA256,
-                                     salt, salt_length,
-                                     in, in_length);
+  prk_hex = g_compute_hmac_for_data (G_CHECKSUM_SHA256, salt, salt_len, in, in_len);
   prk = ephy_sync_crypto_decode_hex (prk_hex);
 
   /* Step 2: Expand */
   counter = 1;
-  n = (out_length + hash_length - 1) / hash_length;
-  out_full = g_malloc (n * hash_length);
+  n = (out_len + hash_len - 1) / hash_len;
+  out_full = g_malloc (n * hash_len);
 
   for (gsize i = 0; i < n; i++, counter++) {
     if (i == 0) {
-      data = concatenate_bytes (info, info_length,
-                                &counter, 1,
-                                NULL);
-      data_length = info_length + 1;
+      data = ephy_sync_utils_concatenate_bytes (info, info_len, &counter, 1, NULL);
+      data_len = info_len + 1;
     } else {
-      data = concatenate_bytes (out_full + (i - 1) * hash_length, hash_length,
-                                info, info_length,
-                                &counter, 1,
-                                NULL);
-      data_length = hash_length + info_length + 1;
+      data = ephy_sync_utils_concatenate_bytes (out_full + (i - 1) * hash_len, hash_len,
+                                                info, info_len, &counter, 1,
+                                                NULL);
+      data_len = hash_len + info_len + 1;
     }
 
-    tmp_hex = g_compute_hmac_for_data (G_CHECKSUM_SHA256,
-                                       prk, hash_length,
-                                       data, data_length);
+    tmp_hex = g_compute_hmac_for_data (G_CHECKSUM_SHA256, prk, hash_len, data, data_len);
     tmp = ephy_sync_crypto_decode_hex (tmp_hex);
-    memcpy (out_full + i * hash_length, tmp, hash_length);
+    memcpy (out_full + i * hash_len, tmp, hash_len);
 
     g_free (data);
     g_free (tmp);
     g_free (tmp_hex);
   }
 
-  memcpy (out, out_full, out_length);
+  memcpy (out, out_full, out_len);
 
   g_free (prk_hex);
   g_free (salt);
@@ -577,71 +422,81 @@ hkdf (guint8 *in,
 }
 
 static void
-random_func (void   *ctx,
-             gsize   length,
-             guint8 *dst)
+ephy_sync_crypto_random_gen (void   *ctx,
+                             gsize   length,
+                             guint8 *dst)
 {
   for (gsize i = 0; i < length; i++)
     dst[i] = g_random_int ();
 }
 
 static void
-base64_to_base64_urlsafe (gchar *text)
+ephy_sync_crypto_b64_to_b64_urlsafe (gchar *text)
 {
+  g_return_if_fail (text != NULL);
+
   /* Replace '+' with '-' and '/' with '_' */
   g_strcanon (text, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789=/", '-');
   g_strcanon (text, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789=-", '_');
 }
 
 static void
-base64_urlsafe_to_base64 (gchar *text)
+ephy_sync_crypto_b64_urlsafe_to_b64 (gchar *text)
 {
+  g_return_if_fail (text != NULL);
+
   /* Replace '-' with '+' and '_' with '/' */
   g_strcanon (text, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789=_", '+');
   g_strcanon (text, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789=+", '/');
 }
 
-EphySyncCryptoProcessedKFT *
-ephy_sync_crypto_process_key_fetch_token (const gchar *keyFetchToken)
+void
+ephy_sync_crypto_process_key_fetch_token (const gchar  *keyFetchToken,
+                                          guint8      **tokenID,
+                                          guint8      **reqHMACkey,
+                                          guint8      **respHMACkey,
+                                          guint8      **respXORkey)
 {
   guint8 *kft;
   guint8 *out1;
   guint8 *out2;
-  guint8 *tokenID;
-  guint8 *reqHMACkey;
-  guint8 *respHMACkey;
-  guint8 *respXORkey;
   guint8 *keyRequestKey;
   gchar *info_kft;
   gchar *info_keys;
 
+  g_return_if_fail (keyFetchToken != NULL);
+  g_return_if_fail (tokenID != NULL);
+  g_return_if_fail (reqHMACkey != NULL);
+  g_return_if_fail (respHMACkey != NULL);
+  g_return_if_fail (respXORkey != NULL);
+
   kft = ephy_sync_crypto_decode_hex (keyFetchToken);
-  info_kft = kw ("keyFetchToken");
-  info_keys = kw ("account/keys");
+  info_kft = ephy_sync_crypto_kw ("keyFetchToken");
+  info_keys = ephy_sync_crypto_kw ("account/keys");
   out1 = g_malloc (3 * EPHY_SYNC_TOKEN_LENGTH);
   out2 = g_malloc (3 * EPHY_SYNC_TOKEN_LENGTH);
 
-  hkdf (kft, EPHY_SYNC_TOKEN_LENGTH,
-        NULL, 0,
-        (guint8 *) info_kft, strlen (info_kft),
-        out1, 3 * EPHY_SYNC_TOKEN_LENGTH);
+  ephy_sync_crypto_hkdf (kft, EPHY_SYNC_TOKEN_LENGTH,
+                         NULL, 0,
+                         (guint8 *) info_kft, strlen (info_kft),
+                         out1, 3 * EPHY_SYNC_TOKEN_LENGTH);
 
-  tokenID = g_malloc (EPHY_SYNC_TOKEN_LENGTH);
-  reqHMACkey = g_malloc (EPHY_SYNC_TOKEN_LENGTH);
+  *tokenID = g_malloc (EPHY_SYNC_TOKEN_LENGTH);
+  *reqHMACkey = g_malloc (EPHY_SYNC_TOKEN_LENGTH);
   keyRequestKey = g_malloc (EPHY_SYNC_TOKEN_LENGTH);
-  memcpy (tokenID, out1, EPHY_SYNC_TOKEN_LENGTH);
-  memcpy (reqHMACkey, out1 + EPHY_SYNC_TOKEN_LENGTH, EPHY_SYNC_TOKEN_LENGTH);
+  memcpy (*tokenID, out1, EPHY_SYNC_TOKEN_LENGTH);
+  memcpy (*reqHMACkey, out1 + EPHY_SYNC_TOKEN_LENGTH, EPHY_SYNC_TOKEN_LENGTH);
   memcpy (keyRequestKey, out1 + 2 * EPHY_SYNC_TOKEN_LENGTH, EPHY_SYNC_TOKEN_LENGTH);
 
-  hkdf (keyRequestKey, EPHY_SYNC_TOKEN_LENGTH,
-        NULL, 0,
-        (guint8 *) info_keys, strlen (info_keys),
-        out2, 3 * EPHY_SYNC_TOKEN_LENGTH);
+  ephy_sync_crypto_hkdf (keyRequestKey, EPHY_SYNC_TOKEN_LENGTH,
+                         NULL, 0,
+                         (guint8 *) info_keys, strlen (info_keys),
+                         out2, 3 * EPHY_SYNC_TOKEN_LENGTH);
 
-  respHMACkey = g_malloc (EPHY_SYNC_TOKEN_LENGTH);
-  respXORkey = g_malloc (2 * EPHY_SYNC_TOKEN_LENGTH);
-  memcpy (respHMACkey, out2, EPHY_SYNC_TOKEN_LENGTH);
-  memcpy (respXORkey, out2 + EPHY_SYNC_TOKEN_LENGTH, 2 * EPHY_SYNC_TOKEN_LENGTH);
+  *respHMACkey = g_malloc (EPHY_SYNC_TOKEN_LENGTH);
+  *respXORkey = g_malloc (2 * EPHY_SYNC_TOKEN_LENGTH);
+  memcpy (*respHMACkey, out2, EPHY_SYNC_TOKEN_LENGTH);
+  memcpy (*respXORkey, out2 + EPHY_SYNC_TOKEN_LENGTH, 2 * EPHY_SYNC_TOKEN_LENGTH);
 
   g_free (kft);
   g_free (out1);
@@ -649,53 +504,51 @@ ephy_sync_crypto_process_key_fetch_token (const gchar *keyFetchToken)
   g_free (info_kft);
   g_free (info_keys);
   g_free (keyRequestKey);
-
-  return ephy_sync_crypto_processed_kft_new (tokenID,
-                                             reqHMACkey,
-                                             respHMACkey,
-                                             respXORkey);
 }
 
-EphySyncCryptoProcessedST *
-ephy_sync_crypto_process_session_token (const gchar *sessionToken)
+void
+ephy_sync_crypto_process_session_token (const gchar  *sessionToken,
+                                        guint8      **tokenID,
+                                        guint8      **reqHMACkey,
+                                        guint8      **requestKey)
 {
   guint8 *st;
   guint8 *out;
-  guint8 *tokenID;
-  guint8 *reqHMACkey;
-  guint8 *requestKey;
   gchar *info;
 
+  g_return_if_fail (sessionToken != NULL);
+  g_return_if_fail (tokenID != NULL);
+  g_return_if_fail (reqHMACkey != NULL);
+  g_return_if_fail (requestKey != NULL);
+
   st = ephy_sync_crypto_decode_hex (sessionToken);
-  info = kw ("sessionToken");
+  info = ephy_sync_crypto_kw ("sessionToken");
   out = g_malloc (3 * EPHY_SYNC_TOKEN_LENGTH);
 
-  hkdf (st, EPHY_SYNC_TOKEN_LENGTH,
-        NULL, 0,
-        (guint8 *) info, strlen (info),
-        out, 3 * EPHY_SYNC_TOKEN_LENGTH);
+  ephy_sync_crypto_hkdf (st, EPHY_SYNC_TOKEN_LENGTH,
+                         NULL, 0,
+                         (guint8 *) info, strlen (info),
+                         out, 3 * EPHY_SYNC_TOKEN_LENGTH);
 
-  tokenID = g_malloc (EPHY_SYNC_TOKEN_LENGTH);
-  reqHMACkey = g_malloc (EPHY_SYNC_TOKEN_LENGTH);
-  requestKey = g_malloc (EPHY_SYNC_TOKEN_LENGTH);
-  memcpy (tokenID, out, EPHY_SYNC_TOKEN_LENGTH);
-  memcpy (reqHMACkey, out + EPHY_SYNC_TOKEN_LENGTH, EPHY_SYNC_TOKEN_LENGTH);
-  memcpy (requestKey, out + 2 * EPHY_SYNC_TOKEN_LENGTH, EPHY_SYNC_TOKEN_LENGTH);
+  *tokenID = g_malloc (EPHY_SYNC_TOKEN_LENGTH);
+  *reqHMACkey = g_malloc (EPHY_SYNC_TOKEN_LENGTH);
+  *requestKey = g_malloc (EPHY_SYNC_TOKEN_LENGTH);
+  memcpy (*tokenID, out, EPHY_SYNC_TOKEN_LENGTH);
+  memcpy (*reqHMACkey, out + EPHY_SYNC_TOKEN_LENGTH, EPHY_SYNC_TOKEN_LENGTH);
+  memcpy (*requestKey, out + 2 * EPHY_SYNC_TOKEN_LENGTH, EPHY_SYNC_TOKEN_LENGTH);
 
   g_free (st);
   g_free (out);
   g_free (info);
-
-  return ephy_sync_crypto_processed_st_new (tokenID,
-                                            reqHMACkey,
-                                            requestKey);
 }
 
-EphySyncCryptoSyncKeys *
-ephy_sync_crypto_retrieve_sync_keys (const gchar *bundle,
-                                     guint8      *respHMACkey,
-                                     guint8      *respXORkey,
-                                     guint8      *unwrapBKey)
+void
+ephy_sync_crypto_compute_sync_keys (const gchar  *bundle,
+                                    guint8       *respHMACkey,
+                                    guint8       *respXORkey,
+                                    guint8       *unwrapBKey,
+                                    guint8      **kA,
+                                    guint8      **kB)
 {
   guint8 *bdl;
   guint8 *ciphertext;
@@ -703,16 +556,20 @@ ephy_sync_crypto_retrieve_sync_keys (const gchar *bundle,
   guint8 *respMAC2;
   guint8 *xored;
   guint8 *wrapKB;
-  guint8 *kA;
-  guint8 *kB;
   gchar *respMAC2_hex;
-  EphySyncCryptoSyncKeys *retval = NULL;
+
+  g_return_if_fail (bundle != NULL);
+  g_return_if_fail (respHMACkey != NULL);
+  g_return_if_fail (respXORkey != NULL);
+  g_return_if_fail (unwrapBKey != NULL);
+  g_return_if_fail (kA != NULL);
+  g_return_if_fail (kB != NULL);
 
   bdl = ephy_sync_crypto_decode_hex (bundle);
   ciphertext = g_malloc (2 * EPHY_SYNC_TOKEN_LENGTH);
   respMAC = g_malloc (EPHY_SYNC_TOKEN_LENGTH);
   wrapKB = g_malloc (EPHY_SYNC_TOKEN_LENGTH);
-  kA = g_malloc (EPHY_SYNC_TOKEN_LENGTH);
+  *kA = g_malloc (EPHY_SYNC_TOKEN_LENGTH);
 
   memcpy (ciphertext, bdl, 2 * EPHY_SYNC_TOKEN_LENGTH);
   memcpy (respMAC, bdl + 2 * EPHY_SYNC_TOKEN_LENGTH, EPHY_SYNC_TOKEN_LENGTH);
@@ -720,27 +577,20 @@ ephy_sync_crypto_retrieve_sync_keys (const gchar *bundle,
                                           respHMACkey, EPHY_SYNC_TOKEN_LENGTH,
                                           ciphertext, 2 * EPHY_SYNC_TOKEN_LENGTH);
   respMAC2 = ephy_sync_crypto_decode_hex (respMAC2_hex);
+  g_assert (ephy_sync_crypto_equals (respMAC, respMAC2, EPHY_SYNC_TOKEN_LENGTH) == TRUE);
 
-  if (are_equal (respMAC, respMAC2) == FALSE) {
-    g_warning ("respMAC and respMAC2 differ");
-    goto out;
-  }
-
-  xored = xor (ciphertext, respXORkey, 2 * EPHY_SYNC_TOKEN_LENGTH);
-  memcpy (kA, xored, EPHY_SYNC_TOKEN_LENGTH);
+  xored = ephy_sync_crypto_xor (ciphertext, respXORkey, 2 * EPHY_SYNC_TOKEN_LENGTH);
+  memcpy (*kA, xored, EPHY_SYNC_TOKEN_LENGTH);
   memcpy (wrapKB, xored + EPHY_SYNC_TOKEN_LENGTH, EPHY_SYNC_TOKEN_LENGTH);
-  kB = xor (unwrapBKey, wrapKB, EPHY_SYNC_TOKEN_LENGTH);
-  retval = ephy_sync_crypto_sync_keys_new (kA, kB, wrapKB);
+  *kB = ephy_sync_crypto_xor (unwrapBKey, wrapKB, EPHY_SYNC_TOKEN_LENGTH);
 
-out:
   g_free (bdl);
   g_free (ciphertext);
   g_free (respMAC);
   g_free (respMAC2);
-  g_free (respMAC2_hex);
   g_free (xored);
-
-  return retval;
+  g_free (wrapKB);
+  g_free (respMAC2_hex);
 }
 
 EphySyncCryptoHawkHeader *
@@ -748,14 +598,12 @@ ephy_sync_crypto_compute_hawk_header (const gchar               *url,
                                       const gchar               *method,
                                       const gchar               *id,
                                       guint8                    *key,
-                                      gsize                      key_length,
+                                      gsize                      key_len,
                                       EphySyncCryptoHawkOptions *options)
 {
   EphySyncCryptoHawkArtifacts *artifacts;
   SoupURI *uri;
-  gboolean has_options;
-  const gchar *hostname;
-  const gchar *resource;
+  gchar *resource;
   gchar *hash;
   gchar *header;
   gchar *mac;
@@ -763,92 +611,87 @@ ephy_sync_crypto_compute_hawk_header (const gchar               *url,
   gchar *payload;
   gchar *timestamp;
   gint64 ts;
-  guint port;
 
-  g_return_val_if_fail (url && strlen (url) > 0, NULL);
-  g_return_val_if_fail (method && strlen (method) > 0, NULL);
-  g_return_val_if_fail (id && strlen (id) > 0, NULL);
-  g_return_val_if_fail (key, NULL);
+  g_return_val_if_fail (url != NULL, NULL);
+  g_return_val_if_fail (method != NULL, NULL);
+  g_return_val_if_fail (id != NULL, NULL);
+  g_return_val_if_fail (key != NULL, NULL);
+
+  ts = ephy_sync_utils_current_time_seconds ();
+  nonce = options && options->nonce ? options->nonce : ephy_sync_crypto_generate_random_hex (6);
+  hash = options ? options->hash : NULL;
+  payload = options ? options->payload : NULL;
+  timestamp = options ? options->timestamp : NULL;
+  uri = soup_uri_new (url);
+  resource = (gchar *) soup_uri_get_path (uri);
 
-  has_options = options != NULL;
-  ts = g_get_real_time () / 1000000;
+  if (soup_uri_get_query (uri) != NULL)
+    resource = g_strconcat (resource, "?", soup_uri_get_query (uri), NULL);
 
-  timestamp = has_options ? options->timestamp : NULL;
-  if (timestamp) {
+  if (timestamp != NULL) {
     gchar *local_time_offset;
     gint64 offset;
 
-    local_time_offset = has_options ? options->local_time_offset : NULL;
+    local_time_offset = options ? options->local_time_offset : NULL;
     offset = local_time_offset ? g_ascii_strtoll (local_time_offset, NULL, 10) : 0;
-
     ts = g_ascii_strtoll (timestamp, NULL, 10) + offset;
   }
 
-  nonce = has_options ? options->nonce : NULL;
-  nonce = nonce ? nonce : ephy_sync_crypto_generate_random_string (6);
-  hash = has_options ? options->hash : NULL;
-  payload = has_options ? options->payload : NULL;
-
-  uri = soup_uri_new (url);
-  g_return_val_if_fail (uri, NULL);
-  hostname = soup_uri_get_host (uri);
-  port = soup_uri_get_port (uri);
-  if (soup_uri_get_query (uri) != NULL)
-    resource = g_strdup_printf ("%s?%s", soup_uri_get_path (uri), soup_uri_get_query (uri));
-  else
-    resource = soup_uri_get_path (uri);
-
-  if (!hash && payload) {
-    const gchar *content_type;
-
-    content_type = has_options ? options->content_type : "text/plain";
-    hash = calculate_payload_hash (payload, content_type);
+  if (hash == NULL && payload != NULL) {
+    const gchar *content_type = options ? options->content_type : "text/plain";
+    hash = ephy_sync_crypto_calculate_payload_hash (payload, content_type);
   }
 
-  artifacts = ephy_sync_crypto_hawk_artifacts_new (has_options ? options->app : NULL,
-                                                   has_options ? options->dlg : NULL,
-                                                   has_options ? options->ext : NULL,
+  artifacts = ephy_sync_crypto_hawk_artifacts_new (options ? options->app : NULL,
+                                                   options ? options->dlg : NULL,
+                                                   options ? options->ext : NULL,
                                                    hash,
-                                                   g_strdup (hostname),
-                                                   g_strdup (method),
+                                                   soup_uri_get_host (uri),
+                                                   method,
                                                    nonce,
-                                                   g_strdup_printf ("%u", port),
-                                                   g_strdup (resource),
-                                                   g_strdup_printf ("%ld", ts));
+                                                   soup_uri_get_port (uri),
+                                                   resource,
+                                                   ts);
 
-  mac = calculate_mac ("header", key, key_length, artifacts);
+  mac = ephy_sync_crypto_calculate_mac ("header", key, key_len, artifacts);
 
   header = g_strconcat ("Hawk id=\"", id, "\"",
                         ", ts=\"", artifacts->ts, "\"",
                         ", nonce=\"", artifacts->nonce, "\"",
                         NULL);
 
-  if (artifacts->hash && strlen (artifacts->hash) > 0)
-    header = append_token_to_header (header, "hash", artifacts->hash);
+  if (artifacts->hash != NULL && strlen (artifacts->hash) > 0)
+    header = ephy_sync_crypto_append_to_header (header, "hash", artifacts->hash);
 
-  if (artifacts->ext && strlen (artifacts->ext) > 0) {
+  if (artifacts->ext != NULL && strlen (artifacts->ext) > 0) {
     gchar *h_ext;
     gchar *tmp_ext;
 
-    tmp_ext = find_and_replace_string (artifacts->ext, "\\", "\\\\");
-    h_ext = find_and_replace_string (tmp_ext, "\n", "\\n");
-    header = append_token_to_header (header, "ext", h_ext);
+    tmp_ext = ephy_sync_utils_find_and_replace (artifacts->ext, "\\", "\\\\");
+    h_ext = ephy_sync_utils_find_and_replace (tmp_ext, "\n", "\\n");
+    header = ephy_sync_crypto_append_to_header (header, "ext", h_ext);
 
     g_free (h_ext);
     g_free (tmp_ext);
   }
 
-  header = append_token_to_header (header, "mac", mac);
+  header = ephy_sync_crypto_append_to_header (header, "mac", mac);
 
-  if (artifacts->app) {
-    header = append_token_to_header (header, "app", artifacts->app);
+  if (artifacts->app != NULL) {
+    header = ephy_sync_crypto_append_to_header (header, "app", artifacts->app);
 
-    if (artifacts->dlg)
-      header = append_token_to_header (header, "dlg", artifacts->dlg);
+    if (artifacts->dlg != NULL)
+      header = ephy_sync_crypto_append_to_header (header, "dlg", artifacts->dlg);
   }
 
   soup_uri_free (uri);
 
+  if (options == NULL || options->nonce == NULL)
+    g_free (nonce);
+
+  if (soup_uri_get_query (uri) != NULL)
+    g_free (resource);
+
   return ephy_sync_crypto_hawk_header_new (header, artifacts);
 }
 
@@ -867,7 +710,7 @@ ephy_sync_crypto_generate_rsa_key_pair (void)
 
   /* Key sizes below 2048 are considered breakable and should not be used */
   retval = rsa_generate_keypair (&public, &private,
-                                 NULL, random_func,
+                                 NULL, ephy_sync_crypto_random_gen,
                                  NULL, NULL, 2048, 0);
   if (retval == 0) {
     g_warning ("Failed to generate RSA key pair");
@@ -900,6 +743,10 @@ ephy_sync_crypto_create_assertion (const gchar              *certificate,
   gsize expected_size;
   gsize count;
 
+  g_return_val_if_fail (certificate != NULL, NULL);
+  g_return_val_if_fail (audience != NULL, NULL);
+  g_return_val_if_fail (keypair != NULL, NULL);
+
   expires_at = g_get_real_time () / 1000 + duration * 1000;
   body = g_strdup_printf ("{\"exp\": %lu, \"aud\": \"%s\"}", expires_at, audience);
   body_b64 = ephy_sync_crypto_base64_urlsafe_encode ((guint8 *) body, strlen (body), TRUE);
@@ -911,7 +758,7 @@ ephy_sync_crypto_create_assertion (const gchar              *certificate,
   digest = ephy_sync_crypto_decode_hex (digest_hex);
 
   if (rsa_sha256_sign_digest_tr (&keypair->public, &keypair->private,
-                                 NULL, random_func,
+                                 NULL, ephy_sync_crypto_random_gen,
                                  digest, signature) == 0) {
     g_warning ("Failed to sign the message. Giving up.");
     goto out;
@@ -927,8 +774,7 @@ ephy_sync_crypto_create_assertion (const gchar              *certificate,
   }
 
   sig_b64 = ephy_sync_crypto_base64_urlsafe_encode (sig, count, TRUE);
-  assertion = g_strdup_printf ("%s~%s.%s.%s",
-                               certificate, header_b64, body_b64, sig_b64);
+  assertion = g_strdup_printf ("%s~%s.%s.%s", certificate, header_b64, body_b64, sig_b64);
 
 out:
   g_free (body);
@@ -945,117 +791,120 @@ out:
 }
 
 gchar *
-ephy_sync_crypto_generate_random_string (gsize length)
+ephy_sync_crypto_generate_random_hex (gsize length)
 {
-  guchar *bytes;
-  gchar *base64_string;
-  gchar *string;
+  FILE *fp;
+  gsize num_bytes;
+  guint8 *bytes;
+  gchar *hex;
+  gchar *out;
 
-  bytes = g_malloc (length);
-  for (gsize i = 0; i < length; i++)
-    bytes[i] = g_random_int ();
+  g_assert (length > 0);
+  num_bytes = (length + 1) / 2;
+  bytes = g_malloc (num_bytes);
 
-  base64_string = g_base64_encode (bytes, length);
-  base64_to_base64_urlsafe (base64_string);
-  string = g_strndup (base64_string, length);
+  fp = fopen ("/dev/urandom", "r");
+  fread (bytes, sizeof (guint8), num_bytes, fp);
+  hex = ephy_sync_crypto_encode_hex (bytes, num_bytes);
+  out = g_strndup (hex, length);
 
   g_free (bytes);
-  g_free (base64_string);
+  g_free (hex);
+  fclose (fp);
 
-  return string;
+  return out;
 }
 
 gchar *
 ephy_sync_crypto_base64_urlsafe_encode (guint8   *data,
-                                        gsize     data_length,
+                                        gsize     data_len,
                                         gboolean  strip)
 {
-  gchar *encoded;
   gchar *base64;
-  gsize start;
+  gchar *out;
+  gsize start = 0;
   gssize end;
 
-  base64 = g_base64_encode (data, data_length);
+  g_return_val_if_fail (data != NULL, NULL);
 
-  if (strip == FALSE) {
-    base64_to_base64_urlsafe (base64);
-    return base64;
-  }
+  base64 = g_base64_encode (data, data_len);
+  end = strlen (base64) - 1;
 
-  /* Strip all the '=' */
-  start = 0;
-  while (start < strlen (base64) && base64[start] == '=')
-    start++;
+  if (strip == TRUE) {
+    while (start < strlen (base64) && base64[start] == '=')
+      start++;
 
-  end = strlen (base64) - 1;
-  while (end >= 0 && base64[end] == '=')
-    end--;
+    while (end >= 0 && base64[end] == '=')
+      end--;
+  }
 
-  encoded = g_strndup (base64 + start, end - start + 1);
-  base64_to_base64_urlsafe (encoded);
+  out = g_strndup (base64 + start, end - start + 1);
+  ephy_sync_crypto_b64_to_b64_urlsafe (out);
 
   g_free (base64);
 
-  return encoded;
+  return out;
 }
 
 guint8 *
-ephy_sync_crypto_base64_urlsafe_decode (gchar *text,
-                                        gsize *out_length)
+ephy_sync_crypto_base64_urlsafe_decode (const gchar *text,
+                                        gsize       *out_len,
+                                        gboolean     fill)
 {
-  guint8 *decoded;
-  gchar *text_copy;
+  guint8 *out;
+  gchar *to_decode;
+  gchar *suffix = NULL;
 
-  text_copy = g_strdup (text);
-  base64_urlsafe_to_base64 (text_copy);
-  decoded = g_base64_decode (text_copy, out_length);
+  g_return_val_if_fail (text != NULL, NULL);
+  g_return_val_if_fail (out_len != NULL, NULL);
 
-  g_free (text_copy);
+  if (fill == TRUE)
+    suffix = g_strnfill ((4 - strlen (text) % 4) % 4, '=');
 
-  return decoded;
+  to_decode = g_strconcat (text, suffix, NULL);
+  ephy_sync_crypto_b64_urlsafe_to_b64 (to_decode);
+  out = g_base64_decode (to_decode, out_len);
+
+  g_free (suffix);
+  g_free (to_decode);
+
+  return out;
 }
 
 guint8 *
 ephy_sync_crypto_aes_256 (EphySyncCryptoAES256Mode  mode,
                           const guint8             *key,
                           const guint8             *data,
-                          gsize                     data_length,
-                          gsize                    *out_length)
+                          gsize                     data_len,
+                          gsize                    *out_len)
 {
   struct aes256_ctx aes;
-  gsize padded_length;
+  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_length % AES_BLOCK_SIZE == 0);
-
-  padded_length = data_length;
-  if (data_length % AES_BLOCK_SIZE != 0)
-    padded_length = data_length + (AES_BLOCK_SIZE - data_length % AES_BLOCK_SIZE);
-
-  out = g_malloc (padded_length);
-  padded_data = g_malloc0 (padded_length);
-  memcpy (padded_data, data, data_length);
-
-  switch (mode) {
-    case AES_256_MODE_ENCRYPT:
-      aes256_set_encrypt_key (&aes, key);
-      aes256_encrypt (&aes, padded_length, out, padded_data);
-      break;
-    case AES_256_MODE_DECRYPT:
-      aes256_set_decrypt_key (&aes, key);
-      aes256_decrypt (&aes, padded_length, out, padded_data);
-      break;
-    default:
-      g_assert_not_reached ();
+  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_malloc0 (padded_len);
+  padded_data = g_malloc0 (padded_len);
+  memcpy (padded_data, data, data_len);
+
+  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_length != NULL)
-    *out_length = padded_length;
+  if (out_len != NULL)
+    *out_len = padded_len;
 
   g_free (padded_data);
 
@@ -1064,12 +913,14 @@ ephy_sync_crypto_aes_256 (EphySyncCryptoAES256Mode  mode,
 
 gchar *
 ephy_sync_crypto_encode_hex (guint8 *data,
-                             gsize   data_length)
+                             gsize   data_len)
 {
   gchar *retval;
   gsize length;
 
-  length = data_length == 0 ? EPHY_SYNC_TOKEN_LENGTH : data_length;
+  g_return_val_if_fail (data != NULL, NULL);
+
+  length = data_len == 0 ? EPHY_SYNC_TOKEN_LENGTH : data_len;
   retval = g_malloc (length * 2 + 1);
 
   for (gsize i = 0; i < length; i++) {
@@ -1085,19 +936,17 @@ ephy_sync_crypto_encode_hex (guint8 *data,
 }
 
 guint8 *
-ephy_sync_crypto_decode_hex (const gchar *hex_string)
+ephy_sync_crypto_decode_hex (const gchar *hex)
 {
   guint8 *retval;
-  gsize hex_length;
-
-  hex_length = strlen (hex_string);
-  g_return_val_if_fail (hex_length % 2 == 0, NULL);
+  gsize hex_len = strlen (hex);
 
-  retval = g_malloc (hex_length / 2);
+  g_return_val_if_fail (hex != NULL, NULL);
+  g_return_val_if_fail (hex_len % 2 == 0, NULL);
 
-  for (gsize i = 0, j = 0; i < hex_length; i += 2, j++) {
-    sscanf(hex_string + i, "%2hhx", retval + j);
-  }
+  retval = g_malloc (hex_len / 2);
+  for (gsize i = 0, j = 0; i < hex_len; i += 2, j++)
+    sscanf(hex + i, "%2hhx", retval + j);
 
   return retval;
 }
diff --git a/src/ephy-sync-crypto.h b/src/ephy-sync-crypto.h
index 4cf1adb..628cb30 100644
--- a/src/ephy-sync-crypto.h
+++ b/src/ephy-sync-crypto.h
@@ -62,93 +62,63 @@ typedef struct {
 } EphySyncCryptoHawkHeader;
 
 typedef struct {
-  guint8 *tokenID;
-  guint8 *reqHMACkey;
-  guint8 *respHMACkey;
-  guint8 *respXORkey;
-} EphySyncCryptoProcessedKFT;
-
-typedef struct {
-  guint8 *tokenID;
-  guint8 *reqHMACkey;
-  guint8 *requestKey;
-} EphySyncCryptoProcessedST;
-
-typedef struct {
-  guint8 *kA;
-  guint8 *kB;
-  guint8 *wrapKB;
-} EphySyncCryptoSyncKeys;
-
-typedef struct {
   struct rsa_public_key public;
   struct rsa_private_key private;
 } EphySyncCryptoRSAKeyPair;
 
-EphySyncCryptoHawkOptions   *ephy_sync_crypto_hawk_options_new       (gchar *app,
-                                                                      gchar *dlg,
-                                                                      gchar *ext,
-                                                                      gchar *content_type,
-                                                                      gchar *hash,
-                                                                      gchar *local_time_offset,
-                                                                      gchar *nonce,
-                                                                      gchar *payload,
-                                                                      gchar *timestamp);
-
-void                        ephy_sync_crypto_hawk_options_free       (EphySyncCryptoHawkOptions 
*hawk_options);
-
-void                        ephy_sync_crypto_hawk_header_free        (EphySyncCryptoHawkHeader *hawk_header);
-
-void                        ephy_sync_crypto_processed_kft_free      (EphySyncCryptoProcessedKFT 
*processed_kft);
-
-void                        ephy_sync_crypto_processed_st_free       (EphySyncCryptoProcessedST 
*processed_st);
-
-void                        ephy_sync_crypto_sync_keys_free          (EphySyncCryptoSyncKeys *sync_keys);
-
-void                        ephy_sync_crypto_rsa_key_pair_free       (EphySyncCryptoRSAKeyPair *keypair);
-
-EphySyncCryptoProcessedKFT *ephy_sync_crypto_process_key_fetch_token (const gchar *keyFetchToken);
-
-EphySyncCryptoProcessedST  *ephy_sync_crypto_process_session_token   (const gchar *sessionToken);
-
-EphySyncCryptoSyncKeys     *ephy_sync_crypto_retrieve_sync_keys      (const gchar *bundle,
-                                                                      guint8      *respHMACkey,
-                                                                      guint8      *respXORkey,
-                                                                      guint8      *unwrapBKey);
-
-EphySyncCryptoHawkHeader   *ephy_sync_crypto_compute_hawk_header     (const gchar               *url,
-                                                                      const gchar               *method,
-                                                                      const gchar               *id,
-                                                                      guint8                    *key,
-                                                                      gsize                      key_length,
-                                                                      EphySyncCryptoHawkOptions *options);
-
-EphySyncCryptoRSAKeyPair   *ephy_sync_crypto_generate_rsa_key_pair   (void);
-
-gchar                      *ephy_sync_crypto_create_assertion        (const gchar              *certificate,
-                                                                      const gchar              *audience,
-                                                                      guint64                   duration,
-                                                                      EphySyncCryptoRSAKeyPair *keypair);
-
-gchar                      *ephy_sync_crypto_generate_random_string  (gsize length);
-
-gchar                      *ephy_sync_crypto_base64_urlsafe_encode   (guint8   *data,
-                                                                      gsize     data_length,
-                                                                      gboolean  strip);
-
-guint8                     *ephy_sync_crypto_base64_urlsafe_decode   (gchar *text,
-                                                                      gsize *out_length);
-
-guint8                     *ephy_sync_crypto_aes_256                 (EphySyncCryptoAES256Mode  mode,
-                                                                      const guint8             *key,
-                                                                      const guint8             *data,
-                                                                      gsize                     data_length,
-                                                                      gsize                    *out_length);
-
-gchar                      *ephy_sync_crypto_encode_hex              (guint8 *data,
-                                                                      gsize   data_length);
-
-guint8                     *ephy_sync_crypto_decode_hex              (const gchar *hex_string);
+EphySyncCryptoHawkOptions *ephy_sync_crypto_hawk_options_new        (const gchar *app,
+                                                                     const gchar *dlg,
+                                                                     const gchar *ext,
+                                                                     const gchar *content_type,
+                                                                     const gchar *hash,
+                                                                     const gchar *local_time_offset,
+                                                                     const gchar *nonce,
+                                                                     const gchar *payload,
+                                                                     const gchar *timestamp);
+void                       ephy_sync_crypto_hawk_options_free       (EphySyncCryptoHawkOptions *options);
+void                       ephy_sync_crypto_hawk_header_free        (EphySyncCryptoHawkHeader *header);
+void                       ephy_sync_crypto_rsa_key_pair_free       (EphySyncCryptoRSAKeyPair *keypair);
+void                       ephy_sync_crypto_process_key_fetch_token (const gchar  *keyFetchToken,
+                                                                     guint8      **tokenID,
+                                                                     guint8      **reqHMACkey,
+                                                                     guint8      **respHMACkey,
+                                                                     guint8      **respXORkey);
+void                       ephy_sync_crypto_process_session_token   (const gchar  *sessionToken,
+                                                                     guint8      **tokenID,
+                                                                     guint8      **reqHMACkey,
+                                                                     guint8      **requestKey);
+void                       ephy_sync_crypto_compute_sync_keys       (const gchar  *bundle,
+                                                                     guint8       *respHMACkey,
+                                                                     guint8       *respXORkey,
+                                                                     guint8       *unwrapBKey,
+                                                                     guint8      **kA,
+                                                                     guint8      **kB);
+EphySyncCryptoHawkHeader  *ephy_sync_crypto_compute_hawk_header     (const gchar               *url,
+                                                                     const gchar               *method,
+                                                                     const gchar               *id,
+                                                                     guint8                    *key,
+                                                                     gsize                      key_len,
+                                                                     EphySyncCryptoHawkOptions *options);
+EphySyncCryptoRSAKeyPair  *ephy_sync_crypto_generate_rsa_key_pair   (void);
+gchar                     *ephy_sync_crypto_create_assertion        (const gchar              *certificate,
+                                                                     const gchar              *audience,
+                                                                     guint64                   duration,
+                                                                     EphySyncCryptoRSAKeyPair *keypair);
+gchar                     *ephy_sync_crypto_generate_random_hex     (gsize length);
+gchar                     *ephy_sync_crypto_base64_urlsafe_encode   (guint8   *data,
+                                                                     gsize     data_len,
+                                                                     gboolean  strip);
+guint8                    *ephy_sync_crypto_base64_urlsafe_decode   (const gchar *text,
+                                                                     gsize       *out_len,
+                                                                     gboolean     fill);
+guint8                    *ephy_sync_crypto_aes_256                 (EphySyncCryptoAES256Mode  mode,
+                                                                     const guint8             *key,
+                                                                     const guint8             *data,
+                                                                     gsize                     data_len,
+                                                                     gsize                    *out_len);
+gchar                     *ephy_sync_crypto_encode_hex              (guint8 *data,
+                                                                     gsize   data_len);
+guint8                    *ephy_sync_crypto_decode_hex              (const gchar *hex_string);
 
 G_END_DECLS
 
diff --git a/src/ephy-sync-secret.c b/src/ephy-sync-secret.c
index d1df199..58603eb 100644
--- a/src/ephy-sync-secret.c
+++ b/src/ephy-sync-secret.c
@@ -63,62 +63,51 @@ store_token_cb (SecretService *service,
 }
 
 void
-ephy_sync_secret_forget_all_tokens (void)
+ephy_sync_secret_forget_tokens (void)
 {
   GHashTable *attributes;
 
   attributes = secret_attributes_build (EPHY_SYNC_TOKEN_SCHEMA, NULL);
-  secret_service_clear (NULL,
-                        EPHY_SYNC_TOKEN_SCHEMA,
-                        attributes,
-                        NULL,
-                        (GAsyncReadyCallback) forget_all_tokens_cb,
-                        NULL);
+  secret_service_clear (NULL, EPHY_SYNC_TOKEN_SCHEMA, attributes,
+                        NULL, (GAsyncReadyCallback) forget_all_tokens_cb, NULL);
 
   g_hash_table_unref (attributes);
 }
 
 void
-ephy_sync_secret_load_tokens (EphySyncService *sync_service)
+ephy_sync_secret_load_tokens (EphySyncService *service)
 {
   SecretItem *secret_item;
   SecretValue *secret_value;
   GHashTable *attributes;
   GError *error = NULL;
   GList *matches;
-  GList *tmp;
-  EphySyncServiceTokenType type;
-  const gchar *emailUTF8;
+  EphySyncTokenType type;
+  const gchar *email;
   const gchar *value;
   gchar *user_email;
 
-  user_email = ephy_sync_service_get_user_email (sync_service);
+  user_email = ephy_sync_service_get_user_email (service);
   attributes = secret_attributes_build (EPHY_SYNC_TOKEN_SCHEMA, NULL);
 
   /* Do this synchronously so the tokens will be available immediately */
-  matches = secret_service_search_sync (NULL,
-                                        EPHY_SYNC_TOKEN_SCHEMA,
-                                        attributes,
+  matches = secret_service_search_sync (NULL, EPHY_SYNC_TOKEN_SCHEMA, attributes,
                                         SECRET_SEARCH_ALL | SECRET_SEARCH_UNLOCK | 
SECRET_SEARCH_LOAD_SECRETS,
-                                        NULL,
-                                        &error);
-
-  for (tmp = matches; tmp != NULL; tmp = tmp->next) {
-    secret_item = tmp->data;
+                                        NULL, &error);
 
+  for (GList *m = matches; m != NULL; m = m->next) {
+    secret_item = m->data;
     attributes = secret_item_get_attributes (secret_item);
-    emailUTF8 = g_hash_table_lookup (attributes, EMAIL_KEY);
-    type = g_ascii_strtoull (g_hash_table_lookup (attributes, TOKEN_TYPE_KEY),
-                             NULL, 10);
+    email = g_hash_table_lookup (attributes, EMAIL_KEY);
+    type = g_ascii_strtoull (g_hash_table_lookup (attributes, TOKEN_TYPE_KEY), NULL, 10);
     secret_value = secret_item_get_secret (secret_item);
     value = secret_value_get_text (secret_value);
 
     /* Sanity check */
-    if (g_strcmp0 (emailUTF8, user_email))
+    if (g_strcmp0 (email, user_email) != 0)
       continue;
 
-    ephy_sync_service_set_token (sync_service, g_strdup (value), type);
-
+    ephy_sync_service_set_token (service, g_strdup (value), type);
     g_hash_table_unref (attributes);
   }
 
@@ -126,22 +115,22 @@ ephy_sync_secret_load_tokens (EphySyncService *sync_service)
 }
 
 void
-ephy_sync_secret_store_token (const gchar              *emailUTF8,
-                              gchar                    *value,
-                              EphySyncServiceTokenType  type)
+ephy_sync_secret_store_token (const gchar       *email,
+                              gchar             *value,
+                              EphySyncTokenType  type)
 {
   SecretValue *secret_value;
   GHashTable *attributes;
   const gchar *name;
   gchar *label;
 
-  g_return_if_fail (emailUTF8);
+  g_return_if_fail (email);
   g_return_if_fail (value);
 
-  name = ephy_sync_service_token_name_from_type (type);
+  name = ephy_sync_utils_token_name_from_type (type);
   secret_value = secret_value_new (value, -1, "text/plain");
   attributes = secret_attributes_build (EPHY_SYNC_TOKEN_SCHEMA,
-                                        EMAIL_KEY, emailUTF8,
+                                        EMAIL_KEY, email,
                                         TOKEN_TYPE_KEY, type,
                                         TOKEN_NAME_KEY, name,
                                         NULL);
@@ -150,15 +139,9 @@ ephy_sync_secret_store_token (const gchar              *emailUTF8,
    */
   label = g_strdup_printf (_("Token value for %s token"), name);
 
-  secret_service_store (NULL,
-                        EPHY_SYNC_TOKEN_SCHEMA,
-                        attributes,
-                        NULL,
-                        label,
-                        secret_value,
-                        NULL,
-                        (GAsyncReadyCallback) store_token_cb,
-                        NULL);
+  secret_service_store (NULL, EPHY_SYNC_TOKEN_SCHEMA, attributes,
+                        NULL, label, secret_value, NULL,
+                        (GAsyncReadyCallback) store_token_cb, NULL);
 
   g_free (label);
   secret_value_unref (secret_value);
diff --git a/src/ephy-sync-secret.h b/src/ephy-sync-secret.h
index 398afd1..cd1e8b5 100644
--- a/src/ephy-sync-secret.h
+++ b/src/ephy-sync-secret.h
@@ -28,19 +28,17 @@ G_BEGIN_DECLS
 
 const SecretSchema *ephy_sync_secret_get_token_schema (void) G_GNUC_CONST;
 
-#define EMAIL_KEY      "email_utf8"
-#define TOKEN_TYPE_KEY "token_type"
-#define TOKEN_NAME_KEY "token_name"
+#define EMAIL_KEY       "email_utf8"
+#define TOKEN_TYPE_KEY  "token_type"
+#define TOKEN_NAME_KEY  "token_name"
 
 #define EPHY_SYNC_TOKEN_SCHEMA (ephy_sync_secret_get_token_schema ())
 
-void ephy_sync_secret_forget_all_tokens (void);
-
-void ephy_sync_secret_load_tokens       (EphySyncService *sync_service);
-
-void ephy_sync_secret_store_token       (const gchar              *emailUTF8,
-                                         gchar                    *value,
-                                         EphySyncServiceTokenType  type);
+void ephy_sync_secret_forget_tokens (void);
+void ephy_sync_secret_load_tokens   (EphySyncService *service);
+void ephy_sync_secret_store_token   (const gchar       *email,
+                                     gchar             *value,
+                                     EphySyncTokenType  type);
 
 G_END_DECLS
 
diff --git a/src/ephy-sync-service.c b/src/ephy-sync-service.c
index 77a3a2b..2e9a366 100644
--- a/src/ephy-sync-service.c
+++ b/src/ephy-sync-service.c
@@ -19,45 +19,47 @@
 #include "config.h"
 #include "ephy-sync-service.h"
 
+#include "ephy-bookmark.h"
+#include "ephy-bookmarks-manager.h"
 #include "ephy-debug.h"
 #include "ephy-settings.h"
+#include "ephy-shell.h"
 #include "ephy-sync-crypto.h"
 #include "ephy-sync-secret.h"
-#include "ephy-sync-utils.h"
 
 #include <json-glib/json-glib.h>
 #include <string.h>
 
-#define MOZILLA_TOKEN_SERVER_URL           "https://token.services.mozilla.com/1.0/sync/1.5";
-#define MOZILLA_FIREFOX_ACCOUNTS_BASE_URL  "https://api.accounts.firefox.com/";
-#define MOZILLA_FIREFOX_ACCOUNTS_VERSION   "v1/"
-
-#define EMAIL_REGEX              "^[a-zA-Z0-9_]([a-zA-Z0-9._]+[a-zA-Z0-9_])?@[a-z0-9.-]+$"
-#define CURRENT_TIME_IN_SECONDS  (g_get_real_time () / 1000000)
+#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 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 *soup_session;
-
-  gchar *uid;
-  gchar *sessionToken;
-  gchar *keyFetchToken;
-  gchar *unwrapBKey;
-  gchar *kA;
-  gchar *kB;
-
-  gchar *user_email;
-  gint64 last_auth_at;
-
-  gboolean is_locked;
-  gchar *storage_endpoint;
-  gchar *storage_credentials_id;
-  gchar *storage_credentials_key;
-  gint64 storage_credentials_expiry_time;
-  GQueue *storage_queue;
-
-  gchar *certificate;
+  GObject      parent_instance;
+
+  SoupSession *session;
+  guint        sync_frequency;
+
+  gchar       *uid;
+  gchar       *sessionToken;
+  gchar       *keyFetchToken;
+  gchar       *unwrapBKey;
+  gchar       *kA;
+  gchar       *kB;
+
+  gchar       *user_email;
+  double       sync_time;
+  gint64       auth_at;
+
+  gboolean     locked;
+  gchar       *storage_endpoint;
+  gchar       *storage_credentials_id;
+  gchar       *storage_credentials_key;
+  gint64       storage_credentials_expiry_time;
+  GQueue      *storage_queue;
+
+  gchar                    *certificate;
   EphySyncCryptoRSAKeyPair *keypair;
 };
 
@@ -111,74 +113,21 @@ storage_server_request_async_data_free (StorageServerRequestAsyncData *data)
   g_slice_free (StorageServerRequestAsyncData, data);
 }
 
-static gchar *
-get_audience_for_url (const gchar *url)
-{
-  SoupURI *uri;
-  const gchar *scheme;
-  const gchar *host;
-  gchar *port_str;
-  guint port;
-  gchar *audience;
-
-  uri = soup_uri_new (url);
-  g_assert (uri != NULL);
-
-  scheme = soup_uri_get_scheme (uri);
-  host = soup_uri_get_host (uri);
-  port = soup_uri_get_port (uri);
-  port_str = g_strdup_printf (":%u", port);
-
-  /* Even if the url doesn't contain the port, soup_uri_get_port() will return
-   * the default port for the url's scheme so we need to check if the port was
-   * really present in the url.
-   */
-  if (g_strstr_len (url, -1, port_str) != NULL)
-    audience = g_strdup_printf ("%s://%s:%u", scheme, host, port);
-  else
-    audience = g_strdup_printf ("%s://%s", scheme, host);
-
-  soup_uri_free (uri);
-  g_free (port_str);
-
-  return audience;
-}
-
-static guchar *
-base64_parse (const gchar *string,
-              gsize       *out_len)
-{
-  gchar *suffix;
-  gchar *full;
-  guchar *decoded;
-  gsize len;
-
-  len = ((4 - strlen (string) % 4) % 4);
-  suffix = g_strnfill (len, '=');
-  full = g_strconcat (string, suffix, NULL);
-  decoded = g_base64_decode (full, out_len);
-
-  g_free (suffix);
-  g_free (full);
-
-  return decoded;
-}
-
 static void
 destroy_session_response_cb (SoupSession *session,
-                             SoupMessage *message,
+                             SoupMessage *msg,
                              gpointer     user_data)
 {
   JsonParser *parser;
   JsonObject *json;
 
-  if (message->status_code == 200) {
+  if (msg->status_code == 200) {
     LOG ("Session destroyed");
     return;
   }
 
   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);
   json = json_node_get_object (json_parser_get_root (parser));
 
   g_warning ("Failed to destroy session: errno: %ld, errmsg: %s",
@@ -188,21 +137,11 @@ destroy_session_response_cb (SoupSession *session,
   g_object_unref (parser);
 }
 
-static void
-ephy_sync_service_clear_storage_credentials (EphySyncService *self)
-{
-  g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
-
-  g_clear_pointer (&self->certificate, g_free);
-  g_clear_pointer (&self->storage_endpoint, g_free);
-  g_clear_pointer (&self->storage_credentials_id, g_free);
-  g_clear_pointer (&self->storage_credentials_key, g_free);
-  self->storage_credentials_expiry_time = 0;
-}
-
 static gboolean
 ephy_sync_service_storage_credentials_is_expired (EphySyncService *self)
 {
+  g_return_val_if_fail (EPHY_IS_SYNC_SERVICE (self), TRUE);
+
   if (self->storage_credentials_id == NULL || self->storage_credentials_key == NULL)
     return TRUE;
 
@@ -210,7 +149,7 @@ ephy_sync_service_storage_credentials_is_expired (EphySyncService *self)
     return TRUE;
 
   /* Consider a 60 seconds safety interval. */
-  return self->storage_credentials_expiry_time < CURRENT_TIME_IN_SECONDS - 60;
+  return self->storage_credentials_expiry_time < ephy_sync_utils_current_time_seconds () - 60;
 }
 
 static void
@@ -223,39 +162,33 @@ ephy_sync_service_fxa_hawk_post_async (EphySyncService     *self,
                                        SoupSessionCallback  callback,
                                        gpointer             user_data)
 {
-  EphySyncCryptoHawkOptions *hawk_options;
-  EphySyncCryptoHawkHeader *hawk_header;
-  SoupMessage *message;
+  EphySyncCryptoHawkOptions *hoptions;
+  EphySyncCryptoHawkHeader *hheader;
+  SoupMessage *msg;
   gchar *url;
   const gchar *content_type = "application/json";
 
-  url = g_strdup_printf ("%s%s%s",
-                         MOZILLA_FIREFOX_ACCOUNTS_BASE_URL,
-                         MOZILLA_FIREFOX_ACCOUNTS_VERSION,
-                         endpoint);
-  message = soup_message_new (SOUP_METHOD_POST, url);
-  soup_message_set_request (message, content_type,
-                            SOUP_MEMORY_COPY,
+  g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
+  g_return_if_fail (endpoint != NULL);
+  g_return_if_fail (id != NULL);
+  g_return_if_fail (key != NULL);
+  g_return_if_fail (request_body != NULL);
+
+  url = g_strdup_printf ("%s%s", MOZILLA_FXA_SERVER_URL, endpoint);
+  msg = soup_message_new (SOUP_METHOD_POST, url);
+  soup_message_set_request (msg, content_type, SOUP_MEMORY_COPY,
                             request_body, strlen (request_body));
 
-  hawk_options = ephy_sync_crypto_hawk_options_new (NULL, NULL, NULL,
-                                                    g_strdup (content_type),
-                                                    NULL, NULL, NULL,
-                                                    g_strdup (request_body),
-                                                    NULL);
-  hawk_header = ephy_sync_crypto_compute_hawk_header (url, "POST",
-                                                      id,
-                                                      key, key_length,
-                                                      hawk_options);
-  soup_message_headers_append (message->request_headers,
-                               "authorization", hawk_header->header);
-  soup_message_headers_append (message->request_headers,
-                               "content-type", content_type);
-  soup_session_queue_message (self->soup_session, message, callback, user_data);
+  hoptions = ephy_sync_crypto_hawk_options_new (NULL, NULL, NULL, content_type,
+                                                NULL, NULL, NULL, request_body, NULL);
+  hheader = ephy_sync_crypto_compute_hawk_header (url, "POST", id, key, key_length, hoptions);
+  soup_message_headers_append (msg->request_headers, "authorization", hheader->header);
+  soup_message_headers_append (msg->request_headers, "content-type", content_type);
+  soup_session_queue_message (self->session, msg, callback, user_data);
 
   g_free (url);
-  ephy_sync_crypto_hawk_options_free (hawk_options);
-  ephy_sync_crypto_hawk_header_free (hawk_header);
+  ephy_sync_crypto_hawk_options_free (hoptions);
+  ephy_sync_crypto_hawk_header_free (hheader);
 }
 
 static guint
@@ -266,98 +199,87 @@ ephy_sync_service_fxa_hawk_get_sync (EphySyncService  *self,
                                      gsize             key_length,
                                      JsonNode        **node)
 {
-  EphySyncCryptoHawkHeader *hawk_header;
-  SoupMessage *message;
+  EphySyncCryptoHawkHeader *hheader;
+  SoupMessage *msg;
   JsonParser *parser;
   gchar *url;
 
-  url = g_strdup_printf ("%s%s%s",
-                         MOZILLA_FIREFOX_ACCOUNTS_BASE_URL,
-                         MOZILLA_FIREFOX_ACCOUNTS_VERSION,
-                         endpoint);
-  message = soup_message_new (SOUP_METHOD_GET, url);
-  hawk_header = ephy_sync_crypto_compute_hawk_header (url, "GET",
-                                                      id,
-                                                      key, key_length,
-                                                      NULL);
-  soup_message_headers_append (message->request_headers,
-                               "authorization", hawk_header->header);
-  soup_session_send_message (self->soup_session, message);
+  g_return_val_if_fail (EPHY_IS_SYNC_SERVICE (self), 0);
+  g_return_val_if_fail (endpoint != NULL, 0);
+  g_return_val_if_fail (id != NULL, 0);
+  g_return_val_if_fail (key != NULL, 0);
+
+  url = g_strdup_printf ("%s%s", MOZILLA_FXA_SERVER_URL, endpoint);
+  msg = soup_message_new (SOUP_METHOD_GET, url);
+  hheader = ephy_sync_crypto_compute_hawk_header (url, "GET", id, key, key_length, NULL);
+  soup_message_headers_append (msg->request_headers, "authorization", hheader->header);
+  soup_session_send_message (self->session, msg);
 
   if (node != NULL) {
     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);
     *node = json_node_copy (json_parser_get_root (parser));
     g_object_unref (parser);
   }
 
   g_free (url);
-  ephy_sync_crypto_hawk_header_free (hawk_header);
+  ephy_sync_crypto_hawk_header_free (hheader);
 
-  return message->status_code;
+  return msg->status_code;
 }
 
 static void
 ephy_sync_service_send_storage_request (EphySyncService               *self,
                                         StorageServerRequestAsyncData *data)
 {
-  EphySyncCryptoHawkOptions *hawk_options = NULL;
-  EphySyncCryptoHawkHeader *hawk_header;
-  SoupMessage *message;
+  EphySyncCryptoHawkOptions *hoptions = NULL;
+  EphySyncCryptoHawkHeader *hheader;
+  SoupMessage *msg;
   gchar *url;
   gchar *if_modified_since = NULL;
   gchar *if_unmodified_since = NULL;
   const gchar *content_type = "application/json";
 
+  g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
+  g_return_if_fail (data != NULL);
+
   url = g_strdup_printf ("%s/%s", self->storage_endpoint, data->endpoint);
-  message = soup_message_new (data->method, url);
+  msg = soup_message_new (data->method, url);
 
   if (data->request_body != NULL) {
-    hawk_options = ephy_sync_crypto_hawk_options_new (NULL, NULL, NULL,
-                                                      g_strdup (content_type),
-                                                      NULL, NULL, NULL,
-                                                      g_strdup (data->request_body),
-                                                      NULL);
-    soup_message_set_request (message, content_type,
-                              SOUP_MEMORY_COPY,
+    hoptions = ephy_sync_crypto_hawk_options_new (NULL, NULL, NULL, content_type,
+                                                  NULL, NULL, NULL, data->request_body, NULL);
+    soup_message_set_request (msg, content_type, SOUP_MEMORY_COPY,
                               data->request_body, strlen (data->request_body));
   }
 
-  if (g_strcmp0 (data->method, SOUP_METHOD_POST) == 0) {
-    soup_message_headers_append (message->request_headers,
-                                 "content-type", content_type);
-  }
+  if (g_strcmp0 (data->method, SOUP_METHOD_POST) == 0)
+    soup_message_headers_append (msg->request_headers, "content-type", content_type);
 
   if (data->modified_since >= 0) {
     if_modified_since = g_strdup_printf ("%.2lf", data->modified_since);
-    soup_message_headers_append (message->request_headers,
-                                 "X-If-Modified-Since", if_modified_since);
+    soup_message_headers_append (msg->request_headers, "X-If-Modified-Since", if_modified_since);
   }
 
   if (data->unmodified_since >= 0) {
     if_unmodified_since = g_strdup_printf ("%.2lf", data->unmodified_since);
-    soup_message_headers_append (message->request_headers,
-                                 "X-If-Unmodified-Since", if_unmodified_since);
+    soup_message_headers_append (msg->request_headers, "X-If-Unmodified-Since", if_unmodified_since);
   }
 
-  hawk_header = ephy_sync_crypto_compute_hawk_header (url, data->method,
-                                                      self->storage_credentials_id,
-                                                      (guint8 *) self->storage_credentials_key,
-                                                      strlen (self->storage_credentials_key),
-                                                      hawk_options);
-  soup_message_headers_append (message->request_headers,
-                               "authorization", hawk_header->header);
-  soup_session_queue_message (self->soup_session, message,
-                              data->callback, data->user_data);
+  hheader = ephy_sync_crypto_compute_hawk_header (url, data->method, self->storage_credentials_id,
+                                                 (guint8 *) self->storage_credentials_key,
+                                                 strlen (self->storage_credentials_key),
+                                                 hoptions);
+  soup_message_headers_append (msg->request_headers, "authorization", hheader->header);
+  soup_session_queue_message (self->session, msg, data->callback, data->user_data);
 
-  if (hawk_options != NULL)
-    ephy_sync_crypto_hawk_options_free (hawk_options);
+  if (hoptions != NULL)
+    ephy_sync_crypto_hawk_options_free (hoptions);
 
   g_free (url);
   g_free (if_modified_since);
   g_free (if_unmodified_since);
+  ephy_sync_crypto_hawk_header_free (hheader);
   storage_server_request_async_data_free (data);
 }
 
@@ -365,34 +287,34 @@ static gboolean
 ephy_sync_service_certificate_is_valid (EphySyncService *self,
                                         const gchar     *certificate)
 {
-  SoupURI *uri;
   JsonParser *parser;
   JsonObject *json;
   JsonObject *principal;
+  SoupURI *uri;
   gchar **pieces;
   gchar *header;
   gchar *payload;
-  gchar *uid_email;
-  const gchar *algorithm;
+  gchar *uid_email = NULL;
+  const gchar *alg;
   const gchar *email;
-  gsize header_len;
-  gsize payload_len;
+  gsize len;
   gboolean retval = FALSE;
 
+  g_return_val_if_fail (EPHY_IS_SYNC_SERVICE (self), FALSE);
   g_return_val_if_fail (certificate != NULL, FALSE);
 
-  uri = soup_uri_new (MOZILLA_FIREFOX_ACCOUNTS_BASE_URL);
+  uri = soup_uri_new (MOZILLA_FXA_SERVER_URL);
   pieces = g_strsplit (certificate, ".", 0);
-  header = (gchar *) base64_parse (pieces[0], &header_len);
-  payload = (gchar *) base64_parse (pieces[1], &payload_len);
+  header = (gchar *) ephy_sync_crypto_base64_urlsafe_decode (pieces[0], &len, TRUE);
+  payload = (gchar *) ephy_sync_crypto_base64_urlsafe_decode (pieces[1], &len, TRUE);
 
   parser = json_parser_new ();
   json_parser_load_from_data (parser, header, -1, NULL);
   json = json_node_get_object (json_parser_get_root (parser));
-  algorithm = json_object_get_string_member (json, "alg");
+  alg = json_object_get_string_member (json, "alg");
 
-  if (g_str_equal (algorithm, "RS256") == FALSE) {
-    g_warning ("Expected algorithm RS256, found %s. Giving up.", algorithm);
+  if (g_strcmp0 (alg, "RS256") != 0) {
+    g_warning ("Expected algorithm RS256, found %s. Giving up.", alg);
     goto out;
   }
 
@@ -400,16 +322,14 @@ ephy_sync_service_certificate_is_valid (EphySyncService *self,
   json = json_node_get_object (json_parser_get_root (parser));
   principal = json_object_get_object_member (json, "principal");
   email = json_object_get_string_member (principal, "email");
-  uid_email = g_strdup_printf ("%s@%s",
-                               self->uid,
-                               soup_uri_get_host (uri));
+  uid_email = g_strdup_printf ("%s@%s", self->uid, soup_uri_get_host (uri));
 
-  if (g_str_equal (uid_email, email) == FALSE) {
+  if (g_strcmp0 (uid_email, email) != 0) {
     g_warning ("Expected email %s, found %s. Giving up.", uid_email, email);
     goto out;
   }
 
-  self->last_auth_at = json_object_get_int_member (json, "fxa-lastAuthAt");
+  self->auth_at = json_object_get_int_member (json, "fxa-lastAuthAt");
   retval = TRUE;
 
 out:
@@ -425,7 +345,7 @@ out:
 
 static void
 obtain_storage_credentials_response_cb (SoupSession *session,
-                                        SoupMessage *message,
+                                        SoupMessage *msg,
                                         gpointer     user_data)
 {
   StorageServerRequestAsyncData *data;
@@ -439,7 +359,7 @@ obtain_storage_credentials_response_cb (SoupSession *session,
   service = EPHY_SYNC_SERVICE (data->service);
 
   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);
   json = json_node_get_object (json_parser_get_root (parser));
 
   /* FIXME: Since a new Firefox Account password means a new kB, and a new kB
@@ -448,12 +368,13 @@ obtain_storage_credentials_response_cb (SoupSession *session,
    * password since the last time he signed in. If this happens, the user needs
    * to be asked to sign in again with the new password.
    */
-  if (message->status_code == 200) {
+  if (msg->status_code == 200) {
     service->storage_endpoint = g_strdup (json_object_get_string_member (json, "api_endpoint"));
     service->storage_credentials_id = g_strdup (json_object_get_string_member (json, "id"));
     service->storage_credentials_key = g_strdup (json_object_get_string_member (json, "key"));
-    service->storage_credentials_expiry_time = json_object_get_int_member (json, "duration") + 
CURRENT_TIME_IN_SECONDS;
-  } else if (message->status_code == 401) {
+    service->storage_credentials_expiry_time = json_object_get_int_member (json, "duration") +
+                                               ephy_sync_utils_current_time_seconds ();
+  } else if (msg->status_code == 401) {
     array = json_object_get_array_member (json, "errors");
     errors = json_node_get_object (json_array_get_element (array, 0));
     g_warning ("Failed to talk to the Token Server: %s: %s",
@@ -464,7 +385,7 @@ obtain_storage_credentials_response_cb (SoupSession *session,
   } else {
     g_warning ("Failed to talk to the Token Server, status code %u. "
                "See https://docs.services.mozilla.com/token/apis.html#error-responses";,
-               message->status_code);
+               msg->status_code);
     storage_server_request_async_data_free (data);
     goto out;
   }
@@ -479,22 +400,20 @@ static void
 ephy_sync_service_obtain_storage_credentials (EphySyncService *self,
                                               gpointer         user_data)
 {
-  SoupMessage *message;
-  guint8 *kB = NULL;
-  gchar *hashed_kB = NULL;
-  gchar *client_state = NULL;
-  gchar *audience = NULL;
-  gchar *assertion = NULL;
-  gchar *authorization = NULL;
+  SoupMessage *msg;
+  guint8 *kB;
+  gchar *hashed_kB;
+  gchar *client_state;
+  gchar *audience;
+  gchar *assertion;
+  gchar *authorization;
 
+  g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
   g_return_if_fail (self->certificate != NULL);
   g_return_if_fail (self->keypair != NULL);
 
-  audience = get_audience_for_url (MOZILLA_TOKEN_SERVER_URL);
-  assertion = ephy_sync_crypto_create_assertion (self->certificate,
-                                                 audience,
-                                                 5 * 60,
-                                                 self->keypair);
+  audience = ephy_sync_utils_make_audience (MOZILLA_TOKEN_SERVER_URL);
+  assertion = ephy_sync_crypto_create_assertion (self->certificate, audience, 300, self->keypair);
   g_return_if_fail (assertion != NULL);
 
   kB = ephy_sync_crypto_decode_hex (self->kB);
@@ -502,16 +421,13 @@ ephy_sync_service_obtain_storage_credentials (EphySyncService *self,
   client_state = g_strndup (hashed_kB, EPHY_SYNC_TOKEN_LENGTH);
   authorization = g_strdup_printf ("BrowserID %s", assertion);
 
-  message = soup_message_new (SOUP_METHOD_GET, MOZILLA_TOKEN_SERVER_URL);
+  msg = soup_message_new (SOUP_METHOD_GET, MOZILLA_TOKEN_SERVER_URL);
   /* We need to add the X-Client-State header so that the Token Server will
    * recognize accounts that were previously used to sync Firefox data too.
    */
-  soup_message_headers_append (message->request_headers,
-                               "X-Client-State", client_state);
-  soup_message_headers_append (message->request_headers,
-                               "authorization", authorization);
-  soup_session_queue_message (self->soup_session, message,
-                              obtain_storage_credentials_response_cb, user_data);
+  soup_message_headers_append (msg->request_headers, "X-Client-State", client_state);
+  soup_message_headers_append (msg->request_headers, "authorization", authorization);
+  soup_session_queue_message (self->session, msg, obtain_storage_credentials_response_cb, user_data);
 
   g_free (kB);
   g_free (hashed_kB);
@@ -523,7 +439,7 @@ ephy_sync_service_obtain_storage_credentials (EphySyncService *self,
 
 static void
 obtain_signed_certificate_response_cb (SoupSession *session,
-                                       SoupMessage *message,
+                                       SoupMessage *msg,
                                        gpointer     user_data)
 {
   StorageServerRequestAsyncData *data;
@@ -536,10 +452,10 @@ obtain_signed_certificate_response_cb (SoupSession *session,
   service = EPHY_SYNC_SERVICE (data->service);
 
   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);
   json = json_node_get_object (json_parser_get_root (parser));
 
-  if (message->status_code != 200) {
+  if (msg->status_code != 200) {
     g_warning ("FxA server errno: %ld, errmsg: %s",
                json_object_get_int_member (json, "errno"),
                json_object_get_string_member (json, "message"));
@@ -568,13 +484,16 @@ static void
 ephy_sync_service_obtain_signed_certificate (EphySyncService *self,
                                              gpointer         user_data)
 {
-  EphySyncCryptoProcessedST *processed_st;
-  gchar *tokenID;
+  guint8 *tokenID;
+  guint8 *reqHMACkey;
+  guint8 *requestKey;
+  gchar *tokenID_hex;
   gchar *public_key_json;
   gchar *request_body;
-  gchar *n_str;
-  gchar *e_str;
+  gchar *n;
+  gchar *e;
 
+  g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
   g_return_if_fail (self->sessionToken != NULL);
 
   if (self->keypair != NULL)
@@ -583,36 +502,30 @@ ephy_sync_service_obtain_signed_certificate (EphySyncService *self,
   self->keypair = ephy_sync_crypto_generate_rsa_key_pair ();
   g_return_if_fail (self->keypair != NULL);
 
-  processed_st = ephy_sync_crypto_process_session_token (self->sessionToken);
-  tokenID = ephy_sync_crypto_encode_hex (processed_st->tokenID, 0);
+  ephy_sync_crypto_process_session_token (self->sessionToken, &tokenID, &reqHMACkey, &requestKey);
+  tokenID_hex = ephy_sync_crypto_encode_hex (tokenID, 0);
 
-  n_str = mpz_get_str (NULL, 10, self->keypair->public.n);
-  e_str = mpz_get_str (NULL, 10, self->keypair->public.e);
-  public_key_json = ephy_sync_utils_build_json_string ("algorithm", "RS",
-                                                       "n", n_str,
-                                                       "e", e_str,
-                                                       NULL);
+  n = mpz_get_str (NULL, 10, self->keypair->public.n);
+  e = mpz_get_str (NULL, 10, self->keypair->public.e);
+  public_key_json = ephy_sync_utils_build_json_string ("algorithm", "RS", "n", n, "e", e, NULL);
   /* Duration is the lifetime of the certificate in milliseconds. The FxA server
    * limits the duration to 24 hours. For our purposes, a duration of 30 minutes
    * will suffice.
    */
   request_body = g_strdup_printf ("{\"publicKey\": %s, \"duration\": %d}",
                                   public_key_json, 30 * 60 * 1000);
-  ephy_sync_service_fxa_hawk_post_async (self,
-                                         "certificate/sign",
-                                         tokenID,
-                                         processed_st->reqHMACkey,
-                                         EPHY_SYNC_TOKEN_LENGTH,
-                                         request_body,
-                                         obtain_signed_certificate_response_cb,
-                                         user_data);
-
-  ephy_sync_crypto_processed_st_free (processed_st);
+  ephy_sync_service_fxa_hawk_post_async (self, "certificate/sign", tokenID_hex,
+                                         reqHMACkey, EPHY_SYNC_TOKEN_LENGTH, request_body,
+                                         obtain_signed_certificate_response_cb, user_data);
+
   g_free (tokenID);
+  g_free (reqHMACkey);
+  g_free (requestKey);
+  g_free (tokenID_hex);
   g_free (public_key_json);
   g_free (request_body);
-  g_free (n_str);
-  g_free (e_str);
+  g_free (n);
+  g_free (e);
 }
 
 static void
@@ -657,10 +570,10 @@ ephy_sync_service_dispose (GObject *object)
 {
   EphySyncService *self = EPHY_SYNC_SERVICE (object);
 
-  g_clear_object (&self->soup_session);
+  g_clear_object (&self->session);
   g_clear_pointer (&self->user_email, g_free);
   ephy_sync_service_clear_storage_credentials (self);
-  ephy_sync_service_delete_all_tokens (self);
+  ephy_sync_service_clear_tokens (self);
 
   G_OBJECT_CLASS (ephy_sync_service_parent_class)->dispose (object);
 }
@@ -679,12 +592,13 @@ ephy_sync_service_init (EphySyncService *self)
 {
   gchar *email;
 
-  self->soup_session = soup_session_new ();
+  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);
 
-  if (g_str_equal (email, "") == FALSE) {
+  if (g_strcmp0 (email, "") != 0) {
     if (g_regex_match_simple (EMAIL_REGEX, email, 0, 0) == TRUE) {
       ephy_sync_service_set_user_email (self, email);
       ephy_sync_secret_load_tokens (self);
@@ -700,36 +614,19 @@ ephy_sync_service_new (void)
   return EPHY_SYNC_SERVICE (g_object_new (EPHY_TYPE_SYNC_SERVICE, NULL));
 }
 
-const gchar *
-ephy_sync_service_token_name_from_type (EphySyncServiceTokenType token_type)
-{
-  switch (token_type) {
-    case TOKEN_UID:
-      return "uid";
-    case TOKEN_SESSIONTOKEN:
-      return "sessionToken";
-    case TOKEN_KEYFETCHTOKEN:
-      return "keyFetchToken";
-    case TOKEN_UNWRAPBKEY:
-      return "unwrapBKey";
-    case TOKEN_KA:
-      return "kA";
-    case TOKEN_KB:
-      return "kB";
-  default:
-    g_assert_not_reached ();
-  }
-}
-
 gboolean
 ephy_sync_service_is_signed_in (EphySyncService *self)
 {
+  g_return_val_if_fail (EPHY_IS_SYNC_SERVICE (self), FALSE);
+
   return self->user_email != NULL;
 }
 
 gchar *
 ephy_sync_service_get_user_email (EphySyncService *self)
 {
+  g_return_val_if_fail (EPHY_IS_SYNC_SERVICE (self), NULL);
+
   return self->user_email;
 }
 
@@ -737,14 +634,58 @@ void
 ephy_sync_service_set_user_email (EphySyncService *self,
                                   const gchar     *email)
 {
+  g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
+
   g_free (self->user_email);
   self->user_email = g_strdup (email);
 }
 
+double
+ephy_sync_service_get_sync_time (EphySyncService *self)
+{
+  g_return_val_if_fail (EPHY_IS_SYNC_SERVICE (self), 0);
+
+  if (self->sync_time != 0)
+    return self->sync_time;
+
+  self->sync_time = g_settings_get_double (EPHY_SETTINGS_MAIN, EPHY_PREFS_SYNC_TIME);
+  return self->sync_time;
+}
+
+
+void
+ephy_sync_service_set_sync_time (EphySyncService *self,
+                                 double           time)
+{
+  g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
+
+  self->sync_time = time;
+  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,
-                             EphySyncServiceTokenType  type)
+ephy_sync_service_get_token (EphySyncService   *self,
+                             EphySyncTokenType  type)
 {
+  g_return_val_if_fail (EPHY_IS_SYNC_SERVICE (self), NULL);
+
   switch (type) {
     case TOKEN_UID:
       return self->uid;
@@ -764,10 +705,13 @@ ephy_sync_service_get_token (EphySyncService          *self,
 }
 
 void
-ephy_sync_service_set_token (EphySyncService          *self,
-                             gchar                    *value,
-                             EphySyncServiceTokenType  type)
+ephy_sync_service_set_token (EphySyncService   *self,
+                             gchar             *value,
+                             EphySyncTokenType  type)
 {
+  g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
+  g_return_if_fail (value != NULL);
+
   switch (type) {
     case TOKEN_UID:
       g_free (self->uid);
@@ -799,30 +743,47 @@ ephy_sync_service_set_token (EphySyncService          *self,
 }
 
 void
-ephy_sync_service_set_and_store_tokens (EphySyncService          *self,
-                                        gchar                    *first_value,
-                                        EphySyncServiceTokenType  first_type,
+ephy_sync_service_set_and_store_tokens (EphySyncService   *self,
+                                        gchar             *value,
+                                        EphySyncTokenType  type,
                                         ...)
 {
-  EphySyncServiceTokenType type;
-  gchar *value;
+  EphySyncTokenType next_type;
+  gchar *next_value;
   va_list args;
 
-  ephy_sync_service_set_token (self, first_value, first_type);
-  ephy_sync_secret_store_token (self->user_email, first_value, first_type);
+  g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
+  g_return_if_fail (value != NULL);
+
+  ephy_sync_service_set_token (self, value, type);
+  ephy_sync_secret_store_token (self->user_email, value, type);
 
-  va_start (args, first_type);
-  while ((value = va_arg (args, gchar *)) != NULL) {
-    type = va_arg (args, EphySyncServiceTokenType);
-    ephy_sync_service_set_token (self, value, type);
-    ephy_sync_secret_store_token (self->user_email, value, type);
+  va_start (args, type);
+  while ((next_value = va_arg (args, gchar *)) != NULL) {
+    next_type = va_arg (args, EphySyncTokenType);
+    ephy_sync_service_set_token (self, next_value, next_type);
+    ephy_sync_secret_store_token (self->user_email, next_value, next_type);
   }
   va_end (args);
 }
 
 void
-ephy_sync_service_delete_all_tokens (EphySyncService *self)
+ephy_sync_service_clear_storage_credentials (EphySyncService *self)
+{
+  g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
+
+  g_clear_pointer (&self->certificate, g_free);
+  g_clear_pointer (&self->storage_endpoint, g_free);
+  g_clear_pointer (&self->storage_credentials_id, g_free);
+  g_clear_pointer (&self->storage_credentials_key, g_free);
+  self->storage_credentials_expiry_time = 0;
+}
+
+void
+ephy_sync_service_clear_tokens (EphySyncService *self)
 {
+  g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
+
   g_clear_pointer (&self->uid, g_free);
   g_clear_pointer (&self->sessionToken, g_free);
   g_clear_pointer (&self->keyFetchToken, g_free);
@@ -835,50 +796,46 @@ void
 ephy_sync_service_destroy_session (EphySyncService *self,
                                    const gchar     *sessionToken)
 {
-  EphySyncCryptoProcessedST *processed_st;
-  EphySyncCryptoHawkOptions *hawk_options;
-  EphySyncCryptoHawkHeader *hawk_header;
-  SoupMessage *message;
-  gchar *tokenID;
+  EphySyncCryptoHawkOptions *hoptions;
+  EphySyncCryptoHawkHeader *hheader;
+  SoupMessage *msg;
+  guint8 *tokenID;
+  guint8 *reqHMACkey;
+  guint8 *requestKey;
+  gchar *tokenID_hex;
   gchar *url;
   const gchar *content_type = "application/json";
   const gchar *endpoint = "session/destroy";
   const gchar *request_body = "{}";
 
+  g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
+
+  if (sessionToken == NULL)
+    sessionToken = ephy_sync_service_get_token (self, TOKEN_SESSIONTOKEN);
   g_return_if_fail (sessionToken != NULL);
 
-  url = g_strdup_printf ("%s%s%s",
-                         MOZILLA_FIREFOX_ACCOUNTS_BASE_URL,
-                         MOZILLA_FIREFOX_ACCOUNTS_VERSION,
-                         endpoint);
-  processed_st = ephy_sync_crypto_process_session_token (sessionToken);
-  tokenID = ephy_sync_crypto_encode_hex (processed_st->tokenID, 0);
+  url = g_strdup_printf ("%s%s", MOZILLA_FXA_SERVER_URL, endpoint);
+  ephy_sync_crypto_process_session_token (sessionToken, &tokenID, &reqHMACkey, &requestKey);
+  tokenID_hex = ephy_sync_crypto_encode_hex (tokenID, 0);
 
-  message = soup_message_new (SOUP_METHOD_POST, url);
-  soup_message_set_request (message, content_type,
-                            SOUP_MEMORY_STATIC,
+  msg = soup_message_new (SOUP_METHOD_POST, url);
+  soup_message_set_request (msg, content_type, SOUP_MEMORY_STATIC,
                             request_body, strlen (request_body));
-  hawk_options = ephy_sync_crypto_hawk_options_new (NULL, NULL, NULL,
-                                                    g_strdup (content_type),
-                                                    NULL, NULL, NULL,
-                                                    g_strdup (request_body),
-                                                    NULL);
-  hawk_header = ephy_sync_crypto_compute_hawk_header (url, "POST",
-                                                      tokenID,
-                                                      processed_st->reqHMACkey,
-                                                      EPHY_SYNC_TOKEN_LENGTH,
-                                                      hawk_options);
-  soup_message_headers_append (message->request_headers,
-                               "authorization", hawk_header->header);
-  soup_message_headers_append (message->request_headers,
-                               "content-type", content_type);
-  soup_session_queue_message (self->soup_session, message,
-                              destroy_session_response_cb, NULL);
-
-  ephy_sync_crypto_hawk_options_free (hawk_options);
-  ephy_sync_crypto_hawk_header_free (hawk_header);
-  ephy_sync_crypto_processed_st_free (processed_st);
+  hoptions = ephy_sync_crypto_hawk_options_new (NULL, NULL, NULL, content_type,
+                                                NULL, NULL, NULL, request_body, NULL);
+  hheader = ephy_sync_crypto_compute_hawk_header (url, "POST", tokenID_hex,
+                                                  reqHMACkey, EPHY_SYNC_TOKEN_LENGTH,
+                                                  hoptions);
+  soup_message_headers_append (msg->request_headers, "authorization", hheader->header);
+  soup_message_headers_append (msg->request_headers, "content-type", content_type);
+  soup_session_queue_message (self->session, msg, destroy_session_response_cb, NULL);
+
+  ephy_sync_crypto_hawk_options_free (hoptions);
+  ephy_sync_crypto_hawk_header_free (hheader);
+  g_free (tokenID_hex);
   g_free (tokenID);
+  g_free (reqHMACkey);
+  g_free (requestKey);
   g_free (url);
 }
 
@@ -888,23 +845,30 @@ ephy_sync_service_fetch_sync_keys (EphySyncService *self,
                                    const gchar     *keyFetchToken,
                                    const gchar     *unwrapBKey)
 {
-  EphySyncCryptoProcessedKFT *processed_kft = NULL;
-  EphySyncCryptoSyncKeys *sync_keys = NULL;
   JsonNode *node;
   JsonObject *json;
   guint8 *unwrapKB;
-  gchar *tokenID;
+  guint8 *tokenID;
+  guint8 *reqHMACkey;
+  guint8 *respHMACkey;
+  guint8 *respXORkey;
+  guint8 *kA;
+  guint8 *kB;
+  gchar *tokenID_hex;
   guint status_code;
   gboolean retval = FALSE;
 
+  g_return_val_if_fail (EPHY_IS_SYNC_SERVICE (self), FALSE);
+  g_return_val_if_fail (email != NULL, FALSE);
+  g_return_val_if_fail (keyFetchToken != NULL, FALSE);
+  g_return_val_if_fail (unwrapBKey != NULL, FALSE);
+
   unwrapKB = ephy_sync_crypto_decode_hex (unwrapBKey);
-  processed_kft = ephy_sync_crypto_process_key_fetch_token (keyFetchToken);
-  tokenID = ephy_sync_crypto_encode_hex (processed_kft->tokenID, 0);
-  status_code = ephy_sync_service_fxa_hawk_get_sync (self,
-                                                     "account/keys",
-                                                     tokenID,
-                                                     processed_kft->reqHMACkey,
-                                                     EPHY_SYNC_TOKEN_LENGTH,
+  ephy_sync_crypto_process_key_fetch_token (keyFetchToken,
+                                            &tokenID, &reqHMACkey, &respHMACkey, &respXORkey);
+  tokenID_hex = ephy_sync_crypto_encode_hex (tokenID, 0);
+  status_code = ephy_sync_service_fxa_hawk_get_sync (self, "account/keys", tokenID_hex,
+                                                     reqHMACkey, EPHY_SYNC_TOKEN_LENGTH,
                                                      &node);
   json = json_node_get_object (node);
 
@@ -915,30 +879,30 @@ ephy_sync_service_fetch_sync_keys (EphySyncService *self,
     goto out;
   }
 
-  sync_keys = ephy_sync_crypto_retrieve_sync_keys (json_object_get_string_member (json, "bundle"),
-                                                   processed_kft->respHMACkey,
-                                                   processed_kft->respXORkey,
-                                                   unwrapKB);
-
-  if (sync_keys == NULL)
-    goto out;
+  ephy_sync_crypto_compute_sync_keys (json_object_get_string_member (json, "bundle"),
+                                      respHMACkey, respXORkey, unwrapKB,
+                                      &kA, &kB);
 
   /* Everything is okay, save the tokens. */
   ephy_sync_service_set_user_email (self, email);
   ephy_sync_service_set_and_store_tokens (self,
                                           g_strdup (keyFetchToken), TOKEN_KEYFETCHTOKEN,
                                           g_strdup (unwrapBKey), TOKEN_UNWRAPBKEY,
-                                          ephy_sync_crypto_encode_hex (sync_keys->kA, 0), TOKEN_KA,
-                                          ephy_sync_crypto_encode_hex (sync_keys->kB, 0), TOKEN_KB,
+                                          ephy_sync_crypto_encode_hex (kA, 0), TOKEN_KA,
+                                          ephy_sync_crypto_encode_hex (kB, 0), TOKEN_KB,
                                           NULL);
+  g_free (kA);
+  g_free (kB);
   retval = TRUE;
 
 out:
-  ephy_sync_crypto_processed_kft_free (processed_kft);
-  ephy_sync_crypto_sync_keys_free (sync_keys);
   json_node_free (node);
-  g_free (tokenID);
   g_free (unwrapKB);
+  g_free (tokenID);
+  g_free (reqHMACkey);
+  g_free (respHMACkey);
+  g_free (respXORkey);
+  g_free (tokenID_hex);
 
   return retval;
 }
@@ -955,16 +919,19 @@ ephy_sync_service_send_storage_message (EphySyncService     *self,
 {
   StorageServerRequestAsyncData *data;
 
-  data = storage_server_request_async_data_new (self, endpoint,
-                                                method, request_body,
+  g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
+  g_return_if_fail (endpoint != NULL);
+  g_return_if_fail (method != NULL);
+
+  data = storage_server_request_async_data_new (self, endpoint, method, request_body,
                                                 modified_since, unmodified_since,
                                                 callback, user_data);
 
   /* 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);
@@ -976,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.
@@ -984,5 +951,445 @@ 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
+upload_bookmark_response_cb (SoupSession *session,
+                             SoupMessage *msg,
+                             gpointer     user_data)
+{
+  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
+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;
+
+  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);
+
+  /* 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_download_bookmark (EphySyncService *self,
+                                     EphyBookmark    *bookmark)
+{
+  gchar *endpoint;
+
+  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));
+  ephy_sync_service_send_storage_message (self, endpoint,
+                                          SOUP_METHOD_GET, NULL, -1, -1,
+                                          download_bookmark_response_cb, NULL);
+
+  g_free (endpoint);
+}
+
+static void
+delete_bookmark_conditional_response_cb (SoupSession *session,
+                                         SoupMessage *msg,
+                                         gpointer     user_data)
+{
+  EphySyncService *service;
+  EphyBookmark *bookmark;
+  EphyBookmarksManager *manager;
+
+  bookmark = EPHY_BOOKMARK (user_data);
+  manager = ephy_shell_get_bookmarks_manager (ephy_shell_get_default ());
+
+  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 delete conditionally. 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);
+}
+
+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_delete_bookmark (EphySyncService *self,
+                                   EphyBookmark    *bookmark,
+                                   gboolean         conditional)
+{
+  gchar *endpoint;
+
+  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));
+
+  /* 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);
+}
+
+static void
+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;
+  const gchar *timestamp;
+  double server_time;
+
+  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, msg->response_body->data, -1, NULL);
+
+  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;
+  }
+
+  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) {
+      local = ephy_bookmarks_manager_get_bookmark_by_url (manager, ephy_bookmark_get_url (remote));
+
+      /* 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));
+
+        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);
+      }
+    }
+  }
+
+  /* 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);
+
+    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 (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_hash_table_unref (marked);
+
+  ephy_sync_service_release_next_storage_message (service);
+}
+
+static void
+sync_bookmarks_response_cb (SoupSession *session,
+                            SoupMessage *msg,
+                            gpointer     user_data)
+{
+  EphySyncService *service;
+  EphyBookmarksManager *manager;
+  GSequence *bookmarks;
+  GSequenceIter *iter;
+  JsonParser *parser;
+  JsonArray *array;
+  const gchar *timestamp;
+  double server_time;
+
+  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);
+
+  /* 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;
+
+  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);
+
+out:
+  g_object_unref (parser);
+
+  ephy_sync_service_release_next_storage_message (service);
+}
+
+void
+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?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);
+}
+
+gboolean
+ephy_sync_service_do_periodical_sync (EphySyncService *self)
+{
+  g_return_val_if_fail (EPHY_IS_SYNC_SERVICE (self), G_SOURCE_REMOVE);
+
+  ephy_sync_service_sync_bookmarks (self, FALSE);
+
+  return G_SOURCE_CONTINUE;
 }
diff --git a/src/ephy-sync-service.h b/src/ephy-sync-service.h
index 8dd043b..2bb0561 100644
--- a/src/ephy-sync-service.h
+++ b/src/ephy-sync-service.h
@@ -19,6 +19,9 @@
 #ifndef EPHY_SYNC_SERVICE_H
 #define EPHY_SYNC_SERVICE_H
 
+#include "ephy-bookmark.h"
+#include "ephy-sync-utils.h"
+
 #include <glib-object.h>
 #include <libsoup/soup.h>
 
@@ -28,48 +31,34 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (EphySyncService, ephy_sync_service, EPHY, SYNC_SERVICE, GObject)
 
-typedef enum {
-  TOKEN_UID,
-  TOKEN_SESSIONTOKEN,
-  TOKEN_KEYFETCHTOKEN,
-  TOKEN_UNWRAPBKEY,
-  TOKEN_KA,
-  TOKEN_KB
-} EphySyncServiceTokenType;
-
 EphySyncService *ephy_sync_service_new                          (void);
-
-const gchar     *ephy_sync_service_token_name_from_type         (EphySyncServiceTokenType token_type);
-
 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);
-
-gchar           *ephy_sync_service_get_token                    (EphySyncService          *self,
-                                                                 EphySyncServiceTokenType  token_type);
-
-void             ephy_sync_service_set_token                    (EphySyncService          *self,
-                                                                 gchar                    *token_value,
-                                                                 EphySyncServiceTokenType  token_type);
-
-void             ephy_sync_service_set_and_store_tokens         (EphySyncService          *self,
-                                                                 gchar                    *token_value,
-                                                                 EphySyncServiceTokenType  token_type,
+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_delete_all_tokens            (EphySyncService *self);
-
+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,
@@ -78,8 +67,18 @@ void             ephy_sync_service_send_storage_message         (EphySyncService
                                                                  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/ephy-sync-utils.c b/src/ephy-sync-utils.c
index c37f885..702fba7 100644
--- a/src/ephy-sync-utils.c
+++ b/src/ephy-sync-utils.c
@@ -19,24 +19,27 @@
 #include "config.h"
 #include "ephy-sync-utils.h"
 
+#include <libsoup/soup.h>
+#include <string.h>
+
 gchar *
-ephy_sync_utils_build_json_string (const gchar *first_key,
-                                   const gchar *first_value,
+ephy_sync_utils_build_json_string (const gchar *key,
+                                   const gchar *value,
                                    ...)
 {
   va_list args;
   gchar *json;
-  gchar *key;
-  gchar *value;
+  gchar *next_key;
+  gchar *next_value;
   gchar *tmp;
 
-  json = g_strconcat ("{\"", first_key, "\": \"", first_value, "\"", NULL);
-  va_start (args, first_value);
+  json = g_strconcat ("{\"", key, "\": \"", value, "\"", NULL);
+  va_start (args, value);
 
-  while ((key = va_arg (args, gchar *)) != NULL) {
-    value = va_arg (args, gchar *);
+  while ((next_key = va_arg (args, gchar *)) != NULL) {
+    next_value = va_arg (args, gchar *);
     tmp = json;
-    json = g_strconcat (json, ", \"", key, "\": \"", value, "\"", NULL);
+    json = g_strconcat (json, ", \"", next_key, "\": \"", next_value, "\"", NULL);
     g_free (tmp);
   }
 
@@ -54,3 +57,117 @@ ephy_sync_utils_create_bso_json (const gchar *id,
 {
   return ephy_sync_utils_build_json_string ("id", id, "payload", payload, NULL);
 }
+
+gchar *
+ephy_sync_utils_make_audience (const gchar *url)
+{
+  SoupURI *uri;
+  const gchar *scheme;
+  const gchar *host;
+  gchar *audience;
+  gchar *port;
+
+  g_return_val_if_fail (url != NULL, NULL);
+
+  uri = soup_uri_new (url);
+  scheme = soup_uri_get_scheme (uri);
+  host = soup_uri_get_host (uri);
+  port = g_strdup_printf (":%u", soup_uri_get_port (uri));
+
+  /* Even if the url doesn't contain the port, soup_uri_get_port() will return
+   * the default port for the url's scheme so we need to check if the port was
+   * really present in the url.
+   */
+  if (g_strstr_len (url, -1, port) != NULL)
+    audience = g_strdup_printf ("%s://%s%s", scheme, host, port);
+  else
+    audience = g_strdup_printf ("%s://%s", scheme, host);
+
+  g_free (port);
+  soup_uri_free (uri);
+
+  return audience;
+}
+
+const gchar *
+ephy_sync_utils_token_name_from_type (EphySyncTokenType type)
+{
+  switch (type) {
+    case TOKEN_UID:
+      return "uid";
+    case TOKEN_SESSIONTOKEN:
+      return "sessionToken";
+    case TOKEN_KEYFETCHTOKEN:
+      return "keyFetchToken";
+    case TOKEN_UNWRAPBKEY:
+      return "unwrapBKey";
+    case TOKEN_KA:
+      return "kA";
+    case TOKEN_KB:
+      return "kB";
+    default:
+      g_assert_not_reached ();
+  }
+}
+
+gchar *
+ephy_sync_utils_find_and_replace (const gchar *src,
+                                  const gchar *find,
+                                  const gchar *repl)
+{
+  const gchar *haystack = src;
+  const gchar *needle = NULL;
+  gsize haystack_len = strlen (src);
+  gsize find_len = strlen (find);
+  gsize repl_len = strlen (repl);
+  gsize new_len = 0;
+  gsize skip_len = 0;
+  gchar *out = g_malloc (haystack_len + 1);
+
+  while ((needle = g_strstr_len (haystack, -1, find)) != NULL) {
+    haystack_len += find_len - repl_len;
+    out = g_realloc (out, haystack_len + 1);
+    skip_len = needle - haystack;
+    memcpy (out + new_len, haystack, skip_len);
+    memcpy (out + new_len + skip_len, repl, repl_len);
+    new_len += skip_len + repl_len;
+    haystack = needle + find_len;
+  }
+  strcpy (out + new_len, haystack);
+
+  return out;
+}
+
+guint8 *
+ephy_sync_utils_concatenate_bytes (guint8 *bytes,
+                                   gsize   bytes_len,
+                                   ...)
+{
+  va_list args;
+  guint8 *next;
+  guint8 *out;
+  gsize next_len;
+  gsize out_len;
+
+  out_len = bytes_len;
+  out = g_malloc (out_len);
+  memcpy (out, bytes, out_len);
+
+  va_start (args, bytes_len);
+  while ((next = va_arg (args, guint8 *)) != NULL) {
+    next_len = va_arg (args, gsize);
+    out = g_realloc (out, out_len + next_len);
+    memcpy (out + out_len, next, next_len);
+    out_len += next_len;
+  }
+
+  va_end (args);
+
+  return out;
+}
+
+gint64
+ephy_sync_utils_current_time_seconds (void)
+{
+  return g_get_real_time () / 1000000;
+}
diff --git a/src/ephy-sync-utils.h b/src/ephy-sync-utils.h
index 8cf72df..2bfc94a 100644
--- a/src/ephy-sync-utils.h
+++ b/src/ephy-sync-utils.h
@@ -21,14 +21,31 @@
 
 #include <glib-object.h>
 
-G_BEGIN_DECLS
+typedef enum {
+  TOKEN_UID,
+  TOKEN_SESSIONTOKEN,
+  TOKEN_KEYFETCHTOKEN,
+  TOKEN_UNWRAPBKEY,
+  TOKEN_KA,
+  TOKEN_KB
+} EphySyncTokenType;
 
-gchar *ephy_sync_utils_build_json_string (const gchar *first_key,
-                                          const gchar *first_value,
-                                          ...) G_GNUC_NULL_TERMINATED;
+G_BEGIN_DECLS
 
-gchar *ephy_sync_utils_create_bso_json   (const gchar *id,
-                                          const gchar *payload);
+gchar       *ephy_sync_utils_build_json_string    (const gchar *key,
+                                                   const gchar *value,
+                                                   ...) G_GNUC_NULL_TERMINATED;
+gchar       *ephy_sync_utils_create_bso_json      (const gchar *id,
+                                                   const gchar *payload);
+gchar       *ephy_sync_utils_make_audience        (const gchar *url);
+const gchar *ephy_sync_utils_token_name_from_type (EphySyncTokenType type);
+gchar       *ephy_sync_utils_find_and_replace     (const gchar *src,
+                                                   const gchar *find,
+                                                   const gchar *repl);
+guint8      *ephy_sync_utils_concatenate_bytes    (guint8 *bytes,
+                                                   gsize   bytes_len,
+                                                   ...) G_GNUC_NULL_TERMINATED;
+gint64       ephy_sync_utils_current_time_seconds  (void);
 
 G_END_DECLS
 
diff --git a/src/prefs-dialog.c b/src/prefs-dialog.c
index ecf5f5e..8a58148 100644
--- a/src/prefs-dialog.c
+++ b/src/prefs-dialog.c
@@ -34,7 +34,6 @@
 #include "ephy-session.h"
 #include "ephy-settings.h"
 #include "ephy-shell.h"
-#include "ephy-sync-bookmarks.h"
 #include "ephy-sync-secret.h"
 #include "ephy-sync-service.h"
 #include "clear-data-dialog.h"
@@ -243,7 +242,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 +294,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_bookmarks_create_storage_collection ();
+    /* 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),
@@ -350,15 +354,14 @@ on_sync_sign_out_button_clicked (GtkWidget   *button,
                                  PrefsDialog *dialog)
 {
   EphySyncService *service;
-  gchar *sessionToken;
 
-  service = ephy_shell_get_global_sync_service (ephy_shell_get_default ());
-  sessionToken = ephy_sync_service_get_token (service, TOKEN_SESSIONTOKEN);
+  service = ephy_shell_get_sync_service (ephy_shell_get_default ());
 
   /* Destroy session and delete tokens. */
-  ephy_sync_service_destroy_session (service, sessionToken);
-  ephy_sync_service_delete_all_tokens (service);
-  ephy_sync_secret_forget_all_tokens ();
+  ephy_sync_service_destroy_session (service, NULL);
+  ephy_sync_service_clear_storage_credentials (service);
+  ephy_sync_service_clear_tokens (service);
+  ephy_sync_secret_forget_tokens ();
   ephy_sync_service_set_user_email (service, NULL);
 
   g_settings_set_string (EPHY_SETTINGS_MAIN, EPHY_PREFS_SYNC_USER, "");
@@ -1452,7 +1455,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]