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