]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/curl-util.c
network: fix typo in log message
[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 23#include "fd-util.h"
e520e0fc 24#include "locale-util.h"
3ffd4af2 25#include "string-util.h"
72648326
LP
26
27static void curl_glue_check_finished(CurlGlue *g) {
28 CURLMsg *msg;
29 int k = 0;
30
31 assert(g);
32
33 msg = curl_multi_info_read(g->curl, &k);
34 if (!msg)
35 return;
36
37 if (msg->msg != CURLMSG_DONE)
38 return;
39
40 if (g->on_finished)
41 g->on_finished(g, msg->easy_handle, msg->data.result);
42}
43
44static int curl_glue_on_io(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
45 CurlGlue *g = userdata;
46 int action, k = 0, translated_fd;
47
48 assert(s);
49 assert(g);
50
23e096cc 51 translated_fd = PTR_TO_FD(hashmap_get(g->translate_fds, FD_TO_PTR(fd)));
72648326
LP
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
71static 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
23e096cc 80 io = hashmap_get(g->ios, FD_TO_PTR(s));
72648326
LP
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
23e096cc
LP
92 hashmap_remove(g->ios, FD_TO_PTR(s));
93 hashmap_remove(g->translate_fds, FD_TO_PTR(fd));
72648326
LP
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
edfd706d 142 (void) sd_event_source_set_description(io, "curl-io");
72648326 143
23e096cc 144 r = hashmap_put(g->ios, FD_TO_PTR(s), io);
72648326
LP
145 if (r < 0) {
146 log_oom();
147 sd_event_source_unref(io);
148 return -1;
149 }
150
23e096cc 151 r = hashmap_put(g->translate_fds, FD_TO_PTR(fd), FD_TO_PTR(s));
72648326
LP
152 if (r < 0) {
153 log_oom();
23e096cc 154 hashmap_remove(g->ios, FD_TO_PTR(s));
72648326
LP
155 sd_event_source_unref(io);
156 return -1;
157 }
158
159 fd = -1;
160 }
161
162 return 0;
163}
164
165static 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
181static 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
edfd706d 209 (void) sd_event_source_set_description(g->timer, "curl-timer");
72648326
LP
210 }
211
212 return 0;
213}
214
215CurlGlue *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
23e096cc 230 hashmap_remove(g->translate_fds, FD_TO_PTR(fd));
72648326
LP
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);
6b430fdb 240 return mfree(g);
72648326
LP
241}
242
243int curl_glue_new(CurlGlue **glue, sd_event *event) {
244 _cleanup_(curl_glue_unrefp) CurlGlue *g = NULL;
245 int r;
246
247 g = new0(CurlGlue, 1);
248 if (!g)
249 return -ENOMEM;
250
251 if (event)
252 g->event = sd_event_ref(event);
253 else {
254 r = sd_event_default(&g->event);
255 if (r < 0)
256 return r;
257 }
258
259 g->curl = curl_multi_init();
260 if (!g->curl)
261 return -ENOMEM;
262
263 if (curl_multi_setopt(g->curl, CURLMOPT_SOCKETDATA, g) != CURLM_OK)
264 return -EINVAL;
265
266 if (curl_multi_setopt(g->curl, CURLMOPT_SOCKETFUNCTION, curl_glue_socket_callback) != CURLM_OK)
267 return -EINVAL;
268
269 if (curl_multi_setopt(g->curl, CURLMOPT_TIMERDATA, g) != CURLM_OK)
270 return -EINVAL;
271
272 if (curl_multi_setopt(g->curl, CURLMOPT_TIMERFUNCTION, curl_glue_timer_callback) != CURLM_OK)
273 return -EINVAL;
274
275 *glue = g;
276 g = NULL;
277
278 return 0;
279}
280
281int curl_glue_make(CURL **ret, const char *url, void *userdata) {
282 const char *useragent;
283 CURL *c;
284 int r;
285
286 assert(ret);
287 assert(url);
288
289 c = curl_easy_init();
290 if (!c)
291 return -ENOMEM;
292
293 /* curl_easy_setopt(c, CURLOPT_VERBOSE, 1L); */
294
295 if (curl_easy_setopt(c, CURLOPT_URL, url) != CURLE_OK) {
296 r = -EIO;
297 goto fail;
298 }
299
300 if (curl_easy_setopt(c, CURLOPT_PRIVATE, userdata) != CURLE_OK) {
301 r = -EIO;
302 goto fail;
303 }
304
63c372cb 305 useragent = strjoina(program_invocation_short_name, "/" PACKAGE_VERSION);
72648326
LP
306 if (curl_easy_setopt(c, CURLOPT_USERAGENT, useragent) != CURLE_OK) {
307 r = -EIO;
308 goto fail;
309 }
310
311 if (curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L) != CURLE_OK) {
312 r = -EIO;
313 goto fail;
314 }
315
316 *ret = c;
317 return 0;
318
319fail:
320 curl_easy_cleanup(c);
321 return r;
322}
323
324int curl_glue_add(CurlGlue *g, CURL *c) {
325 assert(g);
326 assert(c);
327
328 if (curl_multi_add_handle(g->curl, c) != CURLM_OK)
329 return -EIO;
330
331 return 0;
332}
333
334void curl_glue_remove_and_free(CurlGlue *g, CURL *c) {
335 assert(g);
336
337 if (!c)
338 return;
339
340 if (g->curl)
341 curl_multi_remove_handle(g->curl, c);
342
343 curl_easy_cleanup(c);
344}
345
346struct curl_slist *curl_slist_new(const char *first, ...) {
347 struct curl_slist *l;
348 va_list ap;
349
350 if (!first)
351 return NULL;
352
353 l = curl_slist_append(NULL, first);
354 if (!l)
355 return NULL;
356
357 va_start(ap, first);
358
359 for (;;) {
360 struct curl_slist *n;
361 const char *i;
362
363 i = va_arg(ap, const char*);
364 if (!i)
365 break;
366
367 n = curl_slist_append(l, i);
368 if (!n) {
369 va_end(ap);
370 curl_slist_free_all(l);
371 return NULL;
372 }
373
374 l = n;
375 }
376
377 va_end(ap);
378 return l;
379}
380
381int curl_header_strdup(const void *contents, size_t sz, const char *field, char **value) {
382 const char *p = contents;
383 size_t l;
384 char *s;
385
386 l = strlen(field);
387 if (sz < l)
388 return 0;
389
390 if (memcmp(p, field, l) != 0)
391 return 0;
392
393 p += l;
394 sz -= l;
395
396 if (memchr(p, 0, sz))
397 return 0;
398
399 /* Skip over preceeding whitespace */
400 while (sz > 0 && strchr(WHITESPACE, p[0])) {
401 p++;
402 sz--;
403 }
404
13e785f7 405 /* Truncate trailing whitespace */
72648326
LP
406 while (sz > 0 && strchr(WHITESPACE, p[sz-1]))
407 sz--;
408
409 s = strndup(p, sz);
410 if (!s)
411 return -ENOMEM;
412
413 *value = s;
414 return 1;
415}
90199220 416
5fa89b2c 417int curl_parse_http_time(const char *t, usec_t *ret) {
e520e0fc 418 _cleanup_(freelocalep) locale_t loc = (locale_t) 0;
0193ad26 419 const char *e;
90199220
LP
420 struct tm tm;
421 time_t v;
422
423 assert(t);
424 assert(ret);
425
0193ad26
CR
426 loc = newlocale(LC_TIME_MASK, "C", (locale_t) 0);
427 if (loc == (locale_t) 0)
428 return -errno;
429
430 /* RFC822 */
431 e = strptime_l(t, "%a, %d %b %Y %H:%M:%S %Z", &tm, loc);
432 if (!e || *e != 0)
433 /* RFC 850 */
434 e = strptime_l(t, "%A, %d-%b-%y %H:%M:%S %Z", &tm, loc);
435 if (!e || *e != 0)
436 /* ANSI C */
437 e = strptime_l(t, "%a %b %d %H:%M:%S %Y", &tm, loc);
0193ad26
CR
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}