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