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