diff options
Diffstat (limited to 'src/backend/plugins/config-osx/config-osx.c')
-rw-r--r-- | src/backend/plugins/config-osx/config-osx.c | 341 |
1 files changed, 341 insertions, 0 deletions
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; +} |