]> git.ipfire.org Git - people/ms/pakfire.git/blame - src/libpakfire/xfer.c
xfer: Correctly truncate if the download failed
[people/ms/pakfire.git] / src / libpakfire / xfer.c
CommitLineData
c26aca37
MT
1/*#############################################################################
2# #
3# Pakfire - The IPFire package management system #
4# Copyright (C) 2023 Pakfire development team #
5# #
6# This program is free software: you can redistribute it and/or modify #
7# it under the terms of the GNU General Public License as published by #
8# the Free Software Foundation, either version 3 of the License, or #
9# (at your option) any later version. #
10# #
11# This program is distributed in the hope that it will be useful, #
12# but WITHOUT ANY WARRANTY; without even the implied warranty of #
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14# GNU General Public License for more details. #
15# #
16# You should have received a copy of the GNU General Public License #
17# along with this program. If not, see <http://www.gnu.org/licenses/>. #
18# #
19#############################################################################*/
20
21#include <errno.h>
90a6b8e4 22#include <fcntl.h>
c26aca37
MT
23#include <limits.h>
24#include <sys/queue.h>
c26aca37
MT
25
26#include <curl/curl.h>
27
3fcf86a0
MT
28#include <json.h>
29
c26aca37
MT
30#include <openssl/err.h>
31#include <openssl/evp.h>
32
33#include <pakfire/ctx.h>
34#include <pakfire/mirrorlist.h>
35#include <pakfire/path.h>
36#include <pakfire/progress.h>
37#include <pakfire/string.h>
38#include <pakfire/util.h>
39#include <pakfire/xfer.h>
40
41struct pakfire_xfer {
42 struct pakfire_ctx* ctx;
43 int nrefs;
44
10db1fae
MT
45 // Reference to the HTTP client
46 struct pakfire_httpclient* client;
c26aca37
MT
47
48 // Reference to the progress indicator
49 struct pakfire_progress* progress;
50
51 // cURL handle
52 CURL* handle;
53
54 // Headers
55 struct curl_slist* headers;
56
57 // URL
58 CURLU* fullurl;
59
60 char url[PATH_MAX];
61 char title[NAME_MAX];
62 char path[PATH_MAX];
c26aca37
MT
63 int tries;
64
65 // POST MIME Object
66 curl_mime* mime;
67
68 // Xfer direction
69 enum {
70 PAKFIRE_XFER_DOWNLOAD = 0,
71 PAKFIRE_XFER_UPLOAD = 1,
72 } direction;
73
74 // Size
75 size_t expected_size;
76 size_t xferred;
77
c26aca37
MT
78 // File handles for streams
79 FILE* fin;
80 FILE* fout;
81
82 // Crypto Stuff
83 EVP_MD_CTX* evp;
84 const EVP_MD* md;
85 unsigned char computed_digest[EVP_MAX_MD_SIZE];
86 unsigned int computed_digest_length;
87 unsigned char expected_digest[EVP_MAX_MD_SIZE];
88 unsigned int expected_digest_length;
89
90 // Mirrors
91 char baseurl[PATH_MAX];
92 struct pakfire_mirrorlist* mirrors;
93 struct pakfire_mirror* mirror;
94
95 // Effective URL
96 const char* effective_url;
97
98 // Authentication
99 unsigned int auth;
f48f3d09
MT
100
101 // Space for cURL error message
102 char error[CURL_ERROR_SIZE];
c26aca37
MT
103};
104
105static void pakfire_xfer_free(struct pakfire_xfer* xfer) {
c26aca37
MT
106 // Close any streams
107 if (xfer->fin)
108 fclose(xfer->fin);
109
110 // Free OpenSSL EVP context
111 if (xfer->evp)
112 EVP_MD_CTX_free(xfer->evp);
113
de1ecfbb
MT
114 // cURL stuff
115 if (xfer->handle)
116 curl_easy_cleanup(xfer->handle);
c26aca37
MT
117 if (xfer->headers)
118 curl_slist_free_all(xfer->headers);
119 if (xfer->mime)
120 curl_mime_free(xfer->mime);
121 if (xfer->fullurl)
122 curl_url_cleanup(xfer->fullurl);
123
124 if (xfer->mirror)
125 pakfire_mirror_unref(xfer->mirror);
126 if (xfer->mirrors)
127 pakfire_mirrorlist_unref(xfer->mirrors);
128 if (xfer->progress)
129 pakfire_progress_unref(xfer->progress);
bf34147e
MT
130 if (xfer->client)
131 pakfire_httpclient_unref(xfer->client);
c26aca37
MT
132 if (xfer->ctx)
133 pakfire_ctx_unref(xfer->ctx);
134
135 free(xfer);
136}
137
138#ifdef ENABLE_DEBUG
139static int pakfire_xfer_debug_callback(CURL *handle, curl_infotype type,
140 char* data, size_t size, void* private) {
141 struct pakfire_ctx* ctx = private;
142
143 switch (type) {
144 case CURLINFO_TEXT:
145 CTX_DEBUG(ctx, "cURL: %.*s", (int)size, data);
146 break;
147
148 // Log headers
149 case CURLINFO_HEADER_IN:
150 CTX_DEBUG(ctx, "cURL: < %.*s", (int)size, data);
151 break;
152
153 case CURLINFO_HEADER_OUT:
154 CTX_DEBUG(ctx, "cURL: > %.*s", (int)size, data);
155 break;
156
157 // Ignore everything else
158 default:
159 break;
160 }
161
162 return 0;
163}
164#endif
165
166static size_t pakfire_xfer_read(char* data, size_t size, size_t nmemb, void* p) {
167 struct pakfire_xfer* xfer = p;
168
169 return fread(data, size, nmemb, xfer->fout);
170}
171
172static int pakfire_xfer_seek(void* p, curl_off_t offset, int origin) {
173 struct pakfire_xfer* xfer = p;
174 int r;
175
176 // Perform the seek
177 r = fseek(xfer->fout, (long)offset, origin);
178 if (r < 0)
179 return CURL_SEEKFUNC_CANTSEEK;
180
181 return CURL_SEEKFUNC_OK;
182}
183
184static size_t pakfire_xfer_write(
185 char* data, size_t size, size_t nmemb, void* p) {
186 struct pakfire_xfer* xfer = p;
187 struct pakfire_ctx* ctx = xfer->ctx;
188 int r;
189
190 // Do not write empty blocks
191 if (!nmemb)
192 return nmemb;
193
194 // Update message digest
195 if (xfer->evp) {
196 r = EVP_DigestUpdate(xfer->evp, data, nmemb);
197 if (r != 1) {
198 CTX_ERROR(ctx, "EVP_DigestUpdate failed: %s\n",
199 ERR_error_string(ERR_get_error(), NULL));
200 return 0;
201 }
202 }
203
90a6b8e4
MT
204 // If there is no output steam, we just pretent that we have consumed the data
205 if (!xfer->fin)
206 return nmemb;
207
c26aca37
MT
208 // Write everything to the allocated file descriptor
209 return fwrite(data, size, nmemb, xfer->fin);
210}
211
212static int pakfire_xfer_setup(struct pakfire_xfer* xfer) {
213 struct pakfire_config* config = NULL;
214 const char* proxy = NULL;
215 int r;
216
10db1fae 217 CURLSH* share = pakfire_httpclient_share(xfer->client);
c26aca37
MT
218
219 // Configure the share handle
220 r = curl_easy_setopt(xfer->handle, CURLOPT_SHARE, share);
221 if (r) {
222 CTX_ERROR(xfer->ctx, "Could not configure cURL share handle: %s\n",
223 curl_easy_strerror(r));
224 return r;
225 }
226
227 // Fetch global configuration
228 config = pakfire_ctx_get_config(xfer->ctx);
229
230 // Set global configuration
231 if (config) {
232 proxy = pakfire_config_get(config, "general", "proxy", NULL);
233 if (proxy)
234 curl_easy_setopt(xfer->handle, CURLOPT_PROXY, proxy);
235 }
236
237 // Be a good net citizen and set a user agent
238 curl_easy_setopt(xfer->handle, CURLOPT_USERAGENT, PACKAGE_NAME "/" PACKAGE_VERSION);
239
240#ifdef ENABLE_DEBUG
241 // Enable logging/debugging
242 curl_easy_setopt(xfer->handle, CURLOPT_VERBOSE, 1L);
243
244 curl_easy_setopt(xfer->handle, CURLOPT_DEBUGFUNCTION, pakfire_xfer_debug_callback);
245 curl_easy_setopt(xfer->handle, CURLOPT_DEBUGDATA, xfer->ctx);
246#endif
247
f48f3d09
MT
248 // Set error buffer
249 r = curl_easy_setopt(xfer->handle, CURLOPT_ERRORBUFFER, xfer->error);
250 if (r)
251 goto ERROR;
252
c26aca37
MT
253 // Limit protocols to HTTPS, HTTP, FTP and FILE
254 curl_easy_setopt(xfer->handle, CURLOPT_PROTOCOLS_STR, "HTTPS,HTTP,FTP,FILE");
255
4d49c00b
MT
256 // Raise any HTTP errors
257 r = curl_easy_setopt(xfer->handle, CURLOPT_FAILONERROR, 1L);
258 if (r)
259 goto ERROR;
260
541737df
MT
261 // Allow all support encodings
262 curl_easy_setopt(xfer->handle, CURLOPT_ACCEPT_ENCODING, "");
263
c26aca37
MT
264 // Reference back to this xfer
265 curl_easy_setopt(xfer->handle, CURLOPT_PRIVATE, xfer);
266
267 // Follow any redirects
268 curl_easy_setopt(xfer->handle, CURLOPT_FOLLOWLOCATION, 1);
269
270 // Only follow up to 30 redirects
271 curl_easy_setopt(xfer->handle, CURLOPT_MAXREDIRS, 30L);
272
273 // Read any data from a callback function
274 curl_easy_setopt(xfer->handle,
275 CURLOPT_READFUNCTION, pakfire_xfer_read);
276 curl_easy_setopt(xfer->handle, CURLOPT_READDATA, xfer);
277
278 // Write all data to the callback function
279 curl_easy_setopt(xfer->handle,
280 CURLOPT_WRITEFUNCTION, pakfire_xfer_write);
281 curl_easy_setopt(xfer->handle, CURLOPT_WRITEDATA, xfer);
282
283 // Register the seek callback
284 curl_easy_setopt(xfer->handle,
285 CURLOPT_SEEKFUNCTION, pakfire_xfer_seek);
286 curl_easy_setopt(xfer->handle, CURLOPT_SEEKDATA, xfer);
287
288 // Success
289 r = 0;
290
4d49c00b 291ERROR:
c26aca37
MT
292 if (config)
293 pakfire_config_unref(config);
294
295 return r;
296}
297
298int pakfire_xfer_create(struct pakfire_xfer** xfer, struct pakfire_ctx* ctx,
10db1fae 299 struct pakfire_httpclient* client, const char* url) {
c26aca37
MT
300 struct pakfire_xfer* x = NULL;
301 int r;
302
303 // Allocate a new xfer
304 x = calloc(1, sizeof(*x));
305 if (!x)
306 return -errno;
307
308 // Store a reference to the context
309 x->ctx = pakfire_ctx_ref(ctx);
310
311 // Initialize the reference counter
312 x->nrefs = 1;
313
10db1fae
MT
314 // Store a reference to the HTTP client
315 x->client = pakfire_httpclient_ref(client);
c26aca37
MT
316
317 // Store the URL
318 r = pakfire_string_set(x->url, url);
319 if (r)
320 goto ERROR;
321
322 // Allocate a handle
323 x->handle = curl_easy_init();
324 if (!x->handle) {
325 r = 1;
326 goto ERROR;
327 }
328
329 // Allocate the full URL
330 x->fullurl = curl_url();
331 if (!x->fullurl) {
332 r = -errno;
333 goto ERROR;
334 }
335
336 // Setup the xfer
337 r = pakfire_xfer_setup(x);
338 if (r)
339 goto ERROR;
340
341 // Return the reference
342 *xfer = x;
343
344 return 0;
345
346ERROR:
347 if (x)
348 pakfire_xfer_unref(x);
349
350 return r;
351}
352
353struct pakfire_xfer* pakfire_xfer_ref(struct pakfire_xfer* xfer) {
354 ++xfer->nrefs;
355
356 return xfer;
357}
358
359struct pakfire_xfer* pakfire_xfer_unref(struct pakfire_xfer* xfer) {
360 if (--xfer->nrefs > 0)
361 return xfer;
362
363 pakfire_xfer_free(xfer);
364 return NULL;
365}
366
367CURL* pakfire_xfer_handle(struct pakfire_xfer* xfer) {
368 return xfer->handle;
369}
370
371int pakfire_xfer_set_method(struct pakfire_xfer* xfer,
372 const pakfire_xfer_method_t method) {
373 const char* m = NULL;
374
375 switch (method) {
376 case PAKFIRE_METHOD_DELETE:
377 m = "DELETE";
378 break;
379
380 default:
381 return -EINVAL;
382 }
383
384 return curl_easy_setopt(xfer->handle, CURLOPT_CUSTOMREQUEST, m);
385}
386
387
388const char* pakfire_xfer_get_title(struct pakfire_xfer* xfer) {
389 char title[PATH_MAX];
390 int r;
391
392 // Default to the filename if no title is set
393 if (!*xfer->title) {
394 // Only use the basename
395 r = pakfire_path_basename(title, xfer->url);
396 if (r)
397 return NULL;
398
399 // Store the title
400 r = pakfire_xfer_set_title(xfer, title);
401 if (r)
402 return NULL;
403 }
404
405 return xfer->title;
406}
407
408int pakfire_xfer_set_title(struct pakfire_xfer* xfer, const char* title) {
409 return pakfire_string_set(xfer->title, title);
410}
411
412int pakfire_xfer_set_baseurl(struct pakfire_xfer* xfer, const char* baseurl) {
413 return pakfire_string_set(xfer->baseurl, baseurl);
414}
415
416const char* pakfire_xfer_get_effective_url(struct pakfire_xfer* xfer) {
417 return xfer->effective_url;
418}
419
420int pakfire_xfer_set_mirrorlist(struct pakfire_xfer* xfer, struct pakfire_mirrorlist* mirrors) {
421 if (xfer->mirrors)
422 pakfire_mirrorlist_unref(xfer->mirrors);
423
424 xfer->mirrors = pakfire_mirrorlist_ref(mirrors);
425
426 return 0;
427}
428
429// Size
430
431size_t pakfire_xfer_get_size(struct pakfire_xfer* xfer) {
432 return xfer->expected_size;
433}
434
435int pakfire_xfer_set_size(struct pakfire_xfer* xfer, size_t size) {
436 xfer->expected_size = size;
437
438 return 0;
439}
440
441int pakfire_xfer_verify_digest(struct pakfire_xfer* xfer, const enum pakfire_digest_types md,
442 const unsigned char* expected_digest, const size_t expected_digest_length) {
443 // Check inputs
444 if (!expected_digest || !expected_digest_length)
445 return -EINVAL;
446
447 // Expected digest length cannot be too long
448 if (expected_digest_length > sizeof(xfer->expected_digest))
449 return -ENOBUFS;
450
451 // Store digest type
452 switch (md) {
453 case PAKFIRE_DIGEST_SHA3_512:
454 xfer->md = EVP_sha3_512();
455 break;
456
457 case PAKFIRE_DIGEST_SHA3_256:
458 xfer->md = EVP_sha3_256();
459 break;
460
461 case PAKFIRE_DIGEST_BLAKE2B512:
462 xfer->md = EVP_blake2b512();
463 break;
464
465 case PAKFIRE_DIGEST_BLAKE2S256:
466 xfer->md = EVP_blake2s256();
467 break;
468
469 case PAKFIRE_DIGEST_SHA2_512:
470 xfer->md = EVP_sha512();
471 break;
472
473 case PAKFIRE_DIGEST_SHA2_256:
474 xfer->md = EVP_sha256();
475 break;
476
477 default:
478 return -ENOTSUP;
479 }
480
481 // Store the expected digest and its length
482 memcpy(xfer->expected_digest, expected_digest, expected_digest_length);
483 xfer->expected_digest_length = expected_digest_length;
484
485 return 0;
486}
487
488int pakfire_xfer_add_param(struct pakfire_xfer* xfer,
489 const char* key, const char* format, ...) {
490 curl_mimepart* part = NULL;
491 char* buffer = NULL;
492 va_list args;
493 int r;
494
495 // Allocate the MIME object if not done, yet
496 if (!xfer->mime) {
497 xfer->mime = curl_mime_init(xfer->handle);
498
499 if (!xfer->mime) {
500 CTX_ERROR(xfer->ctx, "Could not allocate the MIME object: %s\n",
501 strerror(errno));
502 r = -errno;
503 goto ERROR;
504 }
505 }
506
507 // Format value
508 va_start(args, format);
509 r = vasprintf(&buffer, format, args);
510 va_end(args);
511
512 // Abort if we could not format the value
513 if (r < 0)
514 goto ERROR;
515
516 // Allocate another MIME part
517 part = curl_mime_addpart(xfer->mime);
518 if (!part) {
519 CTX_ERROR(xfer->ctx, "Could not allocate MIME part: %s\n",
520 strerror(errno));
521 r = errno;
522 goto ERROR;
523 }
524
525 // Set the key
526 r = curl_mime_name(part, key);
527 if (r) {
528 CTX_ERROR(xfer->ctx, "Could not set parameter key (%s): %s\n",
529 key, curl_easy_strerror(r));
530 goto ERROR;
531 }
532
533 // Set the data
534 r = curl_mime_data(part, buffer, CURL_ZERO_TERMINATED);
535 if (r) {
536 CTX_ERROR(xfer->ctx, "Could not set parameter data (%s): %s\n",
537 key, curl_easy_strerror(r));
538 goto ERROR;
539 }
540
541ERROR:
542 if (buffer)
543 free(buffer);
544
545 return r;
546}
547
3fcf86a0
MT
548static void pakfire_xfer_reset_output(struct pakfire_xfer* xfer) {
549 if (xfer->fin) {
550 fclose(xfer->fin);
551 xfer->fin = NULL;
552 }
553}
554
c26aca37 555int pakfire_xfer_set_output(struct pakfire_xfer* xfer, FILE* f) {
3fcf86a0
MT
556 pakfire_xfer_reset_output(xfer);
557
558 // Store the new stream
c26aca37
MT
559 xfer->fin = f;
560
561 return 0;
562}
563
564int pakfire_xfer_set_output_buffer(struct pakfire_xfer* xfer,
565 char** buffer, size_t* length) {
566 FILE* f = NULL;
567
568 // Open a memory stream
569 f = open_memstream(buffer, length);
570 if (!f) {
571 CTX_ERROR(xfer->ctx, "Could not open memory stream: %s\n", strerror(errno));
572 return -errno;
573 }
574
575 return pakfire_xfer_set_output(xfer, f);
576}
577
578int pakfire_xfer_set_input(struct pakfire_xfer* xfer, FILE* f) {
579 struct stat stat;
580 int r;
581
582 // Fetch the file descriptor
583 const int fd = fileno(f);
584
585 // Change direction
586 xfer->direction = PAKFIRE_XFER_UPLOAD;
587
588 // Store the file handle
589 xfer->fout = f;
590
591 // Try to find the upload size
592 if (fd > 0) {
593 r = fstat(fd, &stat);
594 if (r)
595 return 0;
596
597 // Store the expected filesize
598 xfer->expected_size = stat.st_size;
599 }
600
601 return 0;
602}
603
90a6b8e4
MT
604static int pakfire_xfer_allocate_tmpfile(struct pakfire_xfer* xfer, const char* path) {
605 char dirname[PATH_MAX];
606 FILE* f = NULL;
607 int fd = -1;
608 int r;
609
610 // Find the directory name
611 r = pakfire_path_dirname(dirname, path);
612 if (r)
613 return r;
614
615 // Ensure the directory exists
616 r = pakfire_mkdir(dirname, 0755);
617 if (r)
618 return r;
619
620 // Open a new temporary file
621 fd = open(dirname, O_TMPFILE|O_RDWR, 0600);
622 if (fd < 0) {
623 CTX_ERROR(xfer->ctx, "Could not open temporary file in %s: %s\n",
624 dirname, strerror(errno));
625 return -errno;
626 }
627
628 // Turn the file descriptor into a FILE handle
629 f = fdopen(fd, "w+");
630 if (!f)
631 return -errno;
632
633 // Set the handle as output
634 return pakfire_xfer_set_output(xfer, f);
635}
636
637int pakfire_xfer_set_output_path(struct pakfire_xfer* xfer, const char* path) {
638 int r;
639
640 // Store the output path
641 r = pakfire_string_set(xfer->path, path);
642 if (r)
643 return r;
644
645 // Allocate a temporary file
646 r = pakfire_xfer_allocate_tmpfile(xfer, path);
647 if (r)
648 return r;
649
650 return 0;
c26aca37
MT
651}
652
653int pakfire_xfer_auth(struct pakfire_xfer* xfer) {
654 // Enable authentication
655 xfer->auth = 1;
656
657 return 0;
658}
659
660static int pakfire_xfer_select_mirror(struct pakfire_xfer* xfer) {
661 // Choose the next mirror
662 if (xfer->mirror)
663 xfer->mirror = pakfire_mirrorlist_get_next(xfer->mirrors, xfer->mirror);
664
665 // If no mirror has been selected yet, choose the first one
666 else
667 xfer->mirror = pakfire_mirrorlist_get_first(xfer->mirrors);
668
669 // Skip this mirror if it is broken
670 while (xfer->mirror && pakfire_mirror_is_broken(xfer->mirror)) {
671 // Move on to the next mirror
672 xfer->mirror = pakfire_mirrorlist_get_next(xfer->mirrors, xfer->mirror);
673 }
674
675 // No mirror found
676 if (!xfer->mirror) {
677 CTX_ERROR(xfer->ctx, "No mirrors left to try\n");
678
679 // No mirrors left
680 return ENOENT;
681 }
682
683 CTX_DEBUG(xfer->ctx, "Selected mirror %s\n", pakfire_mirror_get_url(xfer->mirror));
684
685 return 0;
686}
687
688static const char* curl_http_version(long v) {
689 switch (v) {
690#ifdef CURL_HTTP_VERSION_3_0
691 case CURL_HTTP_VERSION_3_0:
692 return "HTTP/3.0";
693#endif
694
695 case CURL_HTTP_VERSION_2_0:
696 return "HTTP/2.0";
697
698 case CURL_HTTP_VERSION_1_1:
699 return "HTTP/1.1";
700
701 case CURL_HTTP_VERSION_1_0:
702 return "HTTP/1.0";
703 }
704
705 return "unknown";
706}
707
708static int pakfire_xfer_save(struct pakfire_xfer* xfer) {
c26aca37
MT
709 int r;
710
711 // Flush any buffered data out to disk
712 if (xfer->fin)
713 fflush(xfer->fin);
714
715 // Nothing to do if path isn't set
716 if (!*xfer->path)
717 return 0;
718
719 CTX_DEBUG(xfer->ctx, "Download successful. Storing result in %s\n", xfer->path);
720
90a6b8e4
MT
721 int fd = fileno(xfer->fin);
722
c26aca37
MT
723 // Make sure the parent directory exists
724 r = pakfire_mkparentdir(xfer->path, 0755);
725 if (r)
726 return r;
727
728 // Unlink the destination file (if it exists)
729 unlink(xfer->path);
730
731 // Move the temporary file to its destination
90a6b8e4 732 r = linkat(fd, "", AT_FDCWD, xfer->path, AT_EMPTY_PATH);
c26aca37 733 if (r) {
99dc265e 734 CTX_ERROR(xfer->ctx, "Could not link destination file %s: %m\n", xfer->path);
c26aca37
MT
735 return r;
736 }
737
c26aca37
MT
738 return 0;
739}
740
741int pakfire_xfer_fail(struct pakfire_xfer* xfer, int code) {
742 int r;
743
744 CTX_DEBUG(xfer->ctx, "Xfer failed\n");
745
92cc235a
MT
746 // Throw away any downloaded data
747 if (xfer->fin) {
748 // Get file descriptor
749 int fd = fileno(xfer->fin);
750
751 // Truncate downloaded data
752 if (fd >= 0) {
753 r = ftruncate(fd, 0);
754 if (r)
755 return r;
756 }
c26aca37 757
92cc235a
MT
758 // Rewind
759 rewind(xfer->fin);
760 }
c26aca37
MT
761
762 // Did we use a mirror?
763 if (xfer->mirror) {
764 pakfire_mirror_xfer_failed(xfer->mirror);
765
766 // Try again with another mirror
767 return EAGAIN;
768 }
769
770 return 0;
771}
772
773int pakfire_xfer_done(struct pakfire_xfer* xfer, int code) {
774 CURL* h = xfer->handle;
775 int r;
776 char* scheme = NULL;
777 long response_code;
778 long http_version;
779 double total_time;
780
781 curl_off_t download_size = 0;
782 curl_off_t download_speed = 0;
783 curl_off_t upload_size = 0;
784 curl_off_t upload_speed = 0;
785
786 // Finish progress
787 r = pakfire_progress_finish(xfer->progress);
788 if (r)
789 return r;
790
791 CTX_DEBUG(xfer->ctx, "cURL xfer done: %d - %s\n",
792 code, curl_easy_strerror(code));
793
794 // Finish message digest computation
795 if (xfer->evp) {
796 r = EVP_DigestFinal_ex(xfer->evp, xfer->computed_digest, &xfer->computed_digest_length);
797 if (r != 1) {
798 CTX_ERROR(xfer->ctx, "Could not finish message digest computation: %s\n",
799 ERR_error_string(ERR_get_error(), NULL));
800 return 1;
801 }
802 }
803
804 // Protocol
805 curl_easy_getinfo(h, CURLINFO_SCHEME, &scheme);
806
807 // Effective URL
808 curl_easy_getinfo(h, CURLINFO_EFFECTIVE_URL, &xfer->effective_url);
809 if (xfer->effective_url)
810 CTX_DEBUG(xfer->ctx, " Effective URL: %s\n", xfer->effective_url);
811
812 // Response code
813 curl_easy_getinfo(h, CURLINFO_RESPONSE_CODE, &response_code);
814 if (response_code)
815 CTX_DEBUG(xfer->ctx, " Response code: %ld\n", response_code);
816
817 // HTTP Version
818 curl_easy_getinfo(h, CURLINFO_HTTP_VERSION, &http_version);
819 if (http_version)
820 CTX_DEBUG(xfer->ctx, " HTTP Version: %s\n", curl_http_version(http_version));
821
822 // Total Times
823 curl_easy_getinfo(h, CURLINFO_TOTAL_TIME, &total_time);
824 CTX_DEBUG(xfer->ctx, " Total Time: %.2fs\n", total_time);
825
826 // Download Size
827 r = curl_easy_getinfo(h, CURLINFO_SIZE_DOWNLOAD_T, &download_size);
828 if (r)
829 return r;
830
831 if (download_size)
832 CTX_DEBUG(xfer->ctx, " Download Size: %ld bytes\n", download_size);
833
834 // Download Speed
835 r = curl_easy_getinfo(h, CURLINFO_SPEED_DOWNLOAD_T, &download_speed);
836 if (r)
837 return r;
838
839 if (download_speed)
840 CTX_DEBUG(xfer->ctx, " Download Speed: %ld bps\n", download_speed);
841
842 // Upload Size
843 r = curl_easy_getinfo(h, CURLINFO_SIZE_UPLOAD_T, &upload_size);
844 if (r)
845 return r;
846
847 if (upload_size)
848 CTX_DEBUG(xfer->ctx, " Upload Size: %ld bytes\n", upload_size);
849
850 // Upload Speed
851 r = curl_easy_getinfo(h, CURLINFO_SPEED_UPLOAD_T, &upload_speed);
852 if (r)
853 return r;
854
855 if (upload_speed)
856 CTX_DEBUG(xfer->ctx, " Upload Speed: %ld bps\n", upload_speed);
857
858 // Message Digest
859 char* hexdigest = __pakfire_hexlify(xfer->computed_digest, xfer->computed_digest_length);
860 if (hexdigest && *hexdigest) {
861 CTX_DEBUG(xfer->ctx, " Message Digest: %s\n", hexdigest);
862 free(hexdigest);
863 }
864
865 // Check if digests match
866 if (xfer->evp) {
867 r = CRYPTO_memcmp(xfer->computed_digest, xfer->expected_digest,
868 xfer->computed_digest_length);
869
870 // If they don't match, log an error and try again
871 if (r) {
872 char* computed_hexdigest = __pakfire_hexlify(xfer->computed_digest,
873 xfer->computed_digest_length);
874 char* expected_hexdigest = __pakfire_hexlify(xfer->expected_digest,
875 xfer->expected_digest_length);
876
877 CTX_ERROR(xfer->ctx, "Download checksum for %s didn't match:\n", xfer->effective_url);
878 CTX_ERROR(xfer->ctx, " Expected: %s\n", expected_hexdigest);
879 CTX_ERROR(xfer->ctx, " Computed: %s\n", computed_hexdigest);
880
881 if (computed_hexdigest)
882 free(computed_hexdigest);
883 if (expected_hexdigest)
884 free(expected_hexdigest);
885
886 // Make this download fail
887 r = pakfire_xfer_fail(xfer, 0);
888 if (r)
889 return r;
890
891 return 1;
892 }
893 }
894
895 // If we could not determine the scheme...
896 if (!scheme)
897 r = pakfire_xfer_fail(xfer, 0);
898
899 // FILE
900 else if (strcmp(scheme, "FILE") == 0) {
901 // Handle any errors
902 if (code)
903 r = pakfire_xfer_fail(xfer, code);
904 else
905 r = pakfire_xfer_save(xfer);
906
907 return r;
908
909 // HTTPS + HTTP
910 } else if ((strcmp(scheme, "HTTPS") == 0) || (strcmp(scheme, "HTTP") == 0)) {
911 switch (response_code) {
912 // 200 - OK
913 case 200:
914 r = pakfire_xfer_save(xfer);
915 if (r)
916 return r;
917 break;
918
919 // Treat all other response codes as an error
920 default:
921 r = pakfire_xfer_fail(xfer, code);
922 if (r)
923 return r;
924
925 // Error
926 return 1;
927 }
928
929 // FTP
930 } else if (strcmp(scheme, "FTP") == 0) {
931 if (response_code == 226)
932 r = pakfire_xfer_save(xfer);
933 else
934 r = pakfire_xfer_fail(xfer, code);
935
936 return r;
937 }
938
939 // Success
940 return 0;
941}
942
943static int pakfire_xfer_update(void* data,
944 curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
945 struct pakfire_xfer* xfer = data;
946
947 switch (xfer->direction) {
948 case PAKFIRE_XFER_DOWNLOAD:
949 // Update the expected size
950 xfer->expected_size = dltotal;
951
952 // Update the xferred counter
953 xfer->xferred = dlnow;
954 break;
955
956 case PAKFIRE_XFER_UPLOAD:
957 // Update the expected size
958 xfer->expected_size = ultotal;
959
960 // Update the xferred counter
961 xfer->xferred = ulnow;
962 break;
963 }
964
965 // Do nothing if no progress indicator has been set up
966 if (!xfer->progress)
967 return 0;
968
969 // Set maximum (because this might have changed)
970 pakfire_progress_set_max_value(xfer->progress, xfer->expected_size);
971
972 // Update current value
973 return pakfire_progress_update(xfer->progress, xfer->xferred);
974}
975
c26aca37
MT
976static int pakfire_xfer_prepare_progress(struct pakfire_xfer* xfer,
977 struct pakfire_progress* parent, int flags) {
978 const char* title = NULL;
979 int progress_flags =
980 PAKFIRE_PROGRESS_SHOW_PERCENTAGE |
981 PAKFIRE_PROGRESS_SHOW_BYTES_TRANSFERRED |
982 PAKFIRE_PROGRESS_SHOW_TRANSFER_SPEED |
983 PAKFIRE_PROGRESS_SHOW_ETA;
984 int r;
985
986 // If this has already been set up, we skip it
987 if (xfer->progress)
988 return 0;
989
990 // Show no progress if requested
991 if (flags & PAKFIRE_XFER_NO_PROGRESS)
992 progress_flags |= PAKFIRE_PROGRESS_NO_PROGRESS;
993
994 // Show no progress if we have a parent progress
995 else if (parent)
996 progress_flags |= PAKFIRE_PROGRESS_NO_PROGRESS;
997
998 // Make a new progress meter
999 r = pakfire_progress_create(&xfer->progress, xfer->ctx, progress_flags, parent);
1000 if (r)
1001 return r;
1002
1003 // Set the title
1004 title = pakfire_xfer_get_title(xfer);
1005 if (title) {
1006 r = pakfire_progress_set_title(xfer->progress, title);
1007 if (r)
1008 return r;
1009 }
1010
1011 // Configure callbacks
1012 curl_easy_setopt(xfer->handle, CURLOPT_XFERINFOFUNCTION,
1013 pakfire_xfer_update);
1014 curl_easy_setopt(xfer->handle, CURLOPT_XFERINFODATA, xfer);
1015 curl_easy_setopt(xfer->handle, CURLOPT_NOPROGRESS, 0L);
1016
1017 // Start progress
1018 r = pakfire_progress_start(xfer->progress, xfer->expected_size);
1019 if (r)
1020 return r;
1021
1022 return 0;
1023}
1024
c26aca37
MT
1025static int pakfire_xfer_prepare_url(struct pakfire_xfer* xfer) {
1026 int r;
1027
1028 // Simply set absolute URLs
1029 if (pakfire_string_is_url(xfer->url)) {
1030 r = curl_url_set(xfer->fullurl, CURLUPART_URL, xfer->url, 0);
1031 if (r)
1032 goto ERROR;
1033
1034 // Join path if we are using mirrors
1035 } else if (xfer->mirrors && !pakfire_mirrorlist_empty(xfer->mirrors)) {
1036 r = pakfire_xfer_select_mirror(xfer);
1037 if (r)
1038 goto ERROR;
1039
1040 // Set the mirror's base URL first
1041 r = curl_url_set(xfer->fullurl, CURLUPART_URL,
1042 pakfire_mirror_get_url(xfer->mirror), 0);
1043 if (r)
1044 goto ERROR;
1045
1046 // Then append our own part
1047 r = curl_url_set(xfer->fullurl, CURLUPART_URL, xfer->url, 0);
1048 if (r)
1049 goto ERROR;
1050
1051 // Use baseurl
1052 } else if (*xfer->baseurl) {
1053 // Set the base URL first
1054 r = curl_url_set(xfer->fullurl, CURLUPART_URL, xfer->baseurl, 0);
1055 if (r)
1056 goto ERROR;
1057
1058 // Then append our own part
1059 r = curl_url_set(xfer->fullurl, CURLUPART_URL, xfer->url, 0);
1060 if (r)
1061 goto ERROR;
1062
1063 // Fail if we could not set the URL
1064 } else {
1065 CTX_ERROR(xfer->ctx, "Invalid xfer %s\n", xfer->url);
1066 r = -EINVAL;
1067 goto ERROR;
1068 }
1069
1070 // Set the URL
1071 r = curl_easy_setopt(xfer->handle, CURLOPT_CURLU, xfer->fullurl);
1072 if (r) {
1073 CTX_ERROR(xfer->ctx, "Could not set the URL: %s\n", curl_easy_strerror(r));
1074 goto ERROR;
1075 }
1076
1077ERROR:
1078 return r;
1079}
1080
1081int pakfire_xfer_prepare(struct pakfire_xfer* xfer, struct pakfire_progress* progress, int flags) {
1082 int r;
1083
1084 // Increment tries
1085 xfer->tries++;
1086
1087 // Set special options for direction
1088 switch (xfer->direction) {
1089 case PAKFIRE_XFER_DOWNLOAD:
1090 break;
1091
1092 case PAKFIRE_XFER_UPLOAD:
1093 // Let cURL know that we are uploading things
1094 r = curl_easy_setopt(xfer->handle, CURLOPT_UPLOAD, 1L);
1095 if (r) {
1096 CTX_ERROR(xfer->ctx, "Could not enable upload\n");
1097 return r;
1098 }
1099
1100 // Tell it the expected upload size
1101 if (xfer->expected_size) {
1102 r = curl_easy_setopt(xfer->handle,
1103 CURLOPT_INFILESIZE_LARGE, (curl_off_t)xfer->expected_size);
1104 if (r) {
1105 CTX_ERROR(xfer->ctx, "Could not set upload size\n");
1106 return r;
1107 }
1108 }
1109
1110 // Upload files chunked
1111 xfer->headers = curl_slist_append(xfer->headers, "Transfer-Encoding: chunked");
1112 break;
1113 }
1114
1115 // Compose the URL
1116 r = pakfire_xfer_prepare_url(xfer);
1117 if (r) {
1118 CTX_ERROR(xfer->ctx, "Could not compose URL: %m\n");
1119 return r;
1120 }
1121
1122 // Add any headers
1123 if (xfer->headers) {
1124 r = curl_easy_setopt(xfer->handle, CURLOPT_HTTPHEADER, xfer->headers);
1125 if (r) {
1126 CTX_ERROR(xfer->ctx, "Could not set headers: %s\n", curl_easy_strerror(r));
1127 return r;
1128 }
1129 }
1130
1131 // Add any payload
1132 if (xfer->mime) {
1133 r = curl_easy_setopt(xfer->handle, CURLOPT_MIMEPOST, xfer->mime);
1134 if (r) {
1135 CTX_ERROR(xfer->ctx, "Could not set POST payload: %s\n", curl_easy_strerror(r));
1136 return r;
1137 }
1138 }
1139
c26aca37
MT
1140 // Authentication
1141 if (xfer->auth) {
1142 // Request SPNEGO
1143 r = curl_easy_setopt(xfer->handle, CURLOPT_HTTPAUTH, CURLAUTH_NEGOTIATE|CURLAUTH_ONLY);
1144 if (r) {
1145 CTX_ERROR(xfer->ctx, "Could not enable SPNEGO\n");
1146 return r;
1147 }
1148
1149 // Set an empty username
1150 r = curl_easy_setopt(xfer->handle, CURLOPT_USERPWD, ":");
1151 if (r) {
1152 CTX_ERROR(xfer->ctx, "Could not set username\n");
1153 return r;
1154 }
1155 }
1156
1157 // Drop any previously used EVP contexts
1158 if (xfer->evp) {
1159 EVP_MD_CTX_free(xfer->evp);
1160 xfer->evp = NULL;
1161 }
1162
1163 // Create a new EVP context
1164 if (xfer->md) {
1165 xfer->evp = EVP_MD_CTX_new();
1166 if (!xfer->evp) {
1167 CTX_ERROR(xfer->ctx, "Could not create EVP context: %m\n");
1168 return 1;
1169 }
1170
1171 // Initialize the EVP context
1172 r = EVP_DigestInit_ex(xfer->evp, xfer->md, NULL);
1173 if (r != 1) {
1174 CTX_ERROR(xfer->ctx, "Could not initialize EVP context: %s\n",
1175 ERR_error_string(ERR_get_error(), NULL));
1176 return 1;
1177 }
1178 }
1179
1180 // Setup progress
1181 r = pakfire_xfer_prepare_progress(xfer, progress, flags);
1182 if (r)
1183 return r;
1184
1185 return 0;
1186}
1187
1188int pakfire_xfer_run(struct pakfire_xfer* xfer, int flags) {
1189 int r;
1190
1191AGAIN:
1192 // Prepare the xfer
1193 r = pakfire_xfer_prepare(xfer, NULL, flags);
1194 if (r) {
1195 CTX_ERROR(xfer->ctx, "Could not prepare xfer %s: %s\n",
1196 xfer->url, strerror(-r));
1197 return r;
1198 }
1199
1200 // Perform the action
1201 r = curl_easy_perform(xfer->handle);
1202
1203 // Handle the result
1204 r = pakfire_xfer_done(xfer, r);
1205
1206 // Repeat the xfer if asked
1207 switch (-r) {
1208 case EAGAIN:
1209 goto AGAIN;
1210
1211 default:
1212 break;
1213 }
1214
1215 return r;
1216}
3fcf86a0
MT
1217
1218static int pakfire_xfer_handle_api_error(
1219 struct pakfire_xfer* xfer, const struct json_object* error) {
1220 struct json_object* message = NULL;
1221 struct json_object* code = NULL;
1222 const char* m = NULL;
1223 unsigned int c = 0;
1224
1225 // Fetch the URL
1226 const char* url = pakfire_xfer_get_effective_url(xfer);
1227
1228 // Fetch the code
1229 if (!json_object_object_get_ex(error, "code", &code))
1230 return -EBADMSG;
1231
1232 // Check if the code is an integer
1233 if (!json_object_is_type(code, json_type_int))
1234 return -EBADMSG;
1235
1236 // Fetch the message
1237 if (!json_object_object_get_ex(error, "message", &message))
1238 return -EBADMSG;
1239
1240 // Check if the message is a string
1241 if (!json_object_is_type(message, json_type_string))
1242 return -EBADMSG;
1243
1244 c = json_object_get_uint64(code);
1245 m = json_object_get_string(message);
1246
1247 // Log the error
1248 CTX_ERROR(xfer->ctx, "%s responded with error %u (%s):\n %s\n",
1249 url, c, strerror(c), m);
1250
1251 return -c;
1252}
1253
1254/*
1255 This function parses an API response
1256*/
1257static int pakfire_xfer_parse_api_response(struct pakfire_xfer* xfer,
1258 const char* buffer, const size_t length, struct json_object** object) {
1259 struct json_object* error = NULL;
1260 struct json_object* o = NULL;
1261 int r;
1262
1263 // Check if we received any data
1264 if (!length) {
1265 CTX_ERROR(xfer->ctx, "Received an empty response\n");
1266 r = -EBADMSG;
1267 goto ERROR;
1268 }
1269
1270 // XXX Maybe fetch the parser's error message here?!
1271
1272 // Parse the buffer
1273 o = pakfire_json_parse(xfer->ctx, buffer, length);
1274 if (!o) {
1275 CTX_ERROR(xfer->ctx, "Could not parse the response\n");
1276 r = -EBADMSG;
1277 goto ERROR;
1278 }
1279
1280 // Check if the response is a dictionary
1281 if (!json_object_is_type(o, json_type_object)) {
1282 CTX_ERROR(xfer->ctx, "The received object is not a JSON dict\n");
1283 r = -EBADMSG;
1284 goto ERROR;
1285 }
1286
1287 // Fetch error
1288 r = json_object_object_get_ex(o, "error", &error);
1289 if (r) {
1290 r = pakfire_xfer_handle_api_error(xfer, error);
1291 goto ERROR;
1292 }
1293
1294 // Return the object
1295 if (object)
1296 *object = json_object_get(o);
1297
1298ERROR:
1299 if (o)
1300 json_object_put(o);
1301
1302 return r;
1303}
1304
1305static int pakfire_xfer_run_api_request_once(struct pakfire_xfer* xfer, struct json_object** response) {
1306 char* buffer = NULL;
1307 size_t length = 0;
1308 int r;
1309
1310 // Write the response to the buffer
1311 r = pakfire_xfer_set_output_buffer(xfer, &buffer, &length);
1312 if (r)
1313 goto ERROR;
1314
1315 // Run the xfer
1316 r = pakfire_xfer_run(xfer, PAKFIRE_XFER_NO_PROGRESS);
1317 if (r)
1318 goto ERROR;
1319
1320 // Parse the response
1321 r = pakfire_xfer_parse_api_response(xfer, buffer, length, response);
1322 if (r) {
1323 CTX_ERROR(xfer->ctx, "Could not parse the API response: %s\n", strerror(-r));
1324 goto ERROR;
1325 }
1326
1327ERROR:
1328 // Reset the output stream
1329 pakfire_xfer_reset_output(xfer);
1330
1331 if (buffer)
1332 free(buffer);
1333
1334 return r;
1335}
1336
1337/*
1338 This function sends a request and automatically parses the response.
1339 The response might optionally be returned if response is not NULL.
1340*/
1341int pakfire_xfer_run_api_request(struct pakfire_xfer* xfer, struct json_object** response) {
1342 int r;
1343
1344 // Loop indefinitely...
1345 for (;;) {
1346 r = pakfire_xfer_run_api_request_once(xfer, response);
1347 switch (r) {
1348 // XXX need to catch errors and act accordingly
1349
1350 default:
1351 return r;
1352 }
1353 }
1354
1355 return 0;
1356}