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
;
71 PAKFIRE_XFER_DOWNLOAD
= 0,
72 PAKFIRE_XFER_UPLOAD
= 1,
79 // File handles for streams
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
;
92 char baseurl
[PATH_MAX
];
93 struct pakfire_mirrorlist
* mirrors
;
94 struct pakfire_mirror
* mirror
;
97 const char* effective_url
;
103 static void pakfire_xfer_free(struct pakfire_xfer
* xfer
) {
108 // Free OpenSSL EVP context
110 EVP_MD_CTX_free(xfer
->evp
);
114 curl_easy_cleanup(xfer
->handle
);
116 curl_slist_free_all(xfer
->headers
);
118 curl_mime_free(xfer
->mime
);
120 curl_url_cleanup(xfer
->fullurl
);
123 pakfire_mirror_unref(xfer
->mirror
);
125 pakfire_mirrorlist_unref(xfer
->mirrors
);
127 pakfire_progress_unref(xfer
->progress
);
129 pakfire_httpclient_unref(xfer
->client
);
131 pakfire_ctx_unref(xfer
->ctx
);
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;
143 CTX_DEBUG(ctx
, "cURL: %.*s", (int)size
, data
);
147 case CURLINFO_HEADER_IN
:
148 CTX_DEBUG(ctx
, "cURL: < %.*s", (int)size
, data
);
151 case CURLINFO_HEADER_OUT
:
152 CTX_DEBUG(ctx
, "cURL: > %.*s", (int)size
, data
);
155 // Ignore everything else
164 static size_t pakfire_xfer_read(char* data
, size_t size
, size_t nmemb
, void* p
) {
165 struct pakfire_xfer
* xfer
= p
;
167 return fread(data
, size
, nmemb
, xfer
->fout
);
170 static int pakfire_xfer_seek(void* p
, curl_off_t offset
, int origin
) {
171 struct pakfire_xfer
* xfer
= p
;
175 r
= fseek(xfer
->fout
, (long)offset
, origin
);
177 return CURL_SEEKFUNC_CANTSEEK
;
179 return CURL_SEEKFUNC_OK
;
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
;
188 // Do not write empty blocks
192 // Update message digest
194 r
= EVP_DigestUpdate(xfer
->evp
, data
, nmemb
);
196 CTX_ERROR(ctx
, "EVP_DigestUpdate failed: %s\n",
197 ERR_error_string(ERR_get_error(), NULL
));
202 // If there is no output steam, we just pretent that we have consumed the data
206 // Write everything to the allocated file descriptor
207 return fwrite(data
, size
, nmemb
, xfer
->fin
);
210 static int pakfire_xfer_setup(struct pakfire_xfer
* xfer
) {
211 struct pakfire_config
* config
= NULL
;
212 const char* proxy
= NULL
;
215 CURLSH
* share
= pakfire_httpclient_share(xfer
->client
);
217 // Configure the share handle
218 r
= curl_easy_setopt(xfer
->handle
, CURLOPT_SHARE
, share
);
220 CTX_ERROR(xfer
->ctx
, "Could not configure cURL share handle: %s\n",
221 curl_easy_strerror(r
));
225 // Fetch global configuration
226 config
= pakfire_ctx_get_config(xfer
->ctx
);
228 // Set global configuration
230 proxy
= pakfire_config_get(config
, "general", "proxy", NULL
);
232 curl_easy_setopt(xfer
->handle
, CURLOPT_PROXY
, proxy
);
235 // Be a good net citizen and set a user agent
236 curl_easy_setopt(xfer
->handle
, CURLOPT_USERAGENT
, PACKAGE_NAME
"/" PACKAGE_VERSION
);
239 // Enable logging/debugging
240 curl_easy_setopt(xfer
->handle
, CURLOPT_VERBOSE
, 1L);
242 curl_easy_setopt(xfer
->handle
, CURLOPT_DEBUGFUNCTION
, pakfire_xfer_debug_callback
);
243 curl_easy_setopt(xfer
->handle
, CURLOPT_DEBUGDATA
, xfer
->ctx
);
246 // Limit protocols to HTTPS, HTTP, FTP and FILE
247 curl_easy_setopt(xfer
->handle
, CURLOPT_PROTOCOLS_STR
, "HTTPS,HTTP,FTP,FILE");
249 // Allow all support encodings
250 curl_easy_setopt(xfer
->handle
, CURLOPT_ACCEPT_ENCODING
, "");
252 // Reference back to this xfer
253 curl_easy_setopt(xfer
->handle
, CURLOPT_PRIVATE
, xfer
);
255 // Follow any redirects
256 curl_easy_setopt(xfer
->handle
, CURLOPT_FOLLOWLOCATION
, 1);
258 // Only follow up to 30 redirects
259 curl_easy_setopt(xfer
->handle
, CURLOPT_MAXREDIRS
, 30L);
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
);
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
);
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
);
281 pakfire_config_unref(config
);
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
;
291 // Allocate a new xfer
292 x
= calloc(1, sizeof(*x
));
296 // Store a reference to the context
297 x
->ctx
= pakfire_ctx_ref(ctx
);
299 // Initialize the reference counter
302 // Store a reference to the HTTP client
303 x
->client
= pakfire_httpclient_ref(client
);
306 r
= pakfire_string_set(x
->url
, url
);
311 x
->handle
= curl_easy_init();
317 // Allocate the full URL
318 x
->fullurl
= curl_url();
325 r
= pakfire_xfer_setup(x
);
329 // Return the reference
336 pakfire_xfer_unref(x
);
341 struct pakfire_xfer
* pakfire_xfer_ref(struct pakfire_xfer
* xfer
) {
347 struct pakfire_xfer
* pakfire_xfer_unref(struct pakfire_xfer
* xfer
) {
348 if (--xfer
->nrefs
> 0)
351 pakfire_xfer_free(xfer
);
355 CURL
* pakfire_xfer_handle(struct pakfire_xfer
* xfer
) {
359 int pakfire_xfer_set_method(struct pakfire_xfer
* xfer
,
360 const pakfire_xfer_method_t method
) {
361 const char* m
= NULL
;
364 case PAKFIRE_METHOD_DELETE
:
372 return curl_easy_setopt(xfer
->handle
, CURLOPT_CUSTOMREQUEST
, m
);
376 const char* pakfire_xfer_get_title(struct pakfire_xfer
* xfer
) {
377 char title
[PATH_MAX
];
380 // Default to the filename if no title is set
382 // Only use the basename
383 r
= pakfire_path_basename(title
, xfer
->url
);
388 r
= pakfire_xfer_set_title(xfer
, title
);
396 int pakfire_xfer_set_title(struct pakfire_xfer
* xfer
, const char* title
) {
397 return pakfire_string_set(xfer
->title
, title
);
400 int pakfire_xfer_set_baseurl(struct pakfire_xfer
* xfer
, const char* baseurl
) {
401 return pakfire_string_set(xfer
->baseurl
, baseurl
);
404 const char* pakfire_xfer_get_effective_url(struct pakfire_xfer
* xfer
) {
405 return xfer
->effective_url
;
408 int pakfire_xfer_set_mirrorlist(struct pakfire_xfer
* xfer
, struct pakfire_mirrorlist
* mirrors
) {
410 pakfire_mirrorlist_unref(xfer
->mirrors
);
412 xfer
->mirrors
= pakfire_mirrorlist_ref(mirrors
);
419 size_t pakfire_xfer_get_size(struct pakfire_xfer
* xfer
) {
420 return xfer
->expected_size
;
423 int pakfire_xfer_set_size(struct pakfire_xfer
* xfer
, size_t size
) {
424 xfer
->expected_size
= size
;
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
) {
432 if (!expected_digest
|| !expected_digest_length
)
435 // Expected digest length cannot be too long
436 if (expected_digest_length
> sizeof(xfer
->expected_digest
))
441 case PAKFIRE_DIGEST_SHA3_512
:
442 xfer
->md
= EVP_sha3_512();
445 case PAKFIRE_DIGEST_SHA3_256
:
446 xfer
->md
= EVP_sha3_256();
449 case PAKFIRE_DIGEST_BLAKE2B512
:
450 xfer
->md
= EVP_blake2b512();
453 case PAKFIRE_DIGEST_BLAKE2S256
:
454 xfer
->md
= EVP_blake2s256();
457 case PAKFIRE_DIGEST_SHA2_512
:
458 xfer
->md
= EVP_sha512();
461 case PAKFIRE_DIGEST_SHA2_256
:
462 xfer
->md
= EVP_sha256();
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
;
476 int pakfire_xfer_add_param(struct pakfire_xfer
* xfer
,
477 const char* key
, const char* format
, ...) {
478 curl_mimepart
* part
= NULL
;
483 // Allocate the MIME object if not done, yet
485 xfer
->mime
= curl_mime_init(xfer
->handle
);
488 CTX_ERROR(xfer
->ctx
, "Could not allocate the MIME object: %s\n",
496 va_start(args
, format
);
497 r
= vasprintf(&buffer
, format
, args
);
500 // Abort if we could not format the value
504 // Allocate another MIME part
505 part
= curl_mime_addpart(xfer
->mime
);
507 CTX_ERROR(xfer
->ctx
, "Could not allocate MIME part: %s\n",
514 r
= curl_mime_name(part
, key
);
516 CTX_ERROR(xfer
->ctx
, "Could not set parameter key (%s): %s\n",
517 key
, curl_easy_strerror(r
));
522 r
= curl_mime_data(part
, buffer
, CURL_ZERO_TERMINATED
);
524 CTX_ERROR(xfer
->ctx
, "Could not set parameter data (%s): %s\n",
525 key
, curl_easy_strerror(r
));
536 static void pakfire_xfer_reset_output(struct pakfire_xfer
* xfer
) {
543 int pakfire_xfer_set_output(struct pakfire_xfer
* xfer
, FILE* f
) {
544 pakfire_xfer_reset_output(xfer
);
546 // Store the new stream
552 int pakfire_xfer_set_output_buffer(struct pakfire_xfer
* xfer
,
553 char** buffer
, size_t* length
) {
556 // Open a memory stream
557 f
= open_memstream(buffer
, length
);
559 CTX_ERROR(xfer
->ctx
, "Could not open memory stream: %s\n", strerror(errno
));
563 return pakfire_xfer_set_output(xfer
, f
);
566 int pakfire_xfer_set_input(struct pakfire_xfer
* xfer
, FILE* f
) {
570 // Fetch the file descriptor
571 const int fd
= fileno(f
);
574 xfer
->direction
= PAKFIRE_XFER_UPLOAD
;
576 // Store the file handle
579 // Try to find the upload size
581 r
= fstat(fd
, &stat
);
585 // Store the expected filesize
586 xfer
->expected_size
= stat
.st_size
;
592 static int pakfire_xfer_allocate_tmpfile(struct pakfire_xfer
* xfer
, const char* path
) {
593 char dirname
[PATH_MAX
];
598 // Find the directory name
599 r
= pakfire_path_dirname(dirname
, path
);
603 // Ensure the directory exists
604 r
= pakfire_mkdir(dirname
, 0755);
608 // Open a new temporary file
609 fd
= open(dirname
, O_TMPFILE
|O_RDWR
, 0600);
611 CTX_ERROR(xfer
->ctx
, "Could not open temporary file in %s: %s\n",
612 dirname
, strerror(errno
));
616 // Turn the file descriptor into a FILE handle
617 f
= fdopen(fd
, "w+");
621 // Set the handle as output
622 return pakfire_xfer_set_output(xfer
, f
);
625 int pakfire_xfer_set_output_path(struct pakfire_xfer
* xfer
, const char* path
) {
628 // Store the output path
629 r
= pakfire_string_set(xfer
->path
, path
);
633 // Allocate a temporary file
634 r
= pakfire_xfer_allocate_tmpfile(xfer
, path
);
641 int pakfire_xfer_auth(struct pakfire_xfer
* xfer
) {
642 // Enable authentication
648 static int pakfire_xfer_select_mirror(struct pakfire_xfer
* xfer
) {
649 // Choose the next mirror
651 xfer
->mirror
= pakfire_mirrorlist_get_next(xfer
->mirrors
, xfer
->mirror
);
653 // If no mirror has been selected yet, choose the first one
655 xfer
->mirror
= pakfire_mirrorlist_get_first(xfer
->mirrors
);
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
);
665 CTX_ERROR(xfer
->ctx
, "No mirrors left to try\n");
671 CTX_DEBUG(xfer
->ctx
, "Selected mirror %s\n", pakfire_mirror_get_url(xfer
->mirror
));
676 static const char* curl_http_version(long v
) {
678 #ifdef CURL_HTTP_VERSION_3_0
679 case CURL_HTTP_VERSION_3_0
:
683 case CURL_HTTP_VERSION_2_0
:
686 case CURL_HTTP_VERSION_1_1
:
689 case CURL_HTTP_VERSION_1_0
:
696 static int pakfire_xfer_save(struct pakfire_xfer
* xfer
) {
697 struct utimbuf times
= {
703 // Flush any buffered data out to disk
707 // Nothing to do if path isn't set
711 CTX_DEBUG(xfer
->ctx
, "Download successful. Storing result in %s\n", xfer
->path
);
713 int fd
= fileno(xfer
->fin
);
715 // Make sure the parent directory exists
716 r
= pakfire_mkparentdir(xfer
->path
, 0755);
720 // Unlink the destination file (if it exists)
723 // Move the temporary file to its destination
724 r
= linkat(fd
, "", AT_FDCWD
, xfer
->path
, AT_EMPTY_PATH
);
726 CTX_ERROR(xfer
->ctx
, "Could not link destination file %s: %m\n",
732 curl_easy_getinfo(xfer
->handle
, CURLINFO_FILETIME_T
, ×
.modtime
);
734 if (times
.modtime
> 0) {
735 r
= utime(xfer
->path
, ×
);
737 CTX_ERROR(xfer
->ctx
, "Could not set mtime of %s: %m\n", xfer
->path
);
743 int pakfire_xfer_fail(struct pakfire_xfer
* xfer
, int code
) {
746 CTX_DEBUG(xfer
->ctx
, "Xfer failed\n");
748 // Get file descriptor
749 int fd
= fileno(xfer
->fin
);
751 // Truncate downloaded data
752 r
= ftruncate(fd
, 0);
756 // Did we use a mirror?
758 pakfire_mirror_xfer_failed(xfer
->mirror
);
760 // Try again with another mirror
767 int pakfire_xfer_done(struct pakfire_xfer
* xfer
, int code
) {
768 CURL
* h
= xfer
->handle
;
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;
781 r
= pakfire_progress_finish(xfer
->progress
);
785 CTX_DEBUG(xfer
->ctx
, "cURL xfer done: %d - %s\n",
786 code
, curl_easy_strerror(code
));
788 // Finish message digest computation
790 r
= EVP_DigestFinal_ex(xfer
->evp
, xfer
->computed_digest
, &xfer
->computed_digest_length
);
792 CTX_ERROR(xfer
->ctx
, "Could not finish message digest computation: %s\n",
793 ERR_error_string(ERR_get_error(), NULL
));
799 curl_easy_getinfo(h
, CURLINFO_SCHEME
, &scheme
);
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
);
807 curl_easy_getinfo(h
, CURLINFO_RESPONSE_CODE
, &response_code
);
809 CTX_DEBUG(xfer
->ctx
, " Response code: %ld\n", response_code
);
812 curl_easy_getinfo(h
, CURLINFO_HTTP_VERSION
, &http_version
);
814 CTX_DEBUG(xfer
->ctx
, " HTTP Version: %s\n", curl_http_version(http_version
));
817 curl_easy_getinfo(h
, CURLINFO_TOTAL_TIME
, &total_time
);
818 CTX_DEBUG(xfer
->ctx
, " Total Time: %.2fs\n", total_time
);
821 r
= curl_easy_getinfo(h
, CURLINFO_SIZE_DOWNLOAD_T
, &download_size
);
826 CTX_DEBUG(xfer
->ctx
, " Download Size: %ld bytes\n", download_size
);
829 r
= curl_easy_getinfo(h
, CURLINFO_SPEED_DOWNLOAD_T
, &download_speed
);
834 CTX_DEBUG(xfer
->ctx
, " Download Speed: %ld bps\n", download_speed
);
837 r
= curl_easy_getinfo(h
, CURLINFO_SIZE_UPLOAD_T
, &upload_size
);
842 CTX_DEBUG(xfer
->ctx
, " Upload Size: %ld bytes\n", upload_size
);
845 r
= curl_easy_getinfo(h
, CURLINFO_SPEED_UPLOAD_T
, &upload_speed
);
850 CTX_DEBUG(xfer
->ctx
, " Upload Speed: %ld bps\n", upload_speed
);
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
);
859 // Check if digests match
861 r
= CRYPTO_memcmp(xfer
->computed_digest
, xfer
->expected_digest
,
862 xfer
->computed_digest_length
);
864 // If they don't match, log an error and try again
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
);
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
);
875 if (computed_hexdigest
)
876 free(computed_hexdigest
);
877 if (expected_hexdigest
)
878 free(expected_hexdigest
);
880 // Make this download fail
881 r
= pakfire_xfer_fail(xfer
, 0);
889 // If we could not determine the scheme...
891 r
= pakfire_xfer_fail(xfer
, 0);
894 else if (strcmp(scheme
, "FILE") == 0) {
897 r
= pakfire_xfer_fail(xfer
, code
);
899 r
= pakfire_xfer_save(xfer
);
904 } else if ((strcmp(scheme
, "HTTPS") == 0) || (strcmp(scheme
, "HTTP") == 0)) {
905 switch (response_code
) {
908 r
= pakfire_xfer_save(xfer
);
913 // Treat all other response codes as an error
915 r
= pakfire_xfer_fail(xfer
, code
);
924 } else if (strcmp(scheme
, "FTP") == 0) {
925 if (response_code
== 226)
926 r
= pakfire_xfer_save(xfer
);
928 r
= pakfire_xfer_fail(xfer
, code
);
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
;
941 switch (xfer
->direction
) {
942 case PAKFIRE_XFER_DOWNLOAD
:
943 // Update the expected size
944 xfer
->expected_size
= dltotal
;
946 // Update the xferred counter
947 xfer
->xferred
= dlnow
;
950 case PAKFIRE_XFER_UPLOAD
:
951 // Update the expected size
952 xfer
->expected_size
= ultotal
;
954 // Update the xferred counter
955 xfer
->xferred
= ulnow
;
959 // Do nothing if no progress indicator has been set up
963 // Set maximum (because this might have changed)
964 pakfire_progress_set_max_value(xfer
->progress
, xfer
->expected_size
);
966 // Update current value
967 return pakfire_progress_update(xfer
->progress
, xfer
->xferred
);
970 static int pakfire_xfer_prepare_progress(struct pakfire_xfer
* xfer
,
971 struct pakfire_progress
* parent
, int flags
) {
972 const char* title
= NULL
;
974 PAKFIRE_PROGRESS_SHOW_PERCENTAGE
|
975 PAKFIRE_PROGRESS_SHOW_BYTES_TRANSFERRED
|
976 PAKFIRE_PROGRESS_SHOW_TRANSFER_SPEED
|
977 PAKFIRE_PROGRESS_SHOW_ETA
;
980 // If this has already been set up, we skip it
984 // Show no progress if requested
985 if (flags
& PAKFIRE_XFER_NO_PROGRESS
)
986 progress_flags
|= PAKFIRE_PROGRESS_NO_PROGRESS
;
988 // Show no progress if we have a parent progress
990 progress_flags
|= PAKFIRE_PROGRESS_NO_PROGRESS
;
992 // Make a new progress meter
993 r
= pakfire_progress_create(&xfer
->progress
, xfer
->ctx
, progress_flags
, parent
);
998 title
= pakfire_xfer_get_title(xfer
);
1000 r
= pakfire_progress_set_title(xfer
->progress
, title
);
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);
1012 r
= pakfire_progress_start(xfer
->progress
, xfer
->expected_size
);
1019 static int pakfire_xfer_prepare_url(struct pakfire_xfer
* xfer
) {
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);
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
);
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);
1040 // Then append our own part
1041 r
= curl_url_set(xfer
->fullurl
, CURLUPART_URL
, xfer
->url
, 0);
1046 } else if (*xfer
->baseurl
) {
1047 // Set the base URL first
1048 r
= curl_url_set(xfer
->fullurl
, CURLUPART_URL
, xfer
->baseurl
, 0);
1052 // Then append our own part
1053 r
= curl_url_set(xfer
->fullurl
, CURLUPART_URL
, xfer
->url
, 0);
1057 // Fail if we could not set the URL
1059 CTX_ERROR(xfer
->ctx
, "Invalid xfer %s\n", xfer
->url
);
1065 r
= curl_easy_setopt(xfer
->handle
, CURLOPT_CURLU
, xfer
->fullurl
);
1067 CTX_ERROR(xfer
->ctx
, "Could not set the URL: %s\n", curl_easy_strerror(r
));
1075 int pakfire_xfer_prepare(struct pakfire_xfer
* xfer
, struct pakfire_progress
* progress
, int flags
) {
1081 // Set special options for direction
1082 switch (xfer
->direction
) {
1083 case PAKFIRE_XFER_DOWNLOAD
:
1086 case PAKFIRE_XFER_UPLOAD
:
1087 // Let cURL know that we are uploading things
1088 r
= curl_easy_setopt(xfer
->handle
, CURLOPT_UPLOAD
, 1L);
1090 CTX_ERROR(xfer
->ctx
, "Could not enable upload\n");
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
);
1099 CTX_ERROR(xfer
->ctx
, "Could not set upload size\n");
1104 // Upload files chunked
1105 xfer
->headers
= curl_slist_append(xfer
->headers
, "Transfer-Encoding: chunked");
1110 r
= pakfire_xfer_prepare_url(xfer
);
1112 CTX_ERROR(xfer
->ctx
, "Could not compose URL: %m\n");
1117 if (xfer
->headers
) {
1118 r
= curl_easy_setopt(xfer
->handle
, CURLOPT_HTTPHEADER
, xfer
->headers
);
1120 CTX_ERROR(xfer
->ctx
, "Could not set headers: %s\n", curl_easy_strerror(r
));
1127 r
= curl_easy_setopt(xfer
->handle
, CURLOPT_MIMEPOST
, xfer
->mime
);
1129 CTX_ERROR(xfer
->ctx
, "Could not set POST payload: %s\n", curl_easy_strerror(r
));
1137 r
= curl_easy_setopt(xfer
->handle
, CURLOPT_HTTPAUTH
, CURLAUTH_NEGOTIATE
|CURLAUTH_ONLY
);
1139 CTX_ERROR(xfer
->ctx
, "Could not enable SPNEGO\n");
1143 // Set an empty username
1144 r
= curl_easy_setopt(xfer
->handle
, CURLOPT_USERPWD
, ":");
1146 CTX_ERROR(xfer
->ctx
, "Could not set username\n");
1151 // Drop any previously used EVP contexts
1153 EVP_MD_CTX_free(xfer
->evp
);
1157 // Create a new EVP context
1159 xfer
->evp
= EVP_MD_CTX_new();
1161 CTX_ERROR(xfer
->ctx
, "Could not create EVP context: %m\n");
1165 // Initialize the EVP context
1166 r
= EVP_DigestInit_ex(xfer
->evp
, xfer
->md
, NULL
);
1168 CTX_ERROR(xfer
->ctx
, "Could not initialize EVP context: %s\n",
1169 ERR_error_string(ERR_get_error(), NULL
));
1175 r
= pakfire_xfer_prepare_progress(xfer
, progress
, flags
);
1182 int pakfire_xfer_run(struct pakfire_xfer
* xfer
, int flags
) {
1187 r
= pakfire_xfer_prepare(xfer
, NULL
, flags
);
1189 CTX_ERROR(xfer
->ctx
, "Could not prepare xfer %s: %s\n",
1190 xfer
->url
, strerror(-r
));
1194 // Perform the action
1195 r
= curl_easy_perform(xfer
->handle
);
1197 // Handle the result
1198 r
= pakfire_xfer_done(xfer
, r
);
1200 // Repeat the xfer if asked
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
;
1220 const char* url
= pakfire_xfer_get_effective_url(xfer
);
1223 if (!json_object_object_get_ex(error
, "code", &code
))
1226 // Check if the code is an integer
1227 if (!json_object_is_type(code
, json_type_int
))
1230 // Fetch the message
1231 if (!json_object_object_get_ex(error
, "message", &message
))
1234 // Check if the message is a string
1235 if (!json_object_is_type(message
, json_type_string
))
1238 c
= json_object_get_uint64(code
);
1239 m
= json_object_get_string(message
);
1242 CTX_ERROR(xfer
->ctx
, "%s responded with error %u (%s):\n %s\n",
1243 url
, c
, strerror(c
), m
);
1249 This function parses an API response
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
;
1257 // Check if we received any data
1259 CTX_ERROR(xfer
->ctx
, "Received an empty response\n");
1264 // XXX Maybe fetch the parser's error message here?!
1267 o
= pakfire_json_parse(xfer
->ctx
, buffer
, length
);
1269 CTX_ERROR(xfer
->ctx
, "Could not parse the response\n");
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");
1282 r
= json_object_object_get_ex(o
, "error", &error
);
1284 r
= pakfire_xfer_handle_api_error(xfer
, error
);
1288 // Return the object
1290 *object
= json_object_get(o
);
1299 static int pakfire_xfer_run_api_request_once(struct pakfire_xfer
* xfer
, struct json_object
** response
) {
1300 char* buffer
= NULL
;
1304 // Write the response to the buffer
1305 r
= pakfire_xfer_set_output_buffer(xfer
, &buffer
, &length
);
1310 r
= pakfire_xfer_run(xfer
, PAKFIRE_XFER_NO_PROGRESS
);
1314 // Parse the response
1315 r
= pakfire_xfer_parse_api_response(xfer
, buffer
, length
, response
);
1317 CTX_ERROR(xfer
->ctx
, "Could not parse the API response: %s\n", strerror(-r
));
1322 // Reset the output stream
1323 pakfire_xfer_reset_output(xfer
);
1332 This function sends a request and automatically parses the response.
1333 The response might optionally be returned if response is not NULL.
1335 int pakfire_xfer_run_api_request(struct pakfire_xfer
* xfer
, struct json_object
** response
) {
1338 // Loop indefinitely...
1340 r
= pakfire_xfer_run_api_request_once(xfer
, response
);
1342 // XXX need to catch errors and act accordingly