summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordominique.leuenberger@gmail.com <dominique.leuenberger@gmail.com@c587cffe-e639-0410-9787-d7902ae8ed56>2011-04-28 19:45:12 +0000
committerdominique.leuenberger@gmail.com <dominique.leuenberger@gmail.com@c587cffe-e639-0410-9787-d7902ae8ed56>2011-04-28 19:45:12 +0000
commit715875096564493ef5d27d95f6c61a4df4ca2e6e (patch)
tree4dbd78264bf7e385305f632079458fd9f69505d2
parent2defffd39162d1fd74d025c8cad19a4cd8353c06 (diff)
downloadlibproxy-715875096564493ef5d27d95f6c61a4df4ca2e6e.tar.gz
GNOME3: have a pxgsettings helper and a config_gnome3 module
git-svn-id: http://libproxy.googlecode.com/svn/trunk@798 c587cffe-e639-0410-9787-d7902ae8ed56
-rw-r--r--NEWS2
-rw-r--r--libproxy/cmake/modules.cmk3
-rw-r--r--libproxy/cmake/modules/config_gnome.cmk19
-rw-r--r--libproxy/modules/config_gnome3.cpp302
-rw-r--r--libproxy/modules/pxgsettings.cpp166
5 files changed, 487 insertions, 5 deletions
diff --git a/NEWS b/NEWS
index 20c435e..ed27ed1 100644
--- a/NEWS
+++ b/NEWS
@@ -2,12 +2,14 @@ New in version 0.4.7
==============================
* Support/require xulrunner 2.0+
* Support linking againgst libwebkit-gtk3 (-DWITH_WEBKIT3=ON)
+* Port to gsettings for gnome3. (-DWITH_GNOME3=ON[default])
* Issues closed:
- #149: always test for the right python noarch module path
- #155: Cannot compile with Firefox 4
- #156: libproxy should build against webkitgtk-3.0
- #158: Won't compile w/ xulrunner 2.0 final
- #159: libproxy fails with autoconfiguration "http://proxy.domain.com"
+ - #131: GSettings-based GNOME plugin
New in version 0.4.6
==============================
diff --git a/libproxy/cmake/modules.cmk b/libproxy/cmake/modules.cmk
index dfbb9e4..1cc2d24 100644
--- a/libproxy/cmake/modules.cmk
+++ b/libproxy/cmake/modules.cmk
@@ -28,7 +28,8 @@ endif()
#
message("MODULES TO BUILD:")
px_module(config_envvar "${ENVVAR_FOUND}" 1)
-px_module(config_gnome "${GNOME_FOUND}" 0)
+px_module(config_gnome "${GNOME2_FOUND}" 0)
+px_module(config_gnome3 "${GNOME3_FOUND}" 0)
px_module(config_kde4 "${KDE4_FOUND}" 0 ${KDE4_LIBRARIES})
px_module(config_macosx "${SC_FOUND}" 1 ${SC_LIBRARIES} ${CF_LIBRARIES})
px_module(config_w32reg "${WIN32}" 1)
diff --git a/libproxy/cmake/modules/config_gnome.cmk b/libproxy/cmake/modules/config_gnome.cmk
index 2883b2b..18285bf 100644
--- a/libproxy/cmake/modules/config_gnome.cmk
+++ b/libproxy/cmake/modules/config_gnome.cmk
@@ -1,10 +1,21 @@
if (NOT WIN32 AND NOT APPLE)
- px_check_modules(GNOME gconf-2.0 gobject-2.0)
+ option(WITH_GNOME3 "Target a GNOME 3 system (gsettings)" ON)
+ if(WITH_GNOME3)
+ px_check_modules(GNOME3 gio-2.0 gobject-2.0)
+ else(WITH_GNOME3)
+ px_check_modules(GNOME2 gconf-2.0 gobject-2.0)
+ endif(WITH_GNOME3)
# GNOME (gconf) helper
if(GNOME_FOUND)
- add_executable(pxgconf modules/pxgconf.cpp)
- target_link_libraries(pxgconf ${GNOME_LIBRARIES})
- install(TARGETS pxgconf RUNTIME DESTINATION ${LIBEXEC_INSTALL_DIR})
+ if(WITH_GNOME3)
+ add_executable(pxgsettings modules/pxgsettings.cpp)
+ target_link_libraries(pxgsettings ${GNOME_LIBRARIES})
+ install(TARGETS pxgsettings RUNTIME DESTINATION ${LIBEXEC_INSTALL_DIR})
+ else(WITH_GNOME3)
+ add_executable(pxgconf modules/pxgconf.cpp)
+ target_link_libraries(pxgconf ${GNOME_LIBRARIES})
+ install(TARGETS pxgconf RUNTIME DESTINATION ${LIBEXEC_INSTALL_DIR})
+ endif(WITH_GNOME3)
endif()
endif()
diff --git a/libproxy/modules/config_gnome3.cpp b/libproxy/modules/config_gnome3.cpp
new file mode 100644
index 0000000..07f9f14
--- /dev/null
+++ b/libproxy/modules/config_gnome3.cpp
@@ -0,0 +1,302 @@
+/*******************************************************************************
+ * libproxy - A library for proxy configuration
+ * Copyright (C) 2006 Nathaniel McCallum <nathaniel@natemccallum.com>
+ *
+ * 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
+ ******************************************************************************/
+
+#include <cstdio> // For fileno(), fread(), pclose(), popen(), sscanf()
+#include <sys/select.h> // For select()
+#include <fcntl.h> // For fcntl()
+#include <errno.h> // For errno stuff
+#include <sys/types.h> // For stat()
+#include <sys/stat.h> // For stat()
+#include <unistd.h> // For pipe(), close(), vfork(), dup(), execl(), _exit()
+#include <signal.h> // For kill()
+
+#include "../extension_config.hpp"
+using namespace libproxy;
+
+#define BUFFERSIZE 10240
+
+#define PROXY_MODE "org.gnome.system.proxy/mode"
+#define PROXY_USE_AUTHENTICATION "org.gnome.system.proxy.http/use-authentication"
+#define PROXY_AUTH_PASSWORD "org.gnome.system.proxy.http/authentication-password"
+#define PROXY_AUTH_USER "org.gnome.system.proxy.http/authentication-user"
+#define PROXY_AUTOCONFIG_URL "org.gnome.system.proxy/autoconfig-url"
+#define PROXY_IGNORE_HOSTS "org.gnome.system.proxy/ignore-hosts"
+#define PROXY_HTTP_HOST "org.gnome.system.proxy.http/host"
+#define PROXY_HTTP_PORT "org.gnome.system.proxy.http/port"
+#define PROXY_FTP_HOST "org.gnome.system.proxy.ftp/host"
+#define PROXY_FTP_PORT "org.gnome.system.proxy.ftp/port"
+#define PROXY_SECURE_HOST "org.gnome.system.proxy.https/host"
+#define PROXY_SECURE_PORT "org.gnome.system.proxy.https/port"
+#define PROXY_SOCKS_HOST "org.gnome.system.proxy.socks/host"
+#define PROXY_SOCKS_PORT "org.gnome.system.proxy.socks/port"
+#define PROXY_SAME_FOR_ALL "org.gnome.system.proxy/use-same-proxy"
+
+static const char *all_keys[] = {
+ "org.gnome.system.proxy",
+ "org.gnome.system.proxy.http",
+ "org.gnome.system.proxy.https",
+ "org.gnome.system.proxy.ftp",
+ "org.gnome.system.proxy.socks",
+ NULL
+};
+
+static int popen2(const char *program, FILE** read, FILE** write, pid_t* pid) {
+ if (!read || !write || !pid || !program || !*program)
+ return EINVAL;
+ *read = NULL;
+ *write = NULL;
+ *pid = 0;
+
+ // Open the pipes
+ int rpipe[2];
+ int wpipe[2];
+ if (pipe(rpipe) < 0)
+ return errno;
+ if (pipe(wpipe) < 0) {
+ close(rpipe[0]);
+ close(rpipe[1]);
+ return errno;
+ }
+
+ switch (*pid = vfork()) {
+ case -1: // Error
+ close(rpipe[0]);
+ close(rpipe[1]);
+ close(wpipe[0]);
+ close(wpipe[1]);
+ return errno;
+
+ case 0: // Child
+ close(STDIN_FILENO); // Close stdin
+ close(STDOUT_FILENO); // Close stdout
+
+ // Dup the read end of the write pipe to stdin
+ // Dup the write end of the read pipe to stdout
+ if (dup2(wpipe[0], STDIN_FILENO) != STDIN_FILENO) _exit(1);
+ if (dup2(rpipe[1], STDOUT_FILENO) != STDOUT_FILENO) _exit(2);
+
+ // Close unneeded fds
+ for (int i = 3; i < sysconf(_SC_OPEN_MAX); i++)
+ close(i);
+
+ // Exec
+ execl("/bin/sh", "sh", "-c", program, (char*) NULL);
+ _exit(127); // Whatever we do, don't return
+
+ default: // Parent
+ close(rpipe[1]);
+ close(wpipe[0]);
+ *read = fdopen(rpipe[0], "r");
+ *write = fdopen(wpipe[1], "w");
+ if (*read == NULL || *write == NULL) {
+ if (*read != NULL) fclose(*read);
+ if (*write != NULL) fclose(*write);
+ return errno;
+ }
+ return 0;
+ }
+}
+
+static inline uint16_t get_port(string &port)
+{
+ uint16_t retval;
+
+ if (sscanf(port.c_str(), "%hu", &retval) != 1)
+ retval = 0;
+
+ return retval;
+}
+
+class gnome_config_extension : public config_extension {
+public:
+ gnome_config_extension() {
+ // Build the command
+ int count;
+ struct stat st;
+ string cmd = LIBEXECDIR "/pxgsettings";
+ const char *pxgconf = getenv("PX_GSETTINGS");
+
+ if (pxgconf)
+ cmd = string (pxgconf);
+
+ if (stat(cmd.c_str(), &st))
+ throw runtime_error ("Unable to open gsettings helper!");
+
+ for (count=0 ; all_keys[count] ; count++)
+ cmd += string(" ", 1) + all_keys[count];
+
+ // Get our pipes
+ if (popen2(cmd.c_str(), &this->read, &this->write, &this->pid) != 0)
+ throw runtime_error("Unable to run gconf helper!");
+
+ // Read in our initial data
+ this->read_data(count);
+
+ // Set the read pipe to non-blocking
+ if (fcntl(fileno(this->read), F_SETFL, O_NONBLOCK) == -1) {
+ fclose(this->read);
+ fclose(this->write);
+ kill(this->pid, SIGTERM);
+ throw runtime_error("Unable to set pipe to non-blocking!");
+ }
+ }
+
+ ~gnome_config_extension() {
+ fclose(this->read);
+ fclose(this->write);
+ kill(this->pid, SIGTERM);
+ }
+
+ url get_config(url dest) throw (runtime_error) {
+ // Check for changes in the config
+ fd_set rfds;
+ struct timeval timeout = { 0, 0 };
+ FD_ZERO(&rfds);
+ FD_SET(fileno(this->read), &rfds);
+ if (select(fileno(this->read)+1, &rfds, NULL, NULL, &timeout) > 0)
+ this->read_data();
+
+ // Mode is wpad:// or pac+http://...
+ if (this->data[PROXY_MODE] == "auto") {
+ string pac = this->data[PROXY_AUTOCONFIG_URL];
+ return url::is_valid(pac) ? url(string("pac+") + pac) : url("wpad://");
+ }
+
+ // Mode is http://... or socks://...
+ else if (this->data[PROXY_MODE] == "manual") {
+ string type, host, port;
+ bool auth = this->data[PROXY_USE_AUTHENTICATION] == "true";
+ string username = url::encode(this->data[PROXY_AUTH_USER], URL_ALLOWED_IN_USERINFO_ELEMENT);
+ string password = url::encode(this->data[PROXY_AUTH_PASSWORD], URL_ALLOWED_IN_USERINFO_ELEMENT);
+ bool same_proxy = this->data[PROXY_SAME_FOR_ALL] == "true";
+
+ // If socks is set use it (except when same_proxy is set)
+ if (!same_proxy) {
+ type = "socks";
+ host = this->data[PROXY_SOCKS_HOST];
+ port = this->data[PROXY_SOCKS_PORT];
+ }
+
+ if (host == "" || get_port(port) == 0) {
+ // Get the per-scheme proxy settings
+ if (dest.get_scheme() == "http") {
+ type = "http";
+ host = this->data[PROXY_HTTP_HOST];
+ port = this->data[PROXY_HTTP_PORT];
+ }
+ else if (dest.get_scheme() == "https") {
+ // It is expected that the configured server is an
+ // HTTP server that support CONNECT method.
+ type = "http";
+ host = this->data[PROXY_SECURE_HOST];
+ port = this->data[PROXY_SECURE_PORT];
+ }
+ else if (dest.get_scheme() == "ftp") {
+ // It is expected that the configured server is an
+ // HTTP server that handles proxying FTP URLs
+ // (e.g. request with header "Host: ftp://ftp.host.org")
+ type = "http";
+ host = this->data[PROXY_FTP_HOST];
+ port = this->data[PROXY_FTP_PORT];
+ }
+
+ // If no proxy is set and we have the same_proxy option
+ // enabled try socks at the end only.
+ if (same_proxy && (host == "" || get_port(port) == 0)) {
+ type = "socks";
+ host = this->data[PROXY_SOCKS_HOST];
+ port = this->data[PROXY_SOCKS_PORT];
+ }
+ }
+
+
+ // If host and port were found, build config url
+ if (host != "" && get_port(port) != 0) {
+ string tmp = type + "://";
+ if (auth)
+ tmp += username + ":" + password + "@";
+ tmp += host + ":" + port;
+ return url(tmp);
+ }
+ }
+
+ // Mode is direct://
+ return url("direct://");
+ }
+
+ string get_ignore(url) {
+ return this->data[PROXY_IGNORE_HOSTS];
+ }
+
+ bool set_creds(url /*proxy*/, string username, string password) {
+ string auth = PROXY_USE_AUTHENTICATION "\ttrue\n";
+ string user = string(PROXY_AUTH_USER "\t") + username + "\n";
+ string pass = string(PROXY_AUTH_PASSWORD "\t") + password + "\n";
+
+ return (fwrite(auth.c_str(), 1, auth.size(), this->write) == auth.size() &&
+ fwrite(user.c_str(), 1, user.size(), this->write) == user.size() &&
+ fwrite(pass.c_str(), 1, pass.size(), this->write) == pass.size());
+ }
+
+private:
+ FILE* read;
+ FILE* write;
+ pid_t pid;
+ map<string, string> data;
+
+ bool read_data(int num=-1) {
+ if (num == 0) return true;
+ if (!this->read) return false; // We need the pipe to be open
+
+ for (char l[BUFFERSIZE] ; num != 0 && fgets(l, BUFFERSIZE, this->read) != NULL ; ) {
+ string line = l;
+ line = line.substr(0, line.rfind('\n'));
+ string key = line.substr(0, line.find("\t"));
+ string val = line.substr(line.find("\t")+1);
+ this->data[key] = val;
+ if (num > 0) num--;
+ }
+
+ return (num <= 0);
+ }
+};
+
+static base_extension** gnome_config_extension_init() {
+ base_extension** retval = new base_extension*[2];
+ retval[1] = NULL;
+ try {
+ retval[0] = new gnome_config_extension();
+ return retval;
+ }
+ catch (runtime_error) {
+ delete[] retval;
+ return NULL;
+ }
+}
+
+static bool gnome_config_extension_test() {
+ return (getenv("GNOME_DESKTOP_SESSION_ID")
+ || (getenv("DESKTOP_SESSION")
+ && string(getenv("DESKTOP_SESSION")) == "gnome"));
+}
+
+MM_MODULE_INIT(gnome_config_extension,
+ gnome_config_extension_init,
+ gnome_config_extension_test,
+ NULL, NULL);
diff --git a/libproxy/modules/pxgsettings.cpp b/libproxy/modules/pxgsettings.cpp
new file mode 100644
index 0000000..95fd210
--- /dev/null
+++ b/libproxy/modules/pxgsettings.cpp
@@ -0,0 +1,166 @@
+/*******************************************************************************
+ * pxgsettings - A helper binary to query gsettings
+ * Copyright (C) 2006 Nathaniel McCallum <nathaniel@natemccallum.com>
+ * Copyright (C) 2011 Dominique Leuenberger <dominique@leuenberger.net>
+ *
+ * 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
+ ******************************************************************************/
+
+#include <cstdio>
+#include <unistd.h>
+#include <signal.h>
+#include <stdexcept>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+using namespace std;
+
+static GMainLoop* loop = NULL;
+
+static int print_value(GVariant *value, const char *suffix) {
+
+ if (!value) return 0;
+ if (g_variant_is_of_type(value, G_VARIANT_TYPE_STRING)) {
+ return printf("%s%s", g_variant_get_string(value, NULL), suffix);
+ }
+ else if(g_variant_is_of_type(value, G_VARIANT_TYPE_INT32)) {
+ return printf("%d%s", g_variant_get_int32(value), suffix);
+ }
+ else if(g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)) {
+ gboolean result;
+ result = g_variant_get_boolean(value);
+ return printf("%s%s", result ? "true" : "false", suffix);
+ }
+ else if(g_variant_is_of_type(value, G_VARIANT_TYPE_ARRAY)) {
+ int count;
+ const gchar** items;
+ items = g_variant_get_strv(value, NULL);
+ for (count=0; items[count]; printf("%s%s", count < 2 ? "" : ",", items[count++]) );
+ printf("%s", suffix);
+ return count;
+ }
+ else {
+ throw exception();
+ }
+
+ return 0;
+}
+
+static void on_value_change(GSettings *settings, const gchar *key, gpointer user_data) {
+ printf("%s/%s\t", user_data, key);
+ print_value(g_settings_get_value(settings, key), "\n");
+}
+
+static void on_sig(int /*signal*/) {
+ g_main_loop_quit(loop);
+}
+
+static gboolean err(GIOChannel* /*source*/, GIOCondition /*condition*/, gpointer /*data*/) {
+ g_main_loop_quit(loop);
+ return false;
+}
+
+static gboolean in(GIOChannel *source, GIOCondition condition, gpointer data) {
+ gchar *key, *val;
+ GIOStatus st = g_io_channel_read_line(source, &key, NULL, NULL, NULL);
+
+ // Remove the trailing '\n'
+ for (int i=0 ; key && key[i] ; i++)
+ if (key[i] == '\n')
+ key[i] = '\0';
+
+ // If we were successful
+ if (key && st == G_IO_STATUS_NORMAL) {
+ if (!g_strrstr(key, "\t"))
+ goto exit;
+
+ val = g_strrstr(key, "\t") + 1;
+ *(val-1) = '\0';
+
+ g_free(key);
+ return true;
+ }
+ else if (key && st == G_IO_STATUS_AGAIN) {
+ g_free(key);
+ return in(source, condition, data);
+ }
+
+exit:
+ g_free(key);
+ return err(source, condition, data);
+}
+
+int main(int argc, char **argv) {
+ if (argc < 2) return 1;
+
+ // Register sighup handler
+ if (signal(SIGHUP, on_sig) == SIG_ERR || signal(SIGPIPE, on_sig) == SIG_ERR) {
+ fprintf(stderr, "Unable to trap signals!");
+ return 2;
+ }
+
+ // Switch stdout to line buffering
+ if (setvbuf(stdout, NULL, _IOLBF, 0)) {
+ fprintf(stderr, "Unable to switch stdout to line buffering!");
+ return 3;
+ }
+
+ // Switch stdin to line buffering
+ if (setvbuf(stdin, NULL, _IOLBF, 0)) {
+ fprintf(stderr, "Unable to switch stdin to line buffering!");
+ return 4;
+ }
+
+ // Init
+ g_type_init();
+
+ // Get the main loop
+ loop = g_main_loop_new(NULL, false);
+
+ // Setup our GIO Channels
+ GIOChannel* inchan = g_io_channel_unix_new(fileno(stdin));
+ GIOChannel* outchan = g_io_channel_unix_new(fileno(stdout));
+ g_io_add_watch(inchan, G_IO_IN, in, NULL);
+ g_io_add_watch(inchan, G_IO_PRI, in, NULL);
+ g_io_add_watch(inchan, G_IO_ERR, err, NULL);
+ g_io_add_watch(inchan, G_IO_HUP, err, NULL);
+ g_io_add_watch(outchan, G_IO_ERR, err, NULL);
+ g_io_add_watch(outchan, G_IO_HUP, err, NULL);
+
+ // Get GConf client
+ GSettings* client;
+
+ for (int i=1; i<argc; i++) {
+ client = g_settings_new(argv[i]);
+ gchar** keys = g_settings_list_keys(client);
+ for (int j=0; keys[j]; on_value_change(client, keys[j++],argv[i] ));
+ g_signal_connect(client, "changed::", (GCallback) on_value_change, argv[i]);
+ }
+
+
+ g_main_loop_run(loop);
+
+ // Cleanup
+ while (G_IS_OBJECT(client)) {
+ g_object_unref(client);
+ }
+ g_io_channel_shutdown(inchan, FALSE, NULL);
+ g_io_channel_shutdown(outchan, FALSE, NULL);
+ g_io_channel_unref(inchan);
+ g_io_channel_unref(outchan);
+ g_main_loop_unref(loop);
+}