[epiphany/wip/sync: 73/86] bookmark: Implement EphySynchronizable



commit 17b7d62dba47c2216381644c2d511a370383399a
Author: Gabriel Ivascu <ivascu gabriel59 gmail com>
Date:   Tue Mar 21 13:37:47 2017 +0200

    bookmark: Implement EphySynchronizable
    
    Note that ID is now a property

 src/bookmarks/ephy-add-bookmark-popover.c    |    5 +-
 src/bookmarks/ephy-bookmark.c                |  124 ++++++++++++++++++++------
 src/bookmarks/ephy-bookmark.h                |    3 +-
 src/bookmarks/ephy-bookmarks-import.c        |    8 +-
 src/profile-migrator/Makefile.am             |    1 +
 src/profile-migrator/ephy-profile-migrator.c |    7 ++-
 src/sync/ephy-sync-crypto.c                  |   94 +++++++++++--------
 src/sync/ephy-sync-crypto.h                  |    4 +-
 8 files changed, 167 insertions(+), 79 deletions(-)
---
diff --git a/src/bookmarks/ephy-add-bookmark-popover.c b/src/bookmarks/ephy-add-bookmark-popover.c
index 5042934..d9054c7 100644
--- a/src/bookmarks/ephy-add-bookmark-popover.c
+++ b/src/bookmarks/ephy-add-bookmark-popover.c
@@ -215,10 +215,13 @@ ephy_add_bookmark_popover_show (EphyAddBookmarkPopover *self)
 
   self->bookmark = ephy_bookmarks_manager_get_bookmark_by_url (manager, address);
   if (!self->bookmark) {
+    char *id = ephy_sync_crypto_get_random_sync_id ();
     self->bookmark = ephy_bookmark_new (address,
                                         ephy_embed_get_title (embed),
-                                        g_sequence_new (g_free));
+                                        g_sequence_new (g_free),
+                                        id);
     self->is_new_bookmark = TRUE;
+    g_free (id);
   }
 
   g_signal_connect_object (manager, "bookmark-removed",
diff --git a/src/bookmarks/ephy-bookmark.c b/src/bookmarks/ephy-bookmark.c
index 509276c..198118a 100644
--- a/src/bookmarks/ephy-bookmark.c
+++ b/src/bookmarks/ephy-bookmark.c
@@ -27,12 +27,11 @@
 #include <string.h>
 
 #ifdef ENABLE_SYNC
+#include "ephy-synchronizable.h"
 #include "ephy-sync-crypto.h"
 #include "ephy-sync-utils.h"
 #endif
 
-#define ID_LEN 32
-
 struct _EphyBookmark {
   GObject      parent_instance;
 
@@ -49,12 +48,16 @@ struct _EphyBookmark {
 };
 
 static JsonSerializableIface *serializable_iface = NULL;
+static EphySynchronizableInterface *synchronizable_iface = NULL;
 
 static void json_serializable_iface_init (gpointer g_iface);
+static void ephy_synchronizable_iface_init (EphySynchronizableInterface *iface);
 
 G_DEFINE_TYPE_WITH_CODE (EphyBookmark, ephy_bookmark, G_TYPE_OBJECT,
                         G_IMPLEMENT_INTERFACE (JSON_TYPE_SERIALIZABLE,
-                                               json_serializable_iface_init))
+                                               json_serializable_iface_init)
+                        G_IMPLEMENT_INTERFACE (EPHY_TYPE_SYNCHRONIZABLE,
+                                               ephy_synchronizable_iface_init))
 
 enum {
   PROP_0,
@@ -62,7 +65,9 @@ enum {
   PROP_TIME_ADDED,
   PROP_TITLE,
   PROP_URL,
-  LAST_PROP
+  LAST_PROP,
+  /* Firefox related properties. */
+  PROP_ID
 };
 
 enum {
@@ -97,6 +102,9 @@ ephy_bookmark_set_property (GObject      *object,
     case PROP_URL:
       ephy_bookmark_set_url (self, g_value_get_string (value));
       break;
+    case PROP_ID:
+      ephy_bookmark_set_id (self, g_value_get_string (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
   }
@@ -123,6 +131,9 @@ ephy_bookmark_get_property (GObject      *object,
     case PROP_URL:
       g_value_set_string (value, ephy_bookmark_get_url (self));
       break;
+    case PROP_ID:
+      g_value_set_string (value, ephy_bookmark_get_id (self));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
   }
@@ -181,6 +192,7 @@ ephy_bookmark_class_init (EphyBookmarkClass *klass)
                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
 
   g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
+  g_object_class_override_property (object_class, PROP_ID, "id");
 
   signals[TAG_ADDED] =
     g_signal_new ("tag-added",
@@ -204,30 +216,6 @@ ephy_bookmark_class_init (EphyBookmarkClass *klass)
 static void
 ephy_bookmark_init (EphyBookmark *self)
 {
-#ifdef ENABLE_SYNC
-  self->id = g_malloc0 (ID_LEN + 1);
-  ephy_sync_crypto_random_hex_gen (NULL, ID_LEN, (guint8 *)self->id);
-#else
-  static const char hex_digits[] = "0123456789abcdef";
-  FILE *fp;
-  gsize num_bytes;
-  guint8 *bytes;
-
-  num_bytes = (ID_LEN + 1) / 2;
-  bytes = g_malloc (num_bytes);
-
-  fp = fopen ("/dev/urandom", "r");
-  fread (bytes, sizeof (guint8), num_bytes, fp);
-
-  self->id = g_malloc0 (ID_LEN + 1);
-  for (gsize i = 0; i < num_bytes; i++) {
-    self->id[2 * i] = hex_digits[bytes[i] >> 4];
-    self->id[2 * i + 1] = hex_digits[bytes[i] & 0xf];
-  }
-
-  g_free (bytes);
-  fclose (fp);
-#endif
 }
 
 static JsonNode *
@@ -304,14 +292,92 @@ json_serializable_iface_init (gpointer g_iface)
   iface->deserialize_property = ephy_bookmark_json_serializable_deserialize_property;
 }
 
+static const char *
+ephy_bookmark_synchronizable_get_id (EphySynchronizable *synchronizable)
+{
+  EphyBookmark *bookmark = EPHY_BOOKMARK (synchronizable);
+
+  return bookmark->id;
+}
+
+static void
+ephy_bookmark_synchronizable_set_id (EphySynchronizable *synchronizable,
+                                     const char         *id)
+{
+  EphyBookmark *bookmark = EPHY_BOOKMARK (synchronizable);
+
+  g_free (bookmark->id);
+  bookmark->id = g_strdup (id);
+}
+
+static double
+ephy_bookmark_synchronizable_get_modification_time (EphySynchronizable *synchronizable)
+{
+  EphyBookmark *bookmark = EPHY_BOOKMARK (synchronizable);
+
+  return bookmark->modified;
+}
+
+static void
+ephy_bookmark_synchronizable_set_modification_time (EphySynchronizable *synchronizable,
+                                                    double              modified)
+{
+  EphyBookmark *bookmark = EPHY_BOOKMARK (synchronizable);
+
+  bookmark->modified = modified;
+}
+
+static gboolean
+ephy_bookmark_synchronizable_is_uploaded (EphySynchronizable *synchronizable)
+{
+  EphyBookmark *bookmark = EPHY_BOOKMARK (synchronizable);
+
+  return bookmark->uploaded;
+}
+
+static void
+ephy_bookmark_synchronizable_set_is_uploaded (EphySynchronizable *synchronizable,
+                                              gboolean            uploaded)
+{
+
+  EphyBookmark *bookmark = EPHY_BOOKMARK (synchronizable);
+
+  bookmark->uploaded = uploaded;
+}
+
+static char *
+ephy_bookmark_synchronizable_to_bso (EphySynchronizable  *synchronizable,
+                                     GError             **error)
+{
+  return NULL;
+}
+
+static void
+ephy_synchronizable_iface_init (EphySynchronizableInterface *iface)
+{
+  synchronizable_iface = g_type_default_interface_peek (EPHY_TYPE_SYNCHRONIZABLE);
+
+  iface->get_id = ephy_bookmark_synchronizable_get_id;
+  iface->set_id = ephy_bookmark_synchronizable_set_id;
+  iface->get_modification_time = ephy_bookmark_synchronizable_get_modification_time;
+  iface->set_modification_time = ephy_bookmark_synchronizable_set_modification_time;
+  iface->is_uploaded = ephy_bookmark_synchronizable_is_uploaded;
+  iface->set_is_uploaded = ephy_bookmark_synchronizable_set_is_uploaded;
+  iface->to_bso = ephy_bookmark_synchronizable_to_bso;
+}
+
 EphyBookmark *
-ephy_bookmark_new (const char *url, const char *title, GSequence *tags)
+ephy_bookmark_new (const char *url,
+                   const char *title,
+                   GSequence  *tags,
+                   const char *id)
 {
   return g_object_new (EPHY_TYPE_BOOKMARK,
                        "url", url,
                        "title", title,
                        "tags", tags,
                        "time-added", g_get_real_time (),
+                       "id", id,
                        NULL);
 }
 
diff --git a/src/bookmarks/ephy-bookmark.h b/src/bookmarks/ephy-bookmark.h
index 8ce771e..328f6a8 100644
--- a/src/bookmarks/ephy-bookmark.h
+++ b/src/bookmarks/ephy-bookmark.h
@@ -31,7 +31,8 @@ G_DECLARE_FINAL_TYPE (EphyBookmark, ephy_bookmark, EPHY, BOOKMARK, GObject)
 
 EphyBookmark        *ephy_bookmark_new                   (const char *url,
                                                           const char *title,
-                                                          GSequence  *tags);
+                                                          GSequence  *tags,
+                                                          const char *id);
 
 void                 ephy_bookmark_set_time_added        (EphyBookmark *self,
                                                           gint64        time_added);
diff --git a/src/bookmarks/ephy-bookmarks-import.c b/src/bookmarks/ephy-bookmarks-import.c
index 8d252f8..f805db2 100644
--- a/src/bookmarks/ephy-bookmarks-import.c
+++ b/src/bookmarks/ephy-bookmarks-import.c
@@ -77,9 +77,8 @@ get_bookmarks_from_table (GvdbTable *table)
     g_variant_iter_free (iter);
 
     /* Create the new bookmark. */
-    bookmark = ephy_bookmark_new (list[i], title, tags);
+    bookmark = ephy_bookmark_new (list[i], title, tags, id);
     ephy_bookmark_set_time_added (bookmark, time_added);
-    ephy_bookmark_set_id (bookmark, id);
     ephy_bookmark_set_modification_time (bookmark, modified);
     ephy_bookmark_set_is_uploaded (bookmark, uploaded);
     g_sequence_prepend (bookmarks, bookmark);
@@ -214,7 +213,7 @@ ephy_bookmarks_import_from_firefox (EphyBookmarksManager  *manager,
   GSequence *bookmarks = NULL;
   gboolean ret = TRUE;
   gchar *filename;
-  const char *statement_str = "SELECT b.id, p.url, b.title, b.dateAdded "
+  const char *statement_str = "SELECT b.id, p.url, b.title, b.dateAdded, b.guid "
                               "FROM moz_bookmarks b "
                               "JOIN moz_places p ON b.fk=p.id "
                               "WHERE b.type=1 AND p.url NOT LIKE 'about%' "
@@ -260,11 +259,12 @@ ephy_bookmarks_import_from_firefox (EphyBookmarksManager  *manager,
     const char *url = ephy_sqlite_statement_get_column_as_string (statement, 1);
     const char *title = ephy_sqlite_statement_get_column_as_string (statement, 2);
     gint64 time_added = ephy_sqlite_statement_get_column_as_int64 (statement, 3);
+    const char *guid = ephy_sqlite_statement_get_column_as_string (statement, 4);
     EphyBookmark *bookmark;
     GSequence *tags;
 
     tags = g_sequence_new (g_free);
-    bookmark = ephy_bookmark_new (url, title, tags);
+    bookmark = ephy_bookmark_new (url, title, tags, guid);
     ephy_bookmark_set_time_added (bookmark, time_added);
     load_tags_for_bookmark (connection, bookmark, bookmark_id);
 
diff --git a/src/profile-migrator/Makefile.am b/src/profile-migrator/Makefile.am
index 8cf59bd..24a2709 100644
--- a/src/profile-migrator/Makefile.am
+++ b/src/profile-migrator/Makefile.am
@@ -13,6 +13,7 @@ ephy_profile_migrator_CPPFLAGS = \
        -I$(top_srcdir)/lib/history                     \
        -I$(top_srcdir)/src/                            \
        -I$(top_srcdir)/src/bookmarks                   \
+       -I$(top_srcdir)/src/sync                        \
        -DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\"        \
        -DLOCALEDIR=\"$(localedir)\"                    \
        $(GLIB_CFLAGS)                                  \
diff --git a/src/profile-migrator/ephy-profile-migrator.c b/src/profile-migrator/ephy-profile-migrator.c
index 0dcf135..9c44247 100644
--- a/src/profile-migrator/ephy-profile-migrator.c
+++ b/src/profile-migrator/ephy-profile-migrator.c
@@ -41,6 +41,7 @@
 #include "ephy-search-engine-manager.h"
 #include "ephy-settings.h"
 #include "ephy-sqlite-connection.h"
+#include "ephy-sync-crypto.h"
 #include "ephy-uri-tester-shared.h"
 #include "ephy-web-app-utils.h"
 
@@ -641,10 +642,14 @@ parse_rdf_item (EphyBookmarksManager *manager,
 
   if (link) {
     EphyBookmark *bookmark;
+    char *id;
 
     g_sequence_sort (tags, (GCompareDataFunc)ephy_bookmark_tags_compare, NULL);
-    bookmark = ephy_bookmark_new ((const char *)link, (const char *)title, tags);
+    id = ephy_sync_crypto_get_random_sync_id ();
+    bookmark = ephy_bookmark_new ((const char *)link, (const char *)title, tags, id);
     ephy_bookmarks_manager_add_bookmark (manager, bookmark);
+
+    g_free (id);
   } else {
     g_sequence_free (tags);
   }
diff --git a/src/sync/ephy-sync-crypto.c b/src/sync/ephy-sync-crypto.c
index 13b7cc2..2b4bef5 100644
--- a/src/sync/ephy-sync-crypto.c
+++ b/src/sync/ephy-sync-crypto.c
@@ -33,6 +33,7 @@
 #define HAWK_VERSION  1
 #define NONCE_LEN     6
 #define IV_LEN        16
+#define SYNC_ID_LEN   12
 
 static const char hex_digits[] = "0123456789abcdef";
 
@@ -655,6 +656,27 @@ ephy_sync_crypto_hmac_is_valid (const char   *text,
   return retval;
 }
 
+/*
+ * This function is required by Nettle's RSA support.
+ * From Nettle's documentation: random_ctx and random is a randomness generator.
+ * random(random_ctx, length, dst) should generate length random octets and store them at dst.
+ * We don't really use random_ctx, since we have /dev/urandom available.
+ */
+static void
+ephy_sync_crypto_random_bytes_gen (void   *random_ctx,
+                                   gsize   length,
+                                   guint8 *dst)
+{
+  FILE *fp;
+
+  g_assert (length > 0);
+  g_assert (dst);
+
+  fp = fopen ("/dev/urandom", "r");
+  fread (dst, sizeof (guint8), length, fp);
+  fclose (fp);
+}
+
 void
 ephy_sync_crypto_process_key_fetch_token (const char  *keyFetchToken,
                                           guint8     **tokenID,
@@ -935,7 +957,6 @@ ephy_sync_crypto_encrypt_record (const char          *cleartext,
                                  SyncCryptoKeyBundle *bundle)
 {
   char *payload = NULL;
-  char *iv_hex;
   char *iv_b64;
   char *ciphertext_b64;
   char *hmac;
@@ -957,13 +978,8 @@ ephy_sync_crypto_encrypt_record (const char          *cleartext,
   }
 
   /* Generate a random 16 bytes initialization vector. */
-  iv_hex = g_malloc0 (2 * IV_LEN + 1);
-  ephy_sync_crypto_random_hex_gen (NULL, 2 * IV_LEN, (guint8 *)iv_hex);
-  iv = ephy_sync_crypto_decode_hex (iv_hex);
-  if (!iv) {
-    g_warning ("Failed to decode the hex value of IV");
-    goto free_iv;
-  }
+  iv = g_malloc (IV_LEN);
+  ephy_sync_crypto_random_bytes_gen (NULL, IV_LEN, iv);
 
   /* Encrypt the record using the AES key. */
   ciphertext = ephy_sync_crypto_aes_256_encrypt (cleartext, aes_key, iv, &ciphertext_len);
@@ -984,9 +1000,7 @@ ephy_sync_crypto_encrypt_record (const char          *cleartext,
   g_free (iv_b64);
   g_free (ciphertext_b64);
   g_free (ciphertext);
-free_iv:
   g_free (iv);
-  g_free (iv_hex);
 free_keys:
   g_free (aes_key);
   g_free (hmac_key);
@@ -1012,6 +1026,7 @@ ephy_sync_crypto_compute_hawk_header (const char            *url,
   char *nonce;
   char *payload;
   char *timestamp;
+  guint8 *bytes;
   gint64 ts;
 
   g_return_val_if_fail (url, NULL);
@@ -1033,8 +1048,10 @@ ephy_sync_crypto_compute_hawk_header (const char            *url,
   if (options && options->nonce) {
     nonce = g_strdup (options->nonce);
   } else {
-    nonce = g_malloc0 (NONCE_LEN + 1);
-    ephy_sync_crypto_random_hex_gen (NULL, NONCE_LEN, (guint8 *)nonce);
+    bytes = g_malloc (NONCE_LEN / 2);
+    ephy_sync_crypto_random_bytes_gen (NULL, NONCE_LEN / 2, bytes);
+    nonce = ephy_sync_crypto_encode_hex (bytes, NONCE_LEN / 2);
+    g_free (bytes);
   }
 
   if (timestamp) {
@@ -1127,7 +1144,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, ephy_sync_crypto_random_hex_gen,
+                                 NULL, ephy_sync_crypto_random_bytes_gen,
                                  NULL, NULL, 2048, 0);
   if (retval == 0) {
     g_warning ("Failed to generate RSA key pair");
@@ -1178,7 +1195,7 @@ ephy_sync_crypto_create_assertion (const char           *certificate,
   /* Use the provided key pair to RSA sign the message. */
   mpz_init (signature);
   if (rsa_sha256_sign_digest_tr (&keypair->public, &keypair->private,
-                                 NULL, ephy_sync_crypto_random_hex_gen,
+                                 NULL, ephy_sync_crypto_random_bytes_gen,
                                  digest, signature) == 0) {
     g_warning ("Failed to sign the message. Giving up.");
     goto out;
@@ -1211,32 +1228,6 @@ out:
   return assertion;
 }
 
-void
-ephy_sync_crypto_random_hex_gen (void   *ctx,
-                                 gsize   length,
-                                 guint8 *dst)
-{
-  FILE *fp;
-  gsize num_bytes;
-  guint8 *bytes;
-  char *hex;
-
-  g_assert (length > 0);
-  num_bytes = (length + 1) / 2;
-  bytes = g_malloc (num_bytes);
-
-  fp = fopen ("/dev/urandom", "r");
-  fread (bytes, sizeof (guint8), num_bytes, fp);
-  hex = ephy_sync_crypto_encode_hex (bytes, num_bytes);
-
-  for (gsize i = 0; i < length; i++)
-    dst[i] = hex[i];
-
-  g_free (bytes);
-  g_free (hex);
-  fclose (fp);
-}
-
 char *
 ephy_sync_crypto_base64_urlsafe_encode (const guint8 *data,
                                         gsize         data_len,
@@ -1379,3 +1370,26 @@ ephy_sync_crypto_decode_hex (const char *hex)
 
   return retval;
 }
+
+char *
+ephy_sync_crypto_get_random_sync_id (void)
+{
+  char *id;
+  char *base64;
+  guint8 *bytes;
+  gsize bytes_len;
+
+  /* The sync id is a base64-urlsafe string. Base64 uses 4 chars to represent 3 bytes,
+   * therefore we need ceil(len * 3 / 4) bytes to cover the requested length. */
+  bytes_len = (SYNC_ID_LEN + 3) / 4 * 3;
+  bytes = g_malloc (bytes_len);
+
+  ephy_sync_crypto_random_bytes_gen (NULL, bytes_len, bytes);
+  base64 = ephy_sync_crypto_base64_urlsafe_encode (bytes, bytes_len, FALSE);
+  id = g_strndup (base64, SYNC_ID_LEN);
+
+  g_free (base64);
+  g_free (bytes);
+
+  return id;
+}
diff --git a/src/sync/ephy-sync-crypto.h b/src/sync/ephy-sync-crypto.h
index fda35d0..6668449 100644
--- a/src/sync/ephy-sync-crypto.h
+++ b/src/sync/ephy-sync-crypto.h
@@ -119,9 +119,6 @@ char                   *ephy_sync_crypto_create_assertion         (const char
                                                                    const char             *audience,
                                                                    guint64                 duration,
                                                                    SyncCryptoRSAKeyPair   *keypair);
-void                    ephy_sync_crypto_random_hex_gen           (void                   *ctx,
-                                                                   gsize                   length,
-                                                                   guint8                 *dst);
 char                   *ephy_sync_crypto_base64_urlsafe_encode    (const guint8           *data,
                                                                    gsize                   data_len,
                                                                    gboolean                strip);
@@ -136,5 +133,6 @@ guint8                 *ephy_sync_crypto_aes_256                  (SyncCryptoAES
 char                   *ephy_sync_crypto_encode_hex               (const guint8           *data,
                                                                    gsize                   data_len);
 guint8                 *ephy_sync_crypto_decode_hex               (const char             *hex);
+char                   *ephy_sync_crypto_get_random_sync_id       (void);
 
 G_END_DECLS


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