#ifdef HAVE_CURL #include "hscurl.h" #include #include #include #include #include enum RESULT_CODES { RESULT_OK = 0, RESULT_MALLOC_FAIL, RESULT_SELECT_FAIL, RESULT_MULTI_INIT_FAIL, RESULT_EASY_INIT_FAIL, RESULT_SLIST_APPEND_FAIL, RESULT_NO_RUNNING_HANDLES, RESULT_MULTI_INFO_READ_FAIL, RESULT_UNKNOWN_MESSAGE, RESULT_FILE_OPEN_FAIL }; static const char *error_strings[] = { "", "malloc() failed", "select() failed", "curl_multi_init() failed", "curl_easy_init() failed", "curl_slist_append() failed", "curl_multi_perform() - no running handles", "curl_multi_info_read() failed", "curl_multi_info_read() returned unknown message", "fopen() failed" }; struct UrlData { char *url; FILE *file; struct curl_slist *headers; }; static int debug = 0; static const char user_agent[] = "darcs/" PACKAGE_VERSION " libcurl/" LIBCURL_VERSION; static const char *proxypass; static int init_done = 0; static CURLM *multi = NULL; static int msgs_in_queue = 0; static char *last_url = NULL; static const char *perform() { int error; int running_handles, running_handles_last; fd_set fd_read, fd_write, fd_except; int max_fd; long timeout; struct timeval tval; error = curl_multi_perform(multi, &running_handles); if (error != CURLM_OK && error != CURLM_CALL_MULTI_PERFORM) return curl_multi_strerror(error); if (running_handles == 0) return error_strings[RESULT_NO_RUNNING_HANDLES]; running_handles_last = running_handles; while (1) { while (error == CURLM_CALL_MULTI_PERFORM) error = curl_multi_perform(multi, &running_handles); if (error != CURLM_OK) return curl_multi_strerror(error); if (running_handles < running_handles_last) break; FD_ZERO(&fd_read); FD_ZERO(&fd_write); FD_ZERO(&fd_except); error = curl_multi_fdset(multi, &fd_read, &fd_write, &fd_except, &max_fd); if (error != CURLM_OK && error != CURLM_CALL_MULTI_PERFORM) return curl_multi_strerror(error); #ifdef CURL_MULTI_TIMEOUT error = curl_multi_timeout(multi, &timeout); if (error != CURLM_OK && error != CURLM_CALL_MULTI_PERFORM) return curl_multi_strerror(error); if (timeout == -1) #endif timeout = 100; tval.tv_sec = timeout / 1000; tval.tv_usec = timeout % 1000 * 1000; while (select(max_fd + 1, &fd_read, &fd_write, &fd_except, &tval) < 0) if (errno != EINTR) { if (debug) perror(error_strings[RESULT_SELECT_FAIL]); return error_strings[RESULT_SELECT_FAIL]; } error = CURLM_CALL_MULTI_PERFORM; } return NULL; } const char *curl_request_url(const char *url, const char *filename, int cache_time) { int error; if (init_done == 0) { error = curl_global_init(CURL_GLOBAL_ALL); if (error != CURLE_OK) return curl_easy_strerror(error); proxypass = getenv("DARCS_PROXYUSERPWD"); init_done = 1; } if (multi == NULL) { multi = curl_multi_init(); if (multi == NULL) return error_strings[RESULT_MULTI_INIT_FAIL]; #ifdef CURL_PIPELINING error = curl_multi_setopt(multi, CURLMOPT_PIPELINING, 1); if (error != CURLM_OK && error != CURLM_CALL_MULTI_PERFORM) return curl_multi_strerror(error); #endif } CURL *easy = curl_easy_init(); if (easy == NULL) return error_strings[RESULT_EASY_INIT_FAIL]; if (debug) { error = curl_easy_setopt(easy, CURLOPT_VERBOSE, 1); if (error != CURLE_OK) return curl_easy_strerror(error); } struct UrlData *url_data = malloc(sizeof(struct UrlData)); if (url_data == NULL) return error_strings[RESULT_MALLOC_FAIL]; url_data->url = strdup(url); if (url_data->url == NULL) return error_strings[RESULT_MALLOC_FAIL]; url_data->file = fopen(filename,"wb"); if (url_data->file == NULL) { if (debug) perror(error_strings[RESULT_FILE_OPEN_FAIL]); return error_strings[RESULT_FILE_OPEN_FAIL]; } error = curl_easy_setopt(easy, CURLOPT_PRIVATE, url_data); if (error != CURLE_OK) return curl_easy_strerror(error); error = curl_easy_setopt(easy, CURLOPT_URL, url_data->url); if (error != CURLE_OK) return curl_easy_strerror(error); #ifdef CURLOPT_WRITEDATA error = curl_easy_setopt(easy, CURLOPT_WRITEDATA, url_data->file); #else error = curl_easy_setopt(easy, CURLOPT_FILE, url_data->file); #endif if (error != CURLE_OK) return curl_easy_strerror(error); error = curl_easy_setopt(easy, CURLOPT_USERAGENT, user_agent); if (error != CURLE_OK) return curl_easy_strerror(error); error = curl_easy_setopt(easy, CURLOPT_FOLLOWLOCATION, 1); if (error != CURLE_OK) return curl_easy_strerror(error); error = curl_easy_setopt(easy, CURLOPT_FAILONERROR, 1); if (error != CURLE_OK) return curl_easy_strerror(error); error = curl_easy_setopt(easy, CURLOPT_HTTPAUTH, CURLAUTH_ANY); if (error != CURLE_OK) return curl_easy_strerror(error); /* libcurl currently always sends Pragma: no-cache, but never Cache-Control, which is contradictory. We override both, just to be sure. */ url_data->headers = curl_slist_append(NULL, "Accept: */*"); if(cache_time == 0) { url_data->headers = curl_slist_append(url_data->headers, "Pragma: no-cache"); url_data->headers = curl_slist_append(url_data->headers, "Cache-Control: no-cache"); } else if(cache_time > 0) { /* This won't work well with HTTP/1.0 proxies. */ char buf[40]; snprintf(buf, sizeof(buf), "Cache-Control: max-age=%d", cache_time); buf[sizeof(buf) - 1] = '\n'; url_data->headers = curl_slist_append(url_data->headers, "Pragma:"); url_data->headers = curl_slist_append(url_data->headers, buf); } else { url_data->headers = curl_slist_append(url_data->headers, "Pragma:"); url_data->headers = curl_slist_append(url_data->headers, "Cache-Control:"); } if (url_data->headers == NULL) return error_strings[RESULT_SLIST_APPEND_FAIL]; error = curl_easy_setopt(easy, CURLOPT_HTTPHEADER, url_data->headers); if (error != CURLE_OK) return curl_easy_strerror(error); if (proxypass && *proxypass) { error = curl_easy_setopt(easy, CURLOPT_PROXYUSERPWD, proxypass); if (error != CURLE_OK) return curl_easy_strerror(error); } error = curl_multi_add_handle(multi, easy); if (error != CURLM_OK && error != CURLM_CALL_MULTI_PERFORM) return curl_multi_strerror(error); return error_strings[RESULT_OK]; } const char *curl_wait_next_url() { if (last_url != NULL) { free(last_url); last_url = NULL; } if (msgs_in_queue == 0) { const char *error = perform(); if (error != NULL) return error; } CURLMsg *msg = curl_multi_info_read(multi, &msgs_in_queue); if (msg == NULL) return error_strings[RESULT_MULTI_INFO_READ_FAIL]; if (msg->msg == CURLMSG_DONE) { CURL *easy = msg->easy_handle; CURLcode result = msg->data.result; struct UrlData *url_data; int error = curl_easy_getinfo(easy, CURLINFO_PRIVATE, (char **)&url_data); if (error != CURLE_OK) return curl_easy_strerror(error); last_url = url_data->url; fclose(url_data->file); curl_slist_free_all(url_data->headers); free(url_data); error = curl_multi_remove_handle(multi, easy); if (error != CURLM_OK && error != CURLM_CALL_MULTI_PERFORM) return curl_multi_strerror(error); curl_easy_cleanup(easy); if (result != CURLE_OK) return curl_easy_strerror(result); } else return error_strings[RESULT_UNKNOWN_MESSAGE]; return error_strings[RESULT_OK]; } const char *curl_last_url() { return last_url != NULL ? last_url : ""; } void curl_enable_debug() { debug = 1; } #endif