[epiphany/wip/sync-rebase: 50/86] ephy-sync: Prepare the ground for sync
- From: Michael Catanzaro <mcatanzaro src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [epiphany/wip/sync-rebase: 50/86] ephy-sync: Prepare the ground for sync
- Date: Fri, 7 Oct 2016 22:52:55 +0000 (UTC)
commit be577882553329967a0fec414bfd42c8a0793482
Author: Gabriel Ivascu <ivascu gabriel59 gmail com>
Date: Fri Jul 29 16:51:34 2016 +0300
ephy-sync: Prepare the ground for sync
Add sync-bookmarks and sync-utils modules.
Add function to convert bookmark to BSO.
src/Makefile.am | 4 +
src/bookmarks/ephy-bookmark.c | 124 ++++++++++++++++++++--
src/bookmarks/ephy-bookmark.h | 8 ++
src/ephy-sync-bookmarks.c | 124 ++++++++++++++++++++++
src/ephy-sync-bookmarks.h | 36 +++++++
src/ephy-sync-crypto.c | 193 ++++++++++++++++++++++-------------
src/ephy-sync-crypto.h | 24 +++-
src/ephy-sync-service.c | 229 ++++++++++++----------------------------
src/ephy-sync-service.h | 54 ++++++----
src/ephy-sync-utils.c | 56 ++++++++++
src/ephy-sync-utils.h | 35 ++++++
src/prefs-dialog.c | 10 +-
12 files changed, 620 insertions(+), 277 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index ddc4d80..2341708 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -44,12 +44,16 @@ 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 \
ephy-sync-secret.h \
ephy-sync-service.c \
ephy-sync-service.h \
+ ephy-sync-utils.c \
+ ephy-sync-utils.h \
ephy-window.c \
ephy-window.h \
languages.h \
diff --git a/src/bookmarks/ephy-bookmark.c b/src/bookmarks/ephy-bookmark.c
index d872c9d..dbcc956 100644
--- a/src/bookmarks/ephy-bookmark.c
+++ b/src/bookmarks/ephy-bookmark.c
@@ -20,7 +20,12 @@
#include "ephy-bookmark.h"
+#include "ephy-shell.h"
+#include "ephy-sync-crypto.h"
+#include "ephy-sync-utils.h"
+
#include <json-glib/json-glib.h>
+#include <string.h>
struct _EphyBookmark {
GObject parent_instance;
@@ -29,6 +34,9 @@ struct _EphyBookmark {
char *title;
GSequence *tags;
gint64 time_added;
+
+ char *id;
+ double modified;
};
static JsonSerializableIface *serializable_iface = NULL;
@@ -184,32 +192,70 @@ ephy_bookmark_class_init (EphyBookmarkClass *klass)
static void
ephy_bookmark_init (EphyBookmark *self)
{
+ self->id = ephy_sync_crypto_generate_random_string (12);
}
static JsonNode *
ephy_bookmark_json_serializable_serialize_property (JsonSerializable *serializable,
- const gchar *property_name,
+ const gchar *name,
const GValue *value,
GParamSpec *pspec)
{
- return serializable_iface->serialize_property (serializable,
- property_name,
- value,
- pspec);
+ JsonNode *node = NULL;
+
+ if (g_strcmp0 (name, "tags") == 0) {
+ GSequence *tags;
+ GSequenceIter *iter;
+ JsonArray *array;
+
+ node = json_node_new (JSON_NODE_ARRAY);
+ array = json_array_new ();
+ tags = g_value_get_pointer (value);
+
+ for (iter = g_sequence_get_begin_iter (tags);
+ !g_sequence_iter_is_end (iter);
+ iter = g_sequence_iter_next (iter)) {
+ json_array_add_string_element (array, g_sequence_get (iter));
+ }
+
+ json_node_set_array (node, array);
+ } else {
+ node = serializable_iface->serialize_property (serializable, name,
+ value, pspec);
+ }
+
+ return node;
}
static gboolean
ephy_bookmark_json_serializable_deserialize_property (JsonSerializable *serializable,
- const gchar *property_name,
+ const gchar *name,
GValue *value,
GParamSpec *pspec,
- JsonNode *property_node)
+ JsonNode *node)
{
- return serializable_iface->deserialize_property (serializable,
- property_name,
- value,
- pspec,
- property_node);
+ if (g_strcmp0 (name, "tags") == 0) {
+ GSequence *tags;
+ JsonArray *array;
+ const char *tag;
+
+ g_assert (JSON_NODE_HOLDS_ARRAY (node));
+ array = json_node_get_array (node);
+ tags = g_sequence_new (g_free);
+
+ for (gsize i = 0; i < json_array_get_length (array); i++) {
+ tag = json_node_get_string (json_array_get_element (array, i));
+ g_sequence_insert_sorted (tags, g_strdup (tag),
+ (GCompareDataFunc)ephy_bookmark_tags_compare, NULL);
+ }
+
+ g_value_set_pointer (value, tags);
+ } else {
+ serializable_iface->deserialize_property (serializable, name,
+ value, pspec, node);
+ }
+
+ return TRUE;
}
static void
@@ -288,6 +334,31 @@ ephy_bookmark_get_title (EphyBookmark *bookmark)
return bookmark->title;
}
+const char *
+ephy_bookmark_get_id (EphyBookmark *self)
+{
+ g_return_val_if_fail (EPHY_IS_BOOKMARK (self), NULL);
+
+ return self->id;
+}
+
+void
+ephy_bookmark_set_modified (EphyBookmark *self,
+ double modified)
+{
+ g_return_if_fail (EPHY_IS_BOOKMARK (self));
+
+ self->modified = modified;
+}
+
+double
+ephy_bookmark_get_modified (EphyBookmark *self)
+{
+ g_return_val_if_fail (EPHY_IS_BOOKMARK (self), -1);
+
+ return self->modified;
+}
+
void
ephy_bookmark_add_tag (EphyBookmark *self,
const char *tag)
@@ -391,3 +462,32 @@ ephy_bookmark_tags_compare (const char *tag1, const char *tag2)
return result;
}
+
+char *
+ephy_bookmark_to_bso (EphyBookmark *self)
+{
+ EphySyncService *service;
+ guint8 *encrypted;
+ guint8 *sync_key;
+ char *serialized;
+ char *payload;
+ char *bso;
+ gsize length;
+
+ g_return_val_if_fail (EPHY_IS_BOOKMARK (self), NULL);
+
+ service = ephy_shell_get_global_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,
+ (guint8 *)serialized, strlen (serialized), &length);
+ payload = ephy_sync_crypto_base64_urlsafe_encode (encrypted, length, FALSE);
+ bso = ephy_sync_utils_create_bso_json (self->id, payload);
+
+ g_free (sync_key);
+ g_free (serialized);
+ g_free (encrypted);
+ g_free (payload);
+
+ return bso;
+}
diff --git a/src/bookmarks/ephy-bookmark.h b/src/bookmarks/ephy-bookmark.h
index 8a44c03..76508bd 100644
--- a/src/bookmarks/ephy-bookmark.h
+++ b/src/bookmarks/ephy-bookmark.h
@@ -42,6 +42,12 @@ void ephy_bookmark_set_title (EphyBookmark *self,
const char *title);
const char *ephy_bookmark_get_title (EphyBookmark *self);
+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_add_tag (EphyBookmark *self,
const char *tag);
void ephy_bookmark_remove_tag (EphyBookmark *self,
@@ -55,4 +61,6 @@ int ephy_bookmark_bookmarks_sort_func (EphyBookmark *bookmark1,
int ephy_bookmark_tags_compare (const char *tag1,
const char *tag2);
+char *ephy_bookmark_to_bso (EphyBookmark *self);
+
G_END_DECLS
diff --git a/src/ephy-sync-bookmarks.c b/src/ephy-sync-bookmarks.c
new file mode 100644
index 0000000..9344b72
--- /dev/null
+++ b/src/ephy-sync-bookmarks.c
@@ -0,0 +1,124 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2016 Gabriel Ivascu <ivascu gabriel59 gmail com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "ephy-sync-bookmarks.h"
+
+#include "ephy-debug.h"
+#include "ephy-shell.h"
+#include "ephy-sync-utils.h"
+
+#define EPHY_BOOKMARKS_DUMMY_BSO "000000000000"
+#define EPHY_BOOKMARKS_COLLECTION "ephy-bookmarks"
+
+static void
+create_bso_collection_response_cb (SoupSession *session,
+ SoupMessage *message,
+ gpointer user_data)
+{
+ EphySyncService *service;
+ gchar *endpoint;
+
+ service = ephy_shell_get_global_sync_service (ephy_shell_get_default ());
+
+ /* Status code 412 means the BSO already exists. Since we will delete it
+ * anyway, we don't treat this as an error.
+ */
+ if (message->status_code != 200 && message->status_code != 412) {
+ g_warning ("Failed to add dummy BSO to collection, status code: %u, response: %s",
+ message->status_code, message->response_body->data);
+ return;
+ }
+
+ /* The EPHY_BOOKMARKS_COLLECTION collection is now created. We can safely
+ * delete the dummy BSO that we've uploaded. No need to check for response.
+ */
+ endpoint = g_strdup_printf ("storage/%s/%s", EPHY_BOOKMARKS_COLLECTION, EPHY_BOOKMARKS_DUMMY_BSO);
+ ephy_sync_service_send_storage_message (service, endpoint, SOUP_METHOD_DELETE,
+ NULL, -1, -1, NULL, NULL);
+ g_free (endpoint);
+}
+
+void
+ephy_sync_bookmarks_create_storage_collection (void)
+{
+ EphySyncService *service;
+ gchar *endpoint;
+ gchar *bso;
+
+ service = ephy_shell_get_global_sync_service (ephy_shell_get_default ());
+ endpoint = g_strdup_printf ("storage/%s/%s", EPHY_BOOKMARKS_COLLECTION, EPHY_BOOKMARKS_DUMMY_BSO);
+ bso = ephy_sync_utils_create_bso_json (EPHY_BOOKMARKS_DUMMY_BSO, EPHY_BOOKMARKS_DUMMY_BSO);
+
+ /* Send a dummy BSO to the Storage Server so it will create the
+ * EPHY_BOOKMARKS_COLLECTION collection if it doesn't exist already.
+ * In the response callback we will delete the dummy BSO.
+ */
+ ephy_sync_service_send_storage_message (service, endpoint, SOUP_METHOD_PUT,
+ bso, -1, 0,
+ create_bso_collection_response_cb, NULL);
+
+ g_free (endpoint);
+ g_free (bso);
+}
+
+static void
+server_response_default_cb (SoupSession *session,
+ SoupMessage *message,
+ gpointer user_data)
+{
+ LOG ("Server response:");
+ LOG ("status_code: %u", message->status_code);
+ LOG ("response_body: %s", message->response_body->data);
+ LOG ("Retry-After: %s", soup_message_headers_get_one (message->response_headers, "Retry-After"));
+ LOG ("X-Weave-Backoff: %s", soup_message_headers_get_one (message->response_headers, "X-Weave-Backoff"));
+ LOG ("X-Last-Modified: %s", soup_message_headers_get_one (message->response_headers, "X-Last-Modified"));
+ LOG ("X-Weave-Timestamp: %s", soup_message_headers_get_one (message->response_headers,
"X-Weave-Timestamp"));
+ LOG ("X-Weave-Records: %s", soup_message_headers_get_one (message->response_headers, "X-Weave-Records"));
+ LOG ("X-Weave-Next-Offset: %s", soup_message_headers_get_one (message->response_headers,
"X-Weave-Next-Offset"));
+ LOG ("X-Weave-Quota-Remaining: %s", soup_message_headers_get_one (message->response_headers,
"X-Weave-Quota-Remaining"));
+ LOG ("X-Weave-Alert: %s", soup_message_headers_get_one (message->response_headers, "X-Weave-Alert"));
+}
+
+void
+ephy_sync_bookmarks_check_storage_collection (void)
+{
+ EphySyncService *service;
+ gchar *endpoint;
+
+ service = ephy_shell_get_global_sync_service (ephy_shell_get_default ());
+ endpoint = g_strdup_printf ("storage/%s", EPHY_BOOKMARKS_COLLECTION);
+ ephy_sync_service_send_storage_message (service, endpoint,
+ SOUP_METHOD_GET, NULL, -1, -1,
+ server_response_default_cb, NULL);
+ g_free (endpoint);
+}
+
+void
+ephy_sync_bookmarks_delete_storage_collection (void)
+{
+ EphySyncService *service;
+ gchar *endpoint;
+
+ service = ephy_shell_get_global_sync_service (ephy_shell_get_default ());
+ endpoint = g_strdup_printf ("storage/%s", EPHY_BOOKMARKS_COLLECTION);
+ ephy_sync_service_send_storage_message (service, endpoint,
+ SOUP_METHOD_DELETE, NULL, -1, -1,
+ server_response_default_cb, NULL);
+ g_free (endpoint);
+}
diff --git a/src/ephy-sync-bookmarks.h b/src/ephy-sync-bookmarks.h
new file mode 100644
index 0000000..47f0ccc
--- /dev/null
+++ b/src/ephy-sync-bookmarks.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2016 Gabriel Ivascu <ivascu gabriel59 gmail com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EPHY_SYNC_BOOKMARKS_H
+#define EPHY_SYNC_BOOKMARKS_H
+
+#include "ephy-sync-service.h"
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+void ephy_sync_bookmarks_create_storage_collection (void);
+
+void ephy_sync_bookmarks_check_storage_collection (void);
+
+void ephy_sync_bookmarks_delete_storage_collection (void);
+
+G_END_DECLS
+
+#endif
diff --git a/src/ephy-sync-crypto.c b/src/ephy-sync-crypto.c
index 1e6f213..a194360 100644
--- a/src/ephy-sync-crypto.c
+++ b/src/ephy-sync-crypto.c
@@ -264,37 +264,6 @@ kw (const gchar *name)
return g_strconcat ("identity.mozilla.com/picl/v1/", name, NULL);
}
-static gchar *
-base64_urlsafe_strip (guint8 *data,
- gsize data_length)
-{
- gchar *encoded;
- gchar *base64;
- gsize start;
- gssize end;
-
- base64 = g_base64_encode (data, data_length);
-
- start = 0;
- while (start < strlen (base64) && base64[start] == '=')
- start++;
-
- end = strlen (base64) - 1;
- while (end >= 0 && base64[end] == '=')
- end--;
-
- encoded = g_strndup (base64 + start, end - start + 1);
-
- /* Replace '+' with '-' */
- g_strcanon (encoded, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/", '-');
- /* Replace '/' with '_' */
- g_strcanon (encoded, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-", '_');
-
- g_free (base64);
-
- return encoded;
-}
-
static guint8 *
xor (guint8 *a,
guint8 *b,
@@ -328,26 +297,6 @@ are_equal (guint8 *a,
}
static gchar *
-generate_random_string (gsize length)
-{
- guchar *bytes;
- gchar *base64_string;
- gchar *string;
-
- bytes = g_malloc (length);
- for (gsize i = 0; i < length; i++)
- bytes[i] = g_random_int ();
-
- base64_string = g_base64_encode (bytes, length);
- string = g_strndup (base64_string, length);
-
- g_free (bytes);
- g_free (base64_string);
-
- return string;
-}
-
-static gchar *
find_and_replace_string (const gchar *src,
const gchar *find,
const gchar *repl)
@@ -636,6 +585,22 @@ random_func (void *ctx,
dst[i] = g_random_int ();
}
+static void
+base64_to_base64_urlsafe (gchar *text)
+{
+ /* Replace '+' with '-' and '/' with '_' */
+ g_strcanon (text, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789=/", '-');
+ g_strcanon (text, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789=-", '_');
+}
+
+static void
+base64_urlsafe_to_base64 (gchar *text)
+{
+ /* Replace '-' with '+' and '_' with '/' */
+ g_strcanon (text, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789=_", '+');
+ g_strcanon (text, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789=+", '/');
+}
+
EphySyncCryptoProcessedKFT *
ephy_sync_crypto_process_key_fetch_token (const gchar *keyFetchToken)
{
@@ -820,7 +785,7 @@ ephy_sync_crypto_compute_hawk_header (const gchar *url,
}
nonce = has_options ? options->nonce : NULL;
- nonce = nonce ? nonce : generate_random_string (6);
+ nonce = nonce ? nonce : ephy_sync_crypto_generate_random_string (6);
hash = has_options ? options->hash : NULL;
payload = has_options ? options->payload : NULL;
@@ -934,8 +899,8 @@ ephy_sync_crypto_create_assertion (const gchar *certificate,
expires_at = g_get_real_time () / 1000 + duration * 1000;
body = g_strdup_printf ("{\"exp\": %lu, \"aud\": \"%s\"}", expires_at, audience);
- body_b64 = base64_urlsafe_strip ((guint8 *) body, strlen (body));
- header_b64 = base64_urlsafe_strip ((guint8 *) header, strlen (header));
+ body_b64 = ephy_sync_crypto_base64_urlsafe_encode ((guint8 *) body, strlen (body), TRUE);
+ header_b64 = ephy_sync_crypto_base64_urlsafe_encode ((guint8 *) header, strlen (header), TRUE);
to_sign = g_strdup_printf ("%s.%s", header_b64, body_b64);
mpz_init (signature);
@@ -958,7 +923,7 @@ ephy_sync_crypto_create_assertion (const gchar *certificate,
goto out;
}
- sig_b64 = base64_urlsafe_strip (sig, count);
+ 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);
@@ -976,36 +941,120 @@ out:
return assertion;
}
+gchar *
+ephy_sync_crypto_generate_random_string (gsize length)
+{
+ guchar *bytes;
+ gchar *base64_string;
+ gchar *string;
+
+ bytes = g_malloc (length);
+ for (gsize i = 0; i < length; i++)
+ bytes[i] = g_random_int ();
+
+ base64_string = g_base64_encode (bytes, length);
+ base64_to_base64_urlsafe (base64_string);
+ string = g_strndup (base64_string, length);
+
+ g_free (bytes);
+ g_free (base64_string);
+
+ return string;
+}
+
+gchar *
+ephy_sync_crypto_base64_urlsafe_encode (guint8 *data,
+ gsize data_length,
+ gboolean strip)
+{
+ gchar *encoded;
+ gchar *base64;
+ gsize start;
+ gssize end;
+
+ base64 = g_base64_encode (data, data_length);
+
+ if (strip == FALSE) {
+ base64_to_base64_urlsafe (base64);
+ return base64;
+ }
+
+ /* Strip all the '=' */
+ start = 0;
+ while (start < strlen (base64) && base64[start] == '=')
+ start++;
+
+ end = strlen (base64) - 1;
+ while (end >= 0 && base64[end] == '=')
+ end--;
+
+ encoded = g_strndup (base64 + start, end - start + 1);
+ base64_to_base64_urlsafe (encoded);
+
+ g_free (base64);
+
+ return encoded;
+}
+
guint8 *
-ephy_sync_crypto_encode_aes_256 (const guint8 *key,
- const guint8 *data,
- gsize data_length)
+ephy_sync_crypto_base64_urlsafe_decode (gchar *text,
+ gsize *out_length)
{
- struct aes256_ctx aes;
- guint8 *out;
+ guint8 *decoded;
+ gchar *text_copy;
- g_assert (data_length % AES_BLOCK_SIZE == 0);
+ text_copy = g_strdup (text);
+ base64_urlsafe_to_base64 (text_copy);
+ decoded = g_base64_decode (text_copy, out_length);
- out = g_malloc (data_length);
- aes256_set_encrypt_key (&aes, key);
- aes256_encrypt (&aes, data_length, out, data);
+ g_free (text_copy);
- return out;
+ return decoded;
}
guint8 *
-ephy_sync_crypto_decode_aes_256 (const guint8 *key,
- const guint8 *data,
- gsize data_length)
+ephy_sync_crypto_aes_256 (EphySyncCryptoAES256Mode mode,
+ const guint8 *key,
+ const guint8 *data,
+ gsize data_length,
+ gsize *out_length)
{
struct aes256_ctx aes;
+ gsize padded_length;
+ guint8 *padded_data;
guint8 *out;
- g_assert (data_length % AES_BLOCK_SIZE == 0);
+ 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 (out_length != NULL)
+ *out_length = padded_length;
- out = g_malloc (data_length);
- aes256_set_decrypt_key (&aes, key);
- aes256_decrypt (&aes, data_length, out, data);
+ g_free (padded_data);
return out;
}
diff --git a/src/ephy-sync-crypto.h b/src/ephy-sync-crypto.h
index 902f285..4cf1adb 100644
--- a/src/ephy-sync-crypto.h
+++ b/src/ephy-sync-crypto.h
@@ -26,6 +26,11 @@ G_BEGIN_DECLS
#define EPHY_SYNC_TOKEN_LENGTH 32
+typedef enum {
+ AES_256_MODE_ENCRYPT,
+ AES_256_MODE_DECRYPT
+} EphySyncCryptoAES256Mode;
+
typedef struct {
gchar *app;
gchar *dlg;
@@ -125,13 +130,20 @@ gchar *ephy_sync_crypto_create_assertion (const gcha
guint64 duration,
EphySyncCryptoRSAKeyPair *keypair);
-guint8 *ephy_sync_crypto_encode_aes_256 (const guint8 *key,
- const guint8 *data,
- gsize data_length);
+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_decode_aes_256 (const guint8 *key,
- const guint8 *data,
- gsize data_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);
diff --git a/src/ephy-sync-service.c b/src/ephy-sync-service.c
index 425ce28..657a3ec 100644
--- a/src/ephy-sync-service.c
+++ b/src/ephy-sync-service.c
@@ -23,21 +23,17 @@
#include "ephy-settings.h"
#include "ephy-sync-crypto.h"
#include "ephy-sync-secret.h"
+#include "ephy-sync-utils.h"
#include <json-glib/json-glib.h>
-#include <libsoup/soup.h>
#include <string.h>
-#define EPHY_BOOKMARKS_DUMMY_BSO "000000000000"
-#define EPHY_BOOKMARKS_BSO_COLLECTION "ephy-bookmarks"
-
#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-z0-9]([a-z0-9.]+[a-z0-9])?@[a-z0-9.-]+$"
#define CURRENT_TIME_IN_SECONDS (g_get_real_time () / 1000000)
-#define HTTP_STATUS_OK 200
struct _EphySyncService {
GObject parent_instance;
@@ -68,28 +64,23 @@ struct _EphySyncService {
G_DEFINE_TYPE (EphySyncService, ephy_sync_service, G_TYPE_OBJECT);
typedef struct {
- EphySyncService *service;
- gchar *endpoint;
- const gchar *method;
- gchar *request_body;
- gint64 modified_since;
- gint64 unmodified_since;
- SoupSessionCallback callback;
- gpointer user_data;
+ EphySyncService *service;
+ gchar *endpoint;
+ const gchar *method;
+ gchar *request_body;
+ double modified_since;
+ double unmodified_since;
+ SoupSessionCallback callback;
+ gpointer user_data;
} StorageServerRequestAsyncData;
-static gchar *
-build_json_string (const gchar *first_key,
- const gchar *first_value,
- ...) G_GNUC_NULL_TERMINATED;
-
static StorageServerRequestAsyncData *
storage_server_request_async_data_new (EphySyncService *service,
gchar *endpoint,
const gchar *method,
gchar *request_body,
- gint64 modified_since,
- gint64 unmodified_since,
+ double modified_since,
+ double unmodified_since,
SoupSessionCallback callback,
gpointer user_data)
{
@@ -121,35 +112,6 @@ storage_server_request_async_data_free (StorageServerRequestAsyncData *data)
}
static gchar *
-build_json_string (const gchar *first_key,
- const gchar *first_value,
- ...)
-{
- va_list args;
- gchar *json;
- gchar *key;
- gchar *value;
- gchar *tmp;
-
- json = g_strconcat ("{\"", first_key, "\": \"", first_value, "\"", NULL);
- va_start (args, first_value);
-
- while ((key = va_arg (args, gchar *)) != NULL) {
- value = va_arg (args, gchar *);
- tmp = json;
- json = g_strconcat (json, ", \"", key, "\": \"", value, "\"", NULL);
- g_free (tmp);
- }
-
- va_end (args);
- tmp = json;
- json = g_strconcat (json, "}", NULL);
- g_free (tmp);
-
- return json;
-}
-
-static gchar *
get_audience_for_url (const gchar *url)
{
SoupURI *uri;
@@ -210,7 +172,7 @@ destroy_session_response_cb (SoupSession *session,
JsonParser *parser;
JsonObject *json;
- if (message->status_code == HTTP_STATUS_OK) {
+ if (message->status_code == 200) {
LOG ("Session destroyed");
return;
}
@@ -357,13 +319,13 @@ ephy_sync_service_send_storage_request (EphySyncService *self,
}
if (data->modified_since >= 0) {
- if_modified_since = g_strdup_printf ("%ld", data->modified_since);
+ if_modified_since = g_strdup_printf ("%.2lf", data->modified_since);
soup_message_headers_append (message->request_headers,
"X-If-Modified-Since", if_modified_since);
}
if (data->unmodified_since >= 0) {
- if_unmodified_since = g_strdup_printf ("%ld", data->unmodified_since);
+ if_unmodified_since = g_strdup_printf ("%.2lf", data->unmodified_since);
soup_message_headers_append (message->request_headers,
"X-If-Unmodified-Since", if_unmodified_since);
}
@@ -485,7 +447,7 @@ 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 == HTTP_STATUS_OK) {
+ if (message->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"));
@@ -580,7 +542,7 @@ obtain_signed_certificate_response_cb (SoupSession *session,
json_parser_load_from_data (parser, message->response_body->data, -1, NULL);
json = json_node_get_object (json_parser_get_root (parser));
- if (message->status_code != HTTP_STATUS_OK) {
+ if (message->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"));
@@ -629,10 +591,10 @@ ephy_sync_service_obtain_signed_certificate (EphySyncService *self,
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 = build_json_string ("algorithm", "RS",
- "n", n_str,
- "e", e_str,
- NULL);
+ public_key_json = ephy_sync_utils_build_json_string ("algorithm", "RS",
+ "n", n_str,
+ "e", e_str,
+ 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.
@@ -657,88 +619,6 @@ ephy_sync_service_obtain_signed_certificate (EphySyncService *self,
}
static void
-ephy_sync_service_send_storage_message (EphySyncService *self,
- gchar *endpoint,
- const gchar *method,
- gchar *request_body,
- gint64 modified_since,
- gint64 unmodified_since,
- SoupSessionCallback callback,
- gpointer user_data)
-{
- StorageServerRequestAsyncData *data;
-
- data = storage_server_request_async_data_new (self, endpoint,
- method, request_body,
- modified_since, unmodified_since,
- callback, user_data);
-
- if (ephy_sync_service_storage_credentials_is_expired (self) == FALSE) {
- ephy_sync_service_send_storage_request (self, data);
- return;
- }
-
- /* If we are currently obtaining the storage credentials for another message,
- * then the new message is enqueued and will be sent after the credentials are
- * retrieved.
- */
- if (self->is_obtaining_storage_credentials == TRUE) {
- g_queue_push_tail (self->storage_message_queue, data);
- return;
- }
-
- /* This message is the one that will obtain the storage credentials. */
- self->is_obtaining_storage_credentials = TRUE;
-
- /* Drop the old certificate and storage credentials. */
- g_clear_pointer (&self->certificate, 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;
-
- /* The only purpose of certificates is to obtain a signed BrowserID that is
- * needed to talk to the Token Server. From the Token Server we will obtain
- * the credentials needed to talk to the Storage Server. Since both
- * ephy_sync_service_obtain_signed_certificate() and
- * ephy_sync_service_obtain_storage_credentials() complete asynchronously, we
- * need to entrust them the task of sending the request to the Storage Server.
- */
- ephy_sync_service_obtain_signed_certificate (self, data);
-}
-
-static void
-create_bookmarks_bso_collection_response_cb (SoupSession *session,
- SoupMessage *message,
- gpointer user_data)
-{
- EphySyncService *service;
- gchar *endpoint;
-
- service = EPHY_SYNC_SERVICE (user_data);
-
- /* Status code 412 means the BSO already exists. Since we will delete it
- * anyway, we don't treat this as an error.
- */
- if (message->status_code != HTTP_STATUS_OK && message->status_code != 412) {
- g_warning ("Failed to add dummy BSO to collection, status code: %u, response: %s",
- message->status_code, message->response_body->data);
- return;
- }
-
- /* The EPHY_BOOKMARKS_BSO_COLLECTION collection is now created. We can safely
- * delete the dummy BSO that we've uploaded. No need to check for response.
- */
- endpoint = g_strdup_printf ("storage/%s/%s",
- EPHY_BOOKMARKS_BSO_COLLECTION,
- EPHY_BOOKMARKS_DUMMY_BSO);
- ephy_sync_service_send_storage_message (service,
- endpoint, SOUP_METHOD_DELETE,
- NULL, -1, -1,
- NULL, NULL);
- g_free (endpoint);
-}
-
-static void
ephy_sync_service_finalize (GObject *object)
{
EphySyncService *self = EPHY_SYNC_SERVICE (object);
@@ -823,6 +703,12 @@ ephy_sync_service_token_name_from_type (EphySyncServiceTokenType token_type)
}
}
+gboolean
+ephy_sync_service_is_signed_in (EphySyncService *self)
+{
+ return self->user_email != NULL;
+}
+
gchar *
ephy_sync_service_get_user_email (EphySyncService *self)
{
@@ -1004,7 +890,7 @@ ephy_sync_service_fetch_sync_keys (EphySyncService *self,
&node);
json = json_node_get_object (node);
- if (status_code != HTTP_STATUS_OK) {
+ if (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"));
@@ -1040,28 +926,51 @@ out:
}
void
-ephy_sync_service_create_bookmarks_bso_collection (EphySyncService *self)
+ephy_sync_service_send_storage_message (EphySyncService *self,
+ gchar *endpoint,
+ const gchar *method,
+ gchar *request_body,
+ double modified_since,
+ double unmodified_since,
+ SoupSessionCallback callback,
+ gpointer user_data)
{
- gchar *endpoint;
- gchar *request_body;
+ StorageServerRequestAsyncData *data;
- endpoint = g_strdup_printf ("storage/%s/%s",
- EPHY_BOOKMARKS_BSO_COLLECTION,
- EPHY_BOOKMARKS_DUMMY_BSO);
- request_body = build_json_string ("id", EPHY_BOOKMARKS_DUMMY_BSO,
- "payload", EPHY_BOOKMARKS_DUMMY_BSO,
- NULL);
+ data = storage_server_request_async_data_new (self, endpoint,
+ method, request_body,
+ modified_since, unmodified_since,
+ callback, user_data);
- /* Send a dummy BSO to the Storage Server so it will create the
- * EPHY_BOOKMARKS_BSO_COLLECTION collection if it doesn't exist already.
- * In the response callback we will delete the dummy BSO.
+ if (ephy_sync_service_storage_credentials_is_expired (self) == FALSE) {
+ ephy_sync_service_send_storage_request (self, data);
+ return;
+ }
+
+ /* If we are currently obtaining the storage credentials for another message,
+ * then the new message is enqueued and will be sent after the credentials are
+ * retrieved.
*/
- ephy_sync_service_send_storage_message (self,
- endpoint, SOUP_METHOD_PUT,
- request_body, -1, 0,
- create_bookmarks_bso_collection_response_cb,
- self);
+ if (self->is_obtaining_storage_credentials == TRUE) {
+ g_queue_push_tail (self->storage_message_queue, data);
+ return;
+ }
- g_free (endpoint);
- g_free (request_body);
+ /* This message is the one that will obtain the storage credentials. */
+ self->is_obtaining_storage_credentials = TRUE;
+
+ /* Drop the old certificate and storage credentials. */
+ g_clear_pointer (&self->certificate, 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;
+
+ /* The only purpose of certificates is to obtain a signed BrowserID that is
+ * needed to talk to the Token Server. From the Token Server we will obtain
+ * the credentials needed to talk to the Storage Server. Since both
+ * ephy_sync_service_obtain_signed_certificate() and
+ * ephy_sync_service_obtain_storage_credentials() complete asynchronously, we
+ * need to entrust them the task of sending the request to the Storage Server.
+ */
+ ephy_sync_service_obtain_signed_certificate (self, data);
}
diff --git a/src/ephy-sync-service.h b/src/ephy-sync-service.h
index 0cbcf63..6623bad 100644
--- a/src/ephy-sync-service.h
+++ b/src/ephy-sync-service.h
@@ -20,6 +20,7 @@
#define EPHY_SYNC_SERVICE_H
#include <glib-object.h>
+#include <libsoup/soup.h>
G_BEGIN_DECLS
@@ -36,38 +37,47 @@ typedef enum {
TOKEN_KB
} EphySyncServiceTokenType;
-EphySyncService *ephy_sync_service_new (void);
+EphySyncService *ephy_sync_service_new (void);
-const gchar *ephy_sync_service_token_name_from_type (EphySyncServiceTokenType token_type);
+const gchar *ephy_sync_service_token_name_from_type (EphySyncServiceTokenType token_type);
-gchar *ephy_sync_service_get_user_email (EphySyncService *self);
+gboolean ephy_sync_service_is_signed_in (EphySyncService *self);
-void ephy_sync_service_set_user_email (EphySyncService *self,
- const gchar *email);
+gchar *ephy_sync_service_get_user_email (EphySyncService *self);
-gchar *ephy_sync_service_get_token (EphySyncService *self,
- EphySyncServiceTokenType token_type);
+void ephy_sync_service_set_user_email (EphySyncService *self,
+ const gchar *email);
-void ephy_sync_service_set_token (EphySyncService *self,
- gchar *token_value,
- EphySyncServiceTokenType token_type);
+gchar *ephy_sync_service_get_token (EphySyncService *self,
+ EphySyncServiceTokenType token_type);
-void ephy_sync_service_set_and_store_tokens (EphySyncService *self,
- gchar *token_value,
- EphySyncServiceTokenType token_type,
- ...) G_GNUC_NULL_TERMINATED;
+void ephy_sync_service_set_token (EphySyncService *self,
+ gchar *token_value,
+ EphySyncServiceTokenType token_type);
-void ephy_sync_service_delete_all_tokens (EphySyncService *self);
+void ephy_sync_service_set_and_store_tokens (EphySyncService *self,
+ gchar *token_value,
+ EphySyncServiceTokenType token_type,
+ ...) G_GNUC_NULL_TERMINATED;
-void ephy_sync_service_destroy_session (EphySyncService *self,
- const gchar *sessionToken);
+void ephy_sync_service_delete_all_tokens (EphySyncService *self);
-gboolean ephy_sync_service_fetch_sync_keys (EphySyncService *self,
- const gchar *email,
- const gchar *keyFetchToken,
- const gchar *unwrapBKey);
+void ephy_sync_service_destroy_session (EphySyncService *self,
+ const gchar *sessionToken);
-void ephy_sync_service_create_bookmarks_bso_collection (EphySyncService *self);
+gboolean ephy_sync_service_fetch_sync_keys (EphySyncService *self,
+ const gchar *email,
+ const gchar *keyFetchToken,
+ const gchar *unwrapBKey);
+
+void ephy_sync_service_send_storage_message (EphySyncService *self,
+ gchar *endpoint,
+ const gchar *method,
+ gchar *request_body,
+ double modified_since,
+ double unmodified_since,
+ SoupSessionCallback callback,
+ gpointer user_data);
G_END_DECLS
diff --git a/src/ephy-sync-utils.c b/src/ephy-sync-utils.c
new file mode 100644
index 0000000..c37f885
--- /dev/null
+++ b/src/ephy-sync-utils.c
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2016 Gabriel Ivascu <ivascu gabriel59 gmail com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "ephy-sync-utils.h"
+
+gchar *
+ephy_sync_utils_build_json_string (const gchar *first_key,
+ const gchar *first_value,
+ ...)
+{
+ va_list args;
+ gchar *json;
+ gchar *key;
+ gchar *value;
+ gchar *tmp;
+
+ json = g_strconcat ("{\"", first_key, "\": \"", first_value, "\"", NULL);
+ va_start (args, first_value);
+
+ while ((key = va_arg (args, gchar *)) != NULL) {
+ value = va_arg (args, gchar *);
+ tmp = json;
+ json = g_strconcat (json, ", \"", key, "\": \"", value, "\"", NULL);
+ g_free (tmp);
+ }
+
+ va_end (args);
+ tmp = json;
+ json = g_strconcat (json, "}", NULL);
+ g_free (tmp);
+
+ return json;
+}
+
+gchar *
+ephy_sync_utils_create_bso_json (const gchar *id,
+ const gchar *payload)
+{
+ return ephy_sync_utils_build_json_string ("id", id, "payload", payload, NULL);
+}
diff --git a/src/ephy-sync-utils.h b/src/ephy-sync-utils.h
new file mode 100644
index 0000000..8cf72df
--- /dev/null
+++ b/src/ephy-sync-utils.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2016 Gabriel Ivascu <ivascu gabriel59 gmail com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EPHY_SYNC_UTILS_H
+#define EPHY_SYNC_UTILS_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+gchar *ephy_sync_utils_build_json_string (const gchar *first_key,
+ const gchar *first_value,
+ ...) G_GNUC_NULL_TERMINATED;
+
+gchar *ephy_sync_utils_create_bso_json (const gchar *id,
+ const gchar *payload);
+
+G_END_DECLS
+
+#endif
diff --git a/src/prefs-dialog.c b/src/prefs-dialog.c
index dd54fcd..ecf5f5e 100644
--- a/src/prefs-dialog.c
+++ b/src/prefs-dialog.c
@@ -34,6 +34,7 @@
#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"
@@ -295,7 +296,7 @@ server_message_received_cb (WebKitUserContentManager *manager,
NULL);
/* Create our own bookmarks BSO collection on the Storage Server. */
- ephy_sync_service_create_bookmarks_bso_collection (service);
+ ephy_sync_bookmarks_create_storage_collection ();
/* Translators: the %s refers to the email of the currently logged in user. */
gtk_label_set_markup (GTK_LABEL (dialog->sync_sign_out_details),
@@ -1450,12 +1451,10 @@ static void
setup_sync_page (PrefsDialog *dialog)
{
EphySyncService *service;
- gchar *email = NULL;
service = ephy_shell_get_global_sync_service (ephy_shell_get_default ());
- email = ephy_sync_service_get_user_email (service);
- if (email == NULL) {
+ if (ephy_sync_service_is_signed_in (service) == FALSE) {
setup_fxa_sign_in_view (dialog);
gtk_container_remove (GTK_CONTAINER (dialog->sync_authenticate_box),
dialog->sync_sign_out_box);
@@ -1464,7 +1463,8 @@ setup_sync_page (PrefsDialog *dialog)
dialog->sync_sign_in_box);
/* Translators: the %s refers to the email of the currently logged in user. */
gtk_label_set_markup (GTK_LABEL (dialog->sync_sign_out_details),
- g_strdup_printf (_("Currently logged in as <b>%s</b>"), email));
+ g_strdup_printf (_("Currently logged in as <b>%s</b>"),
+ ephy_sync_service_get_user_email (service)));
}
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]