diff options
author | Marcel Holtmann <marcel@holtmann.org> | 2010-10-10 22:07:37 +0200 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2010-10-10 22:07:37 +0200 |
commit | 700698a00a97dae67055d502c0741696b317898e (patch) | |
tree | dcb91b27a6680eadc734e10846cbeec6d515b09e /src | |
parent | 818825d6dec810722cd9cdbf82fca8b62f08025d (diff) | |
download | pacrunner-700698a00a97dae67055d502c0741696b317898e.tar.gz |
Add support for downloading PAC scripts
Diffstat (limited to 'src')
-rw-r--r-- | src/download.c | 251 | ||||
-rw-r--r-- | src/manager.c | 26 | ||||
-rw-r--r-- | src/pacrunner.h | 4 |
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(); |