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