summaryrefslogtreecommitdiff
path: root/src/backend/px-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/px-manager.c')
-rw-r--r--src/backend/px-manager.c724
1 files changed, 724 insertions, 0 deletions
diff --git a/src/backend/px-manager.c b/src/backend/px-manager.c
new file mode 100644
index 0000000..1eab303
--- /dev/null
+++ b/src/backend/px-manager.c
@@ -0,0 +1,724 @@
+/* px-manager.c
+ *
+ * Copyright 2022-2023 The Libproxy Team
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+#include "px-backend-config.h"
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "px-manager.h"
+#include "px-plugin-config.h"
+#include "px-plugin-pacrunner.h"
+
+#ifdef HAVE_CONFIG_ENV
+#include <plugins/config-env/config-env.h>
+#endif
+
+#ifdef HAVE_CONFIG_GNOME
+#include <plugins/config-gnome/config-gnome.h>
+#endif
+
+#ifdef HAVE_CONFIG_KDE
+#include <plugins/config-kde/config-kde.h>
+#endif
+
+#ifdef HAVE_CONFIG_OSX
+#include <plugins/config-osx/config-osx.h>
+#endif
+
+#ifdef HAVE_CONFIG_SYSCONFIG
+#include <plugins/config-sysconfig/config-sysconfig.h>
+#endif
+
+#ifdef HAVE_CONFIG_WINDOWS
+#include <plugins/config-windows/config-windows.h>
+#endif
+
+#ifdef HAVE_PACRUNNER_DUKTAPE
+#include <plugins/pacrunner-duktape/pacrunner-duktape.h>
+#endif
+
+#ifdef HAVE_CURL
+#include <curl/curl.h>
+#endif
+
+enum {
+ PROP_0,
+ PROP_CONFIG_PLUGIN,
+ PROP_CONFIG_OPTION,
+ LAST_PROP
+};
+
+static GParamSpec *obj_properties[LAST_PROP];
+
+/**
+ * PxManager:
+ *
+ * Manage libproxy modules
+ */
+
+struct _PxManager {
+ GObject parent_instance;
+ GList *config_plugins;
+ GList *pacrunner_plugins;
+ GNetworkMonitor *network_monitor;
+#ifdef HAVE_CURL
+ CURL *curl;
+#endif
+
+ char *config_plugin;
+ char *config_option;
+
+ gboolean online;
+ gboolean wpad;
+ GBytes *pac_data;
+ char *pac_url;
+};
+
+G_DEFINE_TYPE (PxManager, px_manager, G_TYPE_OBJECT)
+
+G_DEFINE_QUARK (px - manager - error - quark, px_manager_error)
+
+static void
+px_manager_on_network_changed (GNetworkMonitor *monitor,
+ gboolean network_available,
+ gpointer user_data)
+{
+ PxManager *self = PX_MANAGER (user_data);
+
+ g_debug ("%s: Network connection changed, clearing pac data", __FUNCTION__);
+
+ self->wpad = FALSE;
+ self->online = network_available;
+ g_clear_pointer (&self->pac_url, g_free);
+ g_clear_pointer (&self->pac_data, g_bytes_unref);
+}
+
+static gint
+config_order_compare (gconstpointer a,
+ gconstpointer b)
+{
+ PxConfig *config_a = (PxConfig *)a;
+ PxConfig *config_b = (PxConfig *)b;
+ PxConfigInterface *ifc_a = PX_CONFIG_GET_IFACE (config_a);
+ PxConfigInterface *ifc_b = PX_CONFIG_GET_IFACE (config_b);
+
+ if (ifc_a->priority < ifc_b->priority)
+ return -1;
+
+ if (ifc_a->priority == ifc_b->priority)
+ return 0;
+
+ return 1;
+}
+
+static void
+px_manager_add_config_plugin (PxManager *self,
+ GType type)
+{
+ PxConfig *config = g_object_new (type, "config-option", self->config_option, NULL);
+ PxConfigInterface *ifc = PX_CONFIG_GET_IFACE (config);
+
+ if (!self->config_plugin || g_strcmp0 (ifc->name, self->config_plugin) == 0)
+ self->config_plugins = g_list_insert_sorted (self->config_plugins, config, config_order_compare);
+}
+
+static void
+px_manager_add_pacrunner_plugin (PxManager *self,
+ GType type)
+{
+ PxPacRunner *pacrunner = g_object_new (type, NULL);
+
+ self->pacrunner_plugins = g_list_append (self->pacrunner_plugins, pacrunner);
+}
+
+static void
+px_manager_constructed (GObject *object)
+{
+ PxManager *self = PX_MANAGER (object);
+
+ if (g_getenv ("PX_DEBUG")) {
+ const gchar *g_messages_debug;
+
+ g_messages_debug = g_getenv ("G_MESSAGES_DEBUG");
+
+ if (!g_messages_debug) {
+ g_setenv ("G_MESSAGES_DEBUG", G_LOG_DOMAIN, TRUE);
+ } else {
+ g_autofree char *new_g_messages_debug = NULL;
+
+ new_g_messages_debug = g_strconcat (g_messages_debug, " ", G_LOG_DOMAIN, NULL);
+ if (new_g_messages_debug)
+ g_setenv ("G_MESSAGES_DEBUG", new_g_messages_debug, TRUE);
+ }
+ }
+
+#ifdef HAVE_CONFIG_ENV
+ px_manager_add_config_plugin (self, PX_CONFIG_TYPE_ENV);
+#endif
+#ifdef HAVE_CONFIG_GNOME
+ px_manager_add_config_plugin (self, PX_CONFIG_TYPE_GNOME);
+#endif
+#ifdef HAVE_CONFIG_KDE
+ px_manager_add_config_plugin (self, PX_CONFIG_TYPE_KDE);
+#endif
+#ifdef HAVE_CONFIG_OSX
+ px_manager_add_config_plugin (self, PX_CONFIG_TYPE_OSX);
+#endif
+#ifdef HAVE_CONFIG_SYSCONFIG
+ px_manager_add_config_plugin (self, PX_CONFIG_TYPE_SYSCONFIG);
+#endif
+#ifdef HAVE_CONFIG_WINDOWS
+ px_manager_add_config_plugin (self, PX_CONFIG_TYPE_WINDOWS);
+#endif
+
+ g_debug ("Active config plugins:\n");
+ for (GList *list = self->config_plugins; list && list->data; list = list->next) {
+ PxConfig *config = list->data;
+ PxConfigInterface *ifc = PX_CONFIG_GET_IFACE (config);
+
+ g_debug (" - %s\n", ifc->name);
+ }
+
+#ifdef HAVE_PACRUNNER_DUKTAPE
+ px_manager_add_pacrunner_plugin (self, PX_PACRUNNER_TYPE_DUKTAPE);
+#endif
+
+ self->pac_data = NULL;
+
+ self->network_monitor = g_network_monitor_get_default ();
+ g_signal_connect_object (G_OBJECT (self->network_monitor), "network-changed", G_CALLBACK (px_manager_on_network_changed), self, 0);
+ px_manager_on_network_changed (self->network_monitor, g_network_monitor_get_network_available (self->network_monitor), self);
+ g_debug ("%s: Up and running", __FUNCTION__);
+}
+
+static void
+px_manager_dispose (GObject *object)
+{
+ PxManager *self = PX_MANAGER (object);
+
+ for (GList *list = self->config_plugins; list && list->data; list = list->next)
+ g_clear_object (&list->data);
+
+ for (GList *list = self->pacrunner_plugins; list && list->data; list = list->next)
+ g_clear_object (&list->data);
+
+ g_clear_pointer (&self->config_plugin, g_free);
+
+ G_OBJECT_CLASS (px_manager_parent_class)->dispose (object);
+}
+
+static void
+px_manager_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ PxManager *self = PX_MANAGER (object);
+
+ switch (prop_id) {
+ case PROP_CONFIG_PLUGIN:
+ self->config_plugin = g_strdup (g_value_get_string (value));
+ break;
+ case PROP_CONFIG_OPTION:
+ self->config_option = g_strdup (g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+px_manager_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ case PROP_CONFIG_PLUGIN:
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+px_manager_class_init (PxManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = px_manager_constructed;
+ object_class->dispose = px_manager_dispose;
+ object_class->set_property = px_manager_set_property;
+ object_class->get_property = px_manager_get_property;
+
+ obj_properties[PROP_CONFIG_PLUGIN] = g_param_spec_string ("config-plugin",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+ obj_properties[PROP_CONFIG_OPTION] = g_param_spec_string ("config-option",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
+}
+
+static void
+px_manager_init (PxManager *self)
+{
+}
+
+/**
+ * px_manager_new_with_options:
+ * @optname1: name of first property to set
+ * @...: value of @optname1, followed by additional property/value pairs
+ *
+ * Create a new `PxManager` with the specified options.
+ *
+ * Returns: the newly created `PxManager`
+ */
+PxManager *
+px_manager_new_with_options (const char *optname1,
+ ...)
+{
+ PxManager *self;
+ va_list ap;
+
+ va_start (ap, optname1);
+ self = (PxManager *)g_object_new_valist (PX_TYPE_MANAGER, optname1, ap);
+ va_end (ap);
+
+ return self;
+}
+
+/**
+ * px_manager_new:
+ *
+ * Create a new `PxManager`.
+ *
+ * Returns: the newly created `PxManager`
+ */
+PxManager *
+px_manager_new (void)
+{
+ return px_manager_new_with_options (NULL);
+}
+
+#ifdef HAVE_CURL
+static size_t
+store_data (void *contents,
+ size_t size,
+ size_t nmemb,
+ void *user_pointer)
+{
+ GByteArray *byte_array = user_pointer;
+ size_t real_size = size * nmemb;
+
+ g_byte_array_append (byte_array, contents, real_size);
+
+ return real_size;
+}
+#endif
+
+/**
+ * px_manager_pac_download:
+ * @self: a px manager
+ * @uri: PAC uri
+ *
+ * Downloads a PAC file from provided @url.
+ *
+ * Returns: (nullable): a newly created `GBytes` containing PAC data, or %NULL on error.
+ */
+GBytes *
+px_manager_pac_download (PxManager *self,
+ const char *uri)
+{
+#ifdef HAVE_CURL
+ GByteArray *byte_array = g_byte_array_new ();
+ CURLcode res;
+ const char *url = uri;
+
+ if (!self->curl)
+ self->curl = curl_easy_init ();
+
+ if (!self->curl)
+ return NULL;
+
+ if (g_str_has_prefix (url, "pac+"))
+ url += 4;
+
+ curl_easy_setopt (self->curl, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt (self->curl, CURLOPT_FOLLOWLOCATION, 1);
+ curl_easy_setopt (self->curl, CURLOPT_NOPROXY, "*");
+ curl_easy_setopt (self->curl, CURLOPT_CONNECTTIMEOUT, 30);
+ curl_easy_setopt (self->curl, CURLOPT_USERAGENT, "libproxy");
+
+ curl_easy_setopt (self->curl, CURLOPT_URL, url);
+ curl_easy_setopt (self->curl, CURLOPT_WRITEFUNCTION, store_data);
+ curl_easy_setopt (self->curl, CURLOPT_WRITEDATA, byte_array);
+
+ res = curl_easy_perform (self->curl);
+ if (res != CURLE_OK) {
+ g_debug ("%s: Could not download data: %s", __FUNCTION__, curl_easy_strerror (res));
+ return NULL;
+ }
+
+ return g_byte_array_free_to_bytes (byte_array);
+#else
+ return NULL;
+#endif
+}
+
+/**
+ * px_manager_get_configuration:
+ * @self: a px manager
+ * @uri: PAC uri
+ * @error: a #GError
+ *
+ * Get raw proxy configuration for gien @uri.
+ *
+ * Returns: (transfer full) (nullable): a newly created `GStrv` containing configuration data for @uri.
+ */
+char **
+px_manager_get_configuration (PxManager *self,
+ GUri *uri,
+ GError **error)
+{
+ g_autoptr (GStrvBuilder) builder = g_strv_builder_new ();
+
+ for (GList *list = self->config_plugins; list && list->data; list = list->next) {
+ PxConfig *config = PX_CONFIG (list->data);
+ PxConfigInterface *ifc = PX_CONFIG_GET_IFACE (config);
+
+ ifc->get_config (config, uri, builder);
+ }
+
+ return g_strv_builder_end (builder);
+}
+
+static void
+px_manager_run_pac (PxPacRunner *pacrunner,
+ GBytes *pac,
+ GUri *uri,
+ GStrvBuilder *builder)
+{
+ PxPacRunnerInterface *ifc = PX_PAC_RUNNER_GET_IFACE (pacrunner);
+ g_auto (GStrv) proxies_split = NULL;
+ char *pac_response;
+
+ if (!ifc->set_pac (PX_PAC_RUNNER (pacrunner), pac))
+ return;
+
+ pac_response = ifc->run (PX_PAC_RUNNER (pacrunner), uri);
+
+ /* Split line to handle multiple proxies */
+ proxies_split = g_strsplit (pac_response, ";", -1);
+
+ for (int idx = 0; idx < g_strv_length (proxies_split); idx++) {
+ char *line = g_strstrip (proxies_split[idx]);
+ g_auto (GStrv) word_split = g_strsplit (line, " ", -1);
+ g_autoptr (GUri) proxy_uri = NULL;
+ char *method;
+ char *server;
+
+ /* Check for syntax "METHOD SERVER" */
+ if (g_strv_length (word_split) == 2) {
+ g_autofree char *uri_string = NULL;
+ g_autofree char *proxy_string = NULL;
+
+ method = word_split[0];
+ server = word_split[1];
+
+ uri_string = g_strconcat ("http://", server, NULL);
+ proxy_uri = g_uri_parse (uri_string, G_URI_FLAGS_PARSE_RELAXED, NULL);
+ if (!proxy_uri)
+ continue;
+
+ if (g_ascii_strncasecmp (method, "proxy", 5) == 0) {
+ proxy_string = g_uri_to_string (proxy_uri);
+ } else if (g_ascii_strncasecmp (method, "socks4a", 7) == 0) {
+ proxy_string = g_strconcat ("socks4a://", server, NULL);
+ } else if (g_ascii_strncasecmp (method, "socks4", 6) == 0) {
+ proxy_string = g_strconcat ("socks4://", server, NULL);
+ } else if (g_ascii_strncasecmp (method, "socks5", 6) == 0) {
+ proxy_string = g_strconcat ("socks5://", server, NULL);
+ } else if (g_ascii_strncasecmp (method, "socks", 5) == 0) {
+ proxy_string = g_strconcat ("socks://", server, NULL);
+ }
+
+ px_strv_builder_add_proxy (builder, proxy_string);
+ } else {
+ /* Syntax not found, returning direct */
+ px_strv_builder_add_proxy (builder, "direct://");
+ }
+ }
+}
+
+static gboolean
+px_manager_expand_wpad (PxManager *self,
+ GUri *uri)
+{
+ const char *scheme = g_uri_get_scheme (uri);
+ gboolean ret = FALSE;
+
+ if (g_strcmp0 (scheme, "wpad") == 0) {
+ ret = TRUE;
+
+ if (!self->wpad) {
+ g_clear_pointer (&self->pac_data, g_bytes_unref);
+ g_clear_pointer (&self->pac_url, g_free);
+ self->wpad = TRUE;
+ }
+
+ if (!self->pac_data) {
+ GUri *wpad_url = g_uri_parse ("http://wpad/wpad.dat", G_URI_FLAGS_PARSE_RELAXED, NULL);
+
+ g_debug ("%s: Trying to find the PAC using WPAD...", __FUNCTION__);
+ self->pac_url = g_uri_to_string (wpad_url);
+ self->pac_data = px_manager_pac_download (self, self->pac_url);
+ if (!self->pac_data) {
+ g_clear_pointer (&self->pac_url, g_free);
+ ret = FALSE;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static gboolean
+px_manager_expand_pac (PxManager *self,
+ GUri *uri)
+{
+ gboolean ret = FALSE;
+ const char *scheme = g_uri_get_scheme (uri);
+
+ if (g_str_has_prefix (scheme, "pac+")) {
+ ret = TRUE;
+
+ if (self->wpad)
+ self->wpad = FALSE;
+
+ if (self->pac_data) {
+ g_autofree char *uri_str = g_uri_to_string (uri);
+
+ if (g_strcmp0 (self->pac_url, uri_str) != 0) {
+ g_clear_pointer (&self->pac_url, g_free);
+ g_clear_pointer (&self->pac_data, g_bytes_unref);
+ }
+ }
+
+ if (!self->pac_data) {
+ self->pac_url = g_uri_to_string (uri);
+ self->pac_data = px_manager_pac_download (self, self->pac_url);
+
+ if (!self->pac_data) {
+ g_warning ("%s: Unable to download PAC from %s while online = %d!", __FUNCTION__, self->pac_url, self->online);
+ g_clear_pointer (&self->pac_url, g_free);
+ ret = FALSE;
+ } else {
+ g_debug ("%s: PAC recevied!", __FUNCTION__);
+ }
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * px_manager_get_proxies_sync:
+ * @self: a px manager
+ * @url: a url
+ *
+ * Get proxies for giben @url.
+ *
+ * Returns: (transfer full) (nullable): a newly created `GStrv` containing proxy related information.
+ */
+char **
+px_manager_get_proxies_sync (PxManager *self,
+ const char *url,
+ GError **error)
+{
+ g_autoptr (GStrvBuilder) builder = g_strv_builder_new ();
+ g_autoptr (GUri) uri = g_uri_parse (url, G_URI_FLAGS_PARSE_RELAXED, error);
+ g_auto (GStrv) config = NULL;
+
+ g_debug ("%s: url=%s online=%d", __FUNCTION__, url ? url : "?", self->online);
+ if (!uri || !self->online) {
+ px_strv_builder_add_proxy (builder, "direct://");
+ return g_strv_builder_end (builder);
+ }
+
+ config = px_manager_get_configuration (self, uri, error);
+
+ for (int idx = 0; idx < g_strv_length (config); idx++) {
+ GUri *conf_url = g_uri_parse (config[idx], G_URI_FLAGS_PARSE_RELAXED, NULL);
+
+ g_debug ("%s: Config[%d] = %s", __FUNCTION__, idx, config[idx]);
+
+ if (px_manager_expand_wpad (self, conf_url) || px_manager_expand_pac (self, conf_url)) {
+ GList *list;
+
+ for (list = self->pacrunner_plugins; list && list->data; list = list->next) {
+ PxPacRunner *pacrunner = PX_PAC_RUNNER (list->data);
+
+ px_manager_run_pac (pacrunner, self->pac_data, uri, builder);
+ }
+ } else if (!g_str_has_prefix (g_uri_get_scheme (conf_url), "wpad") && !g_str_has_prefix (g_uri_get_scheme (conf_url), "pac+")) {
+ px_strv_builder_add_proxy (builder, g_uri_to_string (conf_url));
+ }
+ }
+
+ /* In case no proxy could be found, assume direct connection */
+ if (((GPtrArray *)builder)->len == 0)
+ px_strv_builder_add_proxy (builder, "direct://");
+
+ for (int idx = 0; idx < ((GPtrArray *)builder)->len; idx++)
+ g_debug ("%s: Proxy[%d] = %s", __FUNCTION__, idx, (char *)((GPtrArray *)builder)->pdata[idx]);
+
+ return g_strv_builder_end (builder);
+}
+
+void
+px_strv_builder_add_proxy (GStrvBuilder *builder,
+ const char *value)
+{
+ for (int idx = 0; idx < ((GPtrArray *)builder)->len; idx++) {
+ if (g_strcmp0 ((char *)((GPtrArray *)builder)->pdata[idx], value) == 0)
+ return;
+ }
+
+ g_strv_builder_add (builder, value);
+}
+
+static gboolean
+ignore_domain (GUri *uri,
+ char *ignore)
+{
+ g_auto (GStrv) ignore_split = g_strsplit (ignore, ":", -1);
+ const char *host = g_uri_get_host (uri);
+ char *ig_host;
+ int ig_port = -1;
+ int port = g_uri_get_port (uri);
+
+ /* Get our ignore pattern's hostname and port */
+ ig_host = ignore_split[0];
+ if (g_strv_length (ignore_split) == 2)
+ ig_port = atoi (ignore_split[1]);
+
+ /* Hostname match (domain.com or domain.com:80) */
+ if (g_strcmp0 (host, ig_host) == 0)
+ return (ig_port == -1 || port == ig_port);
+
+ /* Endswith (.domain.com or .domain.com:80) */
+ if (ig_host[0] == '.' && g_str_has_suffix (host, ig_host))
+ return (ig_port == -1 || port == ig_port);
+
+ /* Glob (*.domain.com or *.domain.com:80) */
+ if (ig_host[0] == '*' && g_str_has_suffix (host, ig_host + 1))
+ return (ig_port == -1 || port == ig_port);
+
+ /* No match was found */
+ return FALSE;
+}
+
+static gboolean
+ignore_hostname (GUri *uri,
+ char *ignore)
+{
+ const char *host = g_uri_get_host (uri);
+
+ if (g_strcmp0 (ignore, "<local>") == 0 && strchr (host, ':') == NULL && strchr (host, '.') == NULL)
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean
+ignore_ip (GUri *uri,
+ char *ignore)
+{
+ GInetAddress *inet_address1;
+ GInetAddress *inet_address2;
+ g_auto (GStrv) ignore_split = NULL;
+ gboolean is_ip1 = g_hostname_is_ip_address (g_uri_get_host (uri));
+ gboolean is_ip2 = g_hostname_is_ip_address (ignore);
+ int port = g_uri_get_port (uri);
+ int ig_port = -1;
+ gboolean result;
+
+ /*
+ * IPv4
+ * IPv6
+ */
+ if (!is_ip1 || !is_ip2)
+ return FALSE;
+
+ /*
+ * IPv4/CIDR
+ * IPv4/IPv4
+ * IPv6/CIDR
+ * IPv6/IPv6
+ */
+
+ /* MISSING */
+
+ /*
+ * IPv4:port
+ * [IPv6]:port
+ */
+ ignore_split = g_strsplit (ignore, ":", -1);
+ if (g_strv_length (ignore_split) == 2)
+ ig_port = atoi (ignore_split[1]);
+
+ inet_address1 = g_inet_address_new_from_string (g_uri_get_host (uri));
+ inet_address2 = g_inet_address_new_from_string (ignore);
+ result = g_inet_address_equal (inet_address1, inet_address2);
+
+ return port != -1 ? ((port == ig_port) && result) : result;
+}
+gboolean
+px_manager_is_ignore (GUri *uri,
+ GStrv ignores)
+{
+ if (!ignores)
+ return FALSE;
+
+ for (int idx = 0; idx < g_strv_length (ignores); idx++) {
+ if (ignore_hostname (uri, ignores[idx]))
+ return TRUE;
+
+ if (ignore_domain (uri, ignores[idx]))
+ return TRUE;
+
+ if (ignore_ip (uri, ignores[idx]))
+ return TRUE;
+ }
+
+ return FALSE;
+}