aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMarcel Holtmann <marcel@holtmann.org>2010-10-10 22:07:37 +0200
committerMarcel Holtmann <marcel@holtmann.org>2010-10-10 22:07:37 +0200
commit700698a00a97dae67055d502c0741696b317898e (patch)
treedcb91b27a6680eadc734e10846cbeec6d515b09e /src
parent818825d6dec810722cd9cdbf82fca8b62f08025d (diff)
downloadpacrunner-700698a00a97dae67055d502c0741696b317898e.tar.gz
Add support for downloading PAC scripts
Diffstat (limited to 'src')
-rw-r--r--src/download.c251
-rw-r--r--src/manager.c26
-rw-r--r--src/pacrunner.h4
3 files changed, 277 insertions, 4 deletions
diff --git a/src/download.c b/src/download.c
index 6a8244b..e0e025a 100644
--- a/src/download.c
+++ b/src/download.c
@@ -23,16 +23,267 @@
#include <config.h>
#endif
+#include <errno.h>
+
+#include <curl/curl.h>
+
#include "pacrunner.h"
+struct download_data {
+ char *url;
+ CURL *easy;
+ GString *content;
+ __pacrunner_download_cb callback;
+ void *user_data;
+};
+
+static size_t write_callback(void *ptr, size_t size, size_t nitems,
+ void *user_data)
+{
+ struct download_data *download = user_data;
+ size_t realsize = size * nitems;
+
+ DBG("size %zd nitems %zd realsize %zd", size, nitems, realsize);
+
+ g_string_append_len(download->content, ptr, realsize);
+
+ return realsize;
+}
+
+static void check_sockets(CURLM *multi, CURLMcode result, int handles)
+{
+ struct download_data *download;
+ CURLMsg *msg;
+ CURL *easy;
+ int msgs_left;
+ char *str, *eff_url;
+
+ DBG("result %d handles %d", result, handles);
+
+ do {
+ msg = curl_multi_info_read(multi, &msgs_left);
+ if (msg == NULL)
+ break;
+
+ if (msg->msg != CURLMSG_DONE)
+ continue;
+
+ easy = msg->easy_handle;
+
+ curl_easy_getinfo(easy, CURLINFO_PRIVATE, &str);
+ download = (void *) str;
+
+ curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &eff_url);
+
+ DBG("finished %s result %d", eff_url, msg->data.result);
+
+ curl_multi_remove_handle(multi, easy);
+ curl_easy_cleanup(easy);
+
+ if (download->callback) {
+ if (msg->data.result == 0) {
+ str = g_string_free(download->content, FALSE);
+ download->callback(str, download->user_data);
+ } else {
+ g_string_free(download->content, TRUE);
+ download->callback(NULL, download->user_data);
+ }
+ } else
+ g_string_free(download->content, TRUE);
+
+ g_free(download->url);
+ g_free(download);
+ } while (msgs_left > 0);
+}
+
+static guint timeout_source = 0;
+
+static gboolean event_callback(GIOChannel *channel,
+ GIOCondition condition, gpointer user_data)
+{
+ CURLM *multi = user_data;
+ CURLMcode result;
+ int sockfd, handles, action = 0;
+
+ DBG("condition %d", condition);
+
+ sockfd = g_io_channel_unix_get_fd(channel);
+
+ if (condition & G_IO_IN)
+ action |= CURL_CSELECT_IN;
+ if (condition & G_IO_OUT)
+ action |= CURL_CSELECT_OUT;
+
+ result = curl_multi_socket_action(multi, sockfd, action, &handles);
+
+ check_sockets(multi, result, handles);
+
+ if (handles)
+ return TRUE;
+
+ if (timeout_source > 0) {
+ g_source_remove(timeout_source);
+ timeout_source = 0;
+ }
+
+ return FALSE;
+}
+
+static int socket_callback(CURL *easy, curl_socket_t sockfd, int what,
+ void *user_data, void *socket_data)
+{
+ CURLM *multi = user_data;
+ guint source = GPOINTER_TO_UINT(socket_data);
+ GIOChannel *channel;
+
+ DBG("what %d source %d", what, source);
+
+ if (source > 0) {
+ g_source_remove(source);
+ source = 0;
+ }
+
+ switch (what) {
+ case CURL_POLL_NONE:
+ DBG("poll none");
+ break;
+ case CURL_POLL_IN:
+ DBG("poll in");
+ channel = g_io_channel_unix_new(sockfd);
+ source = g_io_add_watch(channel, G_IO_IN,
+ event_callback, multi);
+ g_io_channel_unref(channel);
+
+ curl_multi_assign(multi, sockfd, GUINT_TO_POINTER(source));
+ break;
+ case CURL_POLL_OUT:
+ DBG("poll out");
+ channel = g_io_channel_unix_new(sockfd);
+ source = g_io_add_watch(channel, G_IO_OUT,
+ event_callback, multi);
+ g_io_channel_unref(channel);
+
+ curl_multi_assign(multi, sockfd, GUINT_TO_POINTER(source));
+ break;
+ case CURL_POLL_INOUT:
+ DBG("poll in/out");
+ channel = g_io_channel_unix_new(sockfd);
+ source = g_io_add_watch(channel, G_IO_IN | G_IO_OUT,
+ event_callback, multi);
+ g_io_channel_unref(channel);
+
+ curl_multi_assign(multi, sockfd, GUINT_TO_POINTER(source));
+ break;
+ case CURL_POLL_REMOVE:
+ DBG("poll remove");
+ break;
+ }
+
+ DBG("source %d", source);
+
+ return 0;
+}
+
+static gboolean timeout_callback(gpointer user_data)
+{
+ CURLM *multi = user_data;
+ CURLMcode result;
+ int handles;
+
+ timeout_source = 0;
+
+ result = curl_multi_socket_all(multi, &handles);
+
+ check_sockets(multi, result, handles);
+
+ return FALSE;
+}
+
+static int timer_callback(CURLM *multi, long timeout_ms, void *user_data)
+{
+ guint interval = timeout_ms / 1000;
+
+ DBG("interval %d", interval);
+
+ if (timeout_source > 0) {
+ g_source_remove(timeout_source);
+ timeout_source = 0;
+ }
+
+ if (timeout_ms < 0)
+ return 0;
+
+ timeout_source = g_timeout_add_seconds(interval,
+ timeout_callback, multi);
+
+ return 0;
+}
+
+static CURLM *multi;
+
+int __pacrunner_download_update(const char *interface, const char *url,
+ __pacrunner_download_cb callback, void *user_data)
+{
+ struct download_data *download;
+ CURLMcode result;
+
+ DBG("url %s", url);
+
+ download = g_try_new0(struct download_data, 1);
+ if (download == NULL)
+ return -ENOMEM;
+
+ download->url = g_strdup(url);
+ download->easy = curl_easy_init();
+ download->content = g_string_sized_new(1024);
+
+ download->callback = callback;
+ download->user_data = user_data;
+
+ curl_easy_setopt(download->easy, CURLOPT_PRIVATE, download);
+ curl_easy_setopt(download->easy, CURLOPT_INTERFACE, interface);
+ curl_easy_setopt(download->easy, CURLOPT_URL, download->url);
+
+ curl_easy_setopt(download->easy, CURLOPT_WRITEFUNCTION, write_callback);
+ curl_easy_setopt(download->easy, CURLOPT_WRITEDATA, download);
+
+ curl_easy_setopt(download->easy, CURLOPT_NOPROGRESS, 1);
+ curl_easy_setopt(download->easy, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(download->easy, CURLOPT_NOPROXY, "*");
+ curl_easy_setopt(download->easy, CURLOPT_FOLLOWLOCATION, 1);
+ curl_easy_setopt(download->easy, CURLOPT_CONNECTTIMEOUT, 30);
+ curl_easy_setopt(download->easy, CURLOPT_USERAGENT, "pacrunner");
+
+ result = curl_multi_add_handle(multi, download->easy);
+
+ return 0;
+}
+
int __pacrunner_download_init(void)
{
DBG("");
+ multi = curl_multi_init();
+
+ curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, socket_callback);
+ curl_multi_setopt(multi, CURLMOPT_SOCKETDATA, multi);
+
+ curl_multi_setopt(multi, CURLMOPT_TIMERFUNCTION, timer_callback);
+ curl_multi_setopt(multi, CURLMOPT_TIMERDATA, NULL);
+
+ curl_multi_setopt(multi, CURLMOPT_PIPELINING, 1);
+
return 0;
}
void __pacrunner_download_cleanup(void)
{
DBG("");
+
+ if (timeout_source > 0) {
+ g_source_remove(timeout_source);
+ timeout_source = 0;
+ }
+
+ curl_multi_cleanup(multi);
}
diff --git a/src/manager.c b/src/manager.c
index 846e81a..0aaeecc 100644
--- a/src/manager.c
+++ b/src/manager.c
@@ -81,6 +81,24 @@ static void disconnect_callback(DBusConnection *conn, void *user_data)
g_hash_table_remove(config_list, config->path);
}
+static void download_callback(char *content, void *user_data)
+{
+ struct proxy_config *config = user_data;
+
+ DBG("url %s content %p", config->url, content);
+
+ if (content == NULL) {
+ pacrunner_error("Failed to retrieve PAC script");
+ goto done;
+ }
+
+ if (__pacrunner_mozjs_set_script(config->interface, content) < 0)
+ pacrunner_error("Failed to set retrieved PAC script");
+
+done:
+ g_free(content);
+}
+
static struct proxy_config *create_config(DBusConnection *conn,
const char *sender,
const char *method,
@@ -127,11 +145,11 @@ static struct proxy_config *create_config(DBusConnection *conn,
if (config->script != NULL) {
if (__pacrunner_mozjs_set_script(config->interface,
config->script) < 0)
- pacrunner_error("Failed to set PAC script");
+ pacrunner_error("Failed to set provided PAC script");
} else if (config->url != NULL) {
- if (__pacrunner_mozjs_load_url(config->interface,
- config->url) < 0)
- pacrunner_error("Failed to load PAC");
+ if (__pacrunner_download_update(config->interface,
+ config->url, download_callback, config) < 0)
+ pacrunner_error("Failed to start PAC script download");
}
return config;
diff --git a/src/pacrunner.h b/src/pacrunner.h
index c96130d..e681113 100644
--- a/src/pacrunner.h
+++ b/src/pacrunner.h
@@ -65,8 +65,12 @@ int __pacrunner_log_init(const char *debug, gboolean detach);
void __pacrunner_log_cleanup(void);
+typedef void (* __pacrunner_download_cb) (char *content, void *user_data);
+
int __pacrunner_download_init(void);
void __pacrunner_download_cleanup(void);
+int __pacrunner_download_update(const char *interface, const char *url,
+ __pacrunner_download_cb callback, void *user_data);
int __pacrunner_manager_init(DBusConnection *conn);
void __pacrunner_manager_cleanup();