aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorXan Lopez <xan@gnome.org>2009-12-15 17:31:47 +0800
committerXan Lopez <xan@gnome.org>2009-12-15 17:31:47 +0800
commit7a295e2c5db878dbd076c2f582cbae8aa738c3b7 (patch)
treeb0c4dc94c2b97cf7a7d5a955746d8d84eebd80f5 /lib
parenta31c25320318115914e009048a4081b2a1b58f50 (diff)
downloadgsoc2013-epiphany-7a295e2c5db878dbd076c2f582cbae8aa738c3b7.tar.gz
gsoc2013-epiphany-7a295e2c5db878dbd076c2f582cbae8aa738c3b7.tar.zst
gsoc2013-epiphany-7a295e2c5db878dbd076c2f582cbae8aa738c3b7.zip
Move profile migration tools from src/ to lib/
We'll use them from embed/ for form password saving, and embed/ can't use code from src/
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.am14
-rw-r--r--lib/ephy-nss-glue.c309
-rw-r--r--lib/ephy-nss-glue.h29
-rw-r--r--lib/ephy-profile-migration.c410
-rw-r--r--lib/ephy-profile-migration.h25
5 files changed, 787 insertions, 0 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 3ea7e1c07..58c363a73 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -20,6 +20,7 @@ NOINST_H_FILES = \
ephy-node-common.h \
ephy-object-helpers.h \
ephy-prefs.h \
+ ephy-profile-migration.h \
ephy-print-utils.h \
ephy-shlib-loader.h \
ephy-signal-accumulator.h \
@@ -57,6 +58,7 @@ libephymisc_la_SOURCES = \
ephy-node-db.c \
ephy-object-helpers.c \
ephy-prefs.h \
+ ephy-profile-migration.c \
ephy-print-utils.c \
ephy-shlib-loader.c \
ephy-signal-accumulator.c \
@@ -98,6 +100,18 @@ libephymisc_la_LIBADD += \
$(SPELLCHECKER_LIBS)
endif
+if ENABLE_NSS
+NOINST_H_FILES += \
+ ephy-nss-glue.h \
+ $(NULL)
+
+libephymisc_la_SOURCES += \
+ ephy-nss-glue.c
+ $(NULL)
+
+libephymisc_la_CFLAGS += $(NSS_CFLAGS)
+endif # ENABLE_NSS
+
BUILT_SOURCES = \
ephy-lib-type-builtins.c \
ephy-lib-type-builtins.h \
diff --git a/lib/ephy-nss-glue.c b/lib/ephy-nss-glue.c
new file mode 100644
index 000000000..e1c65b6c0
--- /dev/null
+++ b/lib/ephy-nss-glue.c
@@ -0,0 +1,309 @@
+/*
+ * Copyright © 2009 Igalia S.L.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/* Portions of this file based on Chromium code.
+ * License block as follows:
+ *
+ * Copyright (c) 2009 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * The LICENSE file from Chromium can be found in the LICENSE.chromium
+ * file.
+ */
+
+#include "config.h"
+
+#include "ephy-nss-glue.h"
+
+#include "ephy-file-helpers.h"
+
+#include <nss.h>
+#include <pk11pub.h>
+#include <pk11sdr.h>
+#include <glib/gi18n.h>
+
+static gboolean nss_initialized = FALSE;
+static PK11SlotInfo *db_slot = NULL;
+
+static char*
+ask_for_nss_password (PK11SlotInfo *slot,
+ PRBool retry,
+ void *arg)
+{
+ GtkWidget *dialog;
+ GtkWidget *entry;
+ gint result;
+ char *password = NULL;
+
+ if (retry)
+ return NULL;
+
+ dialog = gtk_message_dialog_new (NULL,
+ 0,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_OK_CANCEL,
+ _("Master password needed"));
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ _("The passwords from the previous version (Gecko) are locked with a master password. If you want Epiphany to import them, please enter your master password below."));
+ entry = gtk_entry_new ();
+ gtk_entry_set_visibility (GTK_ENTRY (entry), FALSE);
+ gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ entry);
+ gtk_widget_show (entry);
+
+ result = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ switch (result) {
+ case GTK_RESPONSE_OK:
+ password = PL_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
+ break;
+ default:
+ break;
+ }
+
+ gtk_widget_destroy (dialog);
+ return password;
+}
+
+gboolean ephy_nss_glue_init ()
+{
+ char *config_dir, *modspec;
+ SECStatus rv;
+
+ config_dir = g_build_filename (ephy_dot_dir (),
+ "mozilla", "epiphany",
+ NULL);
+ rv = NSS_Init (config_dir);
+
+ if (rv < 0) {
+ g_free (config_dir);
+ return FALSE;
+ }
+
+ modspec = g_strdup_printf ("configDir='%s' tokenDescription='Firefox NSS database' "
+ "flags=readOnly", config_dir);
+ db_slot = SECMOD_OpenUserDB (modspec);
+ g_free (config_dir);
+ g_free (modspec);
+
+ if (!db_slot)
+ return FALSE;
+
+ /* It's possibly to set a master password for NSS through the
+ certificate manager extension, so we must support that too */
+ PK11_SetPasswordFunc (ask_for_nss_password);
+
+ nss_initialized = TRUE;
+
+ return TRUE;
+}
+
+void ephy_nss_glue_close ()
+{
+ if (db_slot) {
+ PK11_FreeSlot (db_slot);
+ db_slot = NULL;
+ }
+
+ PK11_SetPasswordFunc (NULL);
+
+ NSS_Shutdown ();
+
+ nss_initialized = FALSE;
+}
+
+typedef struct SDRResult
+{
+ SECItem keyid;
+ SECAlgorithmID alg;
+ SECItem data;
+} SDRResult;
+
+static SEC_ASN1Template g_template[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof (SDRResult) },
+ { SEC_ASN1_OCTET_STRING, offsetof(SDRResult, keyid) },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(SDRResult, alg),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_OCTET_STRING, offsetof(SDRResult, data) },
+ { 0 }
+};
+
+static SECStatus
+unpadBlock(SECItem *data, int blockSize, SECItem *result)
+{
+ SECStatus rv = SECSuccess;
+ int padLength;
+ int i;
+
+ result->data = 0;
+ result->len = 0;
+
+ /* Remove the padding from the end if the input data */
+ if (data->len == 0 || data->len % blockSize != 0) { rv = SECFailure; goto loser; }
+
+ padLength = data->data[data->len-1];
+ if (padLength > blockSize) { rv = SECFailure; goto loser; }
+
+ /* verify padding */
+ for (i=data->len - padLength; (uint32)i < data->len; i++) {
+ if (data->data[i] != padLength) {
+ rv = SECFailure;
+ goto loser;
+ }
+ }
+
+ result->len = data->len - padLength;
+ result->data = (unsigned char *)PORT_Alloc(result->len);
+ if (!result->data) { rv = SECFailure; goto loser; }
+
+ PORT_Memcpy(result->data, data->data, result->len);
+
+ if (padLength < 2) {
+ /* Chromium returns an error here, but it seems to be harmless and
+ if we continue we'll be able to import the password
+ correctly */
+ /* return SECWouldBlock; */
+ }
+
+loser:
+ return rv;
+}
+
+static SECStatus
+pk11Decrypt (PK11SlotInfo *slot, PLArenaPool *arena,
+ CK_MECHANISM_TYPE type, PK11SymKey *key,
+ SECItem *params, SECItem *in, SECItem *result)
+{
+ PK11Context *ctx = 0;
+ SECItem paddedResult;
+ SECStatus rv;
+
+ paddedResult.len = 0;
+ paddedResult.data = 0;
+
+ ctx = PK11_CreateContextBySymKey (type, CKA_DECRYPT, key, params);
+ if (!ctx) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ paddedResult.len = in->len;
+ paddedResult.data = (unsigned char*)PORT_ArenaAlloc (arena, paddedResult.len);
+
+ rv = PK11_CipherOp (ctx, paddedResult.data,
+ (int*)&paddedResult.len, paddedResult.len,
+ in->data, in->len);
+ if (rv != SECSuccess)
+ goto loser;
+
+ PK11_Finalize(ctx);
+
+ /* Remove the padding */
+ rv = unpadBlock (&paddedResult, PK11_GetBlockSize(type, 0), result);
+ if (rv)
+ goto loser;
+
+loser:
+ if (ctx)
+ PK11_DestroyContext (ctx, PR_TRUE);
+
+ return rv;
+}
+
+static SECStatus
+PK11SDR_DecryptWithSlot (PK11SlotInfo *slot, SECItem *data, SECItem *result, void *cx)
+{
+ SECStatus rv = SECSuccess;
+ PK11SymKey *key = NULL;
+ CK_MECHANISM_TYPE type;
+ SDRResult sdrResult;
+ SECItem *params = NULL;
+ SECItem possibleResult = { (SECItemType)0, NULL, 0 };
+ PLArenaPool *arena = NULL;
+
+ arena = PORT_NewArena (SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (!arena) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ /* Decode the incoming data */
+ memset (&sdrResult, 0, sizeof sdrResult);
+ rv = SEC_QuickDERDecodeItem (arena, &sdrResult, g_template, data);
+ if (rv != SECSuccess)
+ goto loser; /* Invalid format */
+
+ /* Get the parameter values from the data */
+ params = PK11_ParamFromAlgid (&sdrResult.alg);
+ if (!params) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ /* Use triple-DES (Should look up the algorithm) */
+ type = CKM_DES3_CBC;
+ key = PK11_FindFixedKey (slot, type, &sdrResult.keyid, cx);
+ if (!key) {
+ rv = SECFailure;
+ } else {
+ rv = pk11Decrypt (slot, arena, type, key, params,
+ &sdrResult.data, result);
+ }
+
+ loser:
+ if (arena)
+ PORT_FreeArena (arena, PR_TRUE);
+
+ if (key)
+ PK11_FreeSymKey(key);
+
+ if (params)
+ SECITEM_ZfreeItem(params, PR_TRUE);
+
+ if (possibleResult.data)
+ SECITEM_ZfreeItem(&possibleResult, PR_FALSE);
+
+ return rv;
+}
+
+char * ephy_nss_glue_decrypt (const unsigned char *data, gsize length)
+{
+ char *plain = NULL;
+ SECItem request, reply;
+ SECStatus result;
+
+ result = PK11_Authenticate (db_slot, PR_TRUE, NULL);
+ if (result != SECSuccess)
+ return NULL;
+
+ request.data = (unsigned char*)data;
+ request.len = length;
+ reply.data = NULL;
+ reply.len = 0;
+
+ result = PK11SDR_DecryptWithSlot (db_slot, &request, &reply, NULL);
+ if (result == SECSuccess)
+ plain = g_strndup ((const char*)reply.data, reply.len);
+
+ SECITEM_FreeItem (&reply, PR_FALSE);
+
+ return plain;
+}
+
diff --git a/lib/ephy-nss-glue.h b/lib/ephy-nss-glue.h
new file mode 100644
index 000000000..0c157226a
--- /dev/null
+++ b/lib/ephy-nss-glue.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright © 2009 Igalia S.L.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef EPHY_NSS_GLUE_H
+#define EPHY_NSS_GLUE_H
+
+#include <glib.h>
+
+gboolean ephy_nss_glue_init (void);
+void ephy_nss_glue_close (void);
+char * ephy_nss_glue_decrypt (const unsigned char *, gsize);
+
+#endif
diff --git a/lib/ephy-profile-migration.c b/lib/ephy-profile-migration.c
new file mode 100644
index 000000000..c0c319d04
--- /dev/null
+++ b/lib/ephy-profile-migration.c
@@ -0,0 +1,410 @@
+/*
+ * Copyright © 2009 Xan López
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/* Portions of this file based on Chromium code.
+ * License block as follows:
+ *
+ * Copyright (c) 2009 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * The LICENSE file from Chromium can be found in the LICENSE.chromium
+ * file.
+ */
+
+#include "config.h"
+
+#include "ephy-profile-migration.h"
+
+#include "ephy-file-helpers.h"
+#ifdef ENABLE_NSS
+#include "ephy-nss-glue.h"
+#endif
+
+#include <glib/gi18n.h>
+#include <gnome-keyring.h>
+#include <libsoup/soup-gnome.h>
+
+/*
+ * What to do to add new migration steps:
+ * - Bump PROFILE_MIGRATION_VERSION
+ * - Add your function at the end of the 'migrators' array
+ */
+
+#define PROFILE_MIGRATION_VERSION 3
+
+typedef void (*EphyProfileMigrator) (void);
+
+static void
+migrate_cookies ()
+{
+ const char *cookies_file_sqlite = "cookies.sqlite";
+ const char *cookies_file_txt = "cookies.txt";
+ char *src_sqlite = NULL, *src_txt = NULL, *dest = NULL;
+
+ dest = g_build_filename (ephy_dot_dir (), cookies_file_sqlite, NULL);
+ /* If we already have a cookies.sqlite file, do nothing */
+ if (g_file_test (dest, G_FILE_TEST_EXISTS))
+ goto out;
+
+ src_sqlite = g_build_filename (ephy_dot_dir (), "mozilla",
+ "epiphany", cookies_file_sqlite, NULL);
+ src_txt = g_build_filename (ephy_dot_dir (), "mozilla",
+ "epiphany", cookies_file_txt, NULL);
+
+ /* First check if we have a cookies.sqlite file in Mozilla */
+ if (g_file_test (src_sqlite, G_FILE_TEST_EXISTS)) {
+ GFile *gsrc, *gdest;
+
+ /* Copy the file */
+ gsrc = g_file_new_for_path (src_sqlite);
+ gdest = g_file_new_for_path (dest);
+
+ if (!g_file_copy (gsrc, gdest, 0, NULL, NULL, NULL, NULL))
+ g_warning (_("Failed to copy cookies file from Mozilla."));
+
+ g_object_unref (gsrc);
+ g_object_unref (gdest);
+ } else if (g_file_test (src_txt, G_FILE_TEST_EXISTS)) {
+ /* Create a SoupCookieJarSQLite with the contents of the txt file */
+ GSList *cookies, *p;
+ SoupCookieJar *txt, *sqlite;
+
+ txt = soup_cookie_jar_text_new (src_txt, TRUE);
+ sqlite = soup_cookie_jar_sqlite_new (dest, FALSE);
+ cookies = soup_cookie_jar_all_cookies (txt);
+
+ for (p = cookies; p; p = p->next) {
+ SoupCookie *cookie = (SoupCookie*)p->data;
+ /* Cookie is stolen, so we won't free it */
+ soup_cookie_jar_add_cookie (sqlite, cookie);
+ }
+
+ g_slist_free (cookies);
+ g_object_unref (txt);
+ g_object_unref (sqlite);
+ }
+
+ out:
+ g_free (src_sqlite);
+ g_free (src_txt);
+ g_free (dest);
+}
+
+#ifdef ENABLE_NSS
+static gchar*
+_g_utf8_substr(const gchar* string, gint start, gint end)
+{
+ gchar *start_ptr, *output;
+ gsize len_in_bytes;
+ glong str_len = g_utf8_strlen (string, -1);
+
+ if (start > str_len || end > str_len)
+ return NULL;
+
+ start_ptr = g_utf8_offset_to_pointer (string, start);
+ len_in_bytes = g_utf8_offset_to_pointer (string, end) - start_ptr + 1;
+ output = g_malloc0 (len_in_bytes + 1);
+
+ return g_utf8_strncpy (output, start_ptr, end - start + 1);
+}
+
+static char*
+decrypt (const char *data)
+{
+ unsigned char *plain;
+ gsize out_len;
+
+ /* The old style password is encoded in base64. They are identified
+ * by a leading '~'. Otherwise, we should decrypt the text.
+ */
+ plain = g_base64_decode (data, &out_len);
+ if (data[0] != '~') {
+ char *decrypted;
+
+ decrypted = ephy_nss_glue_decrypt (plain, out_len);
+ g_free (plain);
+ plain = (unsigned char*)decrypted;
+ }
+
+ return (char*)plain;
+}
+
+static void
+parse_and_decrypt_signons (const char *signons)
+{
+ int version;
+ gchar **lines;
+ int i;
+ guint length;
+
+ lines = g_strsplit (signons, "\r\n", -1);
+ if (!g_ascii_strncasecmp (lines[0], "#2c", 3))
+ version = 1;
+ else if (!g_ascii_strncasecmp (lines[0], "#2d", 3))
+ version = 2;
+ else if (!g_ascii_strncasecmp (lines[0], "#2e", 3))
+ version = 3;
+ else
+ goto out;
+
+ /* Skip the never-saved list */
+ for (i = 1; lines[i] && !g_str_equal (lines[i], "."); i++) {
+ ;
+ }
+
+ i++;
+
+ /*
+ * Read saved passwords. The information is stored in blocks
+ * separated by lines that only contain a dot. We find a block by
+ * the separator and parse them one by one.
+ */
+ length = g_strv_length (lines);
+
+ while (i < length) {
+ size_t begin = i;
+ size_t end = i + 1;
+ const char *realmBracketBegin = " (";
+ const char *realmBracketEnd = ")";
+ SoupURI *uri = NULL;
+ char *realm = NULL;
+
+ while (lines[end] && !g_str_equal (lines[end], "."))
+ end++;
+
+ i = end + 1;
+
+ /* A block has at least five lines */
+ if (end - begin < 5)
+ continue;
+
+ /* The first line is the site URL.
+ * For HTTP authentication logins, the URL may contain http realm,
+ * which will be in bracket:
+ * sitename:8080 (realm)
+ */
+ if (g_strstr_len (lines[begin], -1, realmBracketBegin)) {
+ char *start_ptr, *end_ptr;
+ char *full_url, *url;
+ glong start, end;
+
+ /* In this case the scheme may not exist. We assume that the
+ * scheme is HTTP.
+ */
+ if (!g_strstr_len (lines[begin], -1, "://"))
+ full_url = g_strconcat ("http://", lines[begin], NULL);
+ else
+ full_url = g_strdup (lines[begin]);
+
+ start_ptr = g_strstr_len (full_url, -1, realmBracketBegin);
+ start = g_utf8_pointer_to_offset (full_url, start_ptr);
+ url = _g_utf8_substr (full_url, 0, start);
+ url = g_strstrip (url);
+ uri = soup_uri_new (url);
+ g_free (url);
+
+ start += strlen (realmBracketBegin);
+ end_ptr = g_strstr_len (full_url, -1, realmBracketEnd);
+ end = g_utf8_pointer_to_offset (full_url, end_ptr);
+ realm = _g_utf8_substr (full_url, start, end);
+
+ g_free (full_url);
+ } else {
+ /* Don't have HTTP realm. It is the URL that the following
+ * password belongs to.
+ */
+ uri = soup_uri_new (lines[begin]);
+ }
+
+ if (!SOUP_URI_VALID_FOR_HTTP (uri)) {
+ soup_uri_free (uri);
+ g_free (realm);
+ continue;
+ }
+
+ ++begin;
+
+ /* There may be multiple username/password pairs for this site.
+ * In this case, they are saved in one block without a separated
+ * line (contains a dot).
+ */
+ while (begin + 4 < end) {
+ char *username = NULL;
+ char *password = NULL;
+ guint32 item_id;
+
+ /* The username */
+ begin++; /* Skip username element */
+ username = decrypt (lines[begin++]);
+
+ /* The password */
+ /* The element name has a leading '*' */
+ if (lines[begin][0] == '*') {
+ begin++; /* Skip password element */
+ password = decrypt (lines[begin++]);
+ } else {
+ /* Maybe the file is broken, skip to the next block */
+ g_free (username);
+ break;
+ }
+
+ /* The action attribute for from the form element. This line
+ * exists in version 2 or above
+ */
+ if (version >= 2) {
+ if (begin < end)
+ /* Skip it */ ;
+ begin++;
+
+ /* Version 3 has an extra line for further use */
+ if (version == 3)
+ begin++;
+ }
+
+ if (username && password)
+ gnome_keyring_set_network_password_sync (NULL,
+ username,
+ realm,
+ uri->host,
+ NULL,
+ uri->scheme,
+ NULL,
+ uri->port,
+ password,
+ &item_id);
+ g_free (username);
+ g_free (password);
+ }
+
+ soup_uri_free (uri);
+ g_free (realm);
+ }
+
+ out:
+ g_strfreev (lines);
+}
+#endif
+
+static void
+migrate_passwords ()
+{
+#ifdef ENABLE_NSS
+ char *dest, *contents, *gecko_passwords_backup;
+ gsize length;
+ GError *error = NULL;
+
+ dest = g_build_filename (ephy_dot_dir (),
+ "mozilla", "epiphany", "signons3.txt",
+ NULL);
+ if (!g_file_test (dest, G_FILE_TEST_EXISTS)) {
+ g_free (dest);
+ dest = g_build_filename (ephy_dot_dir (),
+ "mozilla", "epiphany", "signons2.txt",
+ NULL);
+ if (!g_file_test (dest, G_FILE_TEST_EXISTS)) {
+ g_free (dest);
+ return;
+ }
+ }
+
+ if (!ephy_nss_glue_init ())
+ return;
+
+ if (!g_file_get_contents (dest, &contents, &length, &error)) {
+ g_free (dest);
+ }
+
+ parse_and_decrypt_signons (contents);
+
+ /* Save the contents in a backup directory for future data
+ extraction when we support more features */
+ gecko_passwords_backup = g_build_filename (ephy_dot_dir (),
+ "gecko-passwords.txt", NULL);
+
+ if (!g_file_set_contents (gecko_passwords_backup, contents,
+ -1, &error)) {
+ g_error_free (error);
+ }
+
+ g_free (gecko_passwords_backup);
+ g_free (contents);
+
+ ephy_nss_glue_close ();
+#endif
+}
+
+const EphyProfileMigrator migrators[] = {
+ migrate_cookies,
+ migrate_passwords,
+ /* Yes, again! Version 2 had some bugs, so we need to run
+ migrate_passwords again to possibly migrate more passwords*/
+ migrate_passwords
+};
+
+#define PROFILE_MIGRATION_FILE ".migrated"
+
+void
+_ephy_profile_migrate ()
+{
+ int latest, i;
+ char *migrated_file, *contents;
+
+ /* Figure out the latest migration that occured */
+ migrated_file = g_build_filename (ephy_dot_dir (),
+ PROFILE_MIGRATION_FILE,
+ NULL);
+ if (g_file_test (migrated_file, G_FILE_TEST_EXISTS)) {
+ gsize size;
+ int result;
+
+ g_file_get_contents (migrated_file, &contents, &size, NULL);
+ result = sscanf(contents, "%d", &latest);
+ g_free (contents);
+
+ if (result != 1) {
+ g_warning (_("Failed to read latest migration marker, aborting profile migration."));
+ g_free (migrated_file);
+ return;
+ }
+ } else
+ /* Never migrated */
+ latest = 0;
+
+ for (i = latest; i < PROFILE_MIGRATION_VERSION; i++) {
+ EphyProfileMigrator m;
+
+ /* No need to run the password migration twice in a row. It
+ appears twice in the list for the benefit of people that were
+ using the development snapshots, since an early version didn't
+ migrate all passwords correctly. */
+ if (i == 1)
+ continue;
+
+ m = migrators[i];
+ m();
+ }
+
+ /* Write down the latest migration */
+ contents = g_strdup_printf ("%d", PROFILE_MIGRATION_VERSION);
+ g_file_set_contents (migrated_file, contents, -1, NULL);
+ g_free (contents);
+ g_free (migrated_file);
+}
+
diff --git a/lib/ephy-profile-migration.h b/lib/ephy-profile-migration.h
new file mode 100644
index 000000000..51114a88d
--- /dev/null
+++ b/lib/ephy-profile-migration.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright © 2009 Xan López
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef EPHY_PROFILE_MIGRATION_H
+#define EPHY_PROFILE_MIGRATION_H
+
+void _ephy_profile_migrate (void);
+
+#endif