]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/import/curl-util.c
util-lib: split our string related calls from util.[ch] into its own file string...
[thirdparty/systemd.git] / src / import / curl-util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include "string-util.h"
23 #include "curl-util.h"
24
25 static void curl_glue_check_finished(CurlGlue *g) {
26 CURLMsg *msg;
27 int k = 0;
28
29 assert(g);
30
31 msg = curl_multi_info_read(g->curl, &k);
32 if (!msg)
33 return;
34
35 if (msg->msg != CURLMSG_DONE)
36 return;
37
38 if (g->on_finished)
39 g->on_finished(g, msg->easy_handle, msg->data.result);
40 }
41
42 static int curl_glue_on_io(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
43 CurlGlue *g = userdata;
44 int action, k = 0, translated_fd;
45
46 assert(s);
47 assert(g);
48
49 translated_fd = PTR_TO_INT(hashmap_get(g->translate_fds, INT_TO_PTR(fd+1)));
50 assert(translated_fd > 0);
51 translated_fd--;
52
53 if ((revents & (EPOLLIN|EPOLLOUT)) == (EPOLLIN|EPOLLOUT))
54 action = CURL_POLL_INOUT;
55 else if (revents & EPOLLIN)
56 action = CURL_POLL_IN;
57 else if (revents & EPOLLOUT)
58 action = CURL_POLL_OUT;
59 else
60 action = 0;
61
62 if (curl_multi_socket_action(g->curl, translated_fd, action, &k) < 0) {
63 log_debug("Failed to propagate IO event.");
64 return -EINVAL;
65 }
66
67 curl_glue_check_finished(g);
68 return 0;
69 }
70
71 static int curl_glue_socket_callback(CURLM *curl, curl_socket_t s, int action, void *userdata, void *socketp) {
72 sd_event_source *io;
73 CurlGlue *g = userdata;
74 uint32_t events = 0;
75 int r;
76
77 assert(curl);
78 assert(g);
79
80 io = hashmap_get(g->ios, INT_TO_PTR(s+1));
81
82 if (action == CURL_POLL_REMOVE) {
83 if (io) {
84 int fd;
85
86 fd = sd_event_source_get_io_fd(io);
87 assert(fd >= 0);
88
89 sd_event_source_set_enabled(io, SD_EVENT_OFF);
90 sd_event_source_unref(io);
91
92 hashmap_remove(g->ios, INT_TO_PTR(s+1));
93 hashmap_remove(g->translate_fds, INT_TO_PTR(fd+1));
94
95 safe_close(fd);
96 }
97
98 return 0;
99 }
100
101 r = hashmap_ensure_allocated(&g->ios, &trivial_hash_ops);
102 if (r < 0) {
103 log_oom();
104 return -1;
105 }
106
107 r = hashmap_ensure_allocated(&g->translate_fds, &trivial_hash_ops);
108 if (r < 0) {
109 log_oom();
110 return -1;
111 }
112
113 if (action == CURL_POLL_IN)
114 events = EPOLLIN;
115 else if (action == CURL_POLL_OUT)
116 events = EPOLLOUT;
117 else if (action == CURL_POLL_INOUT)
118 events = EPOLLIN|EPOLLOUT;
119
120 if (io) {
121 if (sd_event_source_set_io_events(io, events) < 0)
122 return -1;
123
124 if (sd_event_source_set_enabled(io, SD_EVENT_ON) < 0)
125 return -1;
126 } else {
127 _cleanup_close_ int fd = -1;
128
129 /* When curl needs to remove an fd from us it closes
130 * the fd first, and only then calls into us. This is
131 * nasty, since we cannot pass the fd on to epoll()
132 * anymore. Hence, duplicate the fds here, and keep a
133 * copy for epoll which we control after use. */
134
135 fd = fcntl(s, F_DUPFD_CLOEXEC, 3);
136 if (fd < 0)
137 return -1;
138
139 if (sd_event_add_io(g->event, &io, fd, events, curl_glue_on_io, g) < 0)
140 return -1;
141
142 sd_event_source_set_description(io, "curl-io");
143
144 r = hashmap_put(g->ios, INT_TO_PTR(s+1), io);
145 if (r < 0) {
146 log_oom();
147 sd_event_source_unref(io);
148 return -1;
149 }
150
151 r = hashmap_put(g->translate_fds, INT_TO_PTR(fd+1), INT_TO_PTR(s+1));
152 if (r < 0) {
153 log_oom();
154 hashmap_remove(g->ios, INT_TO_PTR(s+1));
155 sd_event_source_unref(io);
156 return -1;
157 }
158
159 fd = -1;
160 }
161
162 return 0;
163 }
164
165 static int curl_glue_on_timer(sd_event_source *s, uint64_t usec, void *userdata) {
166 CurlGlue *g = userdata;
167 int k = 0;
168
169 assert(s);
170 assert(g);
171
172 if (curl_multi_socket_action(g->curl, CURL_SOCKET_TIMEOUT, 0, &k) != CURLM_OK) {
173 log_debug("Failed to propagate timeout.");
174 return -EINVAL;
175 }
176
177 curl_glue_check_finished(g);
178 return 0;
179 }
180
181 static int curl_glue_timer_callback(CURLM *curl, long timeout_ms, void *userdata) {
182 CurlGlue *g = userdata;
183 usec_t usec;
184
185 assert(curl);
186 assert(g);
187
188 if (timeout_ms < 0) {
189 if (g->timer) {
190 if (sd_event_source_set_enabled(g->timer, SD_EVENT_OFF) < 0)
191 return -1;
192 }
193
194 return 0;
195 }
196
197 usec = now(clock_boottime_or_monotonic()) + (usec_t) timeout_ms * USEC_PER_MSEC + USEC_PER_MSEC - 1;
198
199 if (g->timer) {
200 if (sd_event_source_set_time(g->timer, usec) < 0)
201 return -1;
202
203 if (sd_event_source_set_enabled(g->timer, SD_EVENT_ONESHOT) < 0)
204 return -1;
205 } else {
206 if (sd_event_add_time(g->event, &g->timer, clock_boottime_or_monotonic(), usec, 0, curl_glue_on_timer, g) < 0)
207 return -1;
208
209 sd_event_source_set_description(g->timer, "curl-timer");
210 }
211
212 return 0;
213 }
214
215 CurlGlue *curl_glue_unref(CurlGlue *g) {
216 sd_event_source *io;
217
218 if (!g)
219 return NULL;
220
221 if (g->curl)
222 curl_multi_cleanup(g->curl);
223
224 while ((io = hashmap_steal_first(g->ios))) {
225 int fd;
226
227 fd = sd_event_source_get_io_fd(io);
228 assert(fd >= 0);
229
230 hashmap_remove(g->translate_fds, INT_TO_PTR(fd+1));
231
232 safe_close(fd);
233 sd_event_source_unref(io);
234 }
235
236 hashmap_free(g->ios);
237
238 sd_event_source_unref(g->timer);
239 sd_event_unref(g->event);
240 free(g);
241
242 return NULL;
243 }
244
245 int curl_glue_new(CurlGlue **glue, sd_event *event) {
246 _cleanup_(curl_glue_unrefp) CurlGlue *g = NULL;
247 int r;
248
249 g = new0(CurlGlue, 1);
250 if (!g)
251 return -ENOMEM;
252
253 if (event)
254 g->event = sd_event_ref(event);
255 else {
256 r = sd_event_default(&g->event);
257 if (r < 0)
258 return r;
259 }
260
261 g->curl = curl_multi_init();
262 if (!g->curl)
263 return -ENOMEM;
264
265 if (curl_multi_setopt(g->curl, CURLMOPT_SOCKETDATA, g) != CURLM_OK)
266 return -EINVAL;
267
268 if (curl_multi_setopt(g->curl, CURLMOPT_SOCKETFUNCTION, curl_glue_socket_callback) != CURLM_OK)
269 return -EINVAL;
270
271 if (curl_multi_setopt(g->curl, CURLMOPT_TIMERDATA, g) != CURLM_OK)
272 return -EINVAL;
273
274 if (curl_multi_setopt(g->curl, CURLMOPT_TIMERFUNCTION, curl_glue_timer_callback) != CURLM_OK)
275 return -EINVAL;
276
277 *glue = g;
278 g = NULL;
279
280 return 0;
281 }
282
283 int curl_glue_make(CURL **ret, const char *url, void *userdata) {
284 const char *useragent;
285 CURL *c;
286 int r;
287
288 assert(ret);
289 assert(url);
290
291 c = curl_easy_init();
292 if (!c)
293 return -ENOMEM;
294
295 /* curl_easy_setopt(c, CURLOPT_VERBOSE, 1L); */
296
297 if (curl_easy_setopt(c, CURLOPT_URL, url) != CURLE_OK) {
298 r = -EIO;
299 goto fail;
300 }
301
302 if (curl_easy_setopt(c, CURLOPT_PRIVATE, userdata) != CURLE_OK) {
303 r = -EIO;
304 goto fail;
305 }
306
307 useragent = strjoina(program_invocation_short_name, "/" PACKAGE_VERSION);
308 if (curl_easy_setopt(c, CURLOPT_USERAGENT, useragent) != CURLE_OK) {
309 r = -EIO;
310 goto fail;
311 }
312
313 if (curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L) != CURLE_OK) {
314 r = -EIO;
315 goto fail;
316 }
317
318 *ret = c;
319 return 0;
320
321 fail:
322 curl_easy_cleanup(c);
323 return r;
324 }
325
326 int curl_glue_add(CurlGlue *g, CURL *c) {
327 assert(g);
328 assert(c);
329
330 if (curl_multi_add_handle(g->curl, c) != CURLM_OK)
331 return -EIO;
332
333 return 0;
334 }
335
336 void curl_glue_remove_and_free(CurlGlue *g, CURL *c) {
337 assert(g);
338
339 if (!c)
340 return;
341
342 if (g->curl)
343 curl_multi_remove_handle(g->curl, c);
344
345 curl_easy_cleanup(c);
346 }
347
348 struct curl_slist *curl_slist_new(const char *first, ...) {
349 struct curl_slist *l;
350 va_list ap;
351
352 if (!first)
353 return NULL;
354
355 l = curl_slist_append(NULL, first);
356 if (!l)
357 return NULL;
358
359 va_start(ap, first);
360
361 for (;;) {
362 struct curl_slist *n;
363 const char *i;
364
365 i = va_arg(ap, const char*);
366 if (!i)
367 break;
368
369 n = curl_slist_append(l, i);
370 if (!n) {
371 va_end(ap);
372 curl_slist_free_all(l);
373 return NULL;
374 }
375
376 l = n;
377 }
378
379 va_end(ap);
380 return l;
381 }
382
383 int curl_header_strdup(const void *contents, size_t sz, const char *field, char **value) {
384 const char *p = contents;
385 size_t l;
386 char *s;
387
388 l = strlen(field);
389 if (sz < l)
390 return 0;
391
392 if (memcmp(p, field, l) != 0)
393 return 0;
394
395 p += l;
396 sz -= l;
397
398 if (memchr(p, 0, sz))
399 return 0;
400
401 /* Skip over preceeding whitespace */
402 while (sz > 0 && strchr(WHITESPACE, p[0])) {
403 p++;
404 sz--;
405 }
406
407 /* Truncate trailing whitespace*/
408 while (sz > 0 && strchr(WHITESPACE, p[sz-1]))
409 sz--;
410
411 s = strndup(p, sz);
412 if (!s)
413 return -ENOMEM;
414
415 *value = s;
416 return 1;
417 }
418
419 int curl_parse_http_time(const char *t, usec_t *ret) {
420 const char *e;
421 locale_t loc;
422 struct tm tm;
423 time_t v;
424
425 assert(t);
426 assert(ret);
427
428 loc = newlocale(LC_TIME_MASK, "C", (locale_t) 0);
429 if (loc == (locale_t) 0)
430 return -errno;
431
432 /* RFC822 */
433 e = strptime_l(t, "%a, %d %b %Y %H:%M:%S %Z", &tm, loc);
434 if (!e || *e != 0)
435 /* RFC 850 */
436 e = strptime_l(t, "%A, %d-%b-%y %H:%M:%S %Z", &tm, loc);
437 if (!e || *e != 0)
438 /* ANSI C */
439 e = strptime_l(t, "%a %b %d %H:%M:%S %Y", &tm, loc);
440 freelocale(loc);
441 if (!e || *e != 0)
442 return -EINVAL;
443
444 v = timegm(&tm);
445 if (v == (time_t) -1)
446 return -EINVAL;
447
448 *ret = (usec_t) v * USEC_PER_SEC;
449 return 0;
450 }