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