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