Follow upstream changes -- rest
[git-darcs-import.git] / src / hscurl.c
blob6fe3bd9ebf05e7276ede52b4e849c6dd24889def
1 #ifdef HAVE_CURL
3 #include "hscurl.h"
5 #include <curl/curl.h>
6 #include <errno.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
11 enum RESULT_CODES
13 RESULT_OK = 0,
14 RESULT_MALLOC_FAIL,
15 RESULT_SELECT_FAIL,
16 RESULT_MULTI_INIT_FAIL,
17 RESULT_EASY_INIT_FAIL,
18 RESULT_SLIST_APPEND_FAIL,
19 RESULT_NO_RUNNING_HANDLES,
20 RESULT_MULTI_INFO_READ_FAIL,
21 RESULT_UNKNOWN_MESSAGE,
22 RESULT_FILE_OPEN_FAIL
25 static const char *error_strings[] =
27 "",
28 "malloc() failed",
29 "select() failed",
30 "curl_multi_init() failed",
31 "curl_easy_init() failed",
32 "curl_slist_append() failed",
33 "curl_multi_perform() - no running handles",
34 "curl_multi_info_read() failed",
35 "curl_multi_info_read() returned unknown message",
36 "fopen() failed"
39 struct UrlData
41 char *url;
42 FILE *file;
43 struct curl_slist *headers;
46 static int debug = 0;
47 static const char user_agent[] =
48 "darcs/" PACKAGE_VERSION " libcurl/" LIBCURL_VERSION;
49 static const char *proxypass;
50 static int init_done = 0;
51 static CURLM *multi = NULL;
52 static int msgs_in_queue = 0;
53 static char *last_url = NULL;
55 static const char *perform()
57 int error;
58 int running_handles, running_handles_last;
59 fd_set fd_read, fd_write, fd_except;
60 int max_fd;
61 long timeout;
62 struct timeval tval;
64 error = curl_multi_perform(multi, &running_handles);
65 if (error != CURLM_OK && error != CURLM_CALL_MULTI_PERFORM)
66 return curl_multi_strerror(error);
67 if (running_handles == 0)
68 return error_strings[RESULT_NO_RUNNING_HANDLES];
70 running_handles_last = running_handles;
71 while (1)
73 while (error == CURLM_CALL_MULTI_PERFORM)
74 error = curl_multi_perform(multi, &running_handles);
76 if (error != CURLM_OK)
77 return curl_multi_strerror(error);
79 if (running_handles < running_handles_last)
80 break;
82 FD_ZERO(&fd_read);
83 FD_ZERO(&fd_write);
84 FD_ZERO(&fd_except);
86 error = curl_multi_fdset(multi, &fd_read, &fd_write, &fd_except, &max_fd);
87 if (error != CURLM_OK && error != CURLM_CALL_MULTI_PERFORM)
88 return curl_multi_strerror(error);
90 #ifdef CURL_MULTI_TIMEOUT
91 error = curl_multi_timeout(multi, &timeout);
92 if (error != CURLM_OK && error != CURLM_CALL_MULTI_PERFORM)
93 return curl_multi_strerror(error);
95 if (timeout == -1)
96 #endif
97 timeout = 100;
99 tval.tv_sec = timeout / 1000;
100 tval.tv_usec = timeout % 1000 * 1000;
102 while (select(max_fd + 1, &fd_read, &fd_write, &fd_except, &tval) < 0)
103 if (errno != EINTR)
105 if (debug)
106 perror(error_strings[RESULT_SELECT_FAIL]);
107 return error_strings[RESULT_SELECT_FAIL];
110 error = CURLM_CALL_MULTI_PERFORM;
113 return NULL;
116 const char *curl_request_url(const char *url,
117 const char *filename,
118 int cache_time)
120 int error;
122 if (init_done == 0)
124 error = curl_global_init(CURL_GLOBAL_ALL);
125 if (error != CURLE_OK)
126 return curl_easy_strerror(error);
127 proxypass = getenv("DARCS_PROXYUSERPWD");
128 init_done = 1;
131 if (multi == NULL)
133 multi = curl_multi_init();
134 if (multi == NULL)
135 return error_strings[RESULT_MULTI_INIT_FAIL];
136 #ifdef CURL_PIPELINING
137 error = curl_multi_setopt(multi, CURLMOPT_PIPELINING, 1);
138 if (error != CURLM_OK && error != CURLM_CALL_MULTI_PERFORM)
139 return curl_multi_strerror(error);
140 #endif
143 CURL *easy = curl_easy_init();
144 if (easy == NULL)
145 return error_strings[RESULT_EASY_INIT_FAIL];
147 if (debug)
149 error = curl_easy_setopt(easy, CURLOPT_VERBOSE, 1);
150 if (error != CURLE_OK)
151 return curl_easy_strerror(error);
154 struct UrlData *url_data = malloc(sizeof(struct UrlData));
155 if (url_data == NULL)
156 return error_strings[RESULT_MALLOC_FAIL];
158 url_data->url = strdup(url);
159 if (url_data->url == NULL)
160 return error_strings[RESULT_MALLOC_FAIL];
162 url_data->file = fopen(filename,"wb");
163 if (url_data->file == NULL)
165 if (debug)
166 perror(error_strings[RESULT_FILE_OPEN_FAIL]);
167 return error_strings[RESULT_FILE_OPEN_FAIL];
170 error = curl_easy_setopt(easy, CURLOPT_PRIVATE, url_data);
171 if (error != CURLE_OK)
172 return curl_easy_strerror(error);
174 error = curl_easy_setopt(easy, CURLOPT_URL, url_data->url);
175 if (error != CURLE_OK)
176 return curl_easy_strerror(error);
178 #ifdef CURLOPT_WRITEDATA
179 error = curl_easy_setopt(easy, CURLOPT_WRITEDATA, url_data->file);
180 #else
181 error = curl_easy_setopt(easy, CURLOPT_FILE, url_data->file);
182 #endif
183 if (error != CURLE_OK)
184 return curl_easy_strerror(error);
186 error = curl_easy_setopt(easy, CURLOPT_USERAGENT, user_agent);
187 if (error != CURLE_OK)
188 return curl_easy_strerror(error);
190 error = curl_easy_setopt(easy, CURLOPT_FOLLOWLOCATION, 1);
191 if (error != CURLE_OK)
192 return curl_easy_strerror(error);
194 error = curl_easy_setopt(easy, CURLOPT_FAILONERROR, 1);
195 if (error != CURLE_OK)
196 return curl_easy_strerror(error);
198 error = curl_easy_setopt(easy, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
199 if (error != CURLE_OK)
200 return curl_easy_strerror(error);
202 /* libcurl currently always sends Pragma: no-cache, but never
203 Cache-Control, which is contradictory. We override both, just to
204 be sure. */
205 url_data->headers = curl_slist_append(NULL, "Accept: */*");
206 if(cache_time == 0)
208 url_data->headers =
209 curl_slist_append(url_data->headers, "Pragma: no-cache");
210 url_data->headers =
211 curl_slist_append(url_data->headers, "Cache-Control: no-cache");
213 else if(cache_time > 0)
215 /* This won't work well with HTTP/1.0 proxies. */
216 char buf[40];
217 snprintf(buf, sizeof(buf), "Cache-Control: max-age=%d", cache_time);
218 buf[sizeof(buf) - 1] = '\n';
219 url_data->headers = curl_slist_append(url_data->headers, "Pragma:");
220 url_data->headers = curl_slist_append(url_data->headers, buf);
222 else
224 url_data->headers = curl_slist_append(url_data->headers, "Pragma:");
225 url_data->headers = curl_slist_append(url_data->headers, "Cache-Control:");
227 if (url_data->headers == NULL)
228 return error_strings[RESULT_SLIST_APPEND_FAIL];
230 error = curl_easy_setopt(easy, CURLOPT_HTTPHEADER, url_data->headers);
231 if (error != CURLE_OK)
232 return curl_easy_strerror(error);
234 if (proxypass && *proxypass)
236 error = curl_easy_setopt(easy, CURLOPT_PROXYUSERPWD, proxypass);
237 if (error != CURLE_OK)
238 return curl_easy_strerror(error);
241 error = curl_multi_add_handle(multi, easy);
242 if (error != CURLM_OK && error != CURLM_CALL_MULTI_PERFORM)
243 return curl_multi_strerror(error);
245 return error_strings[RESULT_OK];
248 const char *curl_wait_next_url()
250 if (last_url != NULL)
252 free(last_url);
253 last_url = NULL;
256 if (msgs_in_queue == 0)
258 const char *error = perform();
259 if (error != NULL)
260 return error;
263 CURLMsg *msg = curl_multi_info_read(multi, &msgs_in_queue);
264 if (msg == NULL)
265 return error_strings[RESULT_MULTI_INFO_READ_FAIL];
267 if (msg->msg == CURLMSG_DONE)
269 CURL *easy = msg->easy_handle;
270 CURLcode result = msg->data.result;
271 struct UrlData *url_data;
272 int error = curl_easy_getinfo(easy, CURLINFO_PRIVATE, (char **)&url_data);
273 if (error != CURLE_OK)
274 return curl_easy_strerror(error);
276 last_url = url_data->url;
277 fclose(url_data->file);
278 curl_slist_free_all(url_data->headers);
279 free(url_data);
281 error = curl_multi_remove_handle(multi, easy);
282 if (error != CURLM_OK && error != CURLM_CALL_MULTI_PERFORM)
283 return curl_multi_strerror(error);
284 curl_easy_cleanup(easy);
286 if (result != CURLE_OK)
287 return curl_easy_strerror(result);
289 else
290 return error_strings[RESULT_UNKNOWN_MESSAGE];
292 return error_strings[RESULT_OK];
295 const char *curl_last_url()
297 return last_url != NULL ? last_url : "";
300 void curl_enable_debug()
302 debug = 1;
305 #endif