diff options
Diffstat (limited to 'src/backend/plugins')
22 files changed, 2094 insertions, 0 deletions
diff --git a/src/backend/plugins/config-env/config-env.c b/src/backend/plugins/config-env/config-env.c new file mode 100644 index 0000000..6a326e0 --- /dev/null +++ b/src/backend/plugins/config-env/config-env.c @@ -0,0 +1,167 @@ +/* config-env.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 <gio/gio.h> + +#include "config-env.h" + +#include "px-manager.h" +#include "px-plugin-config.h" + +static void px_config_iface_init (PxConfigInterface *iface); + +struct _PxConfigEnv { + GObject parent_instance; + + GStrv no_proxy; + const char *ftp_proxy; + const char *http_proxy; + const char *https_proxy; +}; + +G_DEFINE_FINAL_TYPE_WITH_CODE (PxConfigEnv, + px_config_env, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (PX_TYPE_CONFIG, px_config_iface_init)) + +enum { + PROP_0, + PROP_CONFIG_OPTION +}; + +static void +px_config_env_init (PxConfigEnv *self) +{ + const char *no_proxy; + + /* Collect data in init() to speed up get_config() calls */ + no_proxy = g_getenv ("no_proxy"); + if (!no_proxy) + no_proxy = g_getenv ("NO_PROXY"); + + if (no_proxy) + self->no_proxy = g_strsplit (no_proxy, ",", -1); + + self->ftp_proxy = g_getenv ("ftp_proxy"); + if (!self->ftp_proxy) + self->ftp_proxy = g_getenv ("FTP_PROXY"); + + self->https_proxy = g_getenv ("https_proxy"); + if (!self->https_proxy) + self->https_proxy = g_getenv ("HTTPS_PROXY"); + + self->http_proxy = g_getenv ("http_proxy"); + if (!self->http_proxy) + self->http_proxy = g_getenv ("HTTP_PROXY"); +} + +static void +px_config_env_dispose (GObject *object) +{ + PxConfigEnv *self = PX_CONFIG_ENV (object); + + g_clear_pointer (&self->no_proxy, g_strfreev); + + G_OBJECT_CLASS (px_config_env_parent_class)->dispose (object); +} + +static void +px_config_env_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) { + case PROP_CONFIG_OPTION: + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +px_config_env_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) { + case PROP_CONFIG_OPTION: + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +px_config_env_class_init (PxConfigEnvClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = px_config_env_dispose; + object_class->set_property = px_config_env_set_property; + object_class->get_property = px_config_env_get_property; + + g_object_class_override_property (object_class, PROP_CONFIG_OPTION, "config-option"); +} + +static gboolean +px_config_env_is_available (PxConfig *self) +{ + return TRUE; +} + +static void +px_config_env_get_config (PxConfig *config, + GUri *uri, + GStrvBuilder *builder) +{ + PxConfigEnv *self = PX_CONFIG_ENV (config); + const char *proxy = NULL; + const char *scheme = g_uri_get_scheme (uri); + + if (px_manager_is_ignore (uri, self->no_proxy)) + return; + + if (g_strcmp0 (scheme, "ftp") == 0) + proxy = self->ftp_proxy; + else if (g_strcmp0 (scheme, "https") == 0) + proxy = self->https_proxy; + + if (!proxy) + proxy = self->http_proxy; + + if (proxy) + px_strv_builder_add_proxy (builder, proxy); +} + +static void +px_config_iface_init (PxConfigInterface *iface) +{ + iface->name = "config-env"; + iface->priority = PX_CONFIG_PRIORITY_FIRST; + iface->is_available = px_config_env_is_available; + iface->get_config = px_config_env_get_config; +} diff --git a/src/backend/plugins/config-env/config-env.h b/src/backend/plugins/config-env/config-env.h new file mode 100644 index 0000000..6b654bd --- /dev/null +++ b/src/backend/plugins/config-env/config-env.h @@ -0,0 +1,34 @@ +/* config-env.h + * + * 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 + */ + +#pragma once + +#include <glib.h> + +G_BEGIN_DECLS + +#define PX_CONFIG_TYPE_ENV (px_config_env_get_type ()) + +G_DECLARE_FINAL_TYPE (PxConfigEnv, px_config_env, PX, CONFIG_ENV, GObject) + +G_END_DECLS + + diff --git a/src/backend/plugins/config-env/meson.build b/src/backend/plugins/config-env/meson.build new file mode 100644 index 0000000..00f3cc3 --- /dev/null +++ b/src/backend/plugins/config-env/meson.build @@ -0,0 +1,9 @@ +plugin_name = 'config-env' + +if get_option(plugin_name) + +px_backend_sources += [ + 'plugins/@0@/@0@.c'.format(plugin_name), +] + +endif
\ No newline at end of file diff --git a/src/backend/plugins/config-gnome/config-gnome.c b/src/backend/plugins/config-gnome/config-gnome.c new file mode 100644 index 0000000..9d6fd68 --- /dev/null +++ b/src/backend/plugins/config-gnome/config-gnome.c @@ -0,0 +1,246 @@ +/* config-gnome.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 <gio/gio.h> + +#include "config-gnome.h" + +#include "px-plugin-config.h" +#include "px-manager.h" + +struct _PxConfigGnome { + GObject parent_instance; + GSettings *proxy_settings; + GSettings *http_proxy_settings; + GSettings *https_proxy_settings; + GSettings *ftp_proxy_settings; + GSettings *socks_proxy_settings; + gboolean available; +}; + +typedef enum { + GNOME_PROXY_MODE_NONE, + GNOME_PROXY_MODE_MANUAL, + GNOME_PROXY_MODE_AUTO +} GnomeProxyMode; + +static void px_config_iface_init (PxConfigInterface *iface); + +G_DEFINE_FINAL_TYPE_WITH_CODE (PxConfigGnome, + px_config_gnome, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (PX_TYPE_CONFIG, px_config_iface_init)) + +enum { + PROP_0, + PROP_CONFIG_OPTION +}; + +static void +px_config_gnome_init (PxConfigGnome *self) +{ + GSettingsSchemaSource *source; + GSettingsSchema *proxy_schema; + const char *desktops; + + self->available = FALSE; + + desktops = getenv ("XDG_CURRENT_DESKTOP"); + if (!desktops) + return; + + /* Remember that XDG_CURRENT_DESKTOP is a list of strings. */ + if (strstr (desktops, "GNOME") == NULL) + return; + + source = g_settings_schema_source_get_default (); + if (!source) { + g_warning ("GNOME desktop detected but no schemes installed, aborting."); + return; + } + + proxy_schema = g_settings_schema_source_lookup (source, "org.gnome.system.proxy", TRUE); + + self->available = proxy_schema != NULL; + g_clear_pointer (&proxy_schema, g_settings_schema_unref); + + if (!self->available) + return; + + self->proxy_settings = g_settings_new ("org.gnome.system.proxy"); + self->http_proxy_settings = g_settings_new ("org.gnome.system.proxy.http"); + self->https_proxy_settings = g_settings_new ("org.gnome.system.proxy.https"); + self->ftp_proxy_settings = g_settings_new ("org.gnome.system.proxy.ftp"); + self->socks_proxy_settings = g_settings_new ("org.gnome.system.proxy.socks"); +} + +static void +px_config_gnome_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) { + case PROP_CONFIG_OPTION: + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +px_config_gnome_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) { + case PROP_CONFIG_OPTION: + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +px_config_gnome_class_init (PxConfigGnomeClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = px_config_gnome_set_property; + object_class->get_property = px_config_gnome_get_property; + + g_object_class_override_property (object_class, PROP_CONFIG_OPTION, "config-option"); +} + +static gboolean +px_config_gnome_is_available (PxConfig *config) +{ + PxConfigGnome *self = PX_CONFIG_GNOME (config); + + return self->available; +} + +static void +store_response (GStrvBuilder *builder, + const char *type, + char *host, + int port, + gboolean auth, + char *username, + char *password) +{ + if (type && host && strlen (type) > 0 && strlen (host) > 0 && port != 0) { + g_autoptr (GString) tmp = g_string_new (type); + + g_string_append (tmp, "://"); + if (auth) + g_string_append_printf (tmp, "%s:%s@", username, password); + + g_string_append_printf (tmp, "%s:%d", host, port); + + px_strv_builder_add_proxy (builder, tmp->str); + } +} + +static void +px_config_gnome_get_config (PxConfig *config, + GUri *uri, + GStrvBuilder *builder) +{ + PxConfigGnome *self = PX_CONFIG_GNOME (config); + g_autofree char *proxy = NULL; + GnomeProxyMode mode; + + if (!self->available) + return; + + if (px_manager_is_ignore (uri, g_settings_get_strv (self->proxy_settings, "ignore-hosts"))) + return; + + mode = g_settings_get_enum (self->proxy_settings, "mode"); + if (mode == GNOME_PROXY_MODE_AUTO) { + char *autoconfig_url = g_settings_get_string (self->proxy_settings, "autoconfig-url"); + + if (strlen (autoconfig_url) != 0) + proxy = g_strdup_printf ("pac+%s", autoconfig_url); + else + proxy = g_strdup ("wpad://"); + + px_strv_builder_add_proxy (builder, proxy); + } else if (mode == GNOME_PROXY_MODE_MANUAL) { + g_autofree char *username = g_settings_get_string (self->http_proxy_settings, "authentication-user"); + g_autofree char *password = g_settings_get_string (self->http_proxy_settings, "authentication-password"); + const char *scheme = g_uri_get_scheme (uri); + gboolean auth = g_settings_get_boolean (self->http_proxy_settings, "use-authentication"); + + if (g_strcmp0 (scheme, "http") == 0) { + g_autofree char *host = g_settings_get_string (self->http_proxy_settings, "host"); + store_response (builder, + "http", + host, + g_settings_get_int (self->http_proxy_settings, "port"), + auth, + username, + password); + } else if (g_strcmp0 (scheme, "https") == 0) { + g_autofree char *host = g_settings_get_string (self->https_proxy_settings, "host"); + store_response (builder, + "http", + host, + g_settings_get_int (self->https_proxy_settings, "port"), + auth, + username, + password); + } else if (g_strcmp0 (scheme, "ftp") == 0) { + g_autofree char *host = g_settings_get_string (self->ftp_proxy_settings, "host"); + store_response (builder, + "http", + host, + g_settings_get_int (self->ftp_proxy_settings, "port"), + auth, + username, + password); + } else { + g_autofree char *host = g_settings_get_string (self->socks_proxy_settings, "host"); + store_response (builder, + "socks", + host, + g_settings_get_int (self->socks_proxy_settings, "port"), + auth, + username, + password); + } + } +} + +static void +px_config_iface_init (PxConfigInterface *iface) +{ + iface->name = "config-gnome"; + iface->priority = PX_CONFIG_PRIORITY_DEFAULT; + iface->is_available = px_config_gnome_is_available; + iface->get_config = px_config_gnome_get_config; +} diff --git a/src/backend/plugins/config-gnome/config-gnome.h b/src/backend/plugins/config-gnome/config-gnome.h new file mode 100644 index 0000000..c5e8c6e --- /dev/null +++ b/src/backend/plugins/config-gnome/config-gnome.h @@ -0,0 +1,34 @@ +/* config-gnome.h + * + * 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 + */ + +#pragma once + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define PX_CONFIG_TYPE_GNOME (px_config_gnome_get_type ()) + +G_DECLARE_FINAL_TYPE (PxConfigGnome, px_config_gnome, PX, CONFIG_GNOME, GObject) + +G_END_DECLS + + diff --git a/src/backend/plugins/config-gnome/meson.build b/src/backend/plugins/config-gnome/meson.build new file mode 100644 index 0000000..e6a9b20 --- /dev/null +++ b/src/backend/plugins/config-gnome/meson.build @@ -0,0 +1,9 @@ +plugin_name = 'config-gnome' + +if get_option(plugin_name) + +px_backend_sources += [ + 'plugins/@0@/@0@.c'.format(plugin_name), +] + +endif
\ No newline at end of file diff --git a/src/backend/plugins/config-kde/config-kde.c b/src/backend/plugins/config-kde/config-kde.c new file mode 100644 index 0000000..ccec002 --- /dev/null +++ b/src/backend/plugins/config-kde/config-kde.c @@ -0,0 +1,303 @@ +/* config-kde.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 <gio/gio.h> + +#include "config-kde.h" + +#include "px-plugin-config.h" +#include "px-manager.h" + +static void px_config_iface_init (PxConfigInterface *iface); + +typedef enum { + KDE_PROXY_TYPE_NONE = 0, + KDE_PROXY_TYPE_MANUAL, + KDE_PROXY_TYPE_PAC, + KDE_PROXY_TYPE_WPAD, + KDE_PROXY_TYPE_SYSTEM, +} KdeProxyType; + +struct _PxConfigKde { + GObject parent_instance; + + char *config_file; + gboolean available; + GFileMonitor *monitor; + + GStrv no_proxy; + char *http_proxy; + char *https_proxy; + char *ftp_proxy; + char *socks_proxy; + KdeProxyType proxy_type; + char *pac_script; +}; + +G_DEFINE_FINAL_TYPE_WITH_CODE (PxConfigKde, + px_config_kde, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (PX_TYPE_CONFIG, px_config_iface_init)) + +enum { + PROP_0, + PROP_CONFIG_OPTION +}; + +static void px_config_kde_set_config_file (PxConfigKde *self, + char *proxy_file); + +static void +on_file_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + PxConfigKde *self = PX_CONFIG_KDE (user_data); + + g_debug ("%s: Reloading configuration\n", __FUNCTION__); + px_config_kde_set_config_file (self, g_file_get_path (file)); +} + +static void +px_config_kde_set_config_file (PxConfigKde *self, + char *proxy_file) +{ + g_autoptr (GError) error = NULL; + g_autofree char *line = NULL; + g_autoptr (GFile) file = NULL; + g_autoptr (GFileInputStream) istr = NULL; + g_autoptr (GDataInputStream) dstr = NULL; + const char *desktops; + + self->available = FALSE; + + desktops = getenv ("XDG_CURRENT_DESKTOP"); + if (!desktops) + return; + + /* Remember that XDG_CURRENT_DESKTOP is a list of strings. */ + if (strstr (desktops, "KDE") == NULL) + return; + + g_clear_pointer (&self->config_file, g_free); + self->config_file = proxy_file ? g_strdup (proxy_file) : g_build_filename (g_get_user_config_dir (), "kioslaverc", NULL); + + file = g_file_new_for_path (self->config_file); + if (!file) { + g_debug ("%s: Could not create file for %s", __FUNCTION__, self->config_file); + return; + } + + istr = g_file_read (file, NULL, NULL); + if (!istr) { + g_debug ("%s: Could not read file %s", __FUNCTION__, self->config_file); + return; + } + + dstr = g_data_input_stream_new (G_INPUT_STREAM (istr)); + if (!dstr) + return; + + g_clear_object (&self->monitor); + self->monitor = g_file_monitor (file, G_FILE_MONITOR_NONE, NULL, &error); + if (!self->monitor) + g_warning ("Could not add a file monitor for %s, error: %s", g_file_get_uri (file), error->message); + else + g_signal_connect_object (G_OBJECT (self->monitor), "changed", G_CALLBACK (on_file_changed), self, 0); + + do { + g_clear_pointer (&line, g_free); + + line = g_data_input_stream_read_line (dstr, NULL, NULL, &error); + if (line) { + g_auto (GStrv) kv = NULL; + g_autoptr (GString) value = NULL; + kv = g_strsplit (line, "=", 2); + + if (g_strv_length (kv) != 2) + continue; + + value = g_string_new (kv[1]); + g_string_replace (value, "\"", "", 0); + g_string_replace (value, " ", ":", 0); + + if (strcmp (kv[0], "httpsProxy") == 0) { + self->https_proxy = g_strdup (value->str); + } else if (strcmp (kv[0], "httpProxy") == 0) { + self->http_proxy = g_strdup (value->str); + } else if (strcmp (kv[0], "ftpProxy") == 0) { + self->ftp_proxy = g_strdup (value->str); + } else if (strcmp (kv[0], "socksProxy") == 0) { + self->socks_proxy = g_strdup (value->str); + } else if (strcmp (kv[0], "NoProxyFor") == 0) { + self->no_proxy = g_strsplit (value->str, ",", -1); + } else if (strcmp (kv[0], "Proxy Config Script") == 0) { + self->pac_script = g_strdup (value->str); + } else if (strcmp (kv[0], "ProxyType") == 0) { + self->proxy_type = atoi (value->str); + } + } + } while (line); + + self->available = TRUE; +} + + +static void +px_config_kde_init (PxConfigKde *self) +{ + px_config_kde_set_config_file (self, NULL); +} + +static void +px_config_kde_dispose (GObject *object) +{ + PxConfigKde *self = PX_CONFIG_KDE (object); + + g_clear_pointer (&self->config_file, g_free); + g_clear_object (&self->monitor); + g_clear_pointer (&self->no_proxy, g_strfreev); + g_clear_pointer (&self->http_proxy, g_free); + g_clear_pointer (&self->https_proxy, g_free); + g_clear_pointer (&self->ftp_proxy, g_free); + g_clear_pointer (&self->socks_proxy, g_free); + g_clear_pointer (&self->pac_script, g_free); + + G_OBJECT_CLASS (px_config_kde_parent_class)->dispose (object); +} + +static void +px_config_kde_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + PxConfigKde *config = PX_CONFIG_KDE (object); + + switch (prop_id) { + case PROP_CONFIG_OPTION: + px_config_kde_set_config_file (config, g_value_dup_string (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +px_config_kde_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + PxConfigKde *config = PX_CONFIG_KDE (object); + + switch (prop_id) { + case PROP_CONFIG_OPTION: + g_value_set_string (value, config->config_file); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +px_config_kde_class_init (PxConfigKdeClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = px_config_kde_dispose; + object_class->set_property = px_config_kde_set_property; + object_class->get_property = px_config_kde_get_property; + + g_object_class_override_property (object_class, PROP_CONFIG_OPTION, "config-option"); +} + +static gboolean +px_config_kde_is_available (PxConfig *config) +{ + PxConfigKde *self = PX_CONFIG_KDE (config); + + return self->available; +} + +static void +px_config_kde_get_config (PxConfig *config, + GUri *uri, + GStrvBuilder *builder) +{ + PxConfigKde *self = PX_CONFIG_KDE (config); + const char *scheme = g_uri_get_scheme (uri); + g_autofree char *proxy = NULL; + + if (!self->available) + return; + + if (!self->proxy_type) + return; + + if (px_manager_is_ignore (uri, self->no_proxy)) + return; + + switch (self->proxy_type) { + case KDE_PROXY_TYPE_MANUAL: + case KDE_PROXY_TYPE_SYSTEM: + /* System is the same as manual, except that a button for auto dection + * is shown. Based on this manual fields are set. + */ + if (g_strcmp0 (scheme, "ftp") == 0) { + proxy = g_strdup (self->ftp_proxy); + } else if (g_strcmp0 (scheme, "https") == 0) { + proxy = g_strdup (self->https_proxy); + } else if (g_strcmp0 (scheme, "http") == 0) { + proxy = g_strdup (self->http_proxy); + } else if (self->socks_proxy && strlen (self->socks_proxy) > 0) { + proxy = g_strdup (self->socks_proxy); + } + break; + case KDE_PROXY_TYPE_WPAD: + proxy = g_strdup ("wpad://"); + break; + case KDE_PROXY_TYPE_PAC: + proxy = g_strdup_printf ("pac+%s", self->pac_script); + break; + case KDE_PROXY_TYPE_NONE: + default: + break; + } + + if (proxy) + px_strv_builder_add_proxy (builder, proxy); +} + +static void +px_config_iface_init (PxConfigInterface *iface) +{ + iface->name = "config-kde"; + iface->priority = PX_CONFIG_PRIORITY_DEFAULT; + iface->is_available = px_config_kde_is_available; + iface->get_config = px_config_kde_get_config; +} diff --git a/src/backend/plugins/config-kde/config-kde.h b/src/backend/plugins/config-kde/config-kde.h new file mode 100644 index 0000000..892c45a --- /dev/null +++ b/src/backend/plugins/config-kde/config-kde.h @@ -0,0 +1,34 @@ +/* config-kde.h + * + * 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 + */ + +#pragma once + +#include <glib.h> + +G_BEGIN_DECLS + +#define PX_CONFIG_TYPE_KDE (px_config_kde_get_type ()) + +G_DECLARE_FINAL_TYPE (PxConfigKde, px_config_kde, PX, CONFIG_KDE, GObject) + +G_END_DECLS + + diff --git a/src/backend/plugins/config-kde/meson.build b/src/backend/plugins/config-kde/meson.build new file mode 100644 index 0000000..b34d78d --- /dev/null +++ b/src/backend/plugins/config-kde/meson.build @@ -0,0 +1,9 @@ +plugin_name = 'config-kde' + +if get_option(plugin_name) + +px_backend_sources += [ + 'plugins/@0@/@0@.c'.format(plugin_name), +] + +endif
\ No newline at end of file diff --git a/src/backend/plugins/config-osx/config-osx.c b/src/backend/plugins/config-osx/config-osx.c new file mode 100644 index 0000000..16813a0 --- /dev/null +++ b/src/backend/plugins/config-osx/config-osx.c @@ -0,0 +1,341 @@ +/* config-osx.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 <SystemConfiguration/SystemConfiguration.h> + +#include <gio/gio.h> + +#include "config-osx.h" + +#include "px-plugin-config.h" +#include "px-manager.h" + +static void px_config_iface_init (PxConfigInterface *iface); + +struct _PxConfigOsX { + GObject parent_instance; +}; + +G_DEFINE_FINAL_TYPE_WITH_CODE (PxConfigOsX, + px_config_osx, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (PX_TYPE_CONFIG, px_config_iface_init)) + +enum { + PROP_0, + PROP_CONFIG_OPTION +}; + +static void +px_config_osx_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) { + case PROP_CONFIG_OPTION: + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +px_config_osx_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +px_config_osx_init (PxConfigOsX *self) +{ +} + +static void +px_config_osx_class_init (PxConfigOsXClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = px_config_osx_set_property; + object_class->get_property = px_config_osx_get_property; + + g_object_class_override_property (object_class, PROP_CONFIG_OPTION, "config-option"); +} + +static gboolean +px_config_osx_is_available (PxConfig *self) +{ + return TRUE; +} + +static CFNumberRef +getobj (CFDictionaryRef settings, + char *key) +{ + CFStringRef k; + CFNumberRef retval; + + if (!settings) + return NULL; + + k = CFStringCreateWithCString (NULL, key, kCFStringEncodingMacRoman); + if (!k) + return NULL; + + retval = (CFNumberRef)CFDictionaryGetValue (settings, k); + + CFRelease (k); + return retval; +} + +static CFStringRef +getobj_str (CFDictionaryRef settings, + char *key) +{ + CFStringRef k; + CFStringRef retval; + + if (!settings) + return NULL; + + k = CFStringCreateWithCString (NULL, key, kCFStringEncodingMacRoman); + if (!k) + return NULL; + + retval = (CFStringRef)CFDictionaryGetValue (settings, k); + + CFRelease (k); + return retval; +} + +static CFArrayRef +getobj_array (CFDictionaryRef settings, + char *key) +{ + CFStringRef k; + CFArrayRef retval; + + if (!settings) + return NULL; + + k = CFStringCreateWithCString (NULL, key, kCFStringEncodingMacRoman); + if (!k) + return NULL; + + retval = (CFArrayRef)CFDictionaryGetValue (settings, k); + + CFRelease (k); + return retval; +} + +static char * +str (CFStringRef ref) +{ + CFIndex size = CFStringGetLength (ref) + 1; + char *ret = g_malloc0 (size); + + CFStringGetCString (ref, ret, size, kCFStringEncodingUTF8); + + return ret; +} + +static gboolean +getint (CFDictionaryRef settings, + char *key, + int64_t *answer) +{ + CFNumberRef n = getobj (settings, key); + + if (!n) + return FALSE; + + if (!CFNumberGetValue (n, kCFNumberSInt64Type, answer)) + return FALSE; + + return TRUE; +} + +static gboolean +getbool (CFDictionaryRef settings, + char *key) +{ + int64_t i = 0; + + if (!getint (settings, key, &i)) + return FALSE; + + return i != 0; +} + +static char * +str_to_upper (const char *str) +{ + char *ret = NULL; + int idx; + + if (!str) + return NULL; + + ret = g_malloc0 (strlen (str) + 1); + + for (idx = 0; idx < strlen (str); idx++) + ret[idx] = g_ascii_toupper (str[idx]); + + return ret; +} + +static char * +protocol_url (CFDictionaryRef settings, + char *protocol) +{ + g_autofree char *tmp = NULL; + g_autoptr (GString) ret = NULL; + g_autofree char *host = NULL; + int64_t port; + CFStringRef ref; + + /* Check ProtocolEnabled */ + tmp = g_strconcat (protocol, "Enable", NULL); + if (!getbool (settings, tmp)) { + g_debug ("%s: %s not set", __FUNCTION__, tmp); + return NULL; + } + g_clear_pointer (&tmp, g_free); + + /* Get ProtocolPort */ + tmp = g_strconcat (protocol, "Port", NULL); + getint (settings, tmp, &port); + if (!port) { + g_debug ("%s: %s not set", __FUNCTION__, tmp); + return NULL; + } + g_clear_pointer (&tmp, g_free); + + /* Get ProtocolProxy */ + tmp = g_strconcat (protocol, "Proxy", NULL); + ref = getobj_str (settings, tmp); + g_clear_pointer (&tmp, g_free); + + host = str (ref); + if (!host || strlen (host) == 0) + return NULL; + + if (strcmp (protocol, "HTTP") == 0 || strcmp (protocol, "HTTPS") == 0 || strcmp (protocol, "FTP") == 0 || strcmp (protocol, "Gopher") == 0) + ret = g_string_new ("http://"); + else if (strcmp (protocol, "RTSP") == 0) + ret = g_string_new ("rtsp://"); + else if (strcmp (protocol, "SOCKS") == 0) + ret = g_string_new ("socks://"); + else + return NULL; + + g_string_append_printf (ret, "%s:%lld", host, port); + + return g_strdup (ret->str); +} + +static GStrv +get_ignore_list (CFDictionaryRef proxies) +{ + CFArrayRef ref = getobj_array (proxies, "ExceptionsList"); + g_autoptr (GStrvBuilder) ret = g_strv_builder_new (); + + if (!ref) + return g_strv_builder_end (ret); + + for (int idx = 0; idx < CFArrayGetCount (ref); idx++) { + CFStringRef s = (CFStringRef)CFArrayGetValueAtIndex (ref, idx); + + px_strv_builder_add_proxy (ret, str (s)); + } + + if (getbool (proxies, "ExcludeSimpleHostnames")) + px_strv_builder_add_proxy (ret, "127.0.0.1"); + + return g_strv_builder_end (ret); +} + +static void +px_config_osx_get_config (PxConfig *self, + GUri *uri, + GStrvBuilder *builder) +{ + const char *proxy = NULL; + CFDictionaryRef proxies = SCDynamicStoreCopyProxies (NULL); + g_auto (GStrv) ignore_list = NULL; + + if (!proxies) { + g_warning ("Unable to fetch proxy configuration"); + return; + } + + ignore_list = get_ignore_list (proxies); + + if (px_manager_is_ignore (uri, ignore_list)) + return; + + if (getbool (proxies, "ProxyAutoDiscoveryEnable")) { + CFRelease (proxies); + px_strv_builder_add_proxy (builder, "wpad://"); + return; + } + + if (getbool (proxies, "ProxyAutoConfigEnable")) { + CFStringRef ref = getobj_str (proxies, "ProxyAutoConfigURLString"); + g_autofree char *tmp = str (ref); + GUri *tmp_uri = g_uri_parse (tmp, G_URI_FLAGS_PARSE_RELAXED, NULL); + + if (tmp_uri) { + g_autofree char *ret = g_strdup_printf ("pac+%s", g_uri_to_string (tmp_uri)); + CFRelease (proxies); + px_strv_builder_add_proxy (builder, ret); + return; + } + } else { + const char *scheme = g_uri_get_scheme (uri); + g_autofree char *capital_scheme = str_to_upper (scheme); + + proxy = protocol_url (proxies, capital_scheme); + + if (!proxy) + proxy = protocol_url (proxies, "SOCKS"); + } + + if (proxy) + px_strv_builder_add_proxy (builder, proxy); +} + +static void +px_config_iface_init (PxConfigInterface *iface) +{ + iface->name = "config-osx"; + iface->priority = PX_CONFIG_PRIORITY_DEFAULT; + iface->is_available = px_config_osx_is_available; + iface->get_config = px_config_osx_get_config; +} diff --git a/src/backend/plugins/config-osx/config-osx.h b/src/backend/plugins/config-osx/config-osx.h new file mode 100644 index 0000000..797b51e --- /dev/null +++ b/src/backend/plugins/config-osx/config-osx.h @@ -0,0 +1,34 @@ +/* config-osx.h + * + * 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 + */ + +#pragma once + +#include <glib.h> + +G_BEGIN_DECLS + +#define PX_CONFIG_TYPE_OSX (px_config_osx_get_type ()) + +G_DECLARE_FINAL_TYPE (PxConfigOsX, px_config_osx, PX, CONFIG_OSX, GObject) + +G_END_DECLS + + diff --git a/src/backend/plugins/config-osx/meson.build b/src/backend/plugins/config-osx/meson.build new file mode 100644 index 0000000..f801d90 --- /dev/null +++ b/src/backend/plugins/config-osx/meson.build @@ -0,0 +1,16 @@ +plugin_name = 'config-osx' + +if get_option(plugin_name) and with_platform_darwin + +foundation_dep = dependency('Foundation') +system_configuration_dep = dependency('SystemConfiguration') +px_backend_deps += [ + foundation_dep, + system_configuration_dep, +] + +px_backend_sources += [ + 'plugins/@0@/@0@.c'.format(plugin_name), +] + +endif diff --git a/src/backend/plugins/config-sysconfig/config-sysconfig.c b/src/backend/plugins/config-sysconfig/config-sysconfig.c new file mode 100644 index 0000000..c640f1c --- /dev/null +++ b/src/backend/plugins/config-sysconfig/config-sysconfig.c @@ -0,0 +1,252 @@ +/* config-sysconfig.c + * + * Copyright 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 <gio/gio.h> + +#include "config-sysconfig.h" + +#include "px-manager.h" +#include "px-plugin-config.h" + +struct _PxConfigSysConfig { + GObject parent_instance; + GFileMonitor *monitor; + + char *config_file; + gboolean available; + + gboolean proxy_enabled; + char *https_proxy; + char *http_proxy; + char *ftp_proxy; + GStrv no_proxy; +}; + +static void px_config_iface_init (PxConfigInterface *iface); + +G_DEFINE_FINAL_TYPE_WITH_CODE (PxConfigSysConfig, + px_config_sysconfig, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (PX_TYPE_CONFIG, px_config_iface_init)) + +enum { + PROP_0, + PROP_CONFIG_OPTION +}; + +static void px_config_sysconfig_set_config_file (PxConfigSysConfig *self, + const char *config_file); + +static void +on_file_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + PxConfigSysConfig *self = PX_CONFIG_SYSCONFIG (user_data); + + g_debug ("%s: Reloading configuration", __FUNCTION__); + px_config_sysconfig_set_config_file (self, g_file_get_path (file)); +} + +static +void +px_config_sysconfig_set_config_file (PxConfigSysConfig *self, + const char *config_file) +{ + g_autofree char *config = NULL; + g_autoptr (GFile) file = NULL; + g_autoptr (GError) error = NULL; + g_autoptr (GFileInputStream) istr = NULL; + g_autoptr (GDataInputStream) dstr = NULL; + char *line = NULL; + + g_clear_pointer (&self->config_file, g_free); + self->config_file = g_strdup (config_file ? config_file : "/etc/sysconfig/proxy"); + self->available = FALSE; + + file = g_file_new_for_path (self->config_file); + if (!file) { + g_debug ("%s: Could not create file for %s", __FUNCTION__, self->config_file); + return; + } + + istr = g_file_read (file, NULL, NULL); + if (!istr) { + g_debug ("%s: Could not read file %s", __FUNCTION__, self->config_file); + return; + } + + dstr = g_data_input_stream_new (G_INPUT_STREAM (istr)); + if (!dstr) + return; + + g_clear_object (&self->monitor); + self->monitor = g_file_monitor (file, G_FILE_MONITOR_NONE, NULL, &error); + if (!self->monitor) + g_warning ("Could not add a file monitor for %s, error: %s", g_file_get_uri (file), error->message); + else + g_signal_connect_object (G_OBJECT (self->monitor), "changed", G_CALLBACK (on_file_changed), self, 0); + + do { + g_clear_pointer (&line, g_free); + + line = g_data_input_stream_read_line (dstr, NULL, NULL, &error); + if (line) { + g_auto (GStrv) kv = NULL; + g_autoptr (GString) value = NULL; + kv = g_strsplit (line, "=", -1); + + if (g_strv_length (kv) != 2) + continue; + + value = g_string_new (kv[1]); + g_string_replace (value, "\"", "", 0); + g_string_replace (value, "\r", "", 0); + + if (strcmp (kv[0], "PROXY_ENABLED") == 0) { + self->proxy_enabled = g_ascii_strncasecmp (value->str, "yes", 3) == 0; + } else if (strcmp (kv[0], "HTTPS_PROXY") == 0) { + self->https_proxy = g_strdup (value->str); + } else if (strcmp (kv[0], "HTTP_PROXY") == 0) { + self->http_proxy = g_strdup (value->str); + } else if (strcmp (kv[0], "FTP_PROXY") == 0) { + self->ftp_proxy = g_strdup (value->str); + } else if (strcmp (kv[0], "NO_PROXY") == 0) { + g_autofree char *tmp = g_strdup (value->str); + self->no_proxy = g_strsplit (tmp, ",", -1); + } + } + } while (line); + + self->available = TRUE; +} + +static void +px_config_sysconfig_init (PxConfigSysConfig *self) +{ +} + +static void +px_config_sysconfig_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + PxConfigSysConfig *config = PX_CONFIG_SYSCONFIG (object); + + switch (prop_id) { + case PROP_CONFIG_OPTION: + px_config_sysconfig_set_config_file (config, g_value_dup_string (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +px_config_sysconfig_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + PxConfigSysConfig *config = PX_CONFIG_SYSCONFIG (object); + + switch (prop_id) { + case PROP_CONFIG_OPTION: + g_value_set_string (value, config->config_file); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +px_config_sysconfig_dispose (GObject *object) +{ + PxConfigSysConfig *self = PX_CONFIG_SYSCONFIG (object); + + g_clear_object (&self->monitor); + g_clear_pointer (&self->no_proxy, g_strfreev); + + G_OBJECT_CLASS (px_config_sysconfig_parent_class)->dispose (object); +} + +static void +px_config_sysconfig_class_init (PxConfigSysConfigClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = px_config_sysconfig_dispose; + object_class->set_property = px_config_sysconfig_set_property; + object_class->get_property = px_config_sysconfig_get_property; + + g_object_class_override_property (object_class, PROP_CONFIG_OPTION, "config-option"); +} + +static gboolean +px_config_sysconfig_is_available (PxConfig *config) +{ + PxConfigSysConfig *self = PX_CONFIG_SYSCONFIG (config); + + return self->available; +} + +static void +px_config_sysconfig_get_config (PxConfig *config, + GUri *uri, + GStrvBuilder *builder) +{ + PxConfigSysConfig *self = PX_CONFIG_SYSCONFIG (config); + const char *scheme = g_uri_get_scheme (uri); + g_autofree char *proxy = NULL; + + if (!self->proxy_enabled) + return; + + if (px_manager_is_ignore (uri, self->no_proxy)) + return; + + if (g_strcmp0 (scheme, "ftp") == 0) { + proxy = g_strdup (self->ftp_proxy); + } else if (g_strcmp0 (scheme, "https") == 0) { + proxy = g_strdup (self->https_proxy); + } else if (g_strcmp0 (scheme, "http") == 0) { + proxy = g_strdup (self->http_proxy); + } + + if (proxy) + px_strv_builder_add_proxy (builder, proxy); +} + +static void +px_config_iface_init (PxConfigInterface *iface) +{ + iface->name = "config-sysconfig"; + iface->priority = PX_CONFIG_PRIORITY_LAST; + iface->is_available = px_config_sysconfig_is_available; + iface->get_config = px_config_sysconfig_get_config; +} diff --git a/src/backend/plugins/config-sysconfig/config-sysconfig.h b/src/backend/plugins/config-sysconfig/config-sysconfig.h new file mode 100644 index 0000000..daa4672 --- /dev/null +++ b/src/backend/plugins/config-sysconfig/config-sysconfig.h @@ -0,0 +1,34 @@ +/* config-sysconfig.h + * + * 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 + */ + +#pragma once + +#include <glib.h> + +G_BEGIN_DECLS + +#define PX_CONFIG_TYPE_SYSCONFIG (px_config_sysconfig_get_type ()) + +G_DECLARE_FINAL_TYPE (PxConfigSysConfig, px_config_sysconfig, PX, CONFIG_SYSCONFIG, GObject) + +G_END_DECLS + + diff --git a/src/backend/plugins/config-sysconfig/meson.build b/src/backend/plugins/config-sysconfig/meson.build new file mode 100644 index 0000000..6d6789b --- /dev/null +++ b/src/backend/plugins/config-sysconfig/meson.build @@ -0,0 +1,9 @@ +plugin_name = 'config-sysconfig' + +if get_option(plugin_name) + +px_backend_sources += [ + 'plugins/@0@/@0@.c'.format(plugin_name), +] + +endif diff --git a/src/backend/plugins/config-windows/config-windows.c b/src/backend/plugins/config-windows/config-windows.c new file mode 100644 index 0000000..cc9c92d --- /dev/null +++ b/src/backend/plugins/config-windows/config-windows.c @@ -0,0 +1,252 @@ +/* config-windows.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 <windows.h> +#include <winreg.h> + +#include <gio/gio.h> + +#include "config-windows.h" + +#include "px-plugin-config.h" +#include "px-manager.h" + +#define W32REG_OFFSET_PAC (1 << 2) +#define W32REG_OFFSET_WPAD (1 << 3) +#define W32REG_BASEKEY "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" +#define W32REG_BUFFLEN 1024 + +struct _PxConfigWindows { + GObject parent_instance; +}; + +static void px_config_iface_init (PxConfigInterface *iface); + +G_DEFINE_FINAL_TYPE_WITH_CODE (PxConfigWindows, + px_config_windows, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (PX_TYPE_CONFIG, px_config_iface_init)) + +enum { + PROP_0, + PROP_CONFIG_OPTION +}; + +static void +px_config_windows_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) { + case PROP_CONFIG_OPTION: + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +px_config_windows_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +px_config_windows_init (PxConfigWindows *self) +{ +} + +static void +px_config_windows_class_init (PxConfigWindowsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = px_config_windows_set_property; + object_class->get_property = px_config_windows_get_property; + + g_object_class_override_property (object_class, PROP_CONFIG_OPTION, "config-option"); +} + +static gboolean +px_config_windows_is_available (PxConfig *self) +{ + return TRUE; +} + +static gboolean +get_registry (const char *key, + const char *name, + char **sval, + guint32 *slen, + guint32 *ival) +{ + HKEY hkey; + LONG result; + DWORD type; + DWORD buflen = W32REG_BUFFLEN; + BYTE buffer[W32REG_BUFFLEN]; + + if (sval && ival) + return FALSE; + + if (RegOpenKeyExA (HKEY_CURRENT_USER, key, 0, KEY_READ, &hkey) != ERROR_SUCCESS) + return FALSE; + + result = RegQueryValueExA (hkey, name, NULL, &type, buffer, &buflen); + RegCloseKey (hkey); + + if (result != ERROR_SUCCESS) + return FALSE; + + switch (type) { + case REG_BINARY: + case REG_EXPAND_SZ: + case REG_SZ: + if (!sval) + return FALSE; + if (slen) + *slen = buflen; + + *sval = g_malloc0 (buflen); + return memcpy (*sval, buffer, buflen) != NULL; + case REG_DWORD: + if (ival) + return memcpy (ival, buffer, buflen < sizeof (guint32) ? buflen : sizeof (guint32)) != NULL; + default: + break; + } + + return FALSE; +} + +static GHashTable * +parse_manual (char *manual) +{ + g_auto (GStrv) split = NULL; + GHashTable *ret = g_hash_table_new (g_str_hash, g_str_equal); + + /* We have to check for two formats: + * - 1.2.3.4:8080 + * - ftp=1.2.4.5:8080;https=1.2.3.4:8080 + */ + + split = g_strsplit (manual, ";", -1); + for (int idx = 0; idx < g_strv_length (split); idx++) { + if (!strchr (split[idx], '=')) { + g_hash_table_insert (ret, (char *)"http", g_strdup_printf ("http://%s", split[idx])); + } else { + g_auto (GStrv) split_kv = g_strsplit (split[idx], "=", -1); + + if (g_strv_length (split_kv) == 2) { + g_hash_table_insert (ret, g_strdup (split_kv[0]), g_strdup_printf ("%s://%s", split_kv[0], split_kv[1])); + } + } + } + + return ret; +} + +static gboolean +is_enabled (char type) +{ + g_autofree char *data = NULL; + guint32 dlen = 0; + gboolean result = FALSE; + + if (!get_registry (W32REG_BASEKEY "\\Connections", "DefaultConnectionSettings", &data, &dlen, NULL)) + return FALSE; + + if (dlen >= 9) + result = (data[8] & type) == type; + + return result; +} + +static void +px_config_windows_get_config (PxConfig *self, + GUri *uri, + GStrvBuilder *builder) +{ + char *tmp = NULL; + guint32 enabled = 0; + + if (get_registry (W32REG_BASEKEY, "ProxyOverride", &tmp, NULL, NULL)) { + g_auto (GStrv) no_proxy = g_strsplit (tmp, ",", -1); + + if (px_manager_is_ignore (uri, no_proxy)) + return; + } + + /* WPAD */ + if (is_enabled (W32REG_OFFSET_WPAD)) { + px_strv_builder_add_proxy (builder, "wpad://"); + return; + } + + /* PAC */ + if (is_enabled (W32REG_OFFSET_PAC) && get_registry (W32REG_BASEKEY, "AutoConfigURL", &tmp, NULL, NULL)) { + g_autofree char *pac_uri = g_strconcat ("pac+", tmp, NULL); + GUri *ac_uri = g_uri_parse (tmp, G_URI_FLAGS_PARSE_RELAXED, NULL); + + if (ac_uri) { + px_strv_builder_add_proxy (builder, pac_uri); + return; + } + } + + /* Manual proxy */ + if (get_registry (W32REG_BASEKEY, "ProxyEnable", NULL, NULL, &enabled) && enabled && get_registry (W32REG_BASEKEY, "ProxyServer", &tmp, NULL, NULL)) { + g_autoptr (GHashTable) table = parse_manual (tmp); + const char *scheme = g_uri_get_scheme (uri); + + if (table) { + char *ret = g_hash_table_lookup (table, scheme); + if (ret) { + px_strv_builder_add_proxy (builder, ret); + return; + } + + ret = g_hash_table_lookup (table, "socks"); + if (ret) { + px_strv_builder_add_proxy (builder, ret); + return; + } + } + } +} + +static void +px_config_iface_init (PxConfigInterface *iface) +{ + iface->name = "config-windows"; + iface->priority = PX_CONFIG_PRIORITY_DEFAULT; + iface->is_available = px_config_windows_is_available; + iface->get_config = px_config_windows_get_config; +} diff --git a/src/backend/plugins/config-windows/config-windows.h b/src/backend/plugins/config-windows/config-windows.h new file mode 100644 index 0000000..7c2afcf --- /dev/null +++ b/src/backend/plugins/config-windows/config-windows.h @@ -0,0 +1,34 @@ +/* config-windows.h + * + * Copyright 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 + */ + +#pragma once + +#include <glib.h> + +G_BEGIN_DECLS + +#define PX_CONFIG_TYPE_WINDOWS (px_config_windows_get_type ()) + +G_DECLARE_FINAL_TYPE (PxConfigWindows, px_config_windows, PX, CONFIG_WINDOWS, GObject) + +G_END_DECLS + + diff --git a/src/backend/plugins/config-windows/meson.build b/src/backend/plugins/config-windows/meson.build new file mode 100644 index 0000000..302a33e --- /dev/null +++ b/src/backend/plugins/config-windows/meson.build @@ -0,0 +1,9 @@ +plugin_name = 'config-windows' + +if get_option(plugin_name) and with_platform_windows + +px_backend_sources += [ + 'plugins/@0@/@0@.c'.format(plugin_name), +] + +endif
\ No newline at end of file diff --git a/src/backend/plugins/meson.build b/src/backend/plugins/meson.build new file mode 100644 index 0000000..c7943d2 --- /dev/null +++ b/src/backend/plugins/meson.build @@ -0,0 +1,18 @@ +subdir('config-env') +subdir('config-gnome') +subdir('config-kde') +subdir('config-osx') +subdir('config-sysconfig') +subdir('config-windows') + +subdir('pacrunner-duktape') + +summary({ + 'Configuration Environment' : get_option('config-env'), + 'Configuration GNOME ' : get_option('config-gnome'), + 'Configuration KDE ' : get_option('config-kde'), + 'Configuration Windows ' : get_option('config-windows'), + 'Configuration sysconfig ' : get_option('config-sysconfig'), + 'Configuration OS X ' : get_option('config-osx'), + 'PAC Runner Duktape ' : get_option('pacrunner-duktape'), +}, section: 'Plugins')
\ No newline at end of file diff --git a/src/backend/plugins/pacrunner-duktape/meson.build b/src/backend/plugins/pacrunner-duktape/meson.build new file mode 100644 index 0000000..f391b6d --- /dev/null +++ b/src/backend/plugins/pacrunner-duktape/meson.build @@ -0,0 +1,17 @@ +plugin_name = 'pacrunner-duktape' + +if get_option(plugin_name) + +duktape_dep = dependency('duktape') +m_dep = cc.find_library('m', required : false) + +px_backend_sources += [ + 'plugins/@0@/@0@.c'.format(plugin_name), +] + +px_backend_deps += [ + duktape_dep, + m_dep +] + +endif
\ No newline at end of file diff --git a/src/backend/plugins/pacrunner-duktape/pacrunner-duktape.c b/src/backend/plugins/pacrunner-duktape/pacrunner-duktape.c new file mode 100644 index 0000000..3676e48 --- /dev/null +++ b/src/backend/plugins/pacrunner-duktape/pacrunner-duktape.c @@ -0,0 +1,201 @@ +/* pacrunner-duktape.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 <gio/gio.h> + +#include <unistd.h> +#ifdef __WIN32__ +#include <ws2tcpip.h> +#else +#include <netdb.h> +#endif + +#include "pacrunner-duktape.h" +#include "pacutils.h" +#include "px-plugin-pacrunner.h" + +#include "duktape.h" + +struct _PxPacRunnerDuktape { + GObject parent_instance; + duk_context *ctx; +}; + +static void px_pacrunner_iface_init (PxPacRunnerInterface *iface); + +G_DEFINE_FINAL_TYPE_WITH_CODE (PxPacRunnerDuktape, + px_pacrunner_duktape, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (PX_TYPE_PACRUNNER, px_pacrunner_iface_init)) + + +static duk_ret_t +dns_resolve (duk_context *ctx) +{ + const char *hostname = NULL; + struct addrinfo *info; + char tmp[INET6_ADDRSTRLEN + 1]; + + if (duk_get_top (ctx) != 1) { + /* Invalid number of arguments */ + return 0; + } + + /* We do not need to free the string - It's managed by Duktape. */ + hostname = duk_get_string (ctx, 0); + if (!hostname) + return 0; + + /* Look it up */ + if (getaddrinfo (hostname, NULL, NULL, &info)) + return 0; + + /* Try for IPv4 */ + if (getnameinfo (info->ai_addr, + info->ai_addrlen, + tmp, + INET6_ADDRSTRLEN + 1, + NULL, + 0, + NI_NUMERICHOST)) { + freeaddrinfo (info); + duk_push_null (ctx); + return 1; + } + freeaddrinfo (info); + + /* Create the return value */ + duk_push_string (ctx, tmp); + + return 1; +} + +static duk_ret_t +my_ip_address (duk_context *ctx) +{ + char hostname[1024]; + + hostname[sizeof (hostname) - 1] = '\0'; + + if (!gethostname (hostname, sizeof (hostname) - 1)) { + duk_push_string (ctx, hostname); + return dns_resolve (ctx); + } + + return duk_error (ctx, DUK_ERR_ERROR, "Unable to find hostname!"); +} + +static void +px_pacrunner_duktape_init (PxPacRunnerDuktape *self) +{ + self->ctx = duk_create_heap_default (); + if (!self->ctx) + return; + + duk_push_c_function (self->ctx, dns_resolve, 1); + duk_put_global_string (self->ctx, "dnsResolve"); + + duk_push_c_function (self->ctx, my_ip_address, 1); + duk_put_global_string (self->ctx, "myIpAddress"); + + duk_push_string (self->ctx, JAVASCRIPT_ROUTINES); + if (duk_peval_noresult (self->ctx)) + goto error; + + return; + +error: + duk_destroy_heap (self->ctx); +} + +static void +px_pacrunner_duktape_dispose (GObject *object) +{ + PxPacRunnerDuktape *self = PX_PACRUNNER_DUKTAPE (object); + + g_clear_pointer (&self->ctx, duk_destroy_heap); + + G_OBJECT_CLASS (px_pacrunner_duktape_parent_class)->dispose (object); +} + +static void +px_pacrunner_duktape_class_init (PxPacRunnerDuktapeClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = px_pacrunner_duktape_dispose; +} + +static gboolean +px_pacrunner_duktape_set_pac (PxPacRunner *pacrunner, + GBytes *pac_data) +{ + PxPacRunnerDuktape *self = PX_PACRUNNER_DUKTAPE (pacrunner); + gsize len; + gconstpointer content = g_bytes_get_data (pac_data, &len); + + duk_push_lstring (self->ctx, content, len); + + if (duk_peval_noresult (self->ctx)) { + return FALSE; + } + + return TRUE; +} + +static char * +px_pacrunner_duktape_run (PxPacRunner *pacrunner, + GUri *uri) +{ + PxPacRunnerDuktape *self = PX_PACRUNNER_DUKTAPE (pacrunner); + duk_int_t result; + + duk_get_global_string (self->ctx, "FindProxyForURL"); + duk_push_string (self->ctx, g_uri_to_string (uri)); + duk_push_string (self->ctx, g_uri_get_host (uri)); + result = duk_pcall (self->ctx, 2); + + if (result == 0) { + const char *proxy = duk_get_string (self->ctx, 0); + char *proxy_string; + + if (!proxy) { + duk_pop (self->ctx); + return g_strdup (""); + } + + proxy_string = g_strdup (proxy); + + duk_pop (self->ctx); + + return proxy_string; + } + + duk_pop (self->ctx); + return g_strdup (""); +} + +static void +px_pacrunner_iface_init (PxPacRunnerInterface *iface) +{ + iface->set_pac = px_pacrunner_duktape_set_pac; + iface->run = px_pacrunner_duktape_run; +} diff --git a/src/backend/plugins/pacrunner-duktape/pacrunner-duktape.h b/src/backend/plugins/pacrunner-duktape/pacrunner-duktape.h new file mode 100644 index 0000000..8721a62 --- /dev/null +++ b/src/backend/plugins/pacrunner-duktape/pacrunner-duktape.h @@ -0,0 +1,32 @@ +/* pacrunner-duktape.h + * + * 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 + */ + +#pragma once + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define PX_PACRUNNER_TYPE_DUKTAPE (px_pacrunner_duktape_get_type ()) + +G_DECLARE_FINAL_TYPE (PxPacRunnerDuktape, px_pacrunner_duktape, PX, PACRUNNER_DUKTAPE, GObject) + +G_END_DECLS |