/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* vim: set sw=2 ts=2 sts=2 et: */
/*
* 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-debug.h"
#include "ephy-file-helpers.h"
#include "ephy-history-service.h"
#include "ephy-profile-utils.h"
#include "ephy-settings.h"
#include "ephy-sqlite-connection.h"
#include "ephy-web-app-utils.h"
#ifdef ENABLE_NSS
#include "ephy-nss-glue.h"
#endif
#include <fcntl.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <gnome-keyring.h>
#include <libsoup/soup-gnome.h>
#include <sys/stat.h>
#include <sys/types.h>
static int do_step_n = -1;
/*
* What to do to add new migration steps:
* - Bump EPHY_PROFILE_MIGRATION_VERSION in lib/ephy-profile-utils.h
* - Add your function at the end of the 'migrators' array
*/
typedef void (*EphyProfileMigrator) (void);
static gboolean
profile_dir_exists ()
{
return g_file_test (ephy_dot_dir (), G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR);
}
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 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,
gboolean handle_forms)
{
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_substring (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) -1;
end = g_utf8_pointer_to_offset (full_url, end_ptr);
realm = g_utf8_substring (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;
char *form_username = NULL;
char *form_password = NULL;
guint32 item_id;
/* The username */
if (handle_forms) {
form_username = g_strdup (lines[begin++]);
} else {
begin++; /* Skip username element */
}
username = decrypt (lines[begin++]);
/* The password */
/* The element name has a leading '*' */
if (lines[begin][0] == '*') {
if (handle_forms) {
form_password = g_strdup (lines[begin++]);
} else {
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 (handle_forms && !realm &&
username && password &&
!g_str_equal (username, "") &&
!g_str_equal (form_username, "") &&
!g_str_equal (form_password, "*")) {
char *u = soup_uri_to_string (uri, FALSE);
/* We skip the '*' at the beginning of form_password. */
_ephy_profile_utils_store_form_auth_data (u,
form_username,
form_password+1,
username,
password);
g_free (u);
} else if (!handle_forms && realm &&
username && password &&
!g_str_equal (username, "") &&
form_username == NULL && form_password == NULL) {
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);
g_free (form_username);
g_free (form_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, FALSE);
/* 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
}
static void
migrate_passwords2 ()
{
#ifdef ENABLE_NSS
char *dest, *contents;
gsize length;
GError *error = NULL;
dest = g_build_filename (ephy_dot_dir (),
"gecko-passwords.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, TRUE);
g_free (contents);
ephy_nss_glue_close ();
#endif
}
/* History migration */
static EphyHistoryService *history_service = NULL;
static gboolean all_done = FALSE;
typedef struct {
char *title;
char *location;
char *current;
long long int visit_count;
long long int last_visit;
long long int first_visit;
double zoom_level;
GList *visits;
} HistoryParseData;
static void
history_parse_start_element (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
gpointer user_data,
GError **error)
{
HistoryParseData *parse_data = user_data;
if (g_str_equal (element_name, "node") && parse_data) {
/* Starting a new node, reset all values */
g_free (parse_data->title);
parse_data->title = NULL;
g_free (parse_data->location);
parse_data->location = NULL;
parse_data->visit_count = 0;
parse_data->last_visit = 0;
parse_data->first_visit = 0;
parse_data->zoom_level = 1.0;
} else if (g_str_equal (element_name, "property")) {
const char **name, **value;
for (name = attribute_names, value = attribute_values; *name; name++, value++) {
if (g_str_equal (*name, "id")) {
parse_data->current = g_strdup (*value);
break;
}
}
}
}
static void
history_parse_text (GMarkupParseContext *context,
const char *text,
gsize text_len,
gpointer user_data,
GError **error)
{
HistoryParseData *parse_data = user_data;
if (!parse_data || ! parse_data->current)
return;
if (g_str_equal (parse_data->current, "2")) {
/* Title */
parse_data->title = g_strndup (text, text_len);
} else if (g_str_equal (parse_data->current, "3")) {
/* Location */
parse_data->location = g_strndup (text, text_len);
} else if (g_str_equal (parse_data->current, "4")) {
/* Visit count */
GString *data = g_string_new_len (text, text_len);
sscanf (data->str, "%lld", &parse_data->visit_count);
g_string_free (data, TRUE);
} else if (g_str_equal (parse_data->current, "5")) {
/* Last visit */
GString *data = g_string_new_len (text, text_len);
sscanf (data->str, "%lld", &parse_data->last_visit);
g_string_free (data, TRUE);
} else if (g_str_equal (parse_data->current, "6")) {
/* First visit */
GString *data = g_string_new_len (text, text_len);
sscanf (data->str, "%lld", &parse_data->first_visit);
g_string_free (data, TRUE);
} else if (g_str_equal (parse_data->current, "10")) {
/* Zoom level. */
GString *data = g_string_new_len (text, text_len);
sscanf (data->str, "%lf", &parse_data->zoom_level);
g_string_free (data, TRUE);
}
g_free (parse_data->current);
parse_data->current = NULL;
}
static void
visit_cb (EphyHistoryService *service, gboolean success, gpointer result, gpointer user_data)
{
all_done = TRUE;
}
static void
history_parse_end_element (GMarkupParseContext *context,
const char *element_name,
gpointer user_data,
GError **error)
{
HistoryParseData *parse_data = user_data;
if (g_str_equal (element_name, "node") && parse_data) {
/* Add one item to History */
EphyHistoryPageVisit *visit = ephy_history_page_visit_new (parse_data->location ? parse_data->location : "",
parse_data->last_visit, EPHY_PAGE_VISIT_TYPED);
g_free (visit->url->title);
visit->url->title = g_strdup (parse_data->title);
if (parse_data->zoom_level != 1.0) {
/* Zoom levels are only stored per host in the old history, so
* creating a new host here is OK. */
g_assert (!visit->url->host);
visit->url->host = ephy_history_host_new (parse_data->location, parse_data->title,
parse_data->visit_count, parse_data->zoom_level);
}
parse_data->visits = g_list_append (parse_data->visits, visit);
}
}
static GMarkupParser history_parse_funcs =
{
history_parse_start_element,
history_parse_end_element,
history_parse_text,
NULL,
NULL,
};
static void
migrate_history ()
{
GFileInputStream *input;
GMarkupParseContext *context;
GError *error = NULL;
GFile *file;
char *filename;
char buffer[1024];
HistoryParseData parse_data;
gchar *temporary_file = g_build_filename (ephy_dot_dir (), "ephy-history.db", NULL);
/* Do nothing if the history file already exists. Safer than wiping
* it out. */
if (g_file_test (temporary_file, G_FILE_TEST_EXISTS)) {
g_warning ("Did not migrate Epiphany's history, the ephy-history.db file already exists");
g_free (temporary_file);
return;
}
history_service = ephy_history_service_new (temporary_file);
g_free (temporary_file);
memset (&parse_data, 0, sizeof (HistoryParseData));
parse_data.location = NULL;
parse_data.title = NULL;
parse_data.visits = NULL;
filename = g_build_filename (ephy_dot_dir (),
"ephy-history.xml",
NULL);
file = g_file_new_for_path (filename);
g_free (filename);
input = g_file_read (file, NULL, &error);
g_object_unref (file);
if (error) {
if (error->code != G_IO_ERROR_NOT_FOUND)
g_warning ("Could not load Epiphany history data, migration aborted: %s", error->message);
g_error_free (error);
return;
}
context = g_markup_parse_context_new (&history_parse_funcs, 0, &parse_data, NULL);
while (TRUE) {
gssize count = g_input_stream_read (G_INPUT_STREAM (input), buffer, sizeof (buffer), NULL, &error);
if (count <= 0)
break;
if (!g_markup_parse_context_parse (context, buffer, count, &error))
break;
}
g_markup_parse_context_free (context);
g_input_stream_close (G_INPUT_STREAM (input), NULL, NULL);
g_object_unref (input);
if (parse_data.visits) {
ephy_history_service_add_visits (history_service, parse_data.visits, NULL, (EphyHistoryJobCallback)visit_cb, NULL);
ephy_history_page_visit_list_free (parse_data.visits);
while (!all_done)
g_main_context_iteration (NULL, FALSE);
}
g_object_unref (history_service);
}
static void
migrate_tabs_visibility ()
{
gboolean always_show_tabs;
always_show_tabs = g_settings_get_boolean (EPHY_SETTINGS_UI,
EPHY_PREFS_UI_ALWAYS_SHOW_TABS_BAR);
if (always_show_tabs)
g_settings_set_enum (EPHY_SETTINGS_UI,
EPHY_PREFS_UI_TABS_BAR_VISIBILITY_POLICY,
EPHY_PREFS_UI_TABS_BAR_VISIBILITY_POLICY_ALWAYS);
}
static void
migrate_profile (const char *old_dir,
const char *new_dir)
{
char *parent_dir;
char *updated;
const char *message;
if (g_file_test (new_dir, G_FILE_TEST_EXISTS) ||
!g_file_test (old_dir, G_FILE_TEST_IS_DIR))
return;
/* Test if we already attempted to migrate first. */
updated = g_build_filename (old_dir, "DEPRECATED-DIRECTORY", NULL);
message = _("Epiphany 3.6 deprecated this directory and tried migrating "
"this configuration to ~/.config/epiphany");
parent_dir = g_path_get_dirname (new_dir);
if (g_mkdir_with_parents (parent_dir, 0700) == 0) {
int fd, res;
/* rename() works fine if the destination directory is empty. */
res = g_rename (old_dir, new_dir);
if (res == -1 && !g_file_test (updated, G_FILE_TEST_EXISTS)) {
fd = g_creat (updated, 0600);
if (fd != -1) {
res = write (fd, message, strlen (message));
close (fd);
}
}
}
g_free (parent_dir);
g_free (updated);
}
static void
migrate_profile_gnome2_to_xdg ()
{
char *old_dir;
char *new_dir;
old_dir = g_build_filename (g_get_home_dir (),
".gnome2",
"epiphany",
NULL);
new_dir = g_build_filename (g_get_user_config_dir (),
"epiphany",
NULL);
migrate_profile (old_dir, new_dir);
g_free (new_dir);
g_free (old_dir);
}
static char *
fix_desktop_file_and_return_new_location (const char *dir)
{
GRegex * regex;
char *result, *old_profile_dir, *replacement, *contents, *new_contents;
gsize length;
old_profile_dir = g_build_filename (g_get_home_dir (),
".gnome2",
NULL);
replacement = g_build_filename (g_get_user_config_dir (),
NULL);
regex = g_regex_new (old_profile_dir, 0, 0, NULL);
/* We want to modify both the link destination and the contents of
* the .desktop file itself. */
result = g_regex_replace (regex, dir, -1,
0, replacement, 0, NULL);
g_file_get_contents (result, &contents, &length, NULL);
new_contents = g_regex_replace (regex, contents, -1, 0,
replacement, 0, NULL);
g_file_set_contents (result, new_contents, length, NULL);
g_free (contents);
g_free (new_contents);
g_free (old_profile_dir);
g_free (replacement);
g_regex_unref (regex);
return result;
}
static void
migrate_web_app_links ()
{
GList *apps, *p;
apps = ephy_web_application_get_application_list ();
for (p = apps; p; p = p->next) {
char *desktop_file, *app_link;
EphyWebApplication *app = (EphyWebApplication*)p->data;
desktop_file = app->desktop_file;
/* Update the link in applications. */
app_link = g_build_filename (g_get_user_data_dir (), "applications", desktop_file, NULL);
if (g_file_test (app_link, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_SYMLINK)) {
/* Change the link to point to the new profile dir. */
GFileInfo *info;
const char *target;
GFile *file = g_file_new_for_path (app_link);
info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET,
0, NULL, NULL);
if (info) {
char *new_target;
target = g_file_info_get_symlink_target (info);
new_target = fix_desktop_file_and_return_new_location (target);
/* FIXME: Updating the file info and setting it again should
* work, but it does not? Just delete and create the link
* again. */
g_file_delete (file, 0, 0);
g_object_unref (file);
file = g_file_new_for_path (app_link);
g_file_make_symbolic_link (file, new_target, NULL, NULL);
g_object_unref (info);
g_free (new_target);
}
g_object_unref (file);
}
g_free (app_link);
}
ephy_web_application_free_application_list (apps);
}
static void
migrate_new_urls_table ()
{
EphySQLiteConnection *history_database;
char *filename;
GError *error = NULL;
filename = g_build_filename (ephy_dot_dir (), "ephy-history.db", NULL);
history_database = ephy_sqlite_connection_new ();
ephy_sqlite_connection_open (history_database, filename, &error);
if (error) {
g_warning ("Failed to open history database: %s\n", error->message);
g_error_free (error);
g_free (filename);
return;
}
ephy_sqlite_connection_execute (history_database,
"ALTER TABLE urls "
"ADD COLUMN thumbnail_update_time INTEGER DEFAULT 0",
&error);
if (error) {
g_warning ("Failed to add new column to table in history backend: %s\n",
error->message);
g_error_free (error);
error = NULL;
}
ephy_sqlite_connection_execute (history_database,
"ALTER TABLE urls "
"ADD COLUMN hidden_from_overview INTEGER DEFAULT 0",
&error);
if (error) {
g_warning ("Failed to add new column to table in history backend: %s\n",
error->message);
g_error_free (error);
error = NULL;
}
g_object_unref (history_database);
g_free (filename);
}
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,
/* Very similar to migrate_passwords, but this migrates
* login/passwords for page forms, which we previously ignored */
migrate_passwords2,
migrate_history,
migrate_tabs_visibility,
migrate_web_app_links,
migrate_new_urls_table,
};
static gboolean
ephy_migrator ()
{
int latest, i;
EphyProfileMigrator m;
/* Always try to migrate the data from the old profile dir at the
* very beginning. */
migrate_profile_gnome2_to_xdg ();
/* If after this point there's no profile dir, there's no point in
* running anything because Epiphany has never run in this sytem, so
* exit here. */
if (!profile_dir_exists ())
return TRUE;
if (do_step_n != -1) {
if (do_step_n >= EPHY_PROFILE_MIGRATION_VERSION)
return FALSE;
LOG ("Running only migrator: %d", do_step_n);
m = migrators[do_step_n];
m();
return TRUE;
}
latest = ephy_profile_utils_get_migration_version ();
LOG ("Running migrators up to version %d, current migration version is %d.",
EPHY_PROFILE_MIGRATION_VERSION, latest);
for (i = latest; i < EPHY_PROFILE_MIGRATION_VERSION; i++) {
LOG ("Running migrator: %d of %d", i, EPHY_PROFILE_MIGRATION_VERSION);
/* 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();
}
if (ephy_profile_utils_set_migration_version (EPHY_PROFILE_MIGRATION_VERSION) != TRUE) {
LOG ("Failed to store the current migration version");
return FALSE;
}
return TRUE;
}
static const GOptionEntry option_entries[] =
{
{ "do-step", 'd', 0, G_OPTION_ARG_INT, &do_step_n,
N_("Executes only the n-th migration step"), NULL },
{ NULL }
};
int
main (int argc, char *argv[])
{
GOptionContext *option_context;
GOptionGroup *option_group;
GError *error = NULL;
g_type_init ();
option_group = g_option_group_new ("ephy-profile-migrator",
N_("Epiphany profile migrator"),
N_("Epiphany profile migrator options"),
NULL, NULL);
g_option_group_set_translation_domain (option_group, GETTEXT_PACKAGE);
g_option_group_add_entries (option_group, option_entries);
option_context = g_option_context_new ("");
g_option_context_set_main_group (option_context, option_group);
if (!g_option_context_parse (option_context, &argc, &argv, &error)) {
g_print ("Failed to parse arguments: %s\n", error->message);
g_error_free (error);
g_option_context_free (option_context);
return 1;
}
g_option_context_free (option_context);
ephy_debug_init ();
if (!ephy_file_helpers_init (NULL, EPHY_FILE_HELPERS_NONE, NULL)) {
LOG ("Something wrong happened with ephy_file_helpers_init()");
return -1;
}
return ephy_migrator () ? 0 : 1;
}