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