Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/libpkg/fetch_libcurl.c
2645 views
1
/*-
2
* Copyright (c) 2023 Baptiste Daroussin <[email protected]>
3
* Copyright (c) 2023 Serenity Cyber Security, LLC <[email protected]>
4
* Author: Gleb Popov <[email protected]>
5
*
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
8
* are met:
9
* 1. Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer
11
* in this position and unchanged.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
17
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
20
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
*/
27
28
#include <sys/param.h>
29
#include <stdlib.h>
30
#include <curl/curl.h>
31
#include <ctype.h>
32
33
#include "pkg.h"
34
#include "private/pkg.h"
35
#include "private/event.h"
36
#include "private/fetch.h"
37
38
#define curl_response_is_ok(res) ((res) == 200 || (res) == 206)
39
40
/*
41
* The choice of 2KB/s is arbitrary; at some point this should be configurable.
42
*/
43
#define LIBPKG_SPEED_LIMIT (2 * 1024) /* bytes per second */
44
45
struct curl_repodata {
46
CURLM *cm;
47
CURLU *url;
48
};
49
50
struct curl_userdata {
51
int fd;
52
CURL *cl;
53
FILE *fh;
54
off_t offset;
55
size_t size;
56
size_t totalsize;
57
size_t content_length;
58
bool started;
59
const char *url;
60
long response;
61
};
62
63
struct http_mirror {
64
CURLU *url;
65
struct http_mirror *next;
66
};
67
68
static
69
void dump(const char *text,
70
unsigned char *ptr, size_t size)
71
{
72
size_t i;
73
size_t c;
74
75
xstring *dumpstr = NULL;
76
77
unsigned int width = 0x40;
78
79
pkg_dbg(PKG_DBG_FETCH, 1, "%s, %10.10lu bytes (0x%8.8lx)", text, (unsigned long)size, (unsigned long)size);
80
81
for(i = 0; i<size; i += width) {
82
xstring_renew(dumpstr);
83
84
for(c = 0; (c < width) && (i + c < size); c++) {
85
/* check for 0D0A; if found, skip past and start a new line of output */
86
if((i + c + 1 < size) && ptr[i + c] == 0x0D &&
87
ptr[i + c + 1] == 0x0A) {
88
i += (c + 2 - width);
89
break;
90
}
91
fprintf(dumpstr->fp, "%c",
92
(ptr[i + c] >= 0x20) && (ptr[i + c]<0x80)?ptr[i + c]:'.');
93
/* check again for 0D0A, to avoid an extra \n if it's at width */
94
if((i + c + 2 < size) && ptr[i + c + 1] == 0x0D &&
95
ptr[i + c + 2] == 0x0A) {
96
i += (c + 3 - width);
97
break;
98
}
99
}
100
fflush(dumpstr->fp);
101
pkg_dbg(PKG_DBG_FETCH, 1, "%s" , dumpstr->buf);
102
}
103
xstring_free(dumpstr);
104
}
105
106
static
107
int my_trace(CURL *handle, curl_infotype type,
108
char *data, size_t size,
109
void *userp __unused)
110
{
111
const char *text;
112
(void)handle; /* prevent compiler warning */
113
114
switch(type) {
115
case CURLINFO_TEXT:
116
pkg_dbg(PKG_DBG_FETCH, 1, "== Info: %s", data);
117
/* FALLTHROUGH */
118
default: /* in case a new one is introduced to shock us */
119
return 0;
120
121
case CURLINFO_HEADER_OUT:
122
text = "=> Send header";
123
124
break;
125
case CURLINFO_DATA_OUT:
126
text = "=> Send data";
127
break;
128
case CURLINFO_SSL_DATA_OUT:
129
text = "=> Send SSL data";
130
size = 0;
131
break;
132
case CURLINFO_HEADER_IN:
133
text = "<= Recv header";
134
break;
135
case CURLINFO_DATA_IN:
136
text = "<= Recv data";
137
break;
138
case CURLINFO_SSL_DATA_IN:
139
text = "<= Recv SSL data";
140
size = 0;
141
break;
142
}
143
144
dump(text, (unsigned char *)data, size);
145
return 0;
146
}
147
148
/* Returns the HTTP response code on success.
149
* Returns -1 and sets the `error` out parameter on failure. */
150
static long
151
curl_do_fetch(struct curl_userdata *data, CURL *cl, struct curl_repodata *cr, CURLcode *error)
152
{
153
char *tmp;
154
int still_running = 1;
155
CURLMsg *msg;
156
int msg_left;
157
158
curl_easy_setopt(cl, CURLOPT_FOLLOWLOCATION, 1L);
159
curl_easy_setopt(cl, CURLOPT_PRIVATE, &data);
160
if (data->offset > 0)
161
curl_easy_setopt(cl, CURLOPT_RESUME_FROM_LARGE, data->offset);
162
if (((ctx.debug_flags & (PKG_DBG_FETCH|PKG_DBG_ALL)) != 0) &&
163
ctx.debug_level >= 1)
164
curl_easy_setopt(cl, CURLOPT_VERBOSE, 1L);
165
if (((ctx.debug_flags & (PKG_DBG_FETCH|PKG_DBG_ALL)) != 0) &&
166
ctx.debug_level >= 1)
167
curl_easy_setopt(cl, CURLOPT_DEBUGFUNCTION, my_trace);
168
169
/* compat with libfetch */
170
if ((tmp = getenv("HTTP_USER_AGENT")) != NULL)
171
curl_easy_setopt(cl, CURLOPT_USERAGENT, tmp);
172
if (getenv("SSL_NO_VERIFY_PEER") != NULL)
173
curl_easy_setopt(cl, CURLOPT_SSL_VERIFYPEER, 0L);
174
if (getenv("SSL_NO_VERIFY_HOSTNAME") != NULL)
175
curl_easy_setopt(cl, CURLOPT_SSL_VERIFYHOST, 0L);
176
curl_multi_add_handle(cr->cm, cl);
177
178
while(still_running) {
179
CURLMcode mc = curl_multi_perform(cr->cm, &still_running);
180
181
if(still_running)
182
/* wait for activity, timeout or "nothing" */
183
mc = curl_multi_poll(cr->cm, NULL, 0, 1000, NULL);
184
185
if(mc)
186
break;
187
}
188
189
while ((msg = curl_multi_info_read(cr->cm, &msg_left))) {
190
if (msg->msg == CURLMSG_DONE) {
191
if (msg->data.result == CURLE_COULDNT_CONNECT
192
|| msg->data.result == CURLE_COULDNT_RESOLVE_HOST
193
|| msg->data.result == CURLE_COULDNT_RESOLVE_PROXY) {
194
pkg_emit_pkg_errno(EPKG_NONETWORK, "curl_do_fetch", NULL);
195
}
196
if (msg->data.result != CURLE_OK) {
197
if (error != NULL) {
198
*error = msg->data.result;
199
}
200
return (-1);
201
}
202
CURL *eh = msg->easy_handle;
203
long response_code = 0;
204
curl_easy_getinfo(eh, CURLINFO_RESPONSE_CODE, &response_code);
205
206
const char *ip = NULL;
207
if (curl_easy_getinfo(eh, CURLINFO_PRIMARY_IP, &ip) == CURLE_OK && ip != NULL) {
208
pkg_dbg(PKG_DBG_FETCH, 1, "CURL> connected to IP %s", ip);
209
}
210
211
return (response_code);
212
}
213
}
214
return (0);
215
}
216
217
static size_t
218
curl_write_cb(char *data, size_t size, size_t nmemb, void *userdata)
219
{
220
struct curl_userdata *d = (struct curl_userdata *)userdata;
221
size_t written;
222
223
written = fwrite(data, size, nmemb, d->fh);
224
d->size += written;
225
d->offset += written;
226
227
return (written);
228
}
229
230
static struct http_mirror *
231
http_getmirrors(struct pkg_repo *r, struct curl_repodata *cr)
232
{
233
CURL *cl;
234
struct curl_userdata data = { 0 };
235
char *buf = NULL, *walk, *line;
236
size_t cap = 0;
237
struct http_mirror *m, *mirrors = NULL;
238
CURLU *url;
239
pkg_dbg(PKG_DBG_FETCH, 2, "CURL> fetching http mirror list if any");
240
241
cl = curl_easy_init();
242
data.fh = open_memstream(& buf, &cap);
243
data.cl = cl;
244
245
curl_easy_setopt(cl, CURLOPT_WRITEFUNCTION, curl_write_cb);
246
curl_easy_setopt(cl, CURLOPT_WRITEDATA, &data);
247
curl_easy_setopt(cl, CURLOPT_MAXFILESIZE_LARGE, 1048576L);
248
curl_easy_setopt(cl, CURLOPT_URL, r->url);
249
curl_easy_setopt(cl, CURLOPT_NOPROGRESS, 1L);
250
data.url = r->url;
251
if (ctx.ip == IPV4)
252
curl_easy_setopt(cl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
253
if (ctx.ip == IPV6)
254
curl_easy_setopt(cl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
255
// FIXME: handle fetch error?
256
curl_do_fetch(&data, cl, cr, NULL);
257
fclose(data.fh);
258
walk = buf;
259
while ((line = strsep(&walk, "\n\r")) != NULL) {
260
if (strncmp(line, "URL:", 4) != 0)
261
continue;
262
line += 4;
263
while (isspace(*line))
264
line++;
265
if (*line == '\0')
266
continue;
267
url = curl_url();
268
if (curl_url_set(url, CURLUPART_URL, line, 0)) {
269
curl_url_cleanup(url);
270
pkg_emit_error("Invalid mirror url: '%s'", line);
271
continue;
272
}
273
m = xmalloc(sizeof(*m));
274
m->url = url;
275
pkg_dbg(PKG_DBG_FETCH, 2, "CURL> appending an http mirror: %s", line);
276
LL_APPEND(mirrors, m);
277
}
278
free(buf);
279
280
return (mirrors);
281
}
282
283
static size_t
284
curl_parseheader_cb(void *ptr __unused, size_t size, size_t nmemb, void *userdata)
285
{
286
struct curl_userdata *d = (struct curl_userdata *)userdata;
287
288
curl_easy_getinfo(d->cl, CURLINFO_RESPONSE_CODE, &d->response);
289
if (d->response == 404)
290
return (CURLE_WRITE_ERROR);
291
if (curl_response_is_ok(d->response) && !d->started) {
292
pkg_emit_fetch_begin(d->url);
293
pkg_emit_progress_start(NULL);
294
d->started = true;
295
}
296
297
return (size * nmemb);
298
}
299
300
static int
301
curl_progress_cb(void *userdata, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal __unused, curl_off_t ulnow __unused)
302
{
303
struct curl_userdata *d = (struct curl_userdata *)userdata;
304
305
if (!curl_response_is_ok(d->response))
306
return (0);
307
308
return pkg_emit_progress_tick(dlnow, dltotal);
309
}
310
311
int
312
curl_open(struct pkg_repo *repo, struct fetch_item *fi __unused)
313
{
314
struct curl_repodata *cr;
315
pkg_dbg(PKG_DBG_FETCH, 2, "curl_open");
316
317
if (repo->fetch_priv != NULL)
318
return (EPKG_OK);
319
320
curl_global_init(CURL_GLOBAL_ALL);
321
cr = xcalloc(1, sizeof(*cr));
322
cr->cm = curl_multi_init();
323
curl_multi_setopt(cr->cm, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
324
curl_multi_setopt(cr->cm, CURLMOPT_MAX_HOST_CONNECTIONS, 1);
325
/*curl_multi_setopt(cm, CURLMOPT_MAX_HOST_TOTAL_CONNECTIONS, 4);*/
326
if (repo->mirror_type == SRV && repo->srv == NULL) {
327
int urloff = 0;
328
cr->url = curl_url();
329
if (strncasecmp(repo->url, "pkg+", 4) == 0)
330
urloff = 4;
331
CURLUcode c = curl_url_set(cr->url, CURLUPART_URL, repo->url + urloff, 0);
332
if (c) {
333
pkg_emit_error("impossible to parse url: '%s'", repo->url);
334
return (EPKG_FATAL);
335
}
336
337
char *zone;
338
char *host = NULL, *scheme = NULL;
339
curl_url_get(cr->url, CURLUPART_HOST, &host, 0);
340
curl_url_get(cr->url, CURLUPART_SCHEME, &scheme, 0);
341
xasprintf(&zone, "_%s._tcp.%s", scheme, host);
342
repo->srv = dns_getsrvinfo(zone);
343
free(zone);
344
free(host);
345
free(scheme);
346
if (repo->srv == NULL) {
347
pkg_emit_error("No SRV record found for the "
348
"repo '%s'", repo->name);
349
repo->mirror_type = NOMIRROR;
350
}
351
}
352
if (repo->mirror_type == HTTP && repo->http == NULL) {
353
if (strncasecmp(repo->url, "pkg+", 4) == 0) {
354
pkg_emit_error("invalid for http mirror mechanism "
355
"scheme '%s'", repo->url);
356
return (EPKG_FATAL);
357
}
358
cr->url = curl_url();
359
CURLUcode c = curl_url_set(cr->url, CURLUPART_URL, repo->url, 0);
360
if (c) {
361
pkg_emit_error("impossible to parse url: '%s'", repo->url);
362
return (EPKG_FATAL);
363
}
364
repo->http = http_getmirrors(repo, cr);
365
if (repo->http == NULL) {
366
pkg_emit_error("No HTTP mirrors founds for the repo "
367
"'%s'", repo->name);
368
repo->mirror_type = NOMIRROR;
369
}
370
}
371
/* TODO: Later for parallel fetching */
372
repo->fetch_priv = cr;
373
374
return (EPKG_OK);
375
}
376
377
int
378
curl_fetch(struct pkg_repo *repo, int dest, struct fetch_item *fi)
379
{
380
CURL *cl;
381
CURLU *hu = NULL;
382
CURLcode res;
383
struct curl_userdata data = { 0 };
384
int64_t retry;
385
int retcode = EPKG_OK;
386
struct dns_srvinfo *srv_current = NULL;
387
struct http_mirror *http_current = NULL;
388
char *urlpath = NULL;
389
const char *relpath = NULL;
390
const char *userpasswd = get_http_auth();
391
const char *http_proxy = getenv("HTTP_PROXY");
392
const char *http_proxy_auth = getenv("HTTP_PROXY_AUTH");
393
const char *sslkey = getenv("SSL_CLIENT_KEY_FILE");
394
const char *sslcert = getenv("SSL_CLIENT_CERT_FILE");
395
const char *ssl_ca_cert_file = getenv("SSL_CA_CERT_FILE");
396
const char *ssl_ca_cert_path = getenv("SSL_CA_CERT_PATH");
397
const char *netrc_file = getenv("NETRC");
398
399
struct curl_repodata *cr = (struct curl_repodata *)repo->fetch_priv;
400
401
data.fh = fdopen(dup(dest), "w");
402
if (data.fh == NULL)
403
return (EPKG_FATAL);
404
data.totalsize = fi->size;
405
data.url = fi->url;
406
data.offset = fi->offset > 0 ? fi->offset : 0;
407
408
pkg_dbg(PKG_DBG_FETCH, 2, "curl> fetching %s\n", fi->url);
409
retry = pkg_object_int(pkg_config_get("FETCH_RETRY"));
410
if (repo->mirror_type == SRV || repo->mirror_type == HTTP) {
411
CURLU *cu = curl_url();
412
curl_url_set(cu, CURLUPART_URL, fi->url, 0);
413
curl_url_get(cu, CURLUPART_PATH, &urlpath, 0);
414
if (urlpath != NULL && repo->mirror_type == SRV)
415
curl_url_set(cr->url, CURLUPART_PATH, urlpath, 0);
416
if (urlpath != NULL && repo->mirror_type == HTTP) {
417
CURLU *ru = curl_url();
418
char *doc = NULL;
419
curl_url_set(ru, CURLUPART_URL, repo->url, 0);
420
curl_url_get(ru, CURLUPART_PATH, &doc, 0);
421
relpath = urlpath;
422
if (doc != NULL)
423
relpath += strlen(doc);
424
free(doc);
425
curl_url_cleanup(ru);
426
}
427
curl_url_cleanup(cu);
428
}
429
if (http_proxy == NULL)
430
http_proxy = getenv("http_proxy");
431
432
do_retry:
433
cl = curl_easy_init();
434
data.cl = cl;
435
if (repo->mirror_type == SRV) {
436
char *portstr;
437
if (srv_current != NULL)
438
srv_current = srv_current->next;
439
if (srv_current == NULL)
440
srv_current = repo->srv;
441
curl_url_set(cr->url, CURLUPART_HOST, srv_current->host, 0);
442
xasprintf(&portstr, "%d", srv_current->port);
443
curl_url_set(cr->url, CURLUPART_PORT, portstr, 0);
444
free(portstr);
445
curl_easy_setopt(cl, CURLOPT_CURLU, cr->url);
446
} else if (repo->mirror_type == HTTP) {
447
if (http_current != NULL)
448
http_current = http_current->next;
449
if (http_current == NULL)
450
http_current = repo->http;
451
char *doc = NULL;
452
char *p = NULL;
453
const char *path = relpath;;
454
curl_url_cleanup(hu);
455
hu = curl_url_dup(http_current->url);
456
curl_url_get(hu, CURLUPART_PATH, &doc, 0);
457
if (doc != NULL) {
458
xasprintf(&p, "%s/%s", doc, relpath);
459
path = p;
460
}
461
curl_url_set(hu, CURLUPART_PATH, path, 0);
462
free(p);
463
char *lurl;
464
curl_url_get(hu, CURLUPART_URL, &lurl, 0);
465
pkg_dbg(PKG_DBG_FETCH, 2, "CURL> new http mirror url: %s", lurl);
466
curl_easy_setopt(cl, CURLOPT_CURLU, hu);
467
} else {
468
pkg_dbg(PKG_DBG_FETCH, 2, "CURL> No mirror set url to %s\n", fi->url);
469
curl_easy_setopt(cl, CURLOPT_URL, fi->url);
470
}
471
if (ctx.debug_flags & PKG_DBG_FETCH && ctx.debug_level >= 1) {
472
const char *lurl = NULL;
473
curl_easy_getinfo(cl, CURLINFO_EFFECTIVE_URL, &lurl);
474
if (lurl) {
475
pkg_dbg(PKG_DBG_FETCH, 2, "CURL> attempting to fetch from %s\n", lurl);
476
}
477
pkg_dbg(PKG_DBG_FETCH, 2, "CURL> retries left: %"PRId64"\n", retry);
478
}
479
curl_easy_setopt(cl, CURLOPT_HTTPAUTH, (long)CURLAUTH_ANY);
480
if (userpasswd != NULL) {
481
curl_easy_setopt(cl, CURLOPT_USERPWD, userpasswd);
482
}
483
if (http_proxy != NULL)
484
curl_easy_setopt(cl, CURLOPT_PROXY, http_proxy);
485
if (http_proxy_auth != NULL) {
486
curl_easy_setopt(cl, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
487
curl_easy_setopt(cl, CURLOPT_PROXYUSERPWD, http_proxy_auth);
488
}
489
if (sslkey != NULL)
490
curl_easy_setopt(cl, CURLOPT_SSLKEY, sslkey);
491
if (sslcert != NULL)
492
curl_easy_setopt(cl, CURLOPT_SSLCERT, sslcert);
493
if (ssl_ca_cert_file != NULL)
494
curl_easy_setopt(cl, CURLOPT_CAINFO, ssl_ca_cert_file);
495
if (ssl_ca_cert_path != NULL)
496
curl_easy_setopt(cl, CURLOPT_CAPATH, ssl_ca_cert_path);
497
if (netrc_file != NULL)
498
curl_easy_setopt(cl, CURLOPT_NETRC_FILE, netrc_file);
499
curl_easy_setopt(cl, CURLOPT_NETRC, 1L);
500
501
if (repo->ip == IPV4)
502
curl_easy_setopt(cl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
503
if (repo->ip == IPV6)
504
curl_easy_setopt(cl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
505
curl_easy_setopt(cl, CURLOPT_NOPROGRESS, 0L);
506
curl_easy_setopt(cl, CURLOPT_WRITEFUNCTION, curl_write_cb);
507
curl_easy_setopt(cl, CURLOPT_WRITEDATA, &data);
508
curl_easy_setopt(cl, CURLOPT_XFERINFOFUNCTION, curl_progress_cb);
509
curl_easy_setopt(cl, CURLOPT_XFERINFODATA, &data);
510
curl_easy_setopt(cl, CURLOPT_HEADERFUNCTION, curl_parseheader_cb);
511
curl_easy_setopt(cl, CURLOPT_HEADERDATA, &data);
512
curl_easy_setopt(cl, CURLOPT_TIMEVALUE_LARGE, (curl_off_t)fi->mtime);
513
curl_easy_setopt(cl, CURLOPT_FILETIME, 1L);
514
curl_easy_setopt(cl, CURLOPT_TIMECONDITION, (long)CURL_TIMECOND_IFMODSINCE);
515
if (repo->fetcher->timeout > 0) {
516
curl_easy_setopt(cl, CURLOPT_CONNECTTIMEOUT, repo->fetcher->timeout);
517
518
curl_easy_setopt(cl, CURLOPT_LOW_SPEED_LIMIT, LIBPKG_SPEED_LIMIT);
519
curl_easy_setopt(cl, CURLOPT_LOW_SPEED_TIME, repo->fetcher->timeout);
520
}
521
522
CURLcode fetch_error;
523
long response_code = curl_do_fetch(&data, cl, cr, &fetch_error);
524
curl_off_t t;
525
res = curl_easy_getinfo(cl, CURLINFO_FILETIME_T, &t);
526
curl_multi_remove_handle(cr->cm, cl);
527
curl_easy_cleanup(cl);
528
if (response_code == -1) {
529
if (fetch_error == CURLE_ABORTED_BY_CALLBACK) {
530
retcode = EPKG_CANCEL;
531
} else {
532
pkg_emit_error("Failed to fetch %s: %s",
533
fi->url, curl_easy_strerror(fetch_error));
534
retcode = EPKG_FATAL;
535
}
536
} else if (response_code == 304) {
537
retcode = EPKG_UPTODATE;
538
} else if (!curl_response_is_ok(response_code)) {
539
--retry;
540
if (retry <= 0) {
541
if (response_code == 404) {
542
pkg_emit_error("Failed to fetch %s: Not found",
543
fi->url);
544
retcode = EPKG_ENOENT;
545
} else {
546
pkg_emit_error("Failed to fetch %s: response code %ld",
547
fi->url, response_code);
548
retcode = EPKG_FATAL;
549
}
550
} else {
551
goto do_retry;
552
}
553
}
554
555
if (res == CURLE_OK && t >= 0) {
556
fi->mtime = t;
557
} else if (response_code != 304 && retcode != EPKG_FATAL &&
558
retcode != EPKG_CANCEL && retcode != EPKG_ENOENT) {
559
pkg_emit_error("Impossible to get the value from Last-Modified"
560
" HTTP header");
561
fi->mtime = 0;
562
}
563
fclose(data.fh);
564
free(urlpath);
565
curl_url_cleanup(hu);
566
567
return (retcode);
568
}
569
570
void
571
curl_cleanup(struct pkg_repo *repo)
572
{
573
struct curl_repodata *cr;
574
575
if (repo->fetch_priv == NULL)
576
return;
577
cr = repo->fetch_priv;
578
curl_multi_cleanup(cr->cm);
579
if (cr->url != NULL)
580
curl_url_cleanup(cr->url);
581
repo->fetch_priv = NULL;
582
}
583
584