summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Catanzaro <mcatanzaro@redhat.com>2022-02-19 09:03:46 -0600
committerMichael Catanzaro <mcatanzaro@redhat.com>2022-07-21 18:09:40 -0500
commit06e83aa84267b154ce2aa76dc18412c9c9839a68 (patch)
treefd600446d72a4ca9761fe93a5012e3422c2218af
parentaf191b187e7217e4104e635bd471a1ae89f11650 (diff)
downloadglib-networking-mcatanzaro/pacrunner.tar.gz
Rewrite pacrunner to not use libproxymcatanzaro/pacrunner
Red Hat wants to get rid of libproxy, but we do not want to get rid of proxy autoconfig. So let's do it ourselves. It's not hard. Fixes #163, fixes #28, fixes #5
-rw-r--r--meson.build9
-rw-r--r--meson_options.txt1
-rw-r--r--proxy/libproxy/glibpacrunner.c173
-rw-r--r--proxy/libproxy/meson.build37
-rw-r--r--proxy/meson.build4
-rw-r--r--proxy/pacrunner/glib-pacrunner.service.in (renamed from proxy/libproxy/glib-pacrunner.service.in)2
-rw-r--r--proxy/pacrunner/meson.build54
-rw-r--r--proxy/pacrunner/org.gtk.GLib.PACRunner.service.in (renamed from proxy/libproxy/org.gtk.GLib.PACRunner.service.in)2
-rw-r--r--proxy/pacrunner/pacrunner-service.c275
-rw-r--r--proxy/pacrunner/pacrunner-worker.c359
-rw-r--r--proxy/pacrunner/pacutils.h242
-rw-r--r--tls/base/meson.build3
-rw-r--r--tls/gnutls/gtlsdatabase-gnutls.c1
-rw-r--r--util/ghttp.c (renamed from tls/base/gtlshttp.c)10
-rw-r--r--util/ghttp.h (renamed from tls/base/gtlshttp.h)6
-rw-r--r--util/meson.build12
16 files changed, 965 insertions, 225 deletions
diff --git a/meson.build b/meson.build
index c816dc6..2e05c7b 100644
--- a/meson.build
+++ b/meson.build
@@ -21,6 +21,7 @@ host_system = host_machine.system()
config_h = configuration_data()
config_h.set_quoted('GETTEXT_PACKAGE', meson.project_name())
+config_h.set_quoted('LIBEXEC_DIR', libexecdir)
config_h.set10('ENABLE_DEBUG_LOGS', get_option('debug_logs'))
# compiler flags
@@ -49,7 +50,7 @@ if host_system.contains('linux') or host_system == 'android'
endif
# *** Check GLib GIO ***
-glib_dep = dependency('glib-2.0', version: '>= 2.69.0',
+glib_dep = dependency('glib-2.0', version: '>= 2.70.0',
fallback: ['glib', 'libglib_dep'])
gio_dep = dependency('gio-2.0',
fallback: ['glib', 'libgio_dep'])
@@ -74,6 +75,9 @@ libproxy_dep = dependency('libproxy-1.0', version: '>= 0.4.16', required: get_op
# *** Checks for GNOME ***
gsettings_desktop_schemas_dep = dependency('gsettings-desktop-schemas', required: get_option('gnome_proxy'))
+# *** Checks for WebKitGTK ***
+javascriptcoregtk_dep = dependency('javascriptcoregtk-4.1', required: get_option('pacrunner'))
+
backends = []
# *** Check for dl ***
@@ -137,9 +141,10 @@ endif
proxy_test_programs = []
-subdir('po')
+subdir('util')
subdir('proxy')
subdir('tls')
+subdir('po')
# Will automatically pick it up from the cross file if defined
gio_querymodules = find_program('gio-querymodules', required : false)
diff --git a/meson_options.txt b/meson_options.txt
index f022e3d..e31c87f 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -5,6 +5,7 @@ option('gnutls', type: 'feature', value: 'auto', description: 'support for GnuTL
option('openssl', type: 'feature', value: 'disabled', description: 'support for OpenSSL networking configration')
option('libproxy', type: 'feature', value: 'auto', description: 'support for libproxy proxy configration')
option('gnome_proxy', type: 'feature', value: 'auto', description: 'support for GNOME desktop proxy configuration')
+option('pacrunner', type: 'feature', value: 'auto', description: 'support for web proxy autoconfig')
option('installed_tests', type: 'boolean', value: false, description: 'enable installed tests')
option('static_modules', type: 'boolean', value: false, description: 'build static modules')
option('debug_logs', type: 'boolean', value: false, description: 'enable debug log messages (slow)')
diff --git a/proxy/libproxy/glibpacrunner.c b/proxy/libproxy/glibpacrunner.c
deleted file mode 100644
index c72304f..0000000
--- a/proxy/libproxy/glibpacrunner.c
+++ /dev/null
@@ -1,173 +0,0 @@
-/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/*
- * GIO - GLib Input, Output and Streaming Library
- *
- * Copyright 2011 Red Hat, Inc.
- *
- * 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, see
- * <http://www.gnu.org/licenses/>.
- */
-
-#include "config.h"
-
-#include <stdlib.h>
-
-#include <gio/gio.h>
-#include "glibproxyresolver.h"
-
-static const gchar introspection_xml[] =
- "<node>"
- " <interface name='org.gtk.GLib.PACRunner'>"
- " <method name='Lookup'>"
- " <arg type='s' name='pac_url' direction='in'/>"
- " <arg type='s' name='lookup_url' direction='in'/>"
- " <arg type='as' name='proxies' direction='out'/>"
- " </method>"
- " </interface>"
- "</node>";
-
-static GProxyResolver *resolver;
-static GMainLoop *loop;
-
-static void
-got_proxies (GObject *source,
- GAsyncResult *result,
- gpointer user_data)
-{
- GDBusMethodInvocation *invocation = user_data;
- gchar **proxies;
- GError *error = NULL;
-
- proxies = g_proxy_resolver_lookup_finish (resolver, result, &error);
- if (error)
- g_dbus_method_invocation_take_error (invocation, error);
- else
- {
- g_dbus_method_invocation_return_value (invocation,
- g_variant_new ("(^as)", proxies));
- g_strfreev (proxies);
- }
-}
-
-static void
-handle_method_call (GDBusConnection *connection,
- const gchar *sender,
- const gchar *object_path,
- const gchar *interface_name,
- const gchar *method_name,
- GVariant *parameters,
- GDBusMethodInvocation *invocation,
- gpointer user_data)
-{
- const gchar *pac_url, *lookup_url;
-
- g_variant_get (parameters, "(&s&s)", &pac_url, &lookup_url);
-
- if (!g_ascii_strncasecmp (pac_url, "http", 4) ||
- !g_ascii_strncasecmp (pac_url, "file:", 5))
- {
- gchar *libproxy_url = g_strdup_printf ("pac+%s", pac_url);
- g_setenv ("http_proxy", libproxy_url, TRUE);
- g_free (libproxy_url);
- }
- else
- g_setenv ("http_proxy", "wpad://", TRUE);
-
- g_proxy_resolver_lookup_async (resolver, lookup_url,
- NULL, got_proxies, invocation);
-}
-
-static const GDBusInterfaceVTable interface_vtable =
- {
- handle_method_call,
- NULL,
- NULL
- };
-
-static void
-on_bus_acquired (GDBusConnection *connection,
- const gchar *name,
- gpointer user_data)
-{
- GDBusNodeInfo *introspection_data;
- GError *error = NULL;
-
- introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
- g_dbus_connection_register_object (connection,
- "/org/gtk/GLib/PACRunner",
- introspection_data->interfaces[0],
- &interface_vtable,
- NULL,
- NULL,
- &error);
- if (error)
- g_error ("Could not register server: %s", error->message);
-}
-
-static void
-on_name_acquired (GDBusConnection *connection,
- const gchar *name,
- gpointer user_data)
-{
-}
-
-static void
-on_name_lost (GDBusConnection *connection,
- const gchar *name,
- gpointer user_data)
-{
- g_main_loop_quit (loop);
-}
-
-int
-main (int argc, char *argv[])
-{
- int owner_id;
-
- /* Unset variables that would make libproxy try to use gconf or ksettings */
- g_unsetenv ("GNOME_DESKTOP_SESSION_ID");
- g_unsetenv ("DESKTOP_SESSION");
- g_unsetenv ("KDE_FULL_SESSION");
-
- /* Unset variables that libproxy would look at if it were smarter, and which
- * it might possibly look at in the future. Just covering our bases. */
- g_unsetenv ("XDG_CURRENT_DESKTOP");
-
- /* Unset static proxy settings */
- g_unsetenv ("http_proxy");
- g_unsetenv ("HTTP_PROXY");
- g_unsetenv ("https_proxy");
- g_unsetenv ("HTTPS_PROXY");
- g_unsetenv ("ftp_proxy");
- g_unsetenv ("FTP_PROXY");
- g_unsetenv ("no_proxy");
- g_unsetenv ("NO_PROXY");
-
- resolver = g_object_new (G_TYPE_LIBPROXY_RESOLVER, NULL);
-
- owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
- "org.gtk.GLib.PACRunner",
- G_BUS_NAME_OWNER_FLAGS_NONE,
- on_bus_acquired,
- on_name_acquired,
- on_name_lost,
- NULL,
- NULL);
-
- loop = g_main_loop_new (NULL, FALSE);
- g_main_loop_run (loop);
-
- g_bus_unown_name (owner_id);
- return 0;
-}
diff --git a/proxy/libproxy/meson.build b/proxy/libproxy/meson.build
index 4db3d48..cc0f62c 100644
--- a/proxy/libproxy/meson.build
+++ b/proxy/libproxy/meson.build
@@ -1,26 +1,3 @@
-service_conf = configuration_data()
-service_conf.set('libexecdir', libexecdir)
-
-service = 'org.gtk.GLib.PACRunner.service'
-
-configure_file(
- input: service + '.in',
- output: service,
- install: true,
- install_dir: join_paths(datadir, 'dbus-1', 'services'),
- configuration: service_conf
-)
-
-service = 'glib-pacrunner.service'
-
-configure_file(
- input: service + '.in',
- output: service,
- install: true,
- install_dir: join_paths('lib', 'systemd', 'user'),
- configuration: service_conf
-)
-
sources = files(
'glibproxyresolver.c',
'libproxy-module.c'
@@ -59,20 +36,6 @@ if get_option('static_modules')
meson.override_dependency('giolibproxy', giolibproxy_dep)
endif
-sources = files(
- 'glibproxyresolver.c',
- 'glibpacrunner.c'
-)
-
-executable(
- 'glib-pacrunner',
- sources,
- include_directories: top_inc,
- dependencies: deps,
- install: true,
- install_dir: libexecdir
-)
-
proxy_test_programs += [['environment', 'libproxy', deps]]
if meson.version().version_compare('>=0.58')
diff --git a/proxy/meson.build b/proxy/meson.build
index c638452..5ab3200 100644
--- a/proxy/meson.build
+++ b/proxy/meson.build
@@ -12,4 +12,8 @@ if not ['windows'].contains(host_system)
subdir('environment')
endif
+if javascriptcoregtk_dep.found()
+ subdir('pacrunner')
+endif
+
subdir('tests')
diff --git a/proxy/libproxy/glib-pacrunner.service.in b/proxy/pacrunner/glib-pacrunner.service.in
index 0f289de..6f977d8 100644
--- a/proxy/libproxy/glib-pacrunner.service.in
+++ b/proxy/pacrunner/glib-pacrunner.service.in
@@ -4,4 +4,4 @@ Description=GLib proxy auto-configuration service
[Service]
Type=dbus
BusName=org.gtk.GLib.PACRunner
-ExecStart=@libexecdir@/glib-pacrunner
+ExecStart=@libexecdir@/glib-pacrunner-service
diff --git a/proxy/pacrunner/meson.build b/proxy/pacrunner/meson.build
new file mode 100644
index 0000000..1f513a3
--- /dev/null
+++ b/proxy/pacrunner/meson.build
@@ -0,0 +1,54 @@
+service_conf = configuration_data()
+service_conf.set('libexecdir', libexecdir)
+
+service = 'org.gtk.GLib.PACRunner.service'
+
+configure_file(
+ input: service + '.in',
+ output: service,
+ install: true,
+ install_dir: join_paths(datadir, 'dbus-1', 'services'),
+ configuration: service_conf
+)
+
+service = 'glib-pacrunner.service'
+
+configure_file(
+ input: service + '.in',
+ output: service,
+ install: true,
+ install_dir: join_paths('lib', 'systemd', 'user'),
+ configuration: service_conf
+)
+
+service_deps = [
+ gio_dep,
+ glib_dep,
+ gobject_dep,
+]
+
+executable(
+ 'glib-pacrunner-service',
+ 'pacrunner-service.c',
+ include_directories: top_inc,
+ dependencies: service_deps,
+ install: true,
+ install_dir: libexecdir
+)
+
+worker_deps = [
+ gio_dep,
+ glib_dep,
+ gobject_dep,
+ javascriptcoregtk_dep,
+ util_dep
+]
+
+executable(
+ 'glib-pacrunner-worker',
+ 'pacrunner-worker.c',
+ include_directories: top_inc,
+ dependencies: worker_deps,
+ install: true,
+ install_dir: libexecdir
+)
diff --git a/proxy/libproxy/org.gtk.GLib.PACRunner.service.in b/proxy/pacrunner/org.gtk.GLib.PACRunner.service.in
index f1bd699..d8230f8 100644
--- a/proxy/libproxy/org.gtk.GLib.PACRunner.service.in
+++ b/proxy/pacrunner/org.gtk.GLib.PACRunner.service.in
@@ -1,4 +1,4 @@
[D-BUS Service]
Name=org.gtk.GLib.PACRunner
-Exec=@libexecdir@/glib-pacrunner
+Exec=@libexecdir@/glib-pacrunner-service
SystemdService=glib-pacrunner.service
diff --git a/proxy/pacrunner/pacrunner-service.c b/proxy/pacrunner/pacrunner-service.c
new file mode 100644
index 0000000..0eea920
--- /dev/null
+++ b/proxy/pacrunner/pacrunner-service.c
@@ -0,0 +1,275 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright © 2011, 2022 Red Hat, Inc.
+ *
+ * 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, see
+ * <http://www.gnu.org/licenses/>.
+ */
+#include "config.h"
+
+#include <gio/gio.h>
+#include <glib/gi18n.h>
+#include <locale.h>
+#include <stdlib.h>
+
+static const gchar introspection_xml[] =
+ "<node>"
+ " <interface name='org.gtk.GLib.PACRunner'>"
+ " <method name='Lookup'>"
+ " <arg type='s' name='pac_url' direction='in'/>"
+ " <arg type='s' name='lookup_url' direction='in'/>"
+ " <arg type='as' name='proxies' direction='out'/>"
+ " </method>"
+ " </interface>"
+ "</node>";
+
+static GMainLoop *loop;
+static GCancellable *cancellable;
+
+static void
+add_to_results_if_valid (const char *scheme,
+ const char *server,
+ GPtrArray *results)
+{
+ char *url_string;
+
+ if (!server || !*server)
+ return;
+
+ url_string = g_strconcat (scheme, server, NULL);
+ if (g_uri_is_valid (url_string, G_URI_FLAGS_NONE, NULL))
+ g_ptr_array_add (results, g_steal_pointer (&url_string));
+
+ g_free (url_string);
+}
+
+/* Loosely based on format_pac_response() in libproxy's proxy.cpp */
+static char **
+format_pac_response (char *response)
+{
+ char **directives;
+ GPtrArray *results;
+
+ if (response[0] == ';')
+ response++;
+
+ response = g_strstrip (response);
+ directives = g_strsplit (response, ";", 0);
+ results = g_ptr_array_sized_new (g_strv_length (directives));
+
+ for (char **remaining_directives = directives;
+ remaining_directives && *remaining_directives;
+ remaining_directives++)
+ {
+ const char *directive = *remaining_directives;
+ const char *method;
+ const char *server;
+ char **split_directive;
+
+ directive = g_strstrip ((char *)directive);
+ if (g_ascii_strcasecmp (directive, "direct") == 0)
+ {
+ g_ptr_array_add (results, g_strdup ("direct://"));
+ continue;
+ }
+
+ split_directive = g_strsplit_set (directive, " \t", 2);
+ method = split_directive[0];
+ server = split_directive[1];
+
+ if (g_ascii_strcasecmp (method, "proxy") == 0)
+ add_to_results_if_valid ("http://", server, results);
+ else if (g_ascii_strcasecmp (method, "socks") == 0)
+ add_to_results_if_valid ("socks://", server, results);
+ else if (g_ascii_strcasecmp (method, "socks4") == 0)
+ add_to_results_if_valid ("socks4://", server, results);
+ else if (g_ascii_strcasecmp (method, "socks4a") == 0)
+ add_to_results_if_valid ("socks4a://", server, results);
+ else if (g_ascii_strcasecmp (method, "socks5") == 0)
+ add_to_results_if_valid ("socks5://", server, results);
+
+ g_strfreev (split_directive);
+ }
+
+ g_ptr_array_add (results, NULL);
+ g_strfreev (directives);
+ return (char **)g_ptr_array_free (results, FALSE);
+}
+
+static void
+subprocess_finished_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GSubprocess *subprocess = G_SUBPROCESS (source);
+ GDBusMethodInvocation *invocation = user_data;
+ char *stdout_buf = NULL;
+ char *stderr_buf = NULL;
+ char **proxies = NULL;
+ GError *error = NULL;
+
+ g_subprocess_communicate_utf8_finish (subprocess, result,
+ &stdout_buf, &stderr_buf,
+ &error);
+ if (error)
+ {
+ g_dbus_method_invocation_take_error (g_steal_pointer (&invocation), error);
+ goto out;
+ }
+
+ proxies = format_pac_response (stdout_buf);
+ g_dbus_method_invocation_return_value (g_steal_pointer (&invocation),
+ g_variant_new ("(^as)", proxies));
+
+out:
+ g_free (stdout_buf);
+ g_free (stderr_buf);
+ g_strfreev (proxies);
+ g_object_unref (subprocess);
+}
+
+static void
+handle_method_call (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ const char *pac_url, *lookup_url;
+ GSubprocessLauncher *launcher;
+ GSubprocess *subprocess;
+ GError *error = NULL;
+
+ g_variant_get (parameters, "(&s&s)", &pac_url, &lookup_url);
+
+ if (g_ascii_strncasecmp (pac_url, "http:", 5) &&
+ g_ascii_strncasecmp (pac_url, "https:", 6) &&
+ g_ascii_strncasecmp (pac_url, "file:", 5))
+ {
+ g_dbus_method_invocation_return_error (g_steal_pointer (&invocation),
+ G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+ "PAC URL %s has unsupported protocol", pac_url);
+ return;
+ }
+
+ launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE);
+ subprocess = g_subprocess_launcher_spawn (launcher, &error,
+ LIBEXEC_DIR "/glib-pacrunner-worker",
+ pac_url, lookup_url,
+ NULL);
+ g_object_unref (launcher);
+ if (!subprocess)
+ {
+ g_prefix_error (&error, _("Failed to spawn pacrunner-worker"));
+ g_dbus_method_invocation_return_gerror (g_steal_pointer (&invocation), error);
+ g_error_free (error);
+ return;
+ }
+
+ g_subprocess_communicate_utf8_async (g_steal_pointer (&subprocess),
+ NULL,
+ cancellable,
+ subprocess_finished_cb,
+ g_steal_pointer (&invocation));
+}
+
+static const GDBusInterfaceVTable interface_vtable =
+ {
+ handle_method_call,
+ NULL,
+ NULL
+ };
+
+static void
+on_bus_acquired (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ GDBusNodeInfo *introspection_data;
+ GError *error = NULL;
+
+ introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
+ g_dbus_connection_register_object (connection,
+ "/org/gtk/GLib/PACRunner",
+ introspection_data->interfaces[0],
+ &interface_vtable,
+ NULL,
+ NULL,
+ &error);
+ if (error)
+ g_error ("Could not register server: %s", error->message);
+}
+
+static void
+on_name_acquired (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+}
+
+static void
+on_name_lost (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ g_cancellable_cancel (cancellable);
+ g_main_loop_quit (loop);
+}
+
+int
+main (int argc, char *argv[])
+{
+ GOptionContext *context;
+ int owner_id;
+ GError *error = NULL;
+
+ setlocale (LC_ALL, "");
+ bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+
+ context = g_option_context_new ("Start pacrunner service");
+ g_option_context_parse (context, &argc, &argv, &error);
+ g_option_context_free (context);
+ if (error)
+ {
+ g_warning ("Failed to parse options: %s", error->message);
+ g_error_free (error);
+ return 1;
+ }
+
+ owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
+ "org.gtk.GLib.PACRunner",
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ on_bus_acquired,
+ on_name_acquired,
+ on_name_lost,
+ NULL,
+ NULL);
+
+ cancellable = g_cancellable_new ();
+
+ loop = g_main_loop_new (NULL, FALSE);
+ g_main_loop_run (loop);
+
+ g_bus_unown_name (owner_id);
+ g_main_loop_unref (loop);
+ g_object_unref (cancellable);
+
+ return 0;
+}
diff --git a/proxy/pacrunner/pacrunner-worker.c b/proxy/pacrunner/pacrunner-worker.c
new file mode 100644
index 0000000..7aeb62d
--- /dev/null
+++ b/proxy/pacrunner/pacrunner-worker.c
@@ -0,0 +1,359 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright © 2022 Red Hat, Inc.
+ *
+ * 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, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+/* This file is (very loosely) based on libproxy's pacrunner_webkit.cpp. */
+
+#include "config.h"
+
+#include "ghttp.h"
+#include "pacutils.h"
+#include <gio/gio.h>
+#include <glib/gstdio.h>
+#include <jsc/jsc.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static char *
+dns_resolve (const char *hostname)
+{
+ GResolver *resolver = g_resolver_get_default ();
+ GList *addresses;
+ char *first_address;
+ GError *error = NULL;
+
+ addresses = g_resolver_lookup_by_name (resolver,
+ hostname,
+ NULL,
+ &error);
+ if (error) {
+ g_warning ("Failed to resolve %s: %s", hostname, error->message);
+ g_error_free (error);
+ return NULL;
+ }
+
+ first_address = g_inet_address_to_string (addresses->data);
+ g_resolver_free_addresses (addresses);
+ return first_address;
+}
+
+static char *
+my_ip_address (void)
+{
+ char hostname[HOST_NAME_MAX + 1];
+
+ if (gethostname (hostname, sizeof (hostname)) == -1)
+ {
+ g_warning ("Failed to get system hostname: %s", g_strerror (errno));
+ return NULL;
+ }
+
+ return dns_resolve (hostname);
+}
+
+static char *
+download_pac (const char *pac_url,
+ GError **error)
+{
+ const char *http = g_intern_static_string ("http");
+ const char *https = g_intern_static_string ("https");
+ const char *file = g_intern_static_string ("file");
+ const char *scheme;
+ char *result = NULL;
+ GInputStream *pac;
+ GByteArray *bytes;
+ guchar buffer[2048];
+ gssize n_read;
+ GFile *f;
+
+ scheme = g_uri_peek_scheme (pac_url);
+ if (scheme == http || scheme == https)
+ {
+ pac = g_request_uri (pac_url, NULL, error);
+ if (!pac)
+ return NULL;
+ }
+ else if (scheme == file)
+ {
+ f = g_file_new_for_uri (pac_url);
+ pac = G_INPUT_STREAM (g_file_read (f, NULL, error));
+ g_object_unref (f);
+ if (!pac)
+ return NULL;
+ }
+ else
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "PAC URL %s has unsupported scheme %s", pac_url, scheme);
+ return NULL;
+ }
+
+ bytes = g_byte_array_sized_new (sizeof (buffer));
+ do
+ {
+ n_read = g_input_stream_read (pac, buffer, sizeof (buffer),
+ NULL, error);
+ if (n_read == -1)
+ {
+ g_byte_array_free (bytes, TRUE);
+ return NULL;
+ }
+ g_byte_array_append (bytes, buffer, n_read);
+ } while (n_read > 0);
+
+ result = (char *)g_byte_array_free (bytes, FALSE);
+
+ g_object_unref (pac);
+ return result;
+}
+
+static char *
+evaluate_pac (const char *pac,
+ const char *lookup_url,
+ const char *host,
+ GError **error)
+{
+ JSCContext *context;
+ JSCValue *value = NULL;
+ char *statement = NULL;
+ char *result = NULL;
+ JSCException *exception = NULL;
+
+ context = jsc_context_new ();
+ value = jsc_value_new_function (context,
+ "dnsResolve",
+ G_CALLBACK (dns_resolve), NULL, NULL,
+ G_TYPE_STRING, 1,
+ G_TYPE_STRING);
+ jsc_context_set_value (context, "dnsResolve", value);
+ g_clear_object (&value);
+
+ value = jsc_value_new_function (context,
+ "myIpAddress",
+ G_CALLBACK (my_ip_address), NULL, NULL,
+ G_TYPE_STRING, 0);
+ jsc_context_set_value (context, "dnsResolve", value);
+ g_clear_object (&value);
+
+ jsc_context_check_syntax (context,
+ JAVASCRIPT_ROUTINES, -1,
+ JSC_CHECK_SYNTAX_MODE_SCRIPT,
+ NULL, 0, &exception);
+ if (exception)
+ g_error ("Fatal: pacrunner JS failed syntax sanity check: %s", jsc_exception_report (exception));
+ value = jsc_context_evaluate (context, JAVASCRIPT_ROUTINES, -1);
+ g_clear_object (&value);
+
+ jsc_context_check_syntax (context,
+ pac, -1,
+ JSC_CHECK_SYNTAX_MODE_SCRIPT,
+ NULL, 0, &exception);
+ if (exception)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Syntax error in proxy autoconfig script: %s", jsc_exception_report (exception));
+ g_object_unref (exception);
+ goto out;
+ }
+ value = jsc_context_evaluate (context, pac, -1);
+ g_clear_object (&value);
+
+ statement = g_strdup_printf ("FindProxyForURL('%s', '%s');", lookup_url, host);
+ jsc_context_check_syntax (context,
+ statement, -1,
+ JSC_CHECK_SYNTAX_MODE_SCRIPT,
+ NULL, 0, &exception);
+ if (exception)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Syntax error in script \"%s\": %s", statement, jsc_exception_report (exception));
+ g_object_unref (exception);
+ goto out;
+ }
+ value = jsc_context_evaluate (context, statement, -1);
+ if (!jsc_value_is_string (value))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Proxy autoconfig script result '%s' is not a string", jsc_value_to_string (value));
+ g_clear_object (&value);
+ }
+
+out:
+ if (value)
+ {
+ result = jsc_value_to_string (value);
+ g_object_unref (value);
+ }
+ g_free (statement);
+ g_object_unref (context);
+ return result;
+}
+
+/* Paranoia: prevent a malicious URL from executing script in the PAC context by
+ * encoding any use of the quote character ' that it needs to use to break out
+ * of its intended context. It's generally better to encode everything that's
+ * not alphanumeric, but in this case that is not possible because the PAC
+ * script will expect to operate on unencoded URLs, so we really cannot encode
+ * anything more than necessary.
+ */
+static char *
+encode_single_quotes (const char *input,
+ GError **error)
+{
+ GString *str;
+ const char *c = input;
+
+ if (!g_utf8_validate (input, -1, NULL))
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Input is not valid UTF-8");
+ return NULL;
+ }
+
+ str = g_string_new (NULL);
+ do
+ {
+ gunichar u = g_utf8_get_char (c);
+ if (u == '\'')
+ g_string_append_printf (str, "\\u%04u", u);
+ else
+ g_string_append_unichar (str, u);
+ c = g_utf8_next_char (c);
+ } while (*c);
+
+ return g_string_free (str, FALSE);
+}
+
+static void
+process_lookup_url (const char *lookup_url,
+ char **out_sanitized_url,
+ char **out_sanitized_host,
+ GError **error)
+{
+ GUri *uri;
+ GUri *tmp;
+ char *url_string = NULL;
+ char *encoded_url = NULL;
+ char *encoded_host = NULL;
+
+ uri = g_uri_parse (lookup_url, G_URI_FLAGS_NONE, error);
+ if (!uri)
+ return;
+
+ /* In the future, we probably want to sanitize all URLs down to only
+ * protocol://host:port, but for now browsers only do this for https
+ * URLs, so let's remain compatible.
+ */
+ if (strcmp (g_uri_get_scheme (uri), "https") == 0)
+ {
+ tmp = g_uri_build (G_URI_FLAGS_NONE,
+ g_uri_get_scheme (uri),
+ NULL,
+ g_uri_get_host (uri),
+ g_uri_get_port (uri),
+ NULL, NULL, NULL);
+ g_uri_unref (uri);
+ uri = g_steal_pointer (&tmp);
+ }
+
+ url_string = g_uri_to_string (uri);
+ encoded_url = encode_single_quotes (url_string, error);
+ if (!encoded_url)
+ goto out;
+
+ encoded_host = encode_single_quotes (g_uri_get_host (uri), error);
+ if (!encoded_url)
+ goto out;
+
+ *out_sanitized_url = g_steal_pointer (&encoded_url);
+ *out_sanitized_host = g_steal_pointer (&encoded_host);
+
+out:
+ g_uri_unref (uri);
+ g_free (url_string);
+ g_free (encoded_url);
+ g_free (encoded_host);
+}
+
+int
+main (int argc, char *argv[])
+{
+ GOptionContext *context;
+ const char *pac_url;
+ const char *lookup_url;
+ char *sanitized_url = NULL;
+ char *sanitized_host = NULL;
+ char *pac = NULL;
+ char *result = NULL;
+ int exit_status = 1;
+ GError *error = NULL;
+
+ context = g_option_context_new ("PAC_URL LOOKUP_URL");
+ g_option_context_parse (context, &argc, &argv, &error);
+ g_option_context_free (context);
+ if (error)
+ {
+ g_warning ("Failed to parse options: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ if (argc != 3)
+ {
+ g_fprintf (stderr, "Usage: %s PAC_URL LOOKUP_URL\n", argv[0]);
+ goto out;
+ }
+
+ pac_url = argv[1];
+ lookup_url = argv[2];
+
+ process_lookup_url (lookup_url, &sanitized_url, &sanitized_host, &error);
+ if (error)
+ {
+ g_warning ("Failed to parse lookup URL %s: %s", lookup_url, error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ pac = download_pac (pac_url, &error);
+ if (!pac)
+ {
+ g_warning ("Failed to download proxy autoconfig script %s: %s", pac_url, error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ result = evaluate_pac (pac, sanitized_url, sanitized_host, &error);
+ if (!result)
+ {
+ g_warning ("Failed to resolve proxy for URL %s using proxy autoconfig script %s: %s",
+ lookup_url, pac_url, error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ g_printf ("%s\n", result);
+ exit_status = 0;
+
+out:
+ g_free (pac);
+ g_free (sanitized_url);
+ g_free (sanitized_host);
+
+ return exit_status;
+}
diff --git a/proxy/pacrunner/pacutils.h b/proxy/pacrunner/pacutils.h
new file mode 100644
index 0000000..3826014
--- /dev/null
+++ b/proxy/pacrunner/pacutils.h
@@ -0,0 +1,242 @@
+/*
+ * The following Javascript code was taken from Mozilla (http://www.mozilla.org)
+ * and is licensed according to the LGPLv2.1+. Below is the original copyright
+ * header as contained in the source file (nsProxyAutoConfig.js)
+ */
+
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: NPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Akhil Arora <akhil.arora@sun.com>
+ * Tomi Leppikangas <Tomi.Leppikangas@oulu.fi>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the NPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the NPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#define JAVASCRIPT_ROUTINES \
+"function dnsDomainIs(host, domain) {\n" \
+" return (host.length >= domain.length &&\n" \
+" host.substring(host.length - domain.length) == domain);\n" \
+"}\n" \
+"function dnsDomainLevels(host) {\n" \
+" return host.split('.').length-1;\n" \
+"}\n" \
+"function convert_addr(ipchars) {\n" \
+" var bytes = ipchars.split('.');\n" \
+" var result = ((bytes[0] & 0xff) << 24) |\n" \
+" ((bytes[1] & 0xff) << 16) |\n" \
+" ((bytes[2] & 0xff) << 8) |\n" \
+" (bytes[3] & 0xff);\n" \
+" return result;\n" \
+"}\n" \
+"function isInNet(ipaddr, pattern, maskstr) {\n"\
+" var test = /^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/.exec(ipaddr);\n"\
+" if (test == null) {\n"\
+" ipaddr = dnsResolve(ipaddr);\n"\
+" if (ipaddr == null)\n"\
+" return false;\n"\
+" } else if (test[1] > 255 || test[2] > 255 || \n"\
+" test[3] > 255 || test[4] > 255) {\n"\
+" return false; // not an IP address\n"\
+" }\n"\
+" var host = convert_addr(ipaddr);\n"\
+" var pat = convert_addr(pattern);\n"\
+" var mask = convert_addr(maskstr);\n"\
+" return ((host & mask) == (pat & mask));\n"\
+" \n"\
+"}\n"\
+"function isPlainHostName(host) {\n" \
+" return (host.search('\\\\.') == -1);\n" \
+"}\n" \
+"function isResolvable(host) {\n" \
+" var ip = dnsResolve(host);\n" \
+" return (ip != null);\n" \
+"}\n" \
+"function localHostOrDomainIs(host, hostdom) {\n" \
+" if (isPlainHostName(host)) {\n" \
+" return (hostdom.search('/^' + host + '/') != -1);\n" \
+" }\n" \
+" else {\n" \
+" return (host == hostdom);\n" \
+" }\n" \
+"}\n" \
+"function shExpMatch(url, pattern) {\n" \
+" pattern = pattern.replace(/\\./g, '\\\\.');\n" \
+" pattern = pattern.replace(/\\*/g, '.*');\n" \
+" pattern = pattern.replace(/\\?/g, '.');\n" \
+" var newRe = new RegExp('^'+pattern+'$');\n" \
+" return newRe.test(url);\n" \
+"}\n" \
+"var wdays = {SUN: 0, MON: 1, TUE: 2, WED: 3, THU: 4, FRI: 5, SAT: 6};\n" \
+"var months = {JAN: 0, FEB: 1, MAR: 2, APR: 3, MAY: 4, JUN: 5, JUL: 6, AUG: 7, SEP: 8, OCT: 9, NOV: 10, DEC: 11};\n"\
+"function weekdayRange() {\n" \
+" function getDay(weekday) {\n" \
+" if (weekday in wdays) {\n" \
+" return wdays[weekday];\n" \
+" }\n" \
+" return -1;\n" \
+" }\n" \
+" var date = new Date();\n" \
+" var argc = arguments.length;\n" \
+" var wday;\n" \
+" if (argc < 1)\n" \
+" return false;\n" \
+" if (arguments[argc - 1] == 'GMT') {\n" \
+" argc--;\n" \
+" wday = date.getUTCDay();\n" \
+" } else {\n" \
+" wday = date.getDay();\n" \
+" }\n" \
+" var wd1 = getDay(arguments[0]);\n" \
+" var wd2 = (argc == 2) ? getDay(arguments[1]) : wd1;\n" \
+" return (wd1 == -1 || wd2 == -1) ? false\n" \
+" : (wd1 <= wday && wday <= wd2);\n" \
+"}\n" \
+"function dateRange() {\n" \
+" function getMonth(name) {\n" \
+" if (name in months) {\n" \
+" return months[name];\n" \
+" }\n" \
+" return -1;\n" \
+" }\n" \
+" var date = new Date();\n" \
+" var argc = arguments.length;\n" \
+" if (argc < 1) {\n" \
+" return false;\n" \
+" }\n" \
+" var isGMT = (arguments[argc - 1] == 'GMT');\n" \
+" if (isGMT) {\n" \
+" argc--;\n" \
+" }\n" \
+" if (argc == 1) {\n" \
+" var tmp = parseInt(arguments[0]);\n" \
+" if (isNaN(tmp)) {\n" \
+" return ((isGMT ? date.getUTCMonth() : date.getMonth()) == getMonth(arguments[0]));\n" \
+" } else if (tmp < 32) {\n" \
+" return ((isGMT ? date.getUTCDate() : date.getDate()) == tmp);\n" \
+" } else {\n" \
+" return ((isGMT ? date.getUTCFullYear() : date.getFullYear()) == tmp);\n" \
+" }\n" \
+" }\n" \
+" var year = date.getFullYear();\n" \
+" var date1, date2;\n" \
+" date1 = new Date(year, 0, 1, 0, 0, 0);\n" \
+" date2 = new Date(year, 11, 31, 23, 59, 59);\n" \
+" var adjustMonth = false;\n" \
+" for (var i = 0; i < (argc >> 1); i++) {\n" \
+" var tmp = parseInt(arguments[i]);\n" \
+" if (isNaN(tmp)) {\n" \
+" var mon = getMonth(arguments[i]);\n" \
+" date1.setMonth(mon);\n" \
+" } else if (tmp < 32) {\n" \
+" adjustMonth = (argc <= 2);\n" \
+" date1.setDate(tmp);\n" \
+" } else {\n" \
+" date1.setFullYear(tmp);\n" \
+" }\n" \
+" }\n" \
+" for (var i = (argc >> 1); i < argc; i++) {\n" \
+" var tmp = parseInt(arguments[i]);\n" \
+" if (isNaN(tmp)) {\n" \
+" var mon = getMonth(arguments[i]);\n" \
+" date2.setMonth(mon);\n" \
+" } else if (tmp < 32) {\n" \
+" date2.setDate(tmp);\n" \
+" } else {\n" \
+" date2.setFullYear(tmp);\n" \
+" }\n" \
+" }\n" \
+" if (adjustMonth) {\n" \
+" date1.setMonth(date.getMonth());\n" \
+" date2.setMonth(date.getMonth());\n" \
+" }\n" \
+" if (isGMT) {\n" \
+" var tmp = date;\n" \
+" tmp.setFullYear(date.getUTCFullYear());\n" \
+" tmp.setMonth(date.getUTCMonth());\n" \
+" tmp.setDate(date.getUTCDate());\n" \
+" tmp.setHours(date.getUTCHours());\n" \
+" tmp.setMinutes(date.getUTCMinutes());\n" \
+" tmp.setSeconds(date.getUTCSeconds());\n" \
+" date = tmp;\n" \
+" }\n" \
+" return ((date1 <= date) && (date <= date2));\n" \
+"}\n" \
+"function timeRange() {\n" \
+" var argc = arguments.length;\n" \
+" var date = new Date();\n" \
+" var isGMT= false;\n" \
+" if (argc < 1) {\n" \
+" return false;\n" \
+" }\n" \
+" if (arguments[argc - 1] == 'GMT') {\n" \
+" isGMT = true;\n" \
+" argc--;\n" \
+" }\n" \
+" var hour = isGMT ? date.getUTCHours() : date.getHours();\n" \
+" var date1, date2;\n" \
+" date1 = new Date();\n" \
+" date2 = new Date();\n" \
+" if (argc == 1) {\n" \
+" return (hour == arguments[0]);\n" \
+" } else if (argc == 2) {\n" \
+" return ((arguments[0] <= hour) && (hour <= arguments[1]));\n" \
+" } else {\n" \
+" switch (argc) {\n" \
+" case 6:\n" \
+" date1.setSeconds(arguments[2]);\n" \
+" date2.setSeconds(arguments[5]);\n" \
+" case 4:\n" \
+" var middle = argc >> 1;\n" \
+" date1.setHours(arguments[0]);\n" \
+" date1.setMinutes(arguments[1]);\n" \
+" date2.setHours(arguments[middle]);\n" \
+" date2.setMinutes(arguments[middle + 1]);\n" \
+" if (middle == 2) {\n" \
+" date2.setSeconds(59);\n" \
+" }\n" \
+" break;\n" \
+" default:\n" \
+" throw 'timeRange: bad number of arguments'\n" \
+" }\n" \
+" }\n" \
+" if (isGMT) {\n" \
+" date.setFullYear(date.getUTCFullYear());\n" \
+" date.setMonth(date.getUTCMonth());\n" \
+" date.setDate(date.getUTCDate());\n" \
+" date.setHours(date.getUTCHours());\n" \
+" date.setMinutes(date.getUTCMinutes());\n" \
+" date.setSeconds(date.getUTCSeconds());\n" \
+" }\n" \
+" return ((date1 <= date) && (date <= date2));\n" \
+"}\n" \
+""
diff --git a/tls/base/meson.build b/tls/base/meson.build
index f25cd35..79acfdc 100644
--- a/tls/base/meson.build
+++ b/tls/base/meson.build
@@ -1,6 +1,5 @@
tlsbase_sources = files(
'gtlsconnection-base.c',
- 'gtlshttp.c',
'gtlsinputstream.c',
'gtlslog.c',
'gtlsoutputstream.c',
@@ -13,4 +12,4 @@ tlsbase = static_library('tlsbase',
tlsbase_dep = declare_dependency(link_with: tlsbase,
include_directories: include_directories('.'),
- dependencies: gio_dep)
+ dependencies: [gio_dep, util_dep])
diff --git a/tls/gnutls/gtlsdatabase-gnutls.c b/tls/gnutls/gtlsdatabase-gnutls.c
index eef1b16..37f35f0 100644
--- a/tls/gnutls/gtlsdatabase-gnutls.c
+++ b/tls/gnutls/gtlsdatabase-gnutls.c
@@ -34,7 +34,6 @@
#include <gnutls/x509.h>
#include "gtlscertificate-gnutls.h"
-#include "gtlshttp.h"
#include "gtlsgnutls-version.h"
typedef struct
diff --git a/tls/base/gtlshttp.c b/util/ghttp.c
index 07334c6..b2441ce 100644
--- a/tls/base/gtlshttp.c
+++ b/util/ghttp.c
@@ -28,7 +28,7 @@
#include <dlfcn.h>
#endif
-#include "gtlshttp.h"
+#include "ghttp.h"
typedef gpointer SoupSession;
typedef gpointer SoupMessage;
@@ -107,7 +107,7 @@ init_libsoup (void)
}
/**
- * g_tls_request_uri:
+ * g_request_uri:
* @uri: An HTTP URI to request
* @cancellable: (nullable): A #GCancellable
* @error: A #GError
@@ -119,9 +119,9 @@ init_libsoup (void)
* Returns: A #GInputStream of the response body or %NULL on failure
*/
GInputStream *
-g_tls_request_uri (const char *uri,
- GCancellable *cancellable,
- GError **error)
+g_request_uri (const char *uri,
+ GCancellable *cancellable,
+ GError **error)
{
GInputStream *istream = NULL;
diff --git a/tls/base/gtlshttp.h b/util/ghttp.h
index 9e527ea..b3c3b14 100644
--- a/tls/base/gtlshttp.h
+++ b/util/ghttp.h
@@ -26,6 +26,6 @@
#include <gio/gio.h>
-GInputStream *g_tls_request_uri (const char *uri,
- GCancellable *cancellable,
- GError **error);
+GInputStream *g_request_uri (const char *uri,
+ GCancellable *cancellable,
+ GError **error);
diff --git a/util/meson.build b/util/meson.build
new file mode 100644
index 0000000..754881d
--- /dev/null
+++ b/util/meson.build
@@ -0,0 +1,12 @@
+util_sources = files(
+ 'ghttp.c'
+)
+
+util = static_library('util',
+ util_sources,
+ dependencies: [gio_dep, gmodule_dep],
+ include_directories: top_inc)
+
+util_dep = declare_dependency(link_with: util,
+ include_directories: include_directories('.'),
+ dependencies: gio_dep)