1 /*#############################################################################
3 # Pakfire - The IPFire package management system #
4 # Copyright (C) 2023 Pakfire development team #
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. #
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. #
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/>. #
19 #############################################################################*/
24 #include <sys/queue.h>
27 #include <curl/curl.h>
31 #include <openssl/err.h>
32 #include <openssl/evp.h>
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>
43 struct pakfire_ctx
* ctx
;
46 // Reference to the HTTP client
47 struct pakfire_httpclient
* client
;
49 // Reference to the progress indicator
50 struct pakfire_progress
* progress
;
56 struct curl_slist
* headers
;
64 pakfire_xfer_flags_t flags
;
72 PAKFIRE_XFER_DOWNLOAD
= 0,
73 PAKFIRE_XFER_UPLOAD
= 1,
80 // File handles for streams
87 unsigned char computed_digest
[EVP_MAX_MD_SIZE
];
88 unsigned int computed_digest_length
;
89 unsigned char expected_digest
[EVP_MAX_MD_SIZE
];
90 unsigned int expected_digest_length
;
93 char baseurl
[PATH_MAX
];
94 struct pakfire_mirrorlist
* mirrors
;
95 struct pakfire_mirror
* mirror
;
98 const char* effective_url
;
104 static void pakfire_xfer_free(struct pakfire_xfer
* xfer
) {
109 // Free OpenSSL EVP context
111 EVP_MD_CTX_free(xfer
->evp
);
115 curl_easy_cleanup(xfer
->handle
);
117 curl_slist_free_all(xfer
->headers
);
119 curl_mime_free(xfer
->mime
);
121 curl_url_cleanup(xfer
->fullurl
);
124 pakfire_mirror_unref(xfer
->mirror
);
126 pakfire_mirrorlist_unref(xfer
->mirrors
);
128 pakfire_progress_unref(xfer
->progress
);
130 pakfire_httpclient_unref(xfer
->client
);
132 pakfire_ctx_unref(xfer
->ctx
);
138 static int pakfire_xfer_debug_callback(CURL
*handle
, curl_infotype type
,
139 char* data
, size_t size
, void* private) {
140 struct pakfire_ctx
* ctx
= private;
144 CTX_DEBUG(ctx
, "cURL: %.*s", (int)size
, data
);
148 case CURLINFO_HEADER_IN
:
149 CTX_DEBUG(ctx
, "cURL: < %.*s", (int)size
, data
);
152 case CURLINFO_HEADER_OUT
:
153 CTX_DEBUG(ctx
, "cURL: > %.*s", (int)size
, data
);
156 // Ignore everything else
165 static size_t pakfire_xfer_read(char* data
, size_t size
, size_t nmemb
, void* p
) {
166 struct pakfire_xfer
* xfer
= p
;
168 return fread(data
, size
, nmemb
, xfer
->fout
);
171 static int pakfire_xfer_seek(void* p
, curl_off_t offset
, int origin
) {
172 struct pakfire_xfer
* xfer
= p
;
176 r
= fseek(xfer
->fout
, (long)offset
, origin
);
178 return CURL_SEEKFUNC_CANTSEEK
;
180 return CURL_SEEKFUNC_OK
;
183 static size_t pakfire_xfer_write(
184 char* data
, size_t size
, size_t nmemb
, void* p
) {
185 struct pakfire_xfer
* xfer
= p
;
186 struct pakfire_ctx
* ctx
= xfer
->ctx
;
189 // Do not write empty blocks
193 // Update message digest
195 r
= EVP_DigestUpdate(xfer
->evp
, data
, nmemb
);
197 CTX_ERROR(ctx
, "EVP_DigestUpdate failed: %s\n",
198 ERR_error_string(ERR_get_error(), NULL
));
203 // If there is no output steam, we just pretent that we have consumed the data
207 // Write everything to the allocated file descriptor
208 return fwrite(data
, size
, nmemb
, xfer
->fin
);
211 static int pakfire_xfer_setup(struct pakfire_xfer
* xfer
) {
212 struct pakfire_config
* config
= NULL
;
213 const char* proxy
= NULL
;
216 CURLSH
* share
= pakfire_httpclient_share(xfer
->client
);
218 // Configure the share handle
219 r
= curl_easy_setopt(xfer
->handle
, CURLOPT_SHARE
, share
);
221 CTX_ERROR(xfer
->ctx
, "Could not configure cURL share handle: %s\n",
222 curl_easy_strerror(r
));
226 // Fetch global configuration
227 config
= pakfire_ctx_get_config(xfer
->ctx
);
229 // Set global configuration
231 proxy
= pakfire_config_get(config
, "general", "proxy", NULL
);
233 curl_easy_setopt(xfer
->handle
, CURLOPT_PROXY
, proxy
);
236 // Be a good net citizen and set a user agent
237 curl_easy_setopt(xfer
->handle
, CURLOPT_USERAGENT
, PACKAGE_NAME
"/" PACKAGE_VERSION
);
240 // Enable logging/debugging
241 curl_easy_setopt(xfer
->handle
, CURLOPT_VERBOSE
, 1L);
243 curl_easy_setopt(xfer
->handle
, CURLOPT_DEBUGFUNCTION
, pakfire_xfer_debug_callback
);
244 curl_easy_setopt(xfer
->handle
, CURLOPT_DEBUGDATA
, xfer
->ctx
);
247 // Limit protocols to HTTPS, HTTP, FTP and FILE
248 curl_easy_setopt(xfer
->handle
, CURLOPT_PROTOCOLS_STR
, "HTTPS,HTTP,FTP,FILE");
250 // Allow all support encodings
251 curl_easy_setopt(xfer
->handle
, CURLOPT_ACCEPT_ENCODING
, "");
253 // Reference back to this xfer
254 curl_easy_setopt(xfer
->handle
, CURLOPT_PRIVATE
, xfer
);
256 // Follow any redirects
257 curl_easy_setopt(xfer
->handle
, CURLOPT_FOLLOWLOCATION
, 1);
259 // Only follow up to 30 redirects
260 curl_easy_setopt(xfer
->handle
, CURLOPT_MAXREDIRS
, 30L);
262 // Read any data from a callback function
263 curl_easy_setopt(xfer
->handle
,
264 CURLOPT_READFUNCTION
, pakfire_xfer_read
);
265 curl_easy_setopt(xfer
->handle
, CURLOPT_READDATA
, xfer
);
267 // Write all data to the callback function
268 curl_easy_setopt(xfer
->handle
,
269 CURLOPT_WRITEFUNCTION
, pakfire_xfer_write
);
270 curl_easy_setopt(xfer
->handle
, CURLOPT_WRITEDATA
, xfer
);
272 // Register the seek callback
273 curl_easy_setopt(xfer
->handle
,
274 CURLOPT_SEEKFUNCTION
, pakfire_xfer_seek
);
275 curl_easy_setopt(xfer
->handle
, CURLOPT_SEEKDATA
, xfer
);
282 pakfire_config_unref(config
);
287 int pakfire_xfer_create(struct pakfire_xfer
** xfer
, struct pakfire_ctx
* ctx
,
288 struct pakfire_httpclient
* client
, const char* url
) {
289 struct pakfire_xfer
* x
= NULL
;
292 // Allocate a new xfer
293 x
= calloc(1, sizeof(*x
));
297 // Store a reference to the context
298 x
->ctx
= pakfire_ctx_ref(ctx
);
300 // Initialize the reference counter
303 // Store a reference to the HTTP client
304 x
->client
= pakfire_httpclient_ref(client
);
307 r
= pakfire_string_set(x
->url
, url
);
312 x
->handle
= curl_easy_init();
318 // Allocate the full URL
319 x
->fullurl
= curl_url();
326 r
= pakfire_xfer_setup(x
);
330 // Return the reference
337 pakfire_xfer_unref(x
);
342 struct pakfire_xfer
* pakfire_xfer_ref(struct pakfire_xfer
* xfer
) {
348 struct pakfire_xfer
* pakfire_xfer_unref(struct pakfire_xfer
* xfer
) {
349 if (--xfer
->nrefs
> 0)
352 pakfire_xfer_free(xfer
);
356 CURL
* pakfire_xfer_handle(struct pakfire_xfer
* xfer
) {
360 int pakfire_xfer_set_method(struct pakfire_xfer
* xfer
,
361 const pakfire_xfer_method_t method
) {
362 const char* m
= NULL
;
365 case PAKFIRE_METHOD_DELETE
:
373 return curl_easy_setopt(xfer
->handle
, CURLOPT_CUSTOMREQUEST
, m
);
377 const char* pakfire_xfer_get_title(struct pakfire_xfer
* xfer
) {
378 char title
[PATH_MAX
];
381 // Default to the filename if no title is set
383 // Only use the basename
384 r
= pakfire_path_basename(title
, xfer
->url
);
389 r
= pakfire_xfer_set_title(xfer
, title
);
397 int pakfire_xfer_set_title(struct pakfire_xfer
* xfer
, const char* title
) {
398 return pakfire_string_set(xfer
->title
, title
);
401 int pakfire_xfer_set_baseurl(struct pakfire_xfer
* xfer
, const char* baseurl
) {
402 return pakfire_string_set(xfer
->baseurl
, baseurl
);
405 const char* pakfire_xfer_get_effective_url(struct pakfire_xfer
* xfer
) {
406 return xfer
->effective_url
;
409 int pakfire_xfer_set_mirrorlist(struct pakfire_xfer
* xfer
, struct pakfire_mirrorlist
* mirrors
) {
411 pakfire_mirrorlist_unref(xfer
->mirrors
);
413 xfer
->mirrors
= pakfire_mirrorlist_ref(mirrors
);
420 size_t pakfire_xfer_get_size(struct pakfire_xfer
* xfer
) {
421 return xfer
->expected_size
;
424 int pakfire_xfer_set_size(struct pakfire_xfer
* xfer
, size_t size
) {
425 xfer
->expected_size
= size
;
430 int pakfire_xfer_verify_digest(struct pakfire_xfer
* xfer
, const enum pakfire_digest_types md
,
431 const unsigned char* expected_digest
, const size_t expected_digest_length
) {
433 if (!expected_digest
|| !expected_digest_length
)
436 // Expected digest length cannot be too long
437 if (expected_digest_length
> sizeof(xfer
->expected_digest
))
442 case PAKFIRE_DIGEST_SHA3_512
:
443 xfer
->md
= EVP_sha3_512();
446 case PAKFIRE_DIGEST_SHA3_256
:
447 xfer
->md
= EVP_sha3_256();
450 case PAKFIRE_DIGEST_BLAKE2B512
:
451 xfer
->md
= EVP_blake2b512();
454 case PAKFIRE_DIGEST_BLAKE2S256
:
455 xfer
->md
= EVP_blake2s256();
458 case PAKFIRE_DIGEST_SHA2_512
:
459 xfer
->md
= EVP_sha512();
462 case PAKFIRE_DIGEST_SHA2_256
:
463 xfer
->md
= EVP_sha256();
470 // Store the expected digest and its length
471 memcpy(xfer
->expected_digest
, expected_digest
, expected_digest_length
);
472 xfer
->expected_digest_length
= expected_digest_length
;
477 int pakfire_xfer_add_param(struct pakfire_xfer
* xfer
,
478 const char* key
, const char* format
, ...) {
479 curl_mimepart
* part
= NULL
;
484 // Allocate the MIME object if not done, yet
486 xfer
->mime
= curl_mime_init(xfer
->handle
);
489 CTX_ERROR(xfer
->ctx
, "Could not allocate the MIME object: %s\n",
497 va_start(args
, format
);
498 r
= vasprintf(&buffer
, format
, args
);
501 // Abort if we could not format the value
505 // Allocate another MIME part
506 part
= curl_mime_addpart(xfer
->mime
);
508 CTX_ERROR(xfer
->ctx
, "Could not allocate MIME part: %s\n",
515 r
= curl_mime_name(part
, key
);
517 CTX_ERROR(xfer
->ctx
, "Could not set parameter key (%s): %s\n",
518 key
, curl_easy_strerror(r
));
523 r
= curl_mime_data(part
, buffer
, CURL_ZERO_TERMINATED
);
525 CTX_ERROR(xfer
->ctx
, "Could not set parameter data (%s): %s\n",
526 key
, curl_easy_strerror(r
));
537 static void pakfire_xfer_reset_output(struct pakfire_xfer
* xfer
) {
544 int pakfire_xfer_set_output(struct pakfire_xfer
* xfer
, FILE* f
) {
545 pakfire_xfer_reset_output(xfer
);
547 // Store the new stream
553 int pakfire_xfer_set_output_buffer(struct pakfire_xfer
* xfer
,
554 char** buffer
, size_t* length
) {
557 // Open a memory stream
558 f
= open_memstream(buffer
, length
);
560 CTX_ERROR(xfer
->ctx
, "Could not open memory stream: %s\n", strerror(errno
));
564 return pakfire_xfer_set_output(xfer
, f
);
567 int pakfire_xfer_set_input(struct pakfire_xfer
* xfer
, FILE* f
) {
571 // Fetch the file descriptor
572 const int fd
= fileno(f
);
575 xfer
->direction
= PAKFIRE_XFER_UPLOAD
;
577 // Store the file handle
580 // Try to find the upload size
582 r
= fstat(fd
, &stat
);
586 // Store the expected filesize
587 xfer
->expected_size
= stat
.st_size
;
593 static int pakfire_xfer_allocate_tmpfile(struct pakfire_xfer
* xfer
, const char* path
) {
594 char dirname
[PATH_MAX
];
599 // Find the directory name
600 r
= pakfire_path_dirname(dirname
, path
);
604 // Ensure the directory exists
605 r
= pakfire_mkdir(dirname
, 0755);
609 // Open a new temporary file
610 fd
= open(dirname
, O_TMPFILE
|O_RDWR
, 0600);
612 CTX_ERROR(xfer
->ctx
, "Could not open temporary file in %s: %s\n",
613 dirname
, strerror(errno
));
617 // Turn the file descriptor into a FILE handle
618 f
= fdopen(fd
, "w+");
622 // Set the handle as output
623 return pakfire_xfer_set_output(xfer
, f
);
626 int pakfire_xfer_set_output_path(struct pakfire_xfer
* xfer
, const char* path
) {
629 // Store the output path
630 r
= pakfire_string_set(xfer
->path
, path
);
634 // Allocate a temporary file
635 r
= pakfire_xfer_allocate_tmpfile(xfer
, path
);
642 int pakfire_xfer_auth(struct pakfire_xfer
* xfer
) {
643 // Enable authentication
649 static int pakfire_xfer_select_mirror(struct pakfire_xfer
* xfer
) {
650 // Choose the next mirror
652 xfer
->mirror
= pakfire_mirrorlist_get_next(xfer
->mirrors
, xfer
->mirror
);
654 // If no mirror has been selected yet, choose the first one
656 xfer
->mirror
= pakfire_mirrorlist_get_first(xfer
->mirrors
);
658 // Skip this mirror if it is broken
659 while (xfer
->mirror
&& pakfire_mirror_is_broken(xfer
->mirror
)) {
660 // Move on to the next mirror
661 xfer
->mirror
= pakfire_mirrorlist_get_next(xfer
->mirrors
, xfer
->mirror
);
666 CTX_ERROR(xfer
->ctx
, "No mirrors left to try\n");
672 CTX_DEBUG(xfer
->ctx
, "Selected mirror %s\n", pakfire_mirror_get_url(xfer
->mirror
));
677 static const char* curl_http_version(long v
) {
679 #ifdef CURL_HTTP_VERSION_3_0
680 case CURL_HTTP_VERSION_3_0
:
684 case CURL_HTTP_VERSION_2_0
:
687 case CURL_HTTP_VERSION_1_1
:
690 case CURL_HTTP_VERSION_1_0
:
697 static int pakfire_xfer_save(struct pakfire_xfer
* xfer
) {
698 struct utimbuf times
= {
704 // Flush any buffered data out to disk
708 // Nothing to do if path isn't set
712 CTX_DEBUG(xfer
->ctx
, "Download successful. Storing result in %s\n", xfer
->path
);
714 int fd
= fileno(xfer
->fin
);
716 // Make sure the parent directory exists
717 r
= pakfire_mkparentdir(xfer
->path
, 0755);
721 // Unlink the destination file (if it exists)
724 // Move the temporary file to its destination
725 r
= linkat(fd
, "", AT_FDCWD
, xfer
->path
, AT_EMPTY_PATH
);
727 CTX_ERROR(xfer
->ctx
, "Could not link destination file %s: %m\n",
733 curl_easy_getinfo(xfer
->handle
, CURLINFO_FILETIME_T
, ×
.modtime
);
736 r
= utime(xfer
->path
, ×
);
738 CTX_ERROR(xfer
->ctx
, "Could not set mtime of %s: %m\n", xfer
->path
);
744 int pakfire_xfer_fail(struct pakfire_xfer
* xfer
, int code
) {
747 CTX_DEBUG(xfer
->ctx
, "Xfer failed\n");
749 // Get file descriptor
750 int fd
= fileno(xfer
->fin
);
752 // Truncate downloaded data
753 r
= ftruncate(fd
, 0);
757 // Did we use a mirror?
759 pakfire_mirror_xfer_failed(xfer
->mirror
);
761 // Try again with another mirror
768 int pakfire_xfer_done(struct pakfire_xfer
* xfer
, int code
) {
769 CURL
* h
= xfer
->handle
;
776 curl_off_t download_size
= 0;
777 curl_off_t download_speed
= 0;
778 curl_off_t upload_size
= 0;
779 curl_off_t upload_speed
= 0;
782 r
= pakfire_progress_finish(xfer
->progress
);
786 CTX_DEBUG(xfer
->ctx
, "cURL xfer done: %d - %s\n",
787 code
, curl_easy_strerror(code
));
789 // Finish message digest computation
791 r
= EVP_DigestFinal_ex(xfer
->evp
, xfer
->computed_digest
, &xfer
->computed_digest_length
);
793 CTX_ERROR(xfer
->ctx
, "Could not finish message digest computation: %s\n",
794 ERR_error_string(ERR_get_error(), NULL
));
800 curl_easy_getinfo(h
, CURLINFO_SCHEME
, &scheme
);
803 curl_easy_getinfo(h
, CURLINFO_EFFECTIVE_URL
, &xfer
->effective_url
);
804 if (xfer
->effective_url
)
805 CTX_DEBUG(xfer
->ctx
, " Effective URL: %s\n", xfer
->effective_url
);
808 curl_easy_getinfo(h
, CURLINFO_RESPONSE_CODE
, &response_code
);
810 CTX_DEBUG(xfer
->ctx
, " Response code: %ld\n", response_code
);
813 curl_easy_getinfo(h
, CURLINFO_HTTP_VERSION
, &http_version
);
815 CTX_DEBUG(xfer
->ctx
, " HTTP Version: %s\n", curl_http_version(http_version
));
818 curl_easy_getinfo(h
, CURLINFO_TOTAL_TIME
, &total_time
);
819 CTX_DEBUG(xfer
->ctx
, " Total Time: %.2fs\n", total_time
);
822 r
= curl_easy_getinfo(h
, CURLINFO_SIZE_DOWNLOAD_T
, &download_size
);
827 CTX_DEBUG(xfer
->ctx
, " Download Size: %ld bytes\n", download_size
);
830 r
= curl_easy_getinfo(h
, CURLINFO_SPEED_DOWNLOAD_T
, &download_speed
);
835 CTX_DEBUG(xfer
->ctx
, " Download Speed: %ld bps\n", download_speed
);
838 r
= curl_easy_getinfo(h
, CURLINFO_SIZE_UPLOAD_T
, &upload_size
);
843 CTX_DEBUG(xfer
->ctx
, " Upload Size: %ld bytes\n", upload_size
);
846 r
= curl_easy_getinfo(h
, CURLINFO_SPEED_UPLOAD_T
, &upload_speed
);
851 CTX_DEBUG(xfer
->ctx
, " Upload Speed: %ld bps\n", upload_speed
);
854 char* hexdigest
= __pakfire_hexlify(xfer
->computed_digest
, xfer
->computed_digest_length
);
855 if (hexdigest
&& *hexdigest
) {
856 CTX_DEBUG(xfer
->ctx
, " Message Digest: %s\n", hexdigest
);
860 // Check if digests match
862 r
= CRYPTO_memcmp(xfer
->computed_digest
, xfer
->expected_digest
,
863 xfer
->computed_digest_length
);
865 // If they don't match, log an error and try again
867 char* computed_hexdigest
= __pakfire_hexlify(xfer
->computed_digest
,
868 xfer
->computed_digest_length
);
869 char* expected_hexdigest
= __pakfire_hexlify(xfer
->expected_digest
,
870 xfer
->expected_digest_length
);
872 CTX_ERROR(xfer
->ctx
, "Download checksum for %s didn't match:\n", xfer
->effective_url
);
873 CTX_ERROR(xfer
->ctx
, " Expected: %s\n", expected_hexdigest
);
874 CTX_ERROR(xfer
->ctx
, " Computed: %s\n", computed_hexdigest
);
876 if (computed_hexdigest
)
877 free(computed_hexdigest
);
878 if (expected_hexdigest
)
879 free(expected_hexdigest
);
881 // Make this download fail
882 r
= pakfire_xfer_fail(xfer
, 0);
890 // If we could not determine the scheme...
892 r
= pakfire_xfer_fail(xfer
, 0);
895 else if (strcmp(scheme
, "FILE") == 0) {
898 r
= pakfire_xfer_fail(xfer
, code
);
900 r
= pakfire_xfer_save(xfer
);
905 } else if ((strcmp(scheme
, "HTTPS") == 0) || (strcmp(scheme
, "HTTP") == 0)) {
906 switch (response_code
) {
909 r
= pakfire_xfer_save(xfer
);
914 // Treat all other response codes as an error
916 r
= pakfire_xfer_fail(xfer
, code
);
925 } else if (strcmp(scheme
, "FTP") == 0) {
926 if (response_code
== 226)
927 r
= pakfire_xfer_save(xfer
);
929 r
= pakfire_xfer_fail(xfer
, code
);
938 static int pakfire_xfer_update(void* data
,
939 curl_off_t dltotal
, curl_off_t dlnow
, curl_off_t ultotal
, curl_off_t ulnow
) {
940 struct pakfire_xfer
* xfer
= data
;
942 switch (xfer
->direction
) {
943 case PAKFIRE_XFER_DOWNLOAD
:
944 // Update the expected size
945 xfer
->expected_size
= dltotal
;
947 // Update the xferred counter
948 xfer
->xferred
= dlnow
;
951 case PAKFIRE_XFER_UPLOAD
:
952 // Update the expected size
953 xfer
->expected_size
= ultotal
;
955 // Update the xferred counter
956 xfer
->xferred
= ulnow
;
960 // Do nothing if no progress indicator has been set up
964 // Set maximum (because this might have changed)
965 pakfire_progress_set_max_value(xfer
->progress
, xfer
->expected_size
);
967 // Update current value
968 return pakfire_progress_update(xfer
->progress
, xfer
->xferred
);
971 static int pakfire_xfer_prepare_progress(struct pakfire_xfer
* xfer
,
972 struct pakfire_progress
* parent
, int flags
) {
973 const char* title
= NULL
;
975 PAKFIRE_PROGRESS_SHOW_PERCENTAGE
|
976 PAKFIRE_PROGRESS_SHOW_BYTES_TRANSFERRED
|
977 PAKFIRE_PROGRESS_SHOW_TRANSFER_SPEED
|
978 PAKFIRE_PROGRESS_SHOW_ETA
;
981 // If this has already been set up, we skip it
985 // Show no progress if requested
986 if (flags
& PAKFIRE_XFER_NO_PROGRESS
)
987 progress_flags
|= PAKFIRE_PROGRESS_NO_PROGRESS
;
989 // Show no progress if we have a parent progress
991 progress_flags
|= PAKFIRE_PROGRESS_NO_PROGRESS
;
993 // Make a new progress meter
994 r
= pakfire_progress_create(&xfer
->progress
, xfer
->ctx
, progress_flags
, parent
);
999 title
= pakfire_xfer_get_title(xfer
);
1001 r
= pakfire_progress_set_title(xfer
->progress
, title
);
1006 // Configure callbacks
1007 curl_easy_setopt(xfer
->handle
, CURLOPT_XFERINFOFUNCTION
,
1008 pakfire_xfer_update
);
1009 curl_easy_setopt(xfer
->handle
, CURLOPT_XFERINFODATA
, xfer
);
1010 curl_easy_setopt(xfer
->handle
, CURLOPT_NOPROGRESS
, 0L);
1013 r
= pakfire_progress_start(xfer
->progress
, xfer
->expected_size
);
1020 static int pakfire_xfer_prepare_url(struct pakfire_xfer
* xfer
) {
1023 // Simply set absolute URLs
1024 if (pakfire_string_is_url(xfer
->url
)) {
1025 r
= curl_url_set(xfer
->fullurl
, CURLUPART_URL
, xfer
->url
, 0);
1029 // Join path if we are using mirrors
1030 } else if (xfer
->mirrors
&& !pakfire_mirrorlist_empty(xfer
->mirrors
)) {
1031 r
= pakfire_xfer_select_mirror(xfer
);
1035 // Set the mirror's base URL first
1036 r
= curl_url_set(xfer
->fullurl
, CURLUPART_URL
,
1037 pakfire_mirror_get_url(xfer
->mirror
), 0);
1041 // Then append our own part
1042 r
= curl_url_set(xfer
->fullurl
, CURLUPART_URL
, xfer
->url
, 0);
1047 } else if (*xfer
->baseurl
) {
1048 // Set the base URL first
1049 r
= curl_url_set(xfer
->fullurl
, CURLUPART_URL
, xfer
->baseurl
, 0);
1053 // Then append our own part
1054 r
= curl_url_set(xfer
->fullurl
, CURLUPART_URL
, xfer
->url
, 0);
1058 // Fail if we could not set the URL
1060 CTX_ERROR(xfer
->ctx
, "Invalid xfer %s\n", xfer
->url
);
1066 r
= curl_easy_setopt(xfer
->handle
, CURLOPT_CURLU
, xfer
->fullurl
);
1068 CTX_ERROR(xfer
->ctx
, "Could not set the URL: %s\n", curl_easy_strerror(r
));
1076 int pakfire_xfer_prepare(struct pakfire_xfer
* xfer
, struct pakfire_progress
* progress
, int flags
) {
1082 // Set special options for direction
1083 switch (xfer
->direction
) {
1084 case PAKFIRE_XFER_DOWNLOAD
:
1087 case PAKFIRE_XFER_UPLOAD
:
1088 // Let cURL know that we are uploading things
1089 r
= curl_easy_setopt(xfer
->handle
, CURLOPT_UPLOAD
, 1L);
1091 CTX_ERROR(xfer
->ctx
, "Could not enable upload\n");
1095 // Tell it the expected upload size
1096 if (xfer
->expected_size
) {
1097 r
= curl_easy_setopt(xfer
->handle
,
1098 CURLOPT_INFILESIZE_LARGE
, (curl_off_t
)xfer
->expected_size
);
1100 CTX_ERROR(xfer
->ctx
, "Could not set upload size\n");
1105 // Upload files chunked
1106 xfer
->headers
= curl_slist_append(xfer
->headers
, "Transfer-Encoding: chunked");
1111 r
= pakfire_xfer_prepare_url(xfer
);
1113 CTX_ERROR(xfer
->ctx
, "Could not compose URL: %m\n");
1118 if (xfer
->headers
) {
1119 r
= curl_easy_setopt(xfer
->handle
, CURLOPT_HTTPHEADER
, xfer
->headers
);
1121 CTX_ERROR(xfer
->ctx
, "Could not set headers: %s\n", curl_easy_strerror(r
));
1128 r
= curl_easy_setopt(xfer
->handle
, CURLOPT_MIMEPOST
, xfer
->mime
);
1130 CTX_ERROR(xfer
->ctx
, "Could not set POST payload: %s\n", curl_easy_strerror(r
));
1138 r
= curl_easy_setopt(xfer
->handle
, CURLOPT_HTTPAUTH
, CURLAUTH_NEGOTIATE
|CURLAUTH_ONLY
);
1140 CTX_ERROR(xfer
->ctx
, "Could not enable SPNEGO\n");
1144 // Set an empty username
1145 r
= curl_easy_setopt(xfer
->handle
, CURLOPT_USERPWD
, ":");
1147 CTX_ERROR(xfer
->ctx
, "Could not set username\n");
1152 // Drop any previously used EVP contexts
1154 EVP_MD_CTX_free(xfer
->evp
);
1158 // Create a new EVP context
1160 xfer
->evp
= EVP_MD_CTX_new();
1162 CTX_ERROR(xfer
->ctx
, "Could not create EVP context: %m\n");
1166 // Initialize the EVP context
1167 r
= EVP_DigestInit_ex(xfer
->evp
, xfer
->md
, NULL
);
1169 CTX_ERROR(xfer
->ctx
, "Could not initialize EVP context: %s\n",
1170 ERR_error_string(ERR_get_error(), NULL
));
1176 r
= pakfire_xfer_prepare_progress(xfer
, progress
, flags
);
1183 int pakfire_xfer_run(struct pakfire_xfer
* xfer
, int flags
) {
1188 r
= pakfire_xfer_prepare(xfer
, NULL
, flags
);
1190 CTX_ERROR(xfer
->ctx
, "Could not prepare xfer %s: %s\n",
1191 xfer
->url
, strerror(-r
));
1195 // Perform the action
1196 r
= curl_easy_perform(xfer
->handle
);
1198 // Handle the result
1199 r
= pakfire_xfer_done(xfer
, r
);
1201 // Repeat the xfer if asked
1213 static int pakfire_xfer_handle_api_error(
1214 struct pakfire_xfer
* xfer
, const struct json_object
* error
) {
1215 struct json_object
* message
= NULL
;
1216 struct json_object
* code
= NULL
;
1217 const char* m
= NULL
;
1221 const char* url
= pakfire_xfer_get_effective_url(xfer
);
1224 if (!json_object_object_get_ex(error
, "code", &code
))
1227 // Check if the code is an integer
1228 if (!json_object_is_type(code
, json_type_int
))
1231 // Fetch the message
1232 if (!json_object_object_get_ex(error
, "message", &message
))
1235 // Check if the message is a string
1236 if (!json_object_is_type(message
, json_type_string
))
1239 c
= json_object_get_uint64(code
);
1240 m
= json_object_get_string(message
);
1243 CTX_ERROR(xfer
->ctx
, "%s responded with error %u (%s):\n %s\n",
1244 url
, c
, strerror(c
), m
);
1250 This function parses an API response
1252 static int pakfire_xfer_parse_api_response(struct pakfire_xfer
* xfer
,
1253 const char* buffer
, const size_t length
, struct json_object
** object
) {
1254 struct json_object
* error
= NULL
;
1255 struct json_object
* o
= NULL
;
1258 // Check if we received any data
1260 CTX_ERROR(xfer
->ctx
, "Received an empty response\n");
1265 // XXX Maybe fetch the parser's error message here?!
1268 o
= pakfire_json_parse(xfer
->ctx
, buffer
, length
);
1270 CTX_ERROR(xfer
->ctx
, "Could not parse the response\n");
1275 // Check if the response is a dictionary
1276 if (!json_object_is_type(o
, json_type_object
)) {
1277 CTX_ERROR(xfer
->ctx
, "The received object is not a JSON dict\n");
1283 r
= json_object_object_get_ex(o
, "error", &error
);
1285 r
= pakfire_xfer_handle_api_error(xfer
, error
);
1289 // Return the object
1291 *object
= json_object_get(o
);
1300 static int pakfire_xfer_run_api_request_once(struct pakfire_xfer
* xfer
, struct json_object
** response
) {
1301 char* buffer
= NULL
;
1305 // Write the response to the buffer
1306 r
= pakfire_xfer_set_output_buffer(xfer
, &buffer
, &length
);
1311 r
= pakfire_xfer_run(xfer
, PAKFIRE_XFER_NO_PROGRESS
);
1315 // Parse the response
1316 r
= pakfire_xfer_parse_api_response(xfer
, buffer
, length
, response
);
1318 CTX_ERROR(xfer
->ctx
, "Could not parse the API response: %s\n", strerror(-r
));
1323 // Reset the output stream
1324 pakfire_xfer_reset_output(xfer
);
1333 This function sends a request and automatically parses the response.
1334 The response might optionally be returned if response is not NULL.
1336 int pakfire_xfer_run_api_request(struct pakfire_xfer
* xfer
, struct json_object
** response
) {
1339 // Loop indefinitely...
1341 r
= pakfire_xfer_run_api_request_once(xfer
, response
);
1343 // XXX need to catch errors and act accordingly