From 715875096564493ef5d27d95f6c61a4df4ca2e6e Mon Sep 17 00:00:00 2001 From: "dominique.leuenberger@gmail.com" Date: Thu, 28 Apr 2011 19:45:12 +0000 Subject: 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 --- NEWS | 2 + libproxy/cmake/modules.cmk | 3 +- libproxy/cmake/modules/config_gnome.cmk | 19 +- libproxy/modules/config_gnome3.cpp | 302 ++++++++++++++++++++++++++++++++ libproxy/modules/pxgsettings.cpp | 166 ++++++++++++++++++ 5 files changed, 487 insertions(+), 5 deletions(-) create mode 100644 libproxy/modules/config_gnome3.cpp create mode 100644 libproxy/modules/pxgsettings.cpp 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 + * + * 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 // For fileno(), fread(), pclose(), popen(), sscanf() +#include // For select() +#include // For fcntl() +#include // For errno stuff +#include // For stat() +#include // For stat() +#include // For pipe(), close(), vfork(), dup(), execl(), _exit() +#include // 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 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 + * Copyright (C) 2011 Dominique Leuenberger + * + * 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 +#include +#include +#include + +#include +#include +#include + +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