[epiphany] Move adblocker filter updating to new EphyFiltersManager class



commit 9c23cd821d869a36dc2b9bf3b3ebef5d8cc6830a
Author: Michael Catanzaro <mcatanzaro gnome org>
Date:   Sun Jan 22 23:55:57 2017 -0600

    Move adblocker filter updating to new EphyFiltersManager class
    
    There is now way too much adblock code in EphyEmbedShell. Move it to a
    new file.

 embed/ephy-embed-shell.c   |  399 ++++++++------------------------------------
 lib/Makefile.am            |    2 +
 lib/ephy-filters-manager.c |  388 ++++++++++++++++++++++++++++++++++++++++++
 lib/ephy-filters-manager.h |   34 ++++
 4 files changed, 495 insertions(+), 328 deletions(-)
---
diff --git a/embed/ephy-embed-shell.c b/embed/ephy-embed-shell.c
index 8e2cfcc..101caf9 100644
--- a/embed/ephy-embed-shell.c
+++ b/embed/ephy-embed-shell.c
@@ -30,6 +30,7 @@
 #include "ephy-embed-utils.h"
 #include "ephy-encodings.h"
 #include "ephy-file-helpers.h"
+#include "ephy-filters-manager.h"
 #include "ephy-history-service.h"
 #include "ephy-profile-utils.h"
 #include "ephy-settings.h"
@@ -51,8 +52,6 @@
 #define PRINT_SETTINGS_FILENAME "print-settings.ini"
 #define OVERVIEW_RELOAD_DELAY 500
 
-#define ADBLOCK_FILTER_UPDATE_FREQUENCY 24 * 60 * 60 /* In seconds */
-
 typedef struct {
   WebKitWebContext *web_context;
   EphyHistoryService *global_history_service;
@@ -69,9 +68,8 @@ typedef struct {
   guint hiding_overview_item;
   GDBusServer *dbus_server;
   GList *web_extensions;
-
-  char *adblock_data_dir;
-  GCancellable *uri_tester_update_cancellable;
+  EphyFiltersManager *filters_manager;
+  GCancellable *cancellable;
 } EphyEmbedShellPrivate;
 
 enum {
@@ -104,9 +102,9 @@ ephy_embed_shell_dispose (GObject *object)
 {
   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (EPHY_EMBED_SHELL (object));
 
-  if (priv->uri_tester_update_cancellable) {
-    g_cancellable_cancel (priv->uri_tester_update_cancellable);
-    g_clear_object (&priv->uri_tester_update_cancellable);
+  if (priv->cancellable) {
+    g_cancellable_cancel (priv->cancellable);
+    g_clear_object (&priv->cancellable);
   }
 
   if (priv->update_overview_timeout_id > 0) {
@@ -125,21 +123,12 @@ ephy_embed_shell_dispose (GObject *object)
   g_clear_object (&priv->hosts_manager);
   g_clear_object (&priv->web_context);
   g_clear_object (&priv->dbus_server);
+  g_clear_object (&priv->filters_manager);
 
   G_OBJECT_CLASS (ephy_embed_shell_parent_class)->dispose (object);
 }
 
 static void
-ephy_embed_shell_finalize (GObject *object)
-{
-  EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (EPHY_EMBED_SHELL (object));
-
-  g_free (priv->adblock_data_dir);
-
-  G_OBJECT_CLASS (ephy_embed_shell_parent_class)->finalize (object);
-}
-
-static void
 web_extension_form_auth_data_message_received_cb (WebKitUserContentManager *manager,
                                                   WebKitJavascriptResult   *message,
                                                   EphyEmbedShell           *shell)
@@ -636,307 +625,6 @@ ftp_request_cb (WebKitURISchemeRequest *request)
   g_object_unref (app_info);
 }
 
-#ifdef HAVE_LIBHTTPSEVERYWHERE
-static void
-https_everywhere_update_cb (HTTPSEverywhereUpdater *updater,
-                            GAsyncResult           *result)
-{
-  GError *error = NULL;
-
-  https_everywhere_updater_update_finish (updater, result, &error);
-
-  if (!error)
-    return;
-
-  if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
-      !g_error_matches (error, HTTPS_EVERYWHERE_UPDATE_ERROR, HTTPS_EVERYWHERE_UPDATE_ERROR_IN_PROGRESS) &&
-      !g_error_matches (error, HTTPS_EVERYWHERE_UPDATE_ERROR, 
HTTPS_EVERYWHERE_UPDATE_ERROR_NO_UPDATE_AVAILABLE))
-    g_warning ("Failed to update HTTPS Everywhere rulesets: %s", error->message);
-  g_error_free (error);
-}
-#endif
-
-static const char *
-ephy_embed_shell_ensure_adblock_data_dir (EphyEmbedShell *shell)
-{
-  EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
-
-  if (priv->adblock_data_dir)
-    return priv->adblock_data_dir;
-
-  if (priv->mode == EPHY_EMBED_SHELL_MODE_APPLICATION) {
-    char *default_dot_dir = ephy_default_dot_dir ();
-
-    priv->adblock_data_dir = g_build_filename (default_dot_dir, "adblock", NULL);
-    g_free (default_dot_dir);
-  } else
-    priv->adblock_data_dir = g_build_filename (ephy_dot_dir (), "adblock", NULL);
-  g_mkdir_with_parents (priv->adblock_data_dir, 0700);
-
-  return priv->adblock_data_dir;
-}
-
-static gboolean
-adblock_filter_file_is_valid (GFile *file)
-{
-  GFileInfo *file_info;
-  gboolean result = FALSE;
-
-  /* Now check if the local file is too old. */
-  file_info = g_file_query_info (file,
-                                 G_FILE_ATTRIBUTE_TIME_MODIFIED","G_FILE_ATTRIBUTE_STANDARD_SIZE,
-                                 G_FILE_QUERY_INFO_NONE,
-                                 NULL,
-                                 NULL);
-  if (file_info) {
-    if (g_file_info_get_size (file_info) > 0) {
-      GTimeVal current_time;
-      GTimeVal mod_time;
-
-      g_get_current_time (&current_time);
-      g_file_info_get_modification_time (file_info, &mod_time);
-
-      if (current_time.tv_sec > mod_time.tv_sec) {
-        gint64 expire_time = mod_time.tv_sec + ADBLOCK_FILTER_UPDATE_FREQUENCY;
-
-        result = current_time.tv_sec < expire_time;
-      }
-    }
-    g_object_unref (file_info);
-  }
-
-  return result;
-}
-
-typedef struct {
-  EphyEmbedShell *shell;
-
-  GFile *src_file;
-  GFile *filter_file;
-  GFile *tmp_file;
-} AdblockFilterRetrieveData;
-
-static AdblockFilterRetrieveData *
-adblock_filter_retrieve_data_new (EphyEmbedShell *shell,
-                                  GFile          *src_file,
-                                  GFile          *filter_file)
-{
-  AdblockFilterRetrieveData* data;
-  char *path, *tmp_path;
-
-  data = g_slice_new (AdblockFilterRetrieveData);
-  data->shell = g_object_ref (shell);
-  data->src_file = g_object_ref (src_file);
-  data->filter_file = g_object_ref (filter_file);
-
-  path = g_file_get_path (filter_file);
-  tmp_path = g_strdup_printf ("%s.tmp", path);
-  g_free (path);
-  data->tmp_file = g_file_new_for_path (tmp_path);
-  g_free (tmp_path);
-
-  return data;
-}
-
-static void
-adblock_filter_retrieve_data_free (AdblockFilterRetrieveData *data)
-{
-  g_object_unref (data->shell);
-  g_object_unref (data->src_file);
-  g_object_unref (data->filter_file);
-  g_object_unref (data->tmp_file);
-
-  g_slice_free (AdblockFilterRetrieveData, data);
-}
-
-static void
-ephy_embed_shell_retrieve_filter_file_finished (GFile                     *src,
-                                                GAsyncResult              *result,
-                                                AdblockFilterRetrieveData *data)
-{
-  GError *error = NULL;
-
-  if (!g_file_copy_finish (src, result, &error) ||
-      !g_file_move (data->tmp_file, data->filter_file, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &error)) {
-    GFileOutputStream *stream;
-
-    /* If failed to retrieve, create an empty file if it doesn't exist to unblock extensions */
-    stream = g_file_create (data->filter_file, G_FILE_CREATE_NONE, NULL, NULL);
-    if (stream)
-      g_object_unref (stream);
-
-    if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-      g_warning ("Error retrieving filter %s: %s\n", g_file_get_uri (data->src_file), error->message);
-    g_error_free (error);
-  }
-
-  adblock_filter_retrieve_data_free (data);
-}
-
-static void
-ephy_embed_shell_retrieve_filter_file (EphyEmbedShell *shell,
-                                       const char     *filter_url,
-                                       GFile          *file)
-{
-  EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
-  GFile *src = g_file_new_for_uri (filter_url);
-  AdblockFilterRetrieveData *data;
-
-  if (!priv->uri_tester_update_cancellable)
-    priv->uri_tester_update_cancellable = g_cancellable_new ();
-
-  data = adblock_filter_retrieve_data_new (shell, src, file);
-
-  g_file_copy_async (src, data->tmp_file,
-                     G_FILE_COPY_OVERWRITE,
-                     G_PRIORITY_DEFAULT,
-                     priv->uri_tester_update_cancellable,
-                     NULL, NULL,
-                     (GAsyncReadyCallback)ephy_embed_shell_retrieve_filter_file_finished,
-                     data);
-
-  g_object_unref (src);
-}
-
-static void
-ephy_embed_shell_remove_old_adblock_filters (EphyEmbedShell *shell,
-                                             GList          *current_files)
-{
-  EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
-  GFile *file;
-  GFile *filters_dir;
-  GFileEnumerator *enumerator;
-  gboolean current_filter;
-  GError *error = NULL;
-
-  filters_dir = g_file_new_for_path (ephy_embed_shell_ensure_adblock_data_dir (shell));
-  enumerator = g_file_enumerate_children (filters_dir,
-                                          G_FILE_ATTRIBUTE_STANDARD_NAME,
-                                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
-                                          NULL,
-                                          &error);
-  if (error != NULL) {
-    g_warning ("Failed to enumerate children of %s: %s", priv->adblock_data_dir, error->message);
-    g_error_free (error);
-    g_object_unref (filters_dir);
-    return;
-  }
-
-  /* For each file in the adblock directory, check if it is a currently-enabled
-   * and remove it if not, since filter files can be quite large. */
-  for (;;) {
-    g_file_enumerator_iterate (enumerator, NULL, &file, NULL, &error);
-    if (error != NULL) {
-      g_warning ("Failed to iterate file enumerator for %s: %s", priv->adblock_data_dir, error->message);
-      g_clear_error (&error);
-      continue;
-    }
-
-    /* Success: no more files left to iterate. */
-    if (file == NULL)
-      break;
-
-    current_filter = FALSE;
-    for (GList *l = current_files; l != NULL; l = l->next) {
-      if (g_file_equal (l->data, file)) {
-        current_filter = TRUE;
-        break;
-      }
-    }
-
-    if (!current_filter) {
-      g_file_delete (file, NULL, &error);
-      if (error != NULL) {
-        g_warning ("Failed to remove %s: %s", g_file_get_path (file), error->message);
-        g_clear_error (&error);
-      }
-    }
-  }
-
-  g_object_unref (filters_dir);
-  g_object_unref (enumerator);
-}
-
-static void
-ephy_embed_shell_update_adblock_filter_files (EphyEmbedShell *shell)
-{
-  EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
-  char **filters;
-  GList *files = NULL;
-
-  /* One at a time please! */
-  if (priv->uri_tester_update_cancellable)
-    g_cancellable_cancel (priv->uri_tester_update_cancellable);
-
-  if (!g_settings_get_boolean (EPHY_SETTINGS_WEB, EPHY_PREFS_WEB_ENABLE_ADBLOCK)) {
-    ephy_embed_shell_remove_old_adblock_filters (shell, files);
-    return;
-  }
-
-  filters = g_settings_get_strv (EPHY_SETTINGS_WEB, EPHY_PREFS_WEB_ADBLOCK_FILTERS);
-  for (guint i = 0; filters[i]; i++) {
-    GFile *filter_file;
-
-    filter_file = ephy_uri_tester_get_adblock_filter_file (ephy_embed_shell_ensure_adblock_data_dir (shell), 
filters[i]);
-    if (!adblock_filter_file_is_valid (filter_file))
-      ephy_embed_shell_retrieve_filter_file (shell, filters[i], filter_file);
-    files = g_list_prepend (files, filter_file);
-  }
-
-  ephy_embed_shell_remove_old_adblock_filters (shell, files);
-
-  g_strfreev (filters);
-  g_list_free_full (files, g_object_unref);
-}
-
-static void
-ephy_uri_tester_adblock_filters_changed_cb (GSettings      *settings,
-                                            char           *key,
-                                            EphyEmbedShell *shell)
-{
-  ephy_embed_shell_update_adblock_filter_files (shell);
-}
-
-static void
-ephy_uri_tester_enable_adblock_changed_cb (GSettings      *settings,
-                                           char           *key,
-                                           EphyEmbedShell *shell)
-{
-  ephy_embed_shell_update_adblock_filter_files (shell);
-}
-
-static void
-ephy_embed_shell_update_uri_tester (EphyEmbedShell *shell)
-{
-  EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
-
-  ephy_embed_shell_update_adblock_filter_files (shell);
-
-  if (priv->mode != EPHY_EMBED_SHELL_MODE_TEST &&
-      priv->mode != EPHY_EMBED_SHELL_MODE_SEARCH_PROVIDER) {
-#ifdef HAVE_LIBHTTPSEVERYWHERE
-    HTTPSEverywhereContext *context;
-    HTTPSEverywhereUpdater *updater;
-#endif
-
-    if (!priv->uri_tester_update_cancellable)
-      priv->uri_tester_update_cancellable = g_cancellable_new ();
-
-#ifdef HAVE_LIBHTTPSEVERYWHERE
-    /* We might want to be smarter about this in the future. For now,
-     * trigger an update of the rulesets once each time Epiphany is started. */
-    context = https_everywhere_context_new ();
-    updater = https_everywhere_updater_new (context);
-    https_everywhere_updater_update (updater,
-                                     priv->uri_tester_update_cancellable,
-                                     (GAsyncReadyCallback)https_everywhere_update_cb,
-                                     NULL);
-    g_object_unref (context);
-    g_object_unref (updater);
-#endif
-  }
-}
-
 static void
 web_extension_destroyed (EphyEmbedShell *shell,
                          GObject        *web_extension)
@@ -980,7 +668,7 @@ initialize_web_extensions (WebKitWebContext *web_context,
   user_data = g_variant_new ("(msssb)",
                              address,
                              ephy_dot_dir (),
-                             ephy_embed_shell_ensure_adblock_data_dir (shell),
+                             ephy_filters_manager_get_adblock_filters_dir (priv->filters_manager),
                              private_profile);
   webkit_web_context_set_web_extensions_initialization_user_data (web_context, user_data);
 }
@@ -1116,6 +804,43 @@ ephy_embed_shell_create_web_context (EphyEmbedShell *shell)
   g_object_unref (manager);
 }
 
+#ifdef HAVE_LIBHTTPSEVERYWHERE
+static void
+https_everywhere_update_cb (HTTPSEverywhereUpdater *updater,
+                            GAsyncResult           *result)
+{
+  GError *error = NULL;
+
+  https_everywhere_updater_update_finish (updater, result, &error);
+
+  if (!error)
+    return;
+
+  if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
+      !g_error_matches (error, HTTPS_EVERYWHERE_UPDATE_ERROR, HTTPS_EVERYWHERE_UPDATE_ERROR_IN_PROGRESS) &&
+      !g_error_matches (error, HTTPS_EVERYWHERE_UPDATE_ERROR, 
HTTPS_EVERYWHERE_UPDATE_ERROR_NO_UPDATE_AVAILABLE))
+    g_warning ("Failed to update HTTPS Everywhere rulesets: %s", error->message);
+  g_error_free (error);
+}
+#endif
+
+static char *
+adblock_filters_dir (EphyEmbedShell *shell)
+{
+  EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
+  char *result;
+
+  if (priv->mode == EPHY_EMBED_SHELL_MODE_APPLICATION) {
+    char *default_dot_dir = ephy_default_dot_dir ();
+
+    result = g_build_filename (default_dot_dir, "adblock", NULL);
+    g_free (default_dot_dir);
+  } else
+    result = g_build_filename (ephy_dot_dir (), "adblock", NULL);
+
+  return result;
+}
+
 static void
 ephy_embed_shell_startup (GApplication *application)
 {
@@ -1125,16 +850,17 @@ ephy_embed_shell_startup (GApplication *application)
   WebKitCookieManager *cookie_manager;
   char *filename;
   char *cookie_policy;
+  char *filters_dir;
+#ifdef HAVE_LIBHTTPSEVERYWHERE
+  HTTPSEverywhereContext *context;
+  HTTPSEverywhereUpdater *updater;
+#endif
 
   G_APPLICATION_CLASS (ephy_embed_shell_parent_class)->startup (application);
 
-  /* Note: up here because we must connect *before* reading the settings. */
-  g_signal_connect (EPHY_SETTINGS_WEB, "changed::" EPHY_PREFS_WEB_ADBLOCK_FILTERS,
-                    G_CALLBACK (ephy_uri_tester_adblock_filters_changed_cb), shell);
-  g_signal_connect (EPHY_SETTINGS_WEB, "changed::" EPHY_PREFS_WEB_ENABLE_ADBLOCK,
-                    G_CALLBACK (ephy_uri_tester_enable_adblock_changed_cb), shell);
-
-  ephy_embed_shell_update_uri_tester (shell);
+  filters_dir = adblock_filters_dir (shell);
+  priv->filters_manager = ephy_filters_manager_new (filters_dir);
+  g_free (filters_dir);
 
   ephy_embed_shell_create_web_context (shell);
 
@@ -1228,6 +954,24 @@ ephy_embed_shell_startup (GApplication *application)
                                          EPHY_PREFS_WEB_COOKIES_POLICY);
   ephy_embed_prefs_set_cookie_accept_policy (cookie_manager, cookie_policy);
   g_free (cookie_policy);
+
+#ifdef HAVE_LIBHTTPSEVERYWHERE
+    /* We might want to be smarter about this in the future. For now,
+     * trigger an update of the rulesets once each time Epiphany is started.
+     * Note that the updated rules will not be used until the next time Epiphany
+     * is started. */
+  if (priv->mode != EPHY_EMBED_SHELL_MODE_TEST &&
+      priv->mode != EPHY_EMBED_SHELL_MODE_SEARCH_PROVIDER) {
+    context = https_everywhere_context_new ();
+    updater = https_everywhere_updater_new (context);
+    https_everywhere_updater_update (updater,
+                                     priv->cancellable,
+                                     (GAsyncReadyCallback)https_everywhere_update_cb,
+                                     NULL);
+    g_object_unref (context);
+    g_object_unref (updater);
+  }
+#endif
 }
 
 static void
@@ -1326,7 +1070,6 @@ ephy_embed_shell_class_init (EphyEmbedShellClass *klass)
   GApplicationClass *application_class = G_APPLICATION_CLASS (klass);
 
   object_class->dispose = ephy_embed_shell_dispose;
-  object_class->finalize = ephy_embed_shell_finalize;
   object_class->set_property = ephy_embed_shell_set_property;
   object_class->get_property = ephy_embed_shell_get_property;
   object_class->constructed = ephy_embed_shell_constructed;
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 43a25f2..784dc1c 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -17,6 +17,8 @@ libephymisc_la_SOURCES = \
        ephy-favicon-helpers.h                  \
        ephy-file-helpers.c                     \
        ephy-file-helpers.h                     \
+       ephy-filters-manager.c                  \
+       ephy-filters-manager.h                  \
        ephy-form-auth-data.c                   \
        ephy-form-auth-data.h                   \
        ephy-gui.c                              \
diff --git a/lib/ephy-filters-manager.c b/lib/ephy-filters-manager.c
new file mode 100644
index 0000000..04504e1
--- /dev/null
+++ b/lib/ephy-filters-manager.c
@@ -0,0 +1,388 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
+/*
+ *  Copyright © 2017 Igalia S.L.
+ *
+ *  This file is part of Epiphany.
+ *
+ *  Epiphany 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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Epiphany 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 Epiphany.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "ephy-filters-manager.h"
+
+#include "ephy-prefs.h"
+#include "ephy-settings.h"
+#include "ephy-uri-tester-shared.h"
+
+#include <gio/gio.h>
+
+#define ADBLOCK_FILTER_UPDATE_FREQUENCY 24 * 60 * 60 /* In seconds */
+
+struct _EphyFiltersManager {
+  GObject parent_instance;
+
+  char *filters_dir;
+  GCancellable *cancellable;
+};
+
+G_DEFINE_TYPE (EphyFiltersManager, ephy_filters_manager, G_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_FILTERS_DIR,
+  N_PROPERTIES
+};
+
+static GParamSpec *object_properties[N_PROPERTIES] = { NULL, };
+
+static gboolean
+adblock_filter_file_is_valid (GFile *file)
+{
+  GFileInfo *file_info;
+  gboolean result = FALSE;
+
+  /* Now check if the local file is too old. */
+  file_info = g_file_query_info (file,
+                                 G_FILE_ATTRIBUTE_TIME_MODIFIED","G_FILE_ATTRIBUTE_STANDARD_SIZE,
+                                 G_FILE_QUERY_INFO_NONE,
+                                 NULL,
+                                 NULL);
+  if (file_info) {
+    if (g_file_info_get_size (file_info) > 0) {
+      GTimeVal current_time;
+      GTimeVal mod_time;
+
+      g_get_current_time (&current_time);
+      g_file_info_get_modification_time (file_info, &mod_time);
+
+      if (current_time.tv_sec > mod_time.tv_sec) {
+        gint64 expire_time = mod_time.tv_sec + ADBLOCK_FILTER_UPDATE_FREQUENCY;
+
+        result = current_time.tv_sec < expire_time;
+      }
+    }
+    g_object_unref (file_info);
+  }
+
+  return result;
+}
+
+typedef struct {
+  EphyFiltersManager *manager;
+
+  GFile *src_file;
+  GFile *filter_file;
+  GFile *tmp_file;
+} AdblockFilterRetrieveData;
+
+static AdblockFilterRetrieveData *
+adblock_filter_retrieve_data_new (EphyFiltersManager *manager,
+                                  GFile              *src_file,
+                                  GFile              *filter_file)
+{
+  AdblockFilterRetrieveData* data;
+  char *path, *tmp_path;
+
+  data = g_slice_new (AdblockFilterRetrieveData);
+  data->manager = g_object_ref (manager);
+  data->src_file = g_object_ref (src_file);
+  data->filter_file = g_object_ref (filter_file);
+
+  path = g_file_get_path (filter_file);
+  tmp_path = g_strdup_printf ("%s.tmp", path);
+  g_free (path);
+  data->tmp_file = g_file_new_for_path (tmp_path);
+  g_free (tmp_path);
+
+  return data;
+}
+
+static void
+adblock_filter_retrieve_data_free (AdblockFilterRetrieveData *data)
+{
+  g_object_unref (data->manager);
+  g_object_unref (data->src_file);
+  g_object_unref (data->filter_file);
+  g_object_unref (data->tmp_file);
+
+  g_slice_free (AdblockFilterRetrieveData, data);
+}
+
+static void
+retrieve_filter_file_finished (GFile                     *src,
+                               GAsyncResult              *result,
+                               AdblockFilterRetrieveData *data)
+{
+  GError *error = NULL;
+
+  if (!g_file_copy_finish (src, result, &error) ||
+      !g_file_move (data->tmp_file, data->filter_file, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &error)) {
+    GFileOutputStream *stream;
+
+    /* If failed to retrieve, create an empty file if it doesn't exist to unblock extensions */
+    stream = g_file_create (data->filter_file, G_FILE_CREATE_NONE, NULL, NULL);
+    if (stream)
+      g_object_unref (stream);
+
+    if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+      g_warning ("Error retrieving filter %s: %s\n", g_file_get_uri (data->src_file), error->message);
+    g_error_free (error);
+  }
+
+  adblock_filter_retrieve_data_free (data);
+}
+
+static void
+retrieve_filter_file (EphyFiltersManager *manager,
+                      const char         *filter_url,
+                      GFile              *file)
+{
+  GFile *src = g_file_new_for_uri (filter_url);
+  AdblockFilterRetrieveData *data;
+
+  data = adblock_filter_retrieve_data_new (manager, src, file);
+
+  g_file_copy_async (src, data->tmp_file,
+                     G_FILE_COPY_OVERWRITE,
+                     G_PRIORITY_DEFAULT,
+                     manager->cancellable,
+                     NULL, NULL,
+                     (GAsyncReadyCallback)retrieve_filter_file_finished,
+                     data);
+
+  g_object_unref (src);
+}
+
+static void
+remove_old_adblock_filters (EphyFiltersManager *manager,
+                            GList              *current_files)
+{
+  GFile *file;
+  GFile *filters_dir;
+  GFileEnumerator *enumerator;
+  gboolean current_filter;
+  GError *error = NULL;
+
+  filters_dir = g_file_new_for_path (manager->filters_dir);
+  enumerator = g_file_enumerate_children (filters_dir,
+                                          G_FILE_ATTRIBUTE_STANDARD_NAME,
+                                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                          NULL,
+                                          &error);
+  if (error != NULL) {
+    g_warning ("Failed to enumerate children of %s: %s", manager->filters_dir, error->message);
+    g_error_free (error);
+    g_object_unref (filters_dir);
+    return;
+  }
+
+  /* For each file in the adblock directory, check if it is a currently-enabled
+   * and remove it if not, since filter files can be quite large. */
+  for (;;) {
+    g_file_enumerator_iterate (enumerator, NULL, &file, NULL, &error);
+    if (error != NULL) {
+      g_warning ("Failed to iterate file enumerator for %s: %s", manager->filters_dir, error->message);
+      g_clear_error (&error);
+      continue;
+    }
+
+    /* Success: no more files left to iterate. */
+    if (file == NULL)
+      break;
+
+    current_filter = FALSE;
+    for (GList *l = current_files; l != NULL; l = l->next) {
+      if (g_file_equal (l->data, file)) {
+        current_filter = TRUE;
+        break;
+      }
+    }
+
+    if (!current_filter) {
+      g_file_delete (file, NULL, &error);
+      if (error != NULL) {
+        g_warning ("Failed to remove %s: %s", g_file_get_path (file), error->message);
+        g_clear_error (&error);
+      }
+    }
+  }
+
+  g_object_unref (filters_dir);
+  g_object_unref (enumerator);
+}
+
+static void
+update_adblock_filter_files (EphyFiltersManager *manager)
+{
+  char **filters;
+  GList *files = NULL;
+
+  /* One at a time please! */
+  if (manager->cancellable)
+    g_cancellable_cancel (manager->cancellable);
+  else
+    manager->cancellable = g_cancellable_new ();
+
+  if (!g_settings_get_boolean (EPHY_SETTINGS_WEB, EPHY_PREFS_WEB_ENABLE_ADBLOCK)) {
+    remove_old_adblock_filters (manager, files);
+    return;
+  }
+
+  filters = g_settings_get_strv (EPHY_SETTINGS_WEB, EPHY_PREFS_WEB_ADBLOCK_FILTERS);
+  for (guint i = 0; filters[i]; i++) {
+    GFile *filter_file;
+
+    filter_file = ephy_uri_tester_get_adblock_filter_file (manager->filters_dir, filters[i]);
+    if (!adblock_filter_file_is_valid (filter_file))
+      retrieve_filter_file (manager, filters[i], filter_file);
+    files = g_list_prepend (files, filter_file);
+  }
+
+  remove_old_adblock_filters (manager, files);
+
+  g_strfreev (filters);
+  g_list_free_full (files, g_object_unref);
+}
+
+static void
+adblock_filters_changed_cb (GSettings          *settings,
+                            char               *key,
+                            EphyFiltersManager *manager)
+{
+  update_adblock_filter_files (manager);
+}
+
+static void
+enable_adblock_changed_cb (GSettings          *settings,
+                           char               *key,
+                           EphyFiltersManager *manager)
+{
+  update_adblock_filter_files (manager);
+}
+
+static void
+ephy_filters_manager_dispose (GObject *object)
+{
+  EphyFiltersManager *manager = EPHY_FILTERS_MANAGER (object);
+
+  if (manager->cancellable) {
+    g_cancellable_cancel (manager->cancellable);
+    g_clear_object (&manager->cancellable);
+  }
+
+  G_OBJECT_CLASS (ephy_filters_manager_parent_class)->dispose (object);
+}
+
+static void
+ephy_filters_manager_finalize (GObject *object)
+{
+  EphyFiltersManager *manager = EPHY_FILTERS_MANAGER (object);
+
+  g_free (manager->filters_dir);
+
+  G_OBJECT_CLASS (ephy_filters_manager_parent_class)->finalize (object);
+}
+
+static void
+ephy_filters_manager_constructed (GObject *object)
+{
+  EphyFiltersManager *manager = EPHY_FILTERS_MANAGER (object);
+
+  G_OBJECT_CLASS (ephy_filters_manager_parent_class)->constructed (object);
+
+  /* Note: up here because we must connect *before* reading the settings. */
+  g_signal_connect (EPHY_SETTINGS_WEB, "changed::" EPHY_PREFS_WEB_ADBLOCK_FILTERS,
+                    G_CALLBACK (adblock_filters_changed_cb), manager);
+  g_signal_connect (EPHY_SETTINGS_WEB, "changed::" EPHY_PREFS_WEB_ENABLE_ADBLOCK,
+                    G_CALLBACK (enable_adblock_changed_cb), manager);
+
+  g_mkdir_with_parents (manager->filters_dir, 0700);
+  update_adblock_filter_files (manager);
+}
+
+static void
+ephy_filters_manager_set_property (GObject      *object,
+                                   guint         prop_id,
+                                   const GValue *value,
+                                   GParamSpec   *pspec)
+{
+  EphyFiltersManager *manager = EPHY_FILTERS_MANAGER (object);
+
+  switch (prop_id) {
+    case PROP_FILTERS_DIR:
+      manager->filters_dir = g_value_dup_string (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+ephy_filters_manager_get_property (GObject    *object,
+                                   guint       prop_id,
+                                   GValue     *value,
+                                   GParamSpec *pspec)
+{
+  EphyFiltersManager *manager = EPHY_FILTERS_MANAGER (object);
+
+  switch (prop_id) {
+    case PROP_FILTERS_DIR:
+      g_value_set_string (value, manager->filters_dir);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+ephy_filters_manager_class_init (EphyFiltersManagerClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->constructed = ephy_filters_manager_constructed;
+  object_class->dispose = ephy_filters_manager_dispose;
+  object_class->finalize = ephy_filters_manager_finalize;
+  object_class->set_property = ephy_filters_manager_set_property;
+  object_class->get_property = ephy_filters_manager_get_property;
+
+  object_properties[PROP_FILTERS_DIR] =
+    g_param_spec_string ("filters-dir",
+                         "Filters directory",
+                         "The directory in which adblock filters are saved",
+                         "",
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (object_class,
+                                     N_PROPERTIES,
+                                     object_properties);
+}
+
+static void
+ephy_filters_manager_init (EphyFiltersManager *manager)
+{
+}
+
+EphyFiltersManager *
+ephy_filters_manager_new (const char *filters_dir)
+{
+  return EPHY_FILTERS_MANAGER (g_object_new (EPHY_TYPE_FILTERS_MANAGER,
+                                            "filters-dir", filters_dir,
+                                            NULL));
+}
+
+const char *
+ephy_filters_manager_get_adblock_filters_dir (EphyFiltersManager *manager)
+{
+  return manager->filters_dir;
+}
diff --git a/lib/ephy-filters-manager.h b/lib/ephy-filters-manager.h
new file mode 100644
index 0000000..38fb53c
--- /dev/null
+++ b/lib/ephy-filters-manager.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
+/*
+ *  Copyright © 2017 Igalia S.L.
+ *
+ *  This file is part of Epiphany.
+ *
+ *  Epiphany 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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Epiphany 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 Epiphany.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_FILTERS_MANAGER (ephy_filters_manager_get_type ())
+
+G_DECLARE_FINAL_TYPE (EphyFiltersManager, ephy_filters_manager, EPHY, FILTERS_MANAGER, GObject)
+
+EphyFiltersManager *ephy_filters_manager_new                     (const char *adblock_filters_dir);
+const char         *ephy_filters_manager_get_adblock_filters_dir (EphyFiltersManager *manager);
+
+G_END_DECLS


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