src/libpakfire/solution.c \
src/libpakfire/string.c \
src/libpakfire/transaction.c \
- src/libpakfire/util.c
+ src/libpakfire/util.c \
+ src/libpakfire/xfer.c
pkginclude_HEADERS += \
src/libpakfire/include/pakfire/arch.h \
src/libpakfire/include/pakfire/solution.h \
src/libpakfire/include/pakfire/string.h \
src/libpakfire/include/pakfire/transaction.h \
- src/libpakfire/include/pakfire/util.h
+ src/libpakfire/include/pakfire/util.h \
+ src/libpakfire/include/pakfire/xfer.h
libpakfire_la_CFLAGS = \
$(AM_CFLAGS) \
#include <pakfire/private.h>
#include <pakfire/string.h>
#include <pakfire/util.h>
+#include <pakfire/xfer.h>
#include <krb5/krb5.h>
return NULL;
}
-static int pakfire_buildservice_create_transfer(struct pakfire_transfer** transfer,
+static int pakfire_buildservice_create_xfer(struct pakfire_xfer** xfer,
struct pakfire_buildservice* service, const char* url) {
- struct pakfire_transfer* t = NULL;
+ struct pakfire_xfer* t = NULL;
int r;
- // Create a new transfer
- r = pakfire_downloader_transfer_create(&t, service->httpclient, url);
+ // Create a new xfer
+ r = pakfire_downloader_create_xfer(&t, service->httpclient, url);
if (r)
goto ERROR;
// Set the base URL
- r = pakfire_downloader_transfer_set_baseurl(t, service->url);
+ r = pakfire_xfer_set_baseurl(t, service->url);
if (r)
goto ERROR;
- // Return the new transfer
- *transfer = pakfire_downloader_transfer_ref(t);
+ // Return the new xfer
+ *xfer = pakfire_xfer_ref(t);
ERROR:
if (t)
- pakfire_downloader_transfer_unref(t);
+ pakfire_xfer_unref(t);
return r;
}
static int pakfire_buildservice_handle_error(struct pakfire_buildservice* service,
- struct pakfire_transfer* transfer, const struct json_object* error) {
+ struct pakfire_xfer* xfer, const struct json_object* error) {
struct json_object* message = NULL;
struct json_object* code = NULL;
const char* m = NULL;
unsigned int c = 0;
// Fetch the URL
- const char* url = pakfire_downloader_transfer_get_effective_url(transfer);
+ const char* url = pakfire_xfer_get_effective_url(xfer);
// Fetch the code
if (!json_object_object_get_ex(error, "code", &code))
This function parses an API response
*/
static int pakfire_buildservice_parse_response(struct pakfire_buildservice* service,
- struct pakfire_transfer* transfer, const char* buffer, const size_t length,
+ struct pakfire_xfer* xfer, const char* buffer, const size_t length,
struct json_object** object) {
struct json_object* error = NULL;
struct json_object* o = NULL;
// Fetch error
r = json_object_object_get_ex(o, "error", &error);
if (r) {
- r = pakfire_buildservice_handle_error(service, transfer, error);
+ r = pakfire_buildservice_handle_error(service, xfer, error);
goto ERROR;
}
int pakfire_buildservice_build(struct pakfire_buildservice* service, const char* upload,
const char* repo, const char** arches, int flags) {
- struct pakfire_transfer* transfer = NULL;
+ struct pakfire_xfer* xfer = NULL;
struct json_object* response = NULL;
char* buffer = NULL;
size_t length = 0;
int r;
- // Create a new transfer
- r = pakfire_buildservice_create_transfer(&transfer, service, "/api/v1/builds");
+ // Create a new xfer
+ r = pakfire_buildservice_create_xfer(&xfer, service, "/api/v1/builds");
if (r)
goto ERROR;
// Enable authentication
- r = pakfire_downloader_transfer_auth(transfer);
+ r = pakfire_xfer_auth(xfer);
if (r)
goto ERROR;
// Add the upload parameter
- r = pakfire_downloader_transfer_add_param(transfer, "upload", "%s", upload);
+ r = pakfire_xfer_add_param(xfer, "upload", "%s", upload);
if (r)
goto ERROR;
// Add the repo parameter
- r = pakfire_downloader_transfer_add_param(transfer, "repo", "%s", repo);
+ r = pakfire_xfer_add_param(xfer, "repo", "%s", repo);
if (r)
goto ERROR;
// Add any arches
if (arches) {
for (const char** arch = arches; *arch; arch++) {
- r = pakfire_downloader_transfer_add_param(transfer, "arch", "%s", *arch);
+ r = pakfire_xfer_add_param(xfer, "arch", "%s", *arch);
if (r)
goto ERROR;
}
// Disable tests?
if (flags & PAKFIRE_BUILD_DISABLE_TESTS) {
- r = pakfire_downloader_transfer_add_param(transfer, "disable_test_builds", "%s", "yes");
+ r = pakfire_xfer_add_param(xfer, "disable_test_builds", "%s", "yes");
if (r)
goto ERROR;
}
// Write the response to the buffer
- r = pakfire_downloader_transfer_set_output_buffer(transfer, &buffer, &length);
+ r = pakfire_xfer_set_output_buffer(xfer, &buffer, &length);
if (r)
goto ERROR;
- // Run the transfer
- r = pakfire_downloader_transfer_run(transfer, PAKFIRE_TRANSFER_NO_PROGRESS);
+ // Run the xfer
+ r = pakfire_xfer_run(xfer, PAKFIRE_XFER_NO_PROGRESS);
if (r)
goto ERROR;
// Parse the response
- r = pakfire_buildservice_parse_response(service, transfer, buffer, length, &response);
+ r = pakfire_buildservice_parse_response(service, xfer, buffer, length, &response);
if (r) {
CTX_ERROR(service->ctx, "Could not parse the response: %s\n", strerror(-r));
goto ERROR;
}
ERROR:
- if (transfer)
- pakfire_downloader_transfer_unref(transfer);
+ if (xfer)
+ pakfire_xfer_unref(xfer);
if (response)
json_object_put(response);
if (buffer)
static int pakfire_buildservice_create_upload(struct pakfire_buildservice* service,
const char* path, const char* filename, FILE* f, char** uuid) {
- struct pakfire_transfer* transfer = NULL;
+ struct pakfire_xfer* xfer = NULL;
struct pakfire_digests digests = {};
struct json_object* response = NULL;
struct json_object* id = NULL;
if (!hexdigest)
goto ERROR;
- // Create a new transfer
- r = pakfire_buildservice_create_transfer(&transfer, service, "/api/v1/uploads");
+ // Create a new xfer
+ r = pakfire_buildservice_create_xfer(&xfer, service, "/api/v1/uploads");
if (r)
goto ERROR;
// Enable authentication
- r = pakfire_downloader_transfer_auth(transfer);
+ r = pakfire_xfer_auth(xfer);
if (r)
goto ERROR;
// Add the filename parameter
- r = pakfire_downloader_transfer_add_param(transfer, "filename", "%s", filename);
+ r = pakfire_xfer_add_param(xfer, "filename", "%s", filename);
if (r)
goto ERROR;
// Add the size parameter
- r = pakfire_downloader_transfer_add_param(transfer, "size", "%jd", stat.st_size);
+ r = pakfire_xfer_add_param(xfer, "size", "%jd", stat.st_size);
if (r)
goto ERROR;
// Add the hexdigest algo parameter
- r = pakfire_downloader_transfer_add_param(transfer, "hexdigest_algo", "%s", "blake2b512");
+ r = pakfire_xfer_add_param(xfer, "hexdigest_algo", "%s", "blake2b512");
if (r)
goto ERROR;
// Add the hexdigest parameter
- r = pakfire_downloader_transfer_add_param(transfer, "hexdigest", "%s", hexdigest);
+ r = pakfire_xfer_add_param(xfer, "hexdigest", "%s", hexdigest);
if (r)
goto ERROR;
// Write the response to the buffer
- r = pakfire_downloader_transfer_set_output_buffer(transfer, &buffer, &length);
+ r = pakfire_xfer_set_output_buffer(xfer, &buffer, &length);
if (r)
goto ERROR;
- // Run the transfer
- r = pakfire_downloader_transfer_run(transfer, PAKFIRE_TRANSFER_NO_PROGRESS);
+ // Run the xfer
+ r = pakfire_xfer_run(xfer, PAKFIRE_XFER_NO_PROGRESS);
if (r)
goto ERROR;
// Parse the response
- r = pakfire_buildservice_parse_response(service, transfer, buffer, length, &response);
+ r = pakfire_buildservice_parse_response(service, xfer, buffer, length, &response);
if (r) {
CTX_ERROR(service->ctx, "Could not parse the response: %s\n", strerror(-r));
goto ERROR;
r = 0;
ERROR:
- if (transfer)
- pakfire_downloader_transfer_unref(transfer);
+ if (xfer)
+ pakfire_xfer_unref(xfer);
if (response)
json_object_put(response);
if (hexdigest)
static int pakfire_buildservice_upload_payload(struct pakfire_buildservice* service,
const char* filename, const char* uuid, FILE* f) {
- struct pakfire_transfer* transfer = NULL;
+ struct pakfire_xfer* xfer = NULL;
struct json_object* response = NULL;
char* buffer = NULL;
size_t length = 0;
if (r)
goto ERROR;
- // Create a new transfer
- r = pakfire_buildservice_create_transfer(&transfer, service, url);
+ // Create a new xfer
+ r = pakfire_buildservice_create_xfer(&xfer, service, url);
if (r)
goto ERROR;
// Set the title
- r = pakfire_downloader_transfer_set_title(transfer, filename);
+ r = pakfire_xfer_set_title(xfer, filename);
if (r)
goto ERROR;
// Set source file
- r = pakfire_downloader_transfer_set_input(transfer, f);
+ r = pakfire_xfer_set_input(xfer, f);
if (r)
goto ERROR;
// Set the output buffer
- r = pakfire_downloader_transfer_set_output_buffer(transfer, &buffer, &length);
+ r = pakfire_xfer_set_output_buffer(xfer, &buffer, &length);
if (r)
goto ERROR;
- // Run the transfer
- r = pakfire_downloader_transfer_run(transfer, 0);
+ // Run the xfer
+ r = pakfire_xfer_run(xfer, 0);
if (r)
goto ERROR;
// Parse the response
- r = pakfire_buildservice_parse_response(service, transfer, buffer, length, &response);
+ r = pakfire_buildservice_parse_response(service, xfer, buffer, length, &response);
if (r) {
CTX_ERROR(service->ctx, "Could not parse the response: %s\n", strerror(-r));
goto ERROR;
}
ERROR:
- if (transfer)
- pakfire_downloader_transfer_unref(transfer);
+ if (xfer)
+ pakfire_xfer_unref(xfer);
if (response)
json_object_put(response);
PAKFIRE_EXPORT int pakfire_buildservice_list_uploads(
struct pakfire_buildservice* service, struct json_object** p) {
- struct pakfire_transfer* transfer = NULL;
+ struct pakfire_xfer* xfer = NULL;
struct json_object* response = NULL;
struct json_object* uploads = NULL;
char* buffer = NULL;
size_t length = 0;
int r;
- // Create a new transfer
- r = pakfire_buildservice_create_transfer(&transfer, service, "/api/v1/uploads");
+ // Create a new xfer
+ r = pakfire_buildservice_create_xfer(&xfer, service, "/api/v1/uploads");
if (r)
goto ERROR;
// Enable authentication
- r = pakfire_downloader_transfer_auth(transfer);
+ r = pakfire_xfer_auth(xfer);
if (r)
goto ERROR;
// Write the response to the buffer
- r = pakfire_downloader_transfer_set_output_buffer(transfer, &buffer, &length);
+ r = pakfire_xfer_set_output_buffer(xfer, &buffer, &length);
if (r)
goto ERROR;
- // Run the transfer
- r = pakfire_downloader_transfer_run(transfer, PAKFIRE_TRANSFER_NO_PROGRESS);
+ // Run the xfer
+ r = pakfire_xfer_run(xfer, PAKFIRE_XFER_NO_PROGRESS);
if (r)
goto ERROR;
// Parse the response
- r = pakfire_buildservice_parse_response(service, transfer, buffer, length, &response);
+ r = pakfire_buildservice_parse_response(service, xfer, buffer, length, &response);
if (r) {
CTX_ERROR(service->ctx, "Could not parse the response: %s\n", strerror(-r));
goto ERROR;
*p = json_object_get(uploads);
ERROR:
- if (transfer)
- pakfire_downloader_transfer_unref(transfer);
+ if (xfer)
+ pakfire_xfer_unref(xfer);
if (response)
json_object_put(response);
if (buffer)
PAKFIRE_EXPORT int pakfire_buildservice_delete_upload(
struct pakfire_buildservice* service, const char* uuid) {
- struct pakfire_transfer* transfer = NULL;
+ struct pakfire_xfer* xfer = NULL;
struct json_object* response = NULL;
char* buffer = NULL;
size_t length = 0;
if (r)
goto ERROR;
- // Create a new transfer
- r = pakfire_buildservice_create_transfer(&transfer, service, url);
+ // Create a new xfer
+ r = pakfire_buildservice_create_xfer(&xfer, service, url);
if (r)
goto ERROR;
// Ask to DELETE
- r = pakfire_downloader_transfer_set_method(transfer, PAKFIRE_METHOD_DELETE);
+ r = pakfire_xfer_set_method(xfer, PAKFIRE_METHOD_DELETE);
if (r)
goto ERROR;
// Enable authentication
- r = pakfire_downloader_transfer_auth(transfer);
+ r = pakfire_xfer_auth(xfer);
if (r)
goto ERROR;
// Write the response to the buffer
- r = pakfire_downloader_transfer_set_output_buffer(transfer, &buffer, &length);
+ r = pakfire_xfer_set_output_buffer(xfer, &buffer, &length);
if (r)
goto ERROR;
- // Run the transfer
- r = pakfire_downloader_transfer_run(transfer, PAKFIRE_TRANSFER_NO_PROGRESS);
+ // Run the xfer
+ r = pakfire_xfer_run(xfer, PAKFIRE_XFER_NO_PROGRESS);
if (r)
goto ERROR;
// Parse the response
- r = pakfire_buildservice_parse_response(service, transfer, buffer, length, &response);
+ r = pakfire_buildservice_parse_response(service, xfer, buffer, length, &response);
if (r) {
CTX_ERROR(service->ctx, "Could not parse the response: %s\n", strerror(-r));
goto ERROR;
}
ERROR:
- if (transfer)
- pakfire_downloader_transfer_unref(transfer);
+ if (xfer)
+ pakfire_xfer_unref(xfer);
if (response)
json_object_put(response);
if (buffer)
PAKFIRE_EXPORT int pakfire_buildservice_list_repos(struct pakfire_buildservice* service,
const char* distro, struct json_object** p) {
- struct pakfire_transfer* transfer = NULL;
+ struct pakfire_xfer* xfer = NULL;
struct json_object* response = NULL;
struct json_object* repos = NULL;
char url[PATH_MAX];
if (r)
goto ERROR;
- // Create a new transfer
- r = pakfire_buildservice_create_transfer(&transfer, service, url);
+ // Create a new xfer
+ r = pakfire_buildservice_create_xfer(&xfer, service, url);
if (r)
goto ERROR;
// Enable authentication
- r = pakfire_downloader_transfer_auth(transfer);
+ r = pakfire_xfer_auth(xfer);
if (r)
goto ERROR;
// Write the response to the buffer
- r = pakfire_downloader_transfer_set_output_buffer(transfer, &buffer, &length);
+ r = pakfire_xfer_set_output_buffer(xfer, &buffer, &length);
if (r)
goto ERROR;
- // Run the transfer
- r = pakfire_downloader_transfer_run(transfer, PAKFIRE_TRANSFER_NO_PROGRESS);
+ // Run the xfer
+ r = pakfire_xfer_run(xfer, PAKFIRE_XFER_NO_PROGRESS);
if (r)
goto ERROR;
// Parse the response
- r = pakfire_buildservice_parse_response(service, transfer, buffer, length, &response);
+ r = pakfire_buildservice_parse_response(service, xfer, buffer, length, &response);
if (r) {
CTX_ERROR(service->ctx, "Could not parse the response: %s\n", strerror(-r));
goto ERROR;
*p = json_object_get(repos);
ERROR:
- if (transfer)
- pakfire_downloader_transfer_unref(transfer);
+ if (xfer)
+ pakfire_xfer_unref(xfer);
if (response)
json_object_put(response);
if (buffer)
PAKFIRE_EXPORT int pakfire_buildservice_get_repo(struct pakfire_buildservice* service,
const char* distro, const char* name, struct json_object** p) {
- struct pakfire_transfer* transfer = NULL;
+ struct pakfire_xfer* xfer = NULL;
struct json_object* response = NULL;
char url[PATH_MAX];
char* buffer = NULL;
if (r)
goto ERROR;
- // Create a new transfer
- r = pakfire_buildservice_create_transfer(&transfer, service, url);
+ // Create a new xfer
+ r = pakfire_buildservice_create_xfer(&xfer, service, url);
if (r)
goto ERROR;
// Enable authentication
- r = pakfire_downloader_transfer_auth(transfer);
+ r = pakfire_xfer_auth(xfer);
if (r)
goto ERROR;
// Write the response to the buffer
- r = pakfire_downloader_transfer_set_output_buffer(transfer, &buffer, &length);
+ r = pakfire_xfer_set_output_buffer(xfer, &buffer, &length);
if (r)
goto ERROR;
- // Run the transfer
- r = pakfire_downloader_transfer_run(transfer, PAKFIRE_TRANSFER_NO_PROGRESS);
+ // Run the xfer
+ r = pakfire_xfer_run(xfer, PAKFIRE_XFER_NO_PROGRESS);
if (r)
goto ERROR;
// Parse the response
- r = pakfire_buildservice_parse_response(service, transfer, buffer, length, &response);
+ r = pakfire_buildservice_parse_response(service, xfer, buffer, length, &response);
if (r) {
CTX_ERROR(service->ctx, "Could not parse the response: %s\n", strerror(-r));
goto ERROR;
*p = json_object_get(response);
ERROR:
- if (transfer)
- pakfire_downloader_transfer_unref(transfer);
+ if (xfer)
+ pakfire_xfer_unref(xfer);
if (response)
json_object_put(response);
if (buffer)
PAKFIRE_EXPORT int pakfire_buildservice_create_repo(struct pakfire_buildservice* service,
const char* distro, const char* name, const char* description, struct json_object** p) {
- struct pakfire_transfer* transfer = NULL;
+ struct pakfire_xfer* xfer = NULL;
struct json_object* response = NULL;
char url[PATH_MAX];
char* buffer = NULL;
if (r)
goto ERROR;
- // Create a new transfer
- r = pakfire_buildservice_create_transfer(&transfer, service, url);
+ // Create a new xfer
+ r = pakfire_buildservice_create_xfer(&xfer, service, url);
if (r)
goto ERROR;
// Enable authentication
- r = pakfire_downloader_transfer_auth(transfer);
+ r = pakfire_xfer_auth(xfer);
if (r)
goto ERROR;
// Set name
- r = pakfire_downloader_transfer_add_param(transfer, "name", "%s", name);
+ r = pakfire_xfer_add_param(xfer, "name", "%s", name);
if (r)
goto ERROR;
// Set description
if (description) {
- r = pakfire_downloader_transfer_add_param(transfer, "description", "%s", description);
+ r = pakfire_xfer_add_param(xfer, "description", "%s", description);
if (r)
goto ERROR;
}
// Write the response to the buffer
- r = pakfire_downloader_transfer_set_output_buffer(transfer, &buffer, &length);
+ r = pakfire_xfer_set_output_buffer(xfer, &buffer, &length);
if (r)
goto ERROR;
- // Run the transfer
- r = pakfire_downloader_transfer_run(transfer, PAKFIRE_TRANSFER_NO_PROGRESS);
+ // Run the xfer
+ r = pakfire_xfer_run(xfer, PAKFIRE_XFER_NO_PROGRESS);
if (r)
goto ERROR;
// Parse the response
- r = pakfire_buildservice_parse_response(service, transfer, buffer, length, &response);
+ r = pakfire_buildservice_parse_response(service, xfer, buffer, length, &response);
if (r) {
CTX_ERROR(service->ctx, "Could not parse the response: %s\n", strerror(-r));
goto ERROR;
*p = json_object_get(response);
ERROR:
- if (transfer)
- pakfire_downloader_transfer_unref(transfer);
+ if (xfer)
+ pakfire_xfer_unref(xfer);
if (response)
json_object_put(response);
if (buffer)
PAKFIRE_EXPORT int pakfire_buildservice_delete_repo(struct pakfire_buildservice* service,
const char* distro, const char* name) {
- struct pakfire_transfer* transfer = NULL;
+ struct pakfire_xfer* xfer = NULL;
struct json_object* response = NULL;
char* buffer = NULL;
size_t length = 0;
if (r)
goto ERROR;
- // Create a new transfer
- r = pakfire_buildservice_create_transfer(&transfer, service, url);
+ // Create a new xfer
+ r = pakfire_buildservice_create_xfer(&xfer, service, url);
if (r)
goto ERROR;
// Ask to DELETE
- r = pakfire_downloader_transfer_set_method(transfer, PAKFIRE_METHOD_DELETE);
+ r = pakfire_xfer_set_method(xfer, PAKFIRE_METHOD_DELETE);
if (r)
goto ERROR;
// Enable authentication
- r = pakfire_downloader_transfer_auth(transfer);
+ r = pakfire_xfer_auth(xfer);
if (r)
goto ERROR;
// Write the response to the buffer
- r = pakfire_downloader_transfer_set_output_buffer(transfer, &buffer, &length);
+ r = pakfire_xfer_set_output_buffer(xfer, &buffer, &length);
if (r)
goto ERROR;
- // Run the transfer
- r = pakfire_downloader_transfer_run(transfer, PAKFIRE_TRANSFER_NO_PROGRESS);
+ // Run the xfer
+ r = pakfire_xfer_run(xfer, PAKFIRE_XFER_NO_PROGRESS);
if (r)
goto ERROR;
// Parse the response
- r = pakfire_buildservice_parse_response(service, transfer, buffer, length, &response);
+ r = pakfire_buildservice_parse_response(service, xfer, buffer, length, &response);
if (r) {
CTX_ERROR(service->ctx, "Could not parse the response: %s\n", strerror(-r));
goto ERROR;
}
ERROR:
- if (transfer)
- pakfire_downloader_transfer_unref(transfer);
+ if (xfer)
+ pakfire_xfer_unref(xfer);
if (response)
json_object_put(response);
if (buffer)
#include <pakfire/repo.h>
#include <pakfire/string.h>
#include <pakfire/util.h>
+#include <pakfire/xfer.h>
#define PAKFIRE_MACROS_DIR "/usr/lib/pakfire/macros"
#define PAKFIRE_MACROS_GLOB_PATTERN PAKFIRE_MACROS_DIR "/*.macro"
static int pakfire_dist_download_source(struct pakfire_downloader* downloader,
struct pakfire_mirrorlist* mirrorlist, const char* filename, const char* cache_path) {
- struct pakfire_transfer* transfer = NULL;
+ struct pakfire_xfer* xfer = NULL;
int r;
// Do not download if the file exists
return 0;
// Create a new transfer
- r = pakfire_downloader_transfer_create(&transfer, downloader, filename);
+ r = pakfire_downloader_create_xfer(&xfer, downloader, filename);
if (r)
goto ERROR;
// Set the mirrorlist
- r = pakfire_downloader_transfer_set_mirrorlist(transfer, mirrorlist);
+ r = pakfire_xfer_set_mirrorlist(xfer, mirrorlist);
if (r)
goto ERROR;
// Set the target
- r = pakfire_downloader_transfer_set_target(transfer, cache_path);
+ r = pakfire_xfer_set_target(xfer, cache_path);
if (r)
goto ERROR;
// Run the download
- r = pakfire_downloader_transfer_run(transfer, 0);
+ r = pakfire_xfer_run(xfer, 0);
if (r)
goto ERROR;
ERROR:
- if (transfer)
- pakfire_downloader_transfer_unref(transfer);
+ if (xfer)
+ pakfire_xfer_unref(xfer);
return r;
}
#############################################################################*/
#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
#include <stdlib.h>
#include <sys/queue.h>
-#include <unistd.h>
-#include <utime.h>
#include <curl/curl.h>
-#include <json.h>
-#include <openssl/err.h>
-#include <openssl/evp.h>
-#include <pakfire/digest.h>
#include <pakfire/downloader.h>
-#include <pakfire/i18n.h>
#include <pakfire/logging.h>
-#include <pakfire/pakfire.h>
-#include <pakfire/path.h>
#include <pakfire/progress.h>
-#include <pakfire/string.h>
-#include <pakfire/util.h>
+#include <pakfire/xfer.h>
// The number of concurrent downloads
#define MAX_PARALLEL 4
-struct pakfire_transfer {
- struct pakfire_ctx* ctx;
- int nrefs;
-
- // Reference to the downloader
- struct pakfire_downloader* downloader;
-
- // Reference to the progress indicator
- struct pakfire_progress* progress;
-
- TAILQ_ENTRY(pakfire_transfer) nodes;
-
- // cURL handle
- CURL* handle;
-
- // Headers
- struct curl_slist* headers;
-
- // URL
- CURLU* fullurl;
-
- char url[PATH_MAX];
- char title[NAME_MAX];
- char path[PATH_MAX];
- enum pakfire_transfer_flags flags;
- int tries;
-
- // POST MIME Object
- curl_mime* mime;
-
- // Transfer direction
- enum {
- PAKFIRE_TRANSFER_DOWNLOAD = 0,
- PAKFIRE_TRANSFER_UPLOAD = 1,
- } direction;
-
- // Size
- size_t expected_size;
- size_t transferred;
-
- // Temporary file
- char tempfile[PATH_MAX];
-
- // File handles for streams
- FILE* fin;
- FILE* fout;
-
- // Crypto Stuff
- EVP_MD_CTX* evp;
- const EVP_MD* md;
- unsigned char computed_digest[EVP_MAX_MD_SIZE];
- unsigned int computed_digest_length;
- unsigned char expected_digest[EVP_MAX_MD_SIZE];
- unsigned int expected_digest_length;
-
- // Mirrors
- char baseurl[PATH_MAX];
- struct pakfire_mirrorlist* mirrors;
- struct pakfire_mirror* mirror;
+struct pakfire_xfer_element {
+ TAILQ_ENTRY(pakfire_xfer_element) nodes;
- // Effective URL
- const char* effective_url;
-
- // Authentication
- unsigned int auth;
+ struct pakfire_xfer* xfer;
};
struct pakfire_downloader {
CURLM* curl;
// Transfers
- TAILQ_HEAD(transfers_queued, pakfire_transfer) transfers_queued;
- TAILQ_HEAD(transfers_running, pakfire_transfer) transfers_running;
- TAILQ_HEAD(transfers_finished, pakfire_transfer) transfers_finished;
+ TAILQ_HEAD(xfers_queued, pakfire_xfer_element) xfers_queued;
+ TAILQ_HEAD(xfers_running, pakfire_xfer_element) xfers_running;
+ TAILQ_HEAD(xfers_finished, pakfire_xfer_element) xfers_finished;
};
static int pakfire_downloader_setup_curl(struct pakfire_downloader* downloader) {
return 0;
}
-static void pakfire_downloader_transfer_free(struct pakfire_transfer* transfer) {
- if (transfer->handle)
- curl_easy_cleanup(transfer->handle);
-
- // Unlink the temporary file
- if (*transfer->tempfile)
- unlink(transfer->tempfile);
-
- // Close any streams
- if (transfer->fin)
- fclose(transfer->fin);
-
- // Free OpenSSL EVP context
- if (transfer->evp)
- EVP_MD_CTX_free(transfer->evp);
-
- if (transfer->headers)
- curl_slist_free_all(transfer->headers);
- if (transfer->mime)
- curl_mime_free(transfer->mime);
- if (transfer->fullurl)
- curl_url_cleanup(transfer->fullurl);
-
- if (transfer->mirror)
- pakfire_mirror_unref(transfer->mirror);
- if (transfer->mirrors)
- pakfire_mirrorlist_unref(transfer->mirrors);
- if (transfer->progress)
- pakfire_progress_unref(transfer->progress);
- if (transfer->ctx)
- pakfire_ctx_unref(transfer->ctx);
-
- free(transfer);
+static struct pakfire_xfer_element* pakfire_downloader_xfer_create(struct pakfire_xfer* xfer) {
+ struct pakfire_xfer_element* x = NULL;
+
+ // Allocate a new element
+ x = calloc(1, sizeof(*x));
+ if (!x)
+ return NULL;
+
+ // Store a reference to the xfer
+ x->xfer = pakfire_xfer_ref(xfer);
+
+ return x;
+}
+
+static struct pakfire_xfer_element* pakfire_downloader_xfer_find_running(
+ struct pakfire_downloader* downloader, struct pakfire_xfer* xfer) {
+ struct pakfire_xfer_element* x = NULL;
+
+ TAILQ_FOREACH(x, &downloader->xfers_running, nodes) {
+ if (x->xfer == xfer)
+ return x;
+ }
+
+ return NULL;
+}
+
+static void pakfire_downloader_xfer_free(struct pakfire_xfer_element* x) {
+ if (x->xfer)
+ pakfire_xfer_unref(x->xfer);
+
+ free(x);
}
static void pakfire_downloader_free(struct pakfire_downloader* downloader) {
- struct pakfire_transfer* transfer = NULL;
+ struct pakfire_xfer_element* x = NULL;
// Free any queued transfers
- while (!TAILQ_EMPTY(&downloader->transfers_queued)) {
- transfer = TAILQ_LAST(&downloader->transfers_queued, transfers_queued);
- TAILQ_REMOVE(&downloader->transfers_queued, transfer, nodes);
+ while (!TAILQ_EMPTY(&downloader->xfers_queued)) {
+ x = TAILQ_LAST(&downloader->xfers_queued, xfers_queued);
+ TAILQ_REMOVE(&downloader->xfers_queued, x, nodes);
- pakfire_downloader_transfer_unref(transfer);
+ pakfire_downloader_xfer_free(x);
}
// Free any running transfers
- while (!TAILQ_EMPTY(&downloader->transfers_running)) {
- transfer = TAILQ_LAST(&downloader->transfers_running, transfers_running);
- TAILQ_REMOVE(&downloader->transfers_running, transfer, nodes);
+ while (!TAILQ_EMPTY(&downloader->xfers_running)) {
+ x = TAILQ_LAST(&downloader->xfers_running, xfers_running);
+ TAILQ_REMOVE(&downloader->xfers_running, x, nodes);
- pakfire_downloader_transfer_unref(transfer);
+ pakfire_downloader_xfer_free(x);
}
// Free any finished transfers
- while (!TAILQ_EMPTY(&downloader->transfers_finished)) {
- transfer = TAILQ_LAST(&downloader->transfers_finished, transfers_finished);
- TAILQ_REMOVE(&downloader->transfers_finished, transfer, nodes);
+ while (!TAILQ_EMPTY(&downloader->xfers_finished)) {
+ x = TAILQ_LAST(&downloader->xfers_finished, xfers_finished);
+ TAILQ_REMOVE(&downloader->xfers_finished, x, nodes);
- pakfire_downloader_transfer_unref(transfer);
+ pakfire_downloader_xfer_free(x);
}
if (downloader->share)
d->parallel = MAX_PARALLEL;
// Init transfer queues
- TAILQ_INIT(&d->transfers_queued);
- TAILQ_INIT(&d->transfers_running);
- TAILQ_INIT(&d->transfers_finished);
+ TAILQ_INIT(&d->xfers_queued);
+ TAILQ_INIT(&d->xfers_running);
+ TAILQ_INIT(&d->xfers_finished);
// Setup cURL
r = pakfire_downloader_setup_curl(d);
return NULL;
}
-#ifdef ENABLE_DEBUG
-static int debug_callback(CURL *handle, curl_infotype type,
- char* data, size_t size, void* private) {
- struct pakfire_ctx* ctx = private;
-
- switch (type) {
- case CURLINFO_TEXT:
- CTX_DEBUG(ctx, "cURL: %.*s", (int)size, data);
- break;
-
- // Log headers
- case CURLINFO_HEADER_IN:
- CTX_DEBUG(ctx, "cURL: < %.*s", (int)size, data);
- break;
-
- case CURLINFO_HEADER_OUT:
- CTX_DEBUG(ctx, "cURL: > %.*s", (int)size, data);
- break;
-
- // Ignore everything else
- default:
- break;
- }
-
- return 0;
-}
-#endif
-
-static size_t pakfire_downloader_transfer_read(char* data, size_t size, size_t nmemb, void* p) {
- struct pakfire_transfer* transfer = p;
-
- return fread(data, size, nmemb, transfer->fout);
-}
-
-static int pakfire_downloader_transfer_seek(void* p, curl_off_t offset, int origin) {
- struct pakfire_transfer* transfer = p;
- int r;
-
- // Perform the seek
- r = fseek(transfer->fout, (long)offset, origin);
- if (r < 0)
- return CURL_SEEKFUNC_CANTSEEK;
-
- return CURL_SEEKFUNC_OK;
+CURLSH* pakfire_downloader_share(struct pakfire_downloader* downloader) {
+ return downloader->share;
}
-static size_t pakfire_downloader_transfer_write(
- char* data, size_t size, size_t nmemb, void* p) {
- struct pakfire_transfer* transfer = p;
- struct pakfire_ctx* ctx = transfer->ctx;
- int r;
-
- // Do not write empty blocks
- if (!nmemb)
- return nmemb;
-
- // Update message digest
- if (transfer->evp) {
- r = EVP_DigestUpdate(transfer->evp, data, nmemb);
- if (r != 1) {
- CTX_ERROR(ctx, "EVP_DigestUpdate failed: %s\n",
- ERR_error_string(ERR_get_error(), NULL));
- return 0;
- }
- }
-
- // Write everything to the allocated file descriptor
- return fwrite(data, size, nmemb, transfer->fin);
+int pakfire_downloader_create_xfer(struct pakfire_xfer** xfer,
+ struct pakfire_downloader* downloader, const char* url) {
+ return pakfire_xfer_create(xfer, downloader->ctx, downloader, url);
}
-static int pakfire_downloader_transfer_setup(
- struct pakfire_downloader* downloader, struct pakfire_transfer* transfer) {
- struct pakfire_config* config = NULL;
- const char* proxy = NULL;
- int r;
-
- // Configure the share handle
- r = curl_easy_setopt(transfer->handle, CURLOPT_SHARE, downloader->share);
- if (r) {
- CTX_ERROR(downloader->ctx, "Could not configure cURL share handle: %s\n",
- curl_easy_strerror(r));
- return r;
- }
-
- // Fetch global configuration
- config = pakfire_ctx_get_config(downloader->ctx);
+int pakfire_downloader_enqueue_xfer(struct pakfire_downloader* downloader,
+ struct pakfire_xfer* xfer) {
+ struct pakfire_xfer_element* x = NULL;
- // Set global configuration
- if (config) {
- proxy = pakfire_config_get(config, "general", "proxy", NULL);
- if (proxy)
- curl_easy_setopt(transfer->handle, CURLOPT_PROXY, proxy);
- }
-
- // Be a good net citizen and set a user agent
- curl_easy_setopt(transfer->handle, CURLOPT_USERAGENT, PACKAGE_NAME "/" PACKAGE_VERSION);
-
-#ifdef ENABLE_DEBUG
- // Enable logging/debugging
- curl_easy_setopt(transfer->handle, CURLOPT_VERBOSE, 1L);
-
- curl_easy_setopt(transfer->handle, CURLOPT_DEBUGFUNCTION, debug_callback);
- curl_easy_setopt(transfer->handle, CURLOPT_DEBUGDATA, downloader->ctx);
-#endif
-
- // Limit protocols to HTTPS, HTTP, FTP and FILE
- curl_easy_setopt(transfer->handle, CURLOPT_PROTOCOLS_STR, "HTTPS,HTTP,FTP,FILE");
-
- // Reference back to this transfer
- curl_easy_setopt(transfer->handle, CURLOPT_PRIVATE, transfer);
-
- // Follow any redirects
- curl_easy_setopt(transfer->handle, CURLOPT_FOLLOWLOCATION, 1);
-
- // Only follow up to 30 redirects
- curl_easy_setopt(transfer->handle, CURLOPT_MAXREDIRS, 30L);
-
- // Read any data from a callback function
- curl_easy_setopt(transfer->handle,
- CURLOPT_READFUNCTION, pakfire_downloader_transfer_read);
- curl_easy_setopt(transfer->handle, CURLOPT_READDATA, transfer);
-
- // Write all data to the callback function
- curl_easy_setopt(transfer->handle,
- CURLOPT_WRITEFUNCTION, pakfire_downloader_transfer_write);
- curl_easy_setopt(transfer->handle, CURLOPT_WRITEDATA, transfer);
-
- // Register the seek callback
- curl_easy_setopt(transfer->handle,
- CURLOPT_SEEKFUNCTION, pakfire_downloader_transfer_seek);
- curl_easy_setopt(transfer->handle, CURLOPT_SEEKDATA, transfer);
-
- // Success
- r = 0;
-
- // Cleanup
- if (config)
- pakfire_config_unref(config);
-
- return r;
-}
-
-int pakfire_downloader_transfer_create(struct pakfire_transfer** transfer,
- struct pakfire_downloader* downloader, const char* url) {
- struct pakfire_transfer* t = NULL;
- int r;
-
- // Allocate a new transfer
- t = calloc(1, sizeof(*t));
- if (!t)
+ // Create a new queueable object
+ x = pakfire_downloader_xfer_create(xfer);
+ if (!x)
return -errno;
- // Store a reference to the context
- t->ctx = pakfire_ctx_ref(downloader->ctx);
-
- // Initialize the reference counter
- t->nrefs = 1;
-
- // Store a reference to the downloader
- t->downloader = pakfire_downloader_ref(downloader);
-
- // Store the URL
- r = pakfire_string_set(t->url, url);
- if (r)
- goto ERROR;
-
- // Allocate a handle
- t->handle = curl_easy_init();
- if (!t->handle) {
- r = 1;
- goto ERROR;
- }
-
- // Allocate the full URL
- t->fullurl = curl_url();
- if (!t->fullurl) {
- r = -errno;
- goto ERROR;
- }
-
- // Setup the transfer
- r = pakfire_downloader_transfer_setup(downloader, t);
- if (r)
- goto ERROR;
-
// Push this transfer onto the queue
- TAILQ_INSERT_HEAD(&downloader->transfers_queued,
- pakfire_downloader_transfer_ref(t), nodes);
-
- // Return the reference
- *transfer = t;
-
- return 0;
-
-ERROR:
- if (t)
- pakfire_downloader_transfer_unref(t);
-
- return r;
-}
-
-struct pakfire_transfer* pakfire_downloader_transfer_ref(struct pakfire_transfer* transfer) {
- ++transfer->nrefs;
-
- return transfer;
-}
-
-struct pakfire_transfer* pakfire_downloader_transfer_unref(struct pakfire_transfer* transfer) {
- if (--transfer->nrefs > 0)
- return transfer;
-
- pakfire_downloader_transfer_free(transfer);
- return NULL;
-}
-
-int pakfire_downloader_transfer_set_method(struct pakfire_transfer* transfer,
- const pakfire_transfer_method_t method) {
- const char* m = NULL;
-
- switch (method) {
- case PAKFIRE_METHOD_DELETE:
- m = "DELETE";
- break;
-
- default:
- return -EINVAL;
- }
-
- return curl_easy_setopt(transfer->handle, CURLOPT_CUSTOMREQUEST, m);
-}
-
-
-const char* pakfire_downloader_transfer_get_title(struct pakfire_transfer* transfer) {
- char title[PATH_MAX];
- int r;
-
- // Default to the filename if no title is set
- if (!*transfer->title) {
- // Only use the basename
- r = pakfire_path_basename(title, transfer->url);
- if (r)
- return NULL;
-
- // Store the title
- r = pakfire_downloader_transfer_set_title(transfer, title);
- if (r)
- return NULL;
- }
-
- return transfer->title;
-}
-
-int pakfire_downloader_transfer_set_title(
- struct pakfire_transfer* transfer, const char* title) {
- return pakfire_string_set(transfer->title, title);
-}
-
-int pakfire_downloader_transfer_set_baseurl(
- struct pakfire_transfer* transfer, const char* baseurl) {
- return pakfire_string_set(transfer->baseurl, baseurl);
-}
-
-const char* pakfire_downloader_transfer_get_effective_url(struct pakfire_transfer* transfer) {
- return transfer->effective_url;
-}
-
-int pakfire_downloader_transfer_set_mirrorlist(
- struct pakfire_transfer* transfer, struct pakfire_mirrorlist* mirrors) {
- if (transfer->mirrors)
- pakfire_mirrorlist_unref(transfer->mirrors);
-
- transfer->mirrors = pakfire_mirrorlist_ref(mirrors);
+ TAILQ_INSERT_HEAD(&downloader->xfers_queued, x, nodes);
return 0;
}
-static size_t pakfire_downloader_transfer_get_size(struct pakfire_transfer* transfer) {
- return transfer->expected_size;
-}
-
-int pakfire_downloader_transfer_set_size(
- struct pakfire_transfer* transfer, size_t size) {
- transfer->expected_size = size;
-
- return 0;
-}
-
-int pakfire_downloader_transfer_verify_digest(
- struct pakfire_transfer* transfer, const enum pakfire_digest_types md,
- const unsigned char* expected_digest, const size_t expected_digest_length) {
- // Check inputs
- if (!expected_digest || !expected_digest_length)
- return -EINVAL;
-
- // Expected digest length cannot be too long
- if (expected_digest_length > sizeof(transfer->expected_digest))
- return -ENOBUFS;
-
- // Store digest type
- switch (md) {
- case PAKFIRE_DIGEST_SHA3_512:
- transfer->md = EVP_sha3_512();
- break;
-
- case PAKFIRE_DIGEST_SHA3_256:
- transfer->md = EVP_sha3_256();
- break;
-
- case PAKFIRE_DIGEST_BLAKE2B512:
- transfer->md = EVP_blake2b512();
- break;
-
- case PAKFIRE_DIGEST_BLAKE2S256:
- transfer->md = EVP_blake2s256();
- break;
-
- case PAKFIRE_DIGEST_SHA2_512:
- transfer->md = EVP_sha512();
- break;
-
- case PAKFIRE_DIGEST_SHA2_256:
- transfer->md = EVP_sha256();
- break;
-
- default:
- return -ENOTSUP;
- }
-
- // Store the expected digest and its length
- memcpy(transfer->expected_digest, expected_digest, expected_digest_length);
- transfer->expected_digest_length = expected_digest_length;
-
- return 0;
-}
-
-int pakfire_downloader_transfer_add_param(struct pakfire_transfer* transfer,
- const char* key, const char* format, ...) {
- curl_mimepart* part = NULL;
- char* buffer = NULL;
- va_list args;
- int r;
-
- // Allocate the MIME object if not done, yet
- if (!transfer->mime) {
- transfer->mime = curl_mime_init(transfer->handle);
-
- if (!transfer->mime) {
- CTX_ERROR(transfer->ctx, "Could not allocate the MIME object: %s\n",
- strerror(errno));
- r = -errno;
- goto ERROR;
- }
- }
-
- // Format value
- va_start(args, format);
- r = vasprintf(&buffer, format, args);
- va_end(args);
-
- // Abort if we could not format the value
- if (r < 0)
- goto ERROR;
-
- // Allocate another MIME part
- part = curl_mime_addpart(transfer->mime);
- if (!part) {
- CTX_ERROR(transfer->ctx, "Could not allocate MIME part: %s\n",
- strerror(errno));
- r = errno;
- goto ERROR;
- }
-
- // Set the key
- r = curl_mime_name(part, key);
- if (r) {
- CTX_ERROR(transfer->ctx, "Could not set parameter key (%s): %s\n",
- key, curl_easy_strerror(r));
- goto ERROR;
- }
-
- // Set the data
- r = curl_mime_data(part, buffer, CURL_ZERO_TERMINATED);
- if (r) {
- CTX_ERROR(transfer->ctx, "Could not set parameter data (%s): %s\n",
- key, curl_easy_strerror(r));
- goto ERROR;
- }
-
-ERROR:
- if (buffer)
- free(buffer);
-
- return r;
-}
-
-int pakfire_downloader_transfer_set_output(struct pakfire_transfer* transfer, FILE* f) {
- transfer->fin = f;
-
- return 0;
-}
-
-int pakfire_downloader_transfer_set_output_buffer(struct pakfire_transfer* transfer,
- char** buffer, size_t* length) {
- FILE* f = NULL;
-
- // Open a memory stream
- f = open_memstream(buffer, length);
- if (!f) {
- CTX_ERROR(transfer->ctx, "Could not open memory stream: %s\n", strerror(errno));
- return -errno;
- }
-
- return pakfire_downloader_transfer_set_output(transfer, f);
-}
-
-int pakfire_downloader_transfer_set_input(struct pakfire_transfer* transfer, FILE* f) {
- struct stat stat;
- int r;
-
- // Fetch the file descriptor
- const int fd = fileno(f);
-
- // Change direction
- transfer->direction = PAKFIRE_TRANSFER_UPLOAD;
-
- // Store the file handle
- transfer->fout = f;
-
- // Try to find the upload size
- if (fd > 0) {
- r = fstat(fd, &stat);
- if (r)
- return 0;
-
- // Store the expected filesize
- transfer->expected_size = stat.st_size;
- }
-
- return 0;
-}
-
-int pakfire_downloader_transfer_set_target(
- struct pakfire_transfer* transfer, const char* path) {
- return pakfire_string_set(transfer->path, path);
-}
-
-int pakfire_downloader_transfer_auth(struct pakfire_transfer* transfer) {
- // Enable authentication
- transfer->auth = 1;
-
- return 0;
-}
-
-static int pakfire_transfer_select_mirror(struct pakfire_downloader* downloader,
- struct pakfire_transfer* transfer) {
- // Choose the next mirror
- if (transfer->mirror)
- transfer->mirror = pakfire_mirrorlist_get_next(transfer->mirrors, transfer->mirror);
-
- // If no mirror has been selected yet, choose the first one
- else
- transfer->mirror = pakfire_mirrorlist_get_first(transfer->mirrors);
-
- // Skip this mirror if it is broken
- while (transfer->mirror && pakfire_mirror_is_broken(transfer->mirror)) {
- // Move on to the next mirror
- transfer->mirror = pakfire_mirrorlist_get_next(transfer->mirrors, transfer->mirror);
- }
-
- // No mirror found
- if (!transfer->mirror) {
- CTX_ERROR(downloader->ctx, "No mirrors left to try\n");
-
- // No mirrors left
- return ENOENT;
- }
-
- CTX_DEBUG(downloader->ctx, "Selected mirror %s\n", pakfire_mirror_get_url(transfer->mirror));
-
- return 0;
-}
-
-static const char* curl_http_version(long v) {
- switch (v) {
-#ifdef CURL_HTTP_VERSION_3_0
- case CURL_HTTP_VERSION_3_0:
- return "HTTP/3.0";
-#endif
-
- case CURL_HTTP_VERSION_2_0:
- return "HTTP/2.0";
-
- case CURL_HTTP_VERSION_1_1:
- return "HTTP/1.1";
-
- case CURL_HTTP_VERSION_1_0:
- return "HTTP/1.0";
- }
-
- return "unknown";
-}
-
-static int pakfire_transfer_save(struct pakfire_downloader* downloader,
- struct pakfire_transfer* transfer) {
- struct utimbuf times = {
- .actime = 0,
- .modtime = 0,
- };
- int r;
-
- // Flush any buffered data out to disk
- if (transfer->fin)
- fflush(transfer->fin);
-
- // Nothing to do if path isn't set
- if (!*transfer->path)
- return 0;
-
- CTX_DEBUG(downloader->ctx,
- "Download successful. Storing result in %s\n", transfer->path);
-
- // Make sure the parent directory exists
- r = pakfire_mkparentdir(transfer->path, 0755);
- if (r)
- return r;
-
- // Unlink the destination file (if it exists)
- unlink(transfer->path);
-
- // Move the temporary file to its destination
- r = link(transfer->tempfile, transfer->path);
- if (r) {
- CTX_ERROR(downloader->ctx, "Could not link destination file %s: %m\n",
- transfer->path);
- return r;
- }
-
- // Filetime
- curl_easy_getinfo(transfer->handle, CURLINFO_FILETIME_T, ×.modtime);
-
- if (times.modtime) {
- r = utime(transfer->path, ×);
- if (r)
- CTX_ERROR(downloader->ctx, "Could not set mtime of %s: %m\n", transfer->path);
- }
-
- return 0;
-}
-
-static int pakfire_transfer_fail(struct pakfire_downloader* downloader,
- struct pakfire_transfer* transfer, int code) {
- int r;
-
- CTX_DEBUG(downloader->ctx, "Transfer failed\n");
-
- // Get file descriptor
- int fd = fileno(transfer->fin);
-
- // Truncate downloaded data
- r = ftruncate(fd, 0);
- if (r)
- return r;
-
- // Did we use a mirror?
- if (transfer->mirror) {
- pakfire_mirror_transfer_failed(transfer->mirror);
-
- // Try again with another mirror
- return EAGAIN;
- }
-
- return 0;
-}
-
-static int pakfire_transfer_done(struct pakfire_downloader* downloader,
- struct pakfire_transfer* transfer, int code) {
- CURL* h = transfer->handle;
- int r;
- char* scheme = NULL;
- long response_code;
- long http_version;
- double total_time;
-
- curl_off_t download_size = 0;
- curl_off_t download_speed = 0;
- curl_off_t upload_size = 0;
- curl_off_t upload_speed = 0;
-
- // Finish progress
- r = pakfire_progress_finish(transfer->progress);
- if (r)
- return r;
-
- CTX_DEBUG(downloader->ctx, "cURL transfer done: %d - %s\n",
- code, curl_easy_strerror(code));
-
- // Finish message digest computation
- if (transfer->evp) {
- r = EVP_DigestFinal_ex(transfer->evp, transfer->computed_digest, &transfer->computed_digest_length);
- if (r != 1) {
- CTX_ERROR(downloader->ctx, "Could not finish message digest computation: %s\n",
- ERR_error_string(ERR_get_error(), NULL));
- return 1;
- }
- }
-
- // Protocol
- curl_easy_getinfo(h, CURLINFO_SCHEME, &scheme);
-
- // Effective URL
- curl_easy_getinfo(h, CURLINFO_EFFECTIVE_URL, &transfer->effective_url);
- if (transfer->effective_url)
- CTX_DEBUG(downloader->ctx, " Effective URL: %s\n", transfer->effective_url);
-
- // Response code
- curl_easy_getinfo(h, CURLINFO_RESPONSE_CODE, &response_code);
- if (response_code)
- CTX_DEBUG(downloader->ctx, " Response code: %ld\n", response_code);
-
- // HTTP Version
- curl_easy_getinfo(h, CURLINFO_HTTP_VERSION, &http_version);
- if (http_version)
- CTX_DEBUG(downloader->ctx, " HTTP Version: %s\n", curl_http_version(http_version));
-
- // Total Times
- curl_easy_getinfo(h, CURLINFO_TOTAL_TIME, &total_time);
- CTX_DEBUG(downloader->ctx, " Total Time: %.2fs\n", total_time);
-
- // Download Size
- r = curl_easy_getinfo(h, CURLINFO_SIZE_DOWNLOAD_T, &download_size);
- if (r)
- return r;
-
- if (download_size)
- CTX_DEBUG(downloader->ctx, " Download Size: %ld bytes\n", download_size);
-
- // Download Speed
- r = curl_easy_getinfo(h, CURLINFO_SPEED_DOWNLOAD_T, &download_speed);
- if (r)
- return r;
-
- if (download_speed)
- CTX_DEBUG(downloader->ctx, " Download Speed: %ld bps\n", download_speed);
-
- // Upload Size
- r = curl_easy_getinfo(h, CURLINFO_SIZE_UPLOAD_T, &upload_size);
- if (r)
- return r;
-
- if (upload_size)
- CTX_DEBUG(downloader->ctx, " Upload Size: %ld bytes\n", upload_size);
-
- // Upload Speed
- r = curl_easy_getinfo(h, CURLINFO_SPEED_UPLOAD_T, &upload_speed);
- if (r)
- return r;
-
- if (upload_speed)
- CTX_DEBUG(downloader->ctx, " Upload Speed: %ld bps\n", upload_speed);
-
- // Message Digest
- char* hexdigest = __pakfire_hexlify(transfer->computed_digest, transfer->computed_digest_length);
- if (hexdigest && *hexdigest) {
- CTX_DEBUG(downloader->ctx, " Message Digest: %s\n", hexdigest);
- free(hexdigest);
- }
-
- // Check if digests match
- if (transfer->evp) {
- r = CRYPTO_memcmp(transfer->computed_digest, transfer->expected_digest,
- transfer->computed_digest_length);
-
- // If they don't match, log an error and try again
- if (r) {
- char* computed_hexdigest = __pakfire_hexlify(transfer->computed_digest,
- transfer->computed_digest_length);
- char* expected_hexdigest = __pakfire_hexlify(transfer->expected_digest,
- transfer->expected_digest_length);
-
- CTX_ERROR(downloader->ctx, "Download checksum for %s didn't match:\n", transfer->effective_url);
- CTX_ERROR(downloader->ctx, " Expected: %s\n", expected_hexdigest);
- CTX_ERROR(downloader->ctx, " Computed: %s\n", computed_hexdigest);
-
- if (computed_hexdigest)
- free(computed_hexdigest);
- if (expected_hexdigest)
- free(expected_hexdigest);
-
- // Make this download fail
- r = pakfire_transfer_fail(downloader, transfer, 0);
- if (r)
- return r;
-
- return 1;
- }
- }
-
- // If we could not determine the scheme...
- if (!scheme)
- r = pakfire_transfer_fail(downloader, transfer, 0);
-
- // FILE
- else if (strcmp(scheme, "FILE") == 0) {
- // Handle any errors
- if (code)
- r = pakfire_transfer_fail(downloader, transfer, code);
- else
- r = pakfire_transfer_save(downloader, transfer);
-
- return r;
-
- // HTTPS + HTTP
- } else if ((strcmp(scheme, "HTTPS") == 0) || (strcmp(scheme, "HTTP") == 0)) {
- switch (response_code) {
- // 200 - OK
- case 200:
- r = pakfire_transfer_save(downloader, transfer);
- if (r)
- return r;
- break;
-
- // Treat all other response codes as an error
- default:
- r = pakfire_transfer_fail(downloader, transfer, code);
- if (r)
- return r;
-
- // Error
- return 1;
- }
-
- // FTP
- } else if (strcmp(scheme, "FTP") == 0) {
- if (response_code == 226)
- r = pakfire_transfer_save(downloader, transfer);
- else
- r = pakfire_transfer_fail(downloader, transfer, code);
-
- return r;
- }
-
- // Success
- return 0;
-}
-
-static int pakfire_downloader_transfer_update(void* data,
- curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
- struct pakfire_transfer* transfer = data;
-
- switch (transfer->direction) {
- case PAKFIRE_TRANSFER_DOWNLOAD:
- // Update the expected size
- transfer->expected_size = dltotal;
-
- // Update the transferred counter
- transfer->transferred = dlnow;
- break;
-
- case PAKFIRE_TRANSFER_UPLOAD:
- // Update the expected size
- transfer->expected_size = ultotal;
-
- // Update the transferred counter
- transfer->transferred = ulnow;
- break;
- }
-
- // Do nothing if no progress indicator has been set up
- if (!transfer->progress)
- return 0;
-
- // Set maximum (because this might have changed)
- pakfire_progress_set_max_value(transfer->progress, transfer->expected_size);
-
- // Update current value
- return pakfire_progress_update(transfer->progress, transfer->transferred);
-}
-
-static int pakfire_downloader_transfer_prepare_progress(struct pakfire_downloader* downloader,
- struct pakfire_transfer* transfer, struct pakfire_progress* parent, int flags) {
- const char* title = NULL;
- int progress_flags =
- PAKFIRE_PROGRESS_SHOW_PERCENTAGE |
- PAKFIRE_PROGRESS_SHOW_BYTES_TRANSFERRED |
- PAKFIRE_PROGRESS_SHOW_TRANSFER_SPEED |
- PAKFIRE_PROGRESS_SHOW_ETA;
- int r;
-
- // If this has already been set up, we skip it
- if (transfer->progress)
- return 0;
-
- // Show no progress if requested
- if (flags & PAKFIRE_TRANSFER_NO_PROGRESS)
- progress_flags |= PAKFIRE_PROGRESS_NO_PROGRESS;
-
- // Show no progress if we have a parent progress
- else if (parent)
- progress_flags |= PAKFIRE_PROGRESS_NO_PROGRESS;
-
- // Make a new progress meter
- r = pakfire_progress_create(&transfer->progress, transfer->ctx, progress_flags, parent);
- if (r)
- return r;
-
- // Set the title
- title = pakfire_downloader_transfer_get_title(transfer);
- if (title) {
- r = pakfire_progress_set_title(transfer->progress, title);
- if (r)
- return r;
- }
-
- // Configure callbacks
- curl_easy_setopt(transfer->handle, CURLOPT_XFERINFOFUNCTION,
- pakfire_downloader_transfer_update);
- curl_easy_setopt(transfer->handle, CURLOPT_XFERINFODATA, transfer);
- curl_easy_setopt(transfer->handle, CURLOPT_NOPROGRESS, 0L);
-
- // Start progress
- r = pakfire_progress_start(transfer->progress, transfer->expected_size);
- if (r)
- return r;
-
- return 0;
-}
-
-static int pakfire_downloader_transfer_prepare_tmpfile(struct pakfire_transfer* transfer) {
- char path[PATH_MAX] = PAKFIRE_TMP_DIR "/pakfire-download.XXXXXX";
- FILE* f = NULL;
- int r;
-
- // Allocate a temporary file
- f = pakfire_mktemp(path, 0600);
- if (!f)
- return -errno;
-
- // Set this as output
- r = pakfire_downloader_transfer_set_output(transfer, f);
- if (r)
- return r;
-
- // Store the path to the temporary file
- return pakfire_string_set(transfer->tempfile, path);
-}
-
-static int pakfire_downloader_transfer_prepare_url(struct pakfire_downloader* downloader,
- struct pakfire_transfer* transfer) {
- int r;
-
- // Simply set absolute URLs
- if (pakfire_string_is_url(transfer->url)) {
- r = curl_url_set(transfer->fullurl, CURLUPART_URL, transfer->url, 0);
- if (r)
- goto ERROR;
-
- // Join path if we are using mirrors
- } else if (transfer->mirrors && !pakfire_mirrorlist_empty(transfer->mirrors)) {
- r = pakfire_transfer_select_mirror(downloader, transfer);
- if (r)
- goto ERROR;
-
- // Set the mirror's base URL first
- r = curl_url_set(transfer->fullurl, CURLUPART_URL,
- pakfire_mirror_get_url(transfer->mirror), 0);
- if (r)
- goto ERROR;
-
- // Then append our own part
- r = curl_url_set(transfer->fullurl, CURLUPART_URL, transfer->url, 0);
- if (r)
- goto ERROR;
-
- // Use baseurl
- } else if (*transfer->baseurl) {
- // Set the base URL first
- r = curl_url_set(transfer->fullurl, CURLUPART_URL, transfer->baseurl, 0);
- if (r)
- goto ERROR;
-
- // Then append our own part
- r = curl_url_set(transfer->fullurl, CURLUPART_URL, transfer->url, 0);
- if (r)
- goto ERROR;
-
- // Fail if we could not set the URL
- } else {
- CTX_ERROR(downloader->ctx, "Invalid transfer %s\n", transfer->url);
- r = -EINVAL;
- goto ERROR;
- }
-
- // Set the URL
- r = curl_easy_setopt(transfer->handle, CURLOPT_CURLU, transfer->fullurl);
- if (r) {
- CTX_ERROR(downloader->ctx, "Could not set the URL: %s\n", curl_easy_strerror(r));
- goto ERROR;
- }
-
-ERROR:
- return r;
-}
-
-static int pakfire_downloader_transfer_prepare(struct pakfire_downloader* downloader,
- struct pakfire_transfer* transfer, struct pakfire_progress* progress, int flags) {
- int r;
-
- // Increment tries
- transfer->tries++;
-
- // Set special options for direction
- switch (transfer->direction) {
- case PAKFIRE_TRANSFER_DOWNLOAD:
- break;
-
- case PAKFIRE_TRANSFER_UPLOAD:
- // Let cURL know that we are uploading things
- r = curl_easy_setopt(transfer->handle, CURLOPT_UPLOAD, 1L);
- if (r) {
- CTX_ERROR(downloader->ctx, "Could not enable upload\n");
- return r;
- }
-
- // Tell it the expected upload size
- if (transfer->expected_size) {
- r = curl_easy_setopt(transfer->handle,
- CURLOPT_INFILESIZE_LARGE, (curl_off_t)transfer->expected_size);
- if (r) {
- CTX_ERROR(downloader->ctx, "Could not set upload size\n");
- return r;
- }
- }
-
- // Transfer files chunked
- transfer->headers = curl_slist_append(transfer->headers, "Transfer-Encoding: chunked");
- break;
- }
-
- // Compose the URL
- r = pakfire_downloader_transfer_prepare_url(downloader, transfer);
- if (r) {
- CTX_ERROR(transfer->ctx, "Could not compose URL: %m\n");
- return r;
- }
-
- // Add any headers
- if (transfer->headers) {
- r = curl_easy_setopt(transfer->handle, CURLOPT_HTTPHEADER, transfer->headers);
- if (r) {
- CTX_ERROR(transfer->ctx, "Could not set headers: %s\n", curl_easy_strerror(r));
- return r;
- }
- }
-
- // Add any payload
- if (transfer->mime) {
- r = curl_easy_setopt(transfer->handle, CURLOPT_MIMEPOST, transfer->mime);
- if (r) {
- CTX_ERROR(transfer->ctx, "Could not set POST payload: %s\n", curl_easy_strerror(r));
- return r;
- }
- }
-
- // If we do not have an output file, we will create a temporary file
- if (!transfer->fout && !transfer->fin) {
- r = pakfire_downloader_transfer_prepare_tmpfile(transfer);
- if (r) {
- CTX_ERROR(downloader->ctx, "Could not open a temporary file: %s\n", strerror(-r));
- return r;
- }
- }
-
- // Authentication
- if (transfer->auth) {
- // Request SPNEGO
- r = curl_easy_setopt(transfer->handle, CURLOPT_HTTPAUTH, CURLAUTH_NEGOTIATE|CURLAUTH_ONLY);
- if (r) {
- CTX_ERROR(downloader->ctx, "Could not enable SPNEGO\n");
- return r;
- }
-
- // Set an empty username
- r = curl_easy_setopt(transfer->handle, CURLOPT_USERPWD, ":");
- if (r) {
- CTX_ERROR(downloader->ctx, "Could not set username\n");
- return r;
- }
- }
-
- // Drop any previously used EVP contexts
- if (transfer->evp) {
- EVP_MD_CTX_free(transfer->evp);
- transfer->evp = NULL;
- }
-
- // Create a new EVP context
- if (transfer->md) {
- transfer->evp = EVP_MD_CTX_new();
- if (!transfer->evp) {
- CTX_ERROR(downloader->ctx, "Could not create EVP context: %m\n");
- return 1;
- }
-
- // Initialize the EVP context
- r = EVP_DigestInit_ex(transfer->evp, transfer->md, NULL);
- if (r != 1) {
- CTX_ERROR(downloader->ctx, "Could not initialize EVP context: %s\n",
- ERR_error_string(ERR_get_error(), NULL));
- return 1;
- }
- }
-
- // Setup progress
- r = pakfire_downloader_transfer_prepare_progress(downloader, transfer, progress, flags);
- if (r)
- return r;
-
- return 0;
-}
-
-int pakfire_downloader_transfer_run(struct pakfire_transfer* transfer, int flags) {
- struct pakfire_downloader* downloader = transfer->downloader;
- int r;
-
-AGAIN:
- // Prepare the transfer
- r = pakfire_downloader_transfer_prepare(downloader, transfer, NULL, flags);
- if (r) {
- CTX_ERROR(transfer->ctx, "Could not prepare transfer %s: %s\n",
- transfer->url, strerror(-r));
- return r;
- }
-
- // Perform the action
- r = curl_easy_perform(transfer->handle);
-
- // Handle the result
- r = pakfire_transfer_done(downloader, transfer, r);
-
- // Repeat the transfer if asked
- switch (-r) {
- case EAGAIN:
- goto AGAIN;
-
- default:
- break;
- }
-
- return r;
-}
-
static size_t pakfire_downloader_total_downloadsize(struct pakfire_downloader* downloader) {
- struct pakfire_transfer* transfer = NULL;
+ struct pakfire_xfer_element* x = NULL;
size_t size;
size_t total_size = 0;
- TAILQ_FOREACH(transfer, &downloader->transfers_queued, nodes) {
- size = pakfire_downloader_transfer_get_size(transfer);
+ TAILQ_FOREACH(x, &downloader->xfers_queued, nodes) {
+ size = pakfire_xfer_get_size(x->xfer);
// Return zero if the size isn't known
if (!size)
return total_size;
}
-static unsigned int pakfire_downloader_total_queued_transfers(struct pakfire_downloader* downloader) {
- struct pakfire_transfer* transfer = NULL;
+static unsigned int pakfire_downloader_total_queued_xfers(struct pakfire_downloader* downloader) {
+ struct pakfire_xfer_element* x = NULL;
unsigned int counter = 0;
- TAILQ_FOREACH(transfer, &downloader->transfers_queued, nodes)
+ TAILQ_FOREACH(x, &downloader->xfers_queued, nodes)
counter++;
return counter;
}
-static int pakfire_downloader_transfer_start(struct pakfire_downloader* downloader,
- struct pakfire_transfer* transfer, struct pakfire_progress* progress) {
- int r;
-
- CTX_DEBUG(downloader->ctx, "Starting transfer %p...\n", transfer);
-
- // Prepare the transfer
- r = pakfire_downloader_transfer_prepare(downloader, transfer, progress, 0);
- if (r)
- return r;
-
- // Add the handle to cURL
- r = curl_multi_add_handle(downloader->curl, transfer->handle);
- if (r) {
- CTX_ERROR(downloader->ctx, "Adding handle failed: %s\n", curl_multi_strerror(r));
- return r;
- }
-
- return r;
-}
-
static int pakfire_downloader_start_transfers(struct pakfire_downloader* downloader,
struct pakfire_progress* progress, unsigned int* running_transfers) {
- struct pakfire_transfer* transfer = NULL;
+ struct pakfire_xfer_element* x = NULL;
int r;
// Keep running until we have reached our ceiling
while (*running_transfers < downloader->parallel) {
// We are done if there are no more transfers in the queue
- if (TAILQ_EMPTY(&downloader->transfers_queued))
+ if (TAILQ_EMPTY(&downloader->xfers_queued))
break;
// Fetch the next transfer
- transfer = TAILQ_LAST(&downloader->transfers_queued, transfers_queued);
- TAILQ_REMOVE(&downloader->transfers_queued, transfer, nodes);
+ x = TAILQ_LAST(&downloader->xfers_queued, xfers_queued);
+ TAILQ_REMOVE(&downloader->xfers_queued, x, nodes);
+
+ // Prepare the xfer
+ r = pakfire_xfer_prepare(x->xfer, progress, 0);
+ if (r)
+ goto ERROR;
- // Start the transfer
- r = pakfire_downloader_transfer_start(downloader, transfer, progress);
+ // Add the handle to cURL
+ r = curl_multi_add_handle(downloader->curl, pakfire_xfer_handle(x->xfer));
if (r) {
- TAILQ_INSERT_TAIL(&downloader->transfers_finished, transfer, nodes);
- return r;
+ CTX_ERROR(downloader->ctx, "Adding handle failed: %s\n", curl_multi_strerror(r));
+ goto ERROR;
}
- TAILQ_INSERT_TAIL(&downloader->transfers_running, transfer, nodes);
+ TAILQ_INSERT_TAIL(&downloader->xfers_running, x, nodes);
(*running_transfers)++;
}
return 0;
+
+ERROR:
+ TAILQ_INSERT_TAIL(&downloader->xfers_finished, x, nodes);
+
+ return r;
}
int pakfire_downloader_run(struct pakfire_downloader* downloader, const char* title) {
struct pakfire_progress* progress = NULL;
- struct pakfire_transfer* transfer = NULL;
- unsigned int running_transfers = 0;
+ struct pakfire_xfer* xfer = NULL;
+ unsigned int running_xfers = 0;
int progress_flags =
PAKFIRE_PROGRESS_SHOW_PERCENTAGE |
PAKFIRE_PROGRESS_SHOW_ETA;
int r = 0;
+ struct pakfire_xfer_element* x = NULL;
+
CURLMsg* msg = NULL;
int still_running;
int msgs_left = -1;
}
// Fetch the total number of queued transfers
- const unsigned int num_queued_transfers = pakfire_downloader_total_queued_transfers(downloader);
+ const unsigned int num_queued_xfers = pakfire_downloader_total_queued_xfers(downloader);
// Create a new progress indicator
r = pakfire_progress_create(&progress, downloader->ctx, progress_flags, NULL);
}
// Start the progress
- r = pakfire_progress_start(progress, (downloadsize) ? downloadsize : num_queued_transfers);
+ r = pakfire_progress_start(progress, (downloadsize) ? downloadsize : num_queued_xfers);
if (r)
goto ERROR;
do {
// Make sure that we have up to parallel transfers active
if (!r) {
- r = pakfire_downloader_start_transfers(downloader, progress, &running_transfers);
+ r = pakfire_downloader_start_transfers(downloader, progress, &running_xfers);
if (r)
goto ERROR;
}
switch (msg->msg) {
case CURLMSG_DONE:
// Update reference to transfer
- curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &transfer);
+ curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &xfer);
// Remove the handle
- curl_multi_remove_handle(downloader->curl, transfer->handle);
+ curl_multi_remove_handle(downloader->curl, pakfire_xfer_handle(xfer));
+
+ // Find the matching xfer element
+ x = pakfire_downloader_xfer_find_running(downloader, xfer);
// Remove the transfer from the running list
- TAILQ_REMOVE(&downloader->transfers_running, transfer, nodes);
- running_transfers--;
+ if (x)
+ TAILQ_REMOVE(&downloader->xfers_running, x, nodes);
+ running_xfers--;
// Call the done callback
- r = pakfire_transfer_done(downloader, transfer, msg->data.result);
+ r = pakfire_xfer_done(xfer, msg->data.result);
switch (-r) {
// If we are asked to try again we will re-queue the transfer
case EAGAIN:
- TAILQ_INSERT_TAIL(&downloader->transfers_queued, transfer, nodes);
+ if (x)
+ TAILQ_INSERT_TAIL(&downloader->xfers_queued, x, nodes);
break;
// Otherwise this transfer has finished
default:
- TAILQ_INSERT_TAIL(&downloader->transfers_finished, transfer, nodes);
+ if (x)
+ TAILQ_INSERT_TAIL(&downloader->xfers_finished, x, nodes);
if (r)
goto ERROR;
break;
}
// Reset transfer
- transfer = NULL;
+ xfer = NULL;
break;
default:
// Wait a little before going through the loop again
if (still_running)
curl_multi_wait(downloader->curl, NULL, 0, 100, NULL);
- } while (still_running || !TAILQ_EMPTY(&downloader->transfers_queued));
+ } while (still_running || !TAILQ_EMPTY(&downloader->xfers_queued));
// We are finished!
r = pakfire_progress_finish(progress);
ERROR:
// If the downloader was aborted, we need to fail all remaining running transfers
- while (!TAILQ_EMPTY(&downloader->transfers_running)) {
- transfer = TAILQ_LAST(&downloader->transfers_running, transfers_running);
- TAILQ_REMOVE(&downloader->transfers_running, transfer, nodes);
+ while (!TAILQ_EMPTY(&downloader->xfers_running)) {
+ x = TAILQ_LAST(&downloader->xfers_running, xfers_running);
+ TAILQ_REMOVE(&downloader->xfers_running, x, nodes);
// Fail the transfer
- pakfire_transfer_fail(downloader, transfer, 0);
+ pakfire_xfer_fail(x->xfer, 0);
}
if (progress)
#ifdef PAKFIRE_PRIVATE
+#include <curl/curl.h>
+
struct pakfire_downloader;
#include <pakfire/ctx.h>
-#include <pakfire/digest.h>
-#include <pakfire/mirrorlist.h>
-
-enum pakfire_transfer_flags {
- PAKFIRE_TRANSFER_NO_PROGRESS = (1 << 0),
- PAKFIRE_TRANSFER_NOTEMP = (1 << 1),
-};
-
-typedef enum pakfire_transfer_method {
- PAKFIRE_METHOD_DELETE,
-} pakfire_transfer_method_t;
+#include <pakfire/xfer.h>
int pakfire_downloader_create(struct pakfire_downloader** downloader, struct pakfire_ctx* ctx);
struct pakfire_downloader* pakfire_downloader_ref(struct pakfire_downloader* downloader);
struct pakfire_downloader* pakfire_downloader_unref(struct pakfire_downloader* downloader);
-struct pakfire_transfer;
+CURLSH* pakfire_downloader_share(struct pakfire_downloader* downloader);
-int pakfire_downloader_transfer_create(
- struct pakfire_transfer** transfer, struct pakfire_downloader* downloader, const char* url);
-struct pakfire_transfer* pakfire_downloader_transfer_ref(struct pakfire_transfer* transfer);
-struct pakfire_transfer* pakfire_downloader_transfer_unref(struct pakfire_transfer* transfer);
-int pakfire_downloader_transfer_set_method(struct pakfire_transfer* transfer,
- const pakfire_transfer_method_t method);
-const char* pakfire_downloader_transfer_get_title(struct pakfire_transfer* transfer);
-int pakfire_downloader_transfer_set_title(
- struct pakfire_transfer* transfer, const char* title);
-int pakfire_downloader_transfer_set_baseurl(
- struct pakfire_transfer* transfer, const char* baseurl);
-const char* pakfire_downloader_transfer_get_effective_url(struct pakfire_transfer* transfer);
-int pakfire_downloader_transfer_set_mirrorlist(
- struct pakfire_transfer* transfer, struct pakfire_mirrorlist* mirrors);
-int pakfire_downloader_transfer_set_size(
- struct pakfire_transfer* transfer, size_t size);
-int pakfire_downloader_transfer_verify_digest(
- struct pakfire_transfer* transfer, const enum pakfire_digest_types md,
- const unsigned char* expected_digest, const size_t expected_digest_length);
-int pakfire_downloader_transfer_add_param(struct pakfire_transfer* transfer,
- const char* key, const char* format, ...) __attribute__((format(printf, 3, 4)));
-int pakfire_downloader_transfer_set_output(struct pakfire_transfer* transfer, FILE* f);
-int pakfire_downloader_transfer_set_output_buffer(struct pakfire_transfer* transfer,
- char** buffer, size_t* length);
-int pakfire_downloader_transfer_set_input(struct pakfire_transfer* transfer, FILE* f);
-int pakfire_downloader_transfer_set_target(struct pakfire_transfer* transfer, const char* path);
-int pakfire_downloader_transfer_auth(struct pakfire_transfer* transfer);
+int pakfire_downloader_create_xfer(struct pakfire_xfer** xfer,
+ struct pakfire_downloader* downloader, const char* url);
+
+int pakfire_downloader_enqueue_xfer(
+ struct pakfire_downloader* downloader, struct pakfire_xfer* xfer);
-int pakfire_downloader_transfer_run(struct pakfire_transfer* transfer, int flags);
int pakfire_downloader_run(struct pakfire_downloader* downloader, const char* title);
#endif /* PAKFIRE_PRIVATE */
-
#endif /* PAKFIRE_DOWNLOADER_H */
int pakfire_mirror_is_broken(struct pakfire_mirror* mirror);
void pakfire_mirror_mark_as_broken(struct pakfire_mirror* mirror);
-void pakfire_mirror_transfer_failed(struct pakfire_mirror* mirror);
+void pakfire_mirror_xfer_failed(struct pakfire_mirror* mirror);
#endif /* PAKFIRE_MIRROR_H */
#include <pakfire/downloader.h>
#include <pakfire/package.h>
#include <pakfire/packagelist.h>
+#include <pakfire/xfer.h>
#define PAKFIRE_REPO_COMMANDLINE "@commandline"
#define PAKFIRE_REPO_DUMMY "@dummy"
struct pakfire_repo* repo,
struct pakfire_downloader* downloader,
struct pakfire_package* pkg,
- struct pakfire_transfer** transfer);
+ struct pakfire_xfer** xfer);
int pakfire_repo_add(struct pakfire_repo* repo, const char* path,
struct pakfire_package** package);
--- /dev/null
+/*#############################################################################
+# #
+# Pakfire - The IPFire package management system #
+# Copyright (C) 2021 Pakfire development team #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+#############################################################################*/
+
+#ifndef PAKFIRE_XFER_H
+#define PAKFIRE_XFER_H
+
+#ifdef PAKFIRE_PRIVATE
+
+#include <curl/curl.h>
+
+struct pakfire_xfer;
+
+#include <pakfire/ctx.h>
+#include <pakfire/downloader.h>
+#include <pakfire/mirrorlist.h>
+#include <pakfire/progress.h>
+
+typedef enum pakfire_xfer_flags {
+ PAKFIRE_XFER_NO_PROGRESS = (1 << 0),
+ // XXX CAN THIS BE REMOVED?
+ PAKFIRE_XFER_NOTEMP = (1 << 1),
+} pakfire_xfer_flags_t;
+
+typedef enum pakfire_transfer_method {
+ PAKFIRE_METHOD_DELETE,
+} pakfire_xfer_method_t;
+
+int pakfire_xfer_create(struct pakfire_xfer** transfer, struct pakfire_ctx* ctx,
+ struct pakfire_downloader* downloader, const char* url);
+
+struct pakfire_xfer* pakfire_xfer_ref(struct pakfire_xfer* xfer);
+struct pakfire_xfer* pakfire_xfer_unref(struct pakfire_xfer* xfer);
+
+CURL* pakfire_xfer_handle(struct pakfire_xfer* xfer);
+
+int pakfire_xfer_set_method(struct pakfire_xfer* xfer,
+ const pakfire_xfer_method_t method);
+
+// Title
+const char* pakfire_xfer_get_title(struct pakfire_xfer* xfer);
+int pakfire_xfer_set_title(struct pakfire_xfer* xfer, const char* title);
+
+int pakfire_xfer_set_baseurl(struct pakfire_xfer* xfer, const char* baseurl);
+const char* pakfire_xfer_get_effective_url(struct pakfire_xfer* xfer);
+
+int pakfire_xfer_set_mirrorlist(struct pakfire_xfer* xfer, struct pakfire_mirrorlist* mirrors);
+
+size_t pakfire_xfer_get_size(struct pakfire_xfer* xfer);
+int pakfire_xfer_set_size(struct pakfire_xfer* xfer, size_t size);
+
+int pakfire_xfer_verify_digest(struct pakfire_xfer* xfer, const enum pakfire_digest_types md,
+ const unsigned char* expected_digest, const size_t expected_digest_length);
+
+int pakfire_xfer_add_param(struct pakfire_xfer* xfer,
+ const char* key, const char* format, ...) __attribute__((format(printf, 3, 4)));
+
+// Output
+int pakfire_xfer_set_output(struct pakfire_xfer* xfer, FILE* f);
+int pakfire_xfer_set_output_buffer(struct pakfire_xfer* xfer, char** buffer, size_t* length);
+
+// Input
+int pakfire_xfer_set_input(struct pakfire_xfer* xfer, FILE* f);
+
+int pakfire_xfer_set_target(struct pakfire_xfer* xfer, const char* path);
+
+// Authentication
+int pakfire_xfer_auth(struct pakfire_xfer* xfer);
+
+int pakfire_xfer_prepare(struct pakfire_xfer* xfer, struct pakfire_progress* progress, int flags);
+int pakfire_xfer_done(struct pakfire_xfer* xfer, int code);
+int pakfire_xfer_fail(struct pakfire_xfer* xfer, int code);
+
+int pakfire_xfer_run(struct pakfire_xfer* xfer, int flags);
+
+#endif /* PAKFIRE_PRIVATE */
+#endif /* PAKFIRE_XFER_H */
mirror->is_broken = 1;
}
-void pakfire_mirror_transfer_failed(struct pakfire_mirror* mirror) {
+void pakfire_mirror_xfer_failed(struct pakfire_mirror* mirror) {
if (!mirror->retries_left--)
pakfire_mirror_mark_as_broken(mirror);
}
#include <pakfire/repo.h>
#include <pakfire/string.h>
#include <pakfire/util.h>
+#include <pakfire/xfer.h>
// Refresh mirror lists once every 6 hours
#define REFRESH_AGE_MIRRORLIST 6 * 3600
}
/*
- This is a convenience function to create a transfer with the
+ This is a convenience function to create a xfer with the
settings of this repository.
*/
-static int pakfire_repo_create_transfer(struct pakfire_transfer** transfer,
+static int pakfire_repo_create_xfer(struct pakfire_xfer** xfer,
struct pakfire_repo* repo, struct pakfire_downloader* downloader, const char* url) {
struct pakfire_mirrorlist* mirrorlist = NULL;
- struct pakfire_transfer* t = NULL;
+ struct pakfire_xfer* x = NULL;
const char* baseurl = NULL;
int r;
// Create a new transfer
- r = pakfire_downloader_transfer_create(&t, downloader, url);
+ r = pakfire_downloader_create_xfer(&x, downloader, url);
if (r)
goto ERROR;
// Set the baseurl
baseurl = pakfire_repo_get_expanded_baseurl(repo);
if (baseurl) {
- r = pakfire_downloader_transfer_set_baseurl(t, baseurl);
+ r = pakfire_xfer_set_baseurl(x, baseurl);
if (r)
goto ERROR;
}
// Set the mirrorlist
mirrorlist = pakfire_repo_get_mirrorlist(repo);
if (mirrorlist) {
- r = pakfire_downloader_transfer_set_mirrorlist(t, mirrorlist);
+ r = pakfire_xfer_set_mirrorlist(x, mirrorlist);
if (r)
goto ERROR;
}
// Success
- *transfer = t;
+ *xfer = x;
r = 0;
goto CLEANUP;
ERROR:
- if (t)
- pakfire_downloader_transfer_unref(t);
+ if (x)
+ pakfire_xfer_unref(x);
CLEANUP:
if (mirrorlist)
static int pakfire_repo_download_database(struct pakfire_repo* repo,
const char* filename, const char* path) {
struct pakfire_downloader* downloader = NULL;
- struct pakfire_transfer* transfer = NULL;
+ struct pakfire_xfer* xfer = NULL;
char title[NAME_MAX];
char url[PATH_MAX];
int r;
goto ERROR;
// Create a new transfer
- r = pakfire_repo_create_transfer(&transfer, repo, downloader, url);
+ r = pakfire_repo_create_xfer(&xfer, repo, downloader, url);
if (r)
goto ERROR;
// Set title
- r = pakfire_downloader_transfer_set_title(transfer, title);
+ r = pakfire_xfer_set_title(xfer, title);
if (r)
goto ERROR;
// Set path
- r = pakfire_downloader_transfer_set_target(transfer, path);
+ r = pakfire_xfer_set_target(xfer, path);
if (r)
goto ERROR;
- // Run the transfer
- r = pakfire_downloader_transfer_run(transfer, 0);
+ // Run the xfer
+ r = pakfire_xfer_run(xfer, 0);
if (r)
goto ERROR;
ERROR:
- if (transfer)
- pakfire_downloader_transfer_unref(transfer);
+ if (xfer)
+ pakfire_xfer_unref(xfer);
if (downloader)
pakfire_downloader_unref(downloader);
static int pakfire_repo_refresh_mirrorlist(struct pakfire_repo* repo, const int force) {
struct pakfire_downloader* downloader = NULL;
- struct pakfire_transfer* transfer = NULL;
+ struct pakfire_xfer* xfer = NULL;
char* url = NULL;
int r;
if (r)
goto ERROR;
- // Create a new transfer
- r = pakfire_repo_create_transfer(&transfer, repo, downloader, url);
+ // Create a new xfer
+ r = pakfire_repo_create_xfer(&xfer, repo, downloader, url);
if (r)
goto ERROR;
- // Run the transfer
- r = pakfire_downloader_transfer_run(transfer, PAKFIRE_TRANSFER_NO_PROGRESS);
+ // Run the xfer
+ r = pakfire_xfer_run(xfer, PAKFIRE_XFER_NO_PROGRESS);
if (r)
goto ERROR;
ERROR:
- if (transfer)
- pakfire_downloader_transfer_unref(transfer);
+ if (xfer)
+ pakfire_xfer_unref(xfer);
if (downloader)
pakfire_downloader_unref(downloader);
if (url)
static int pakfire_repo_download_metadata(struct pakfire_repo* repo, const char* path, int force) {
struct pakfire_downloader* downloader = NULL;
- struct pakfire_transfer* transfer = NULL;
+ struct pakfire_xfer* xfer = NULL;
int r;
// Local repositories don't need to download metadata
goto ERROR;
// Create a new transfer
- r = pakfire_repo_create_transfer(&transfer, repo, downloader, "repodata/repomd.json");
+ r = pakfire_repo_create_xfer(&xfer, repo, downloader, "repodata/repomd.json");
if (r)
goto ERROR;
// Set the target path
- r = pakfire_downloader_transfer_set_target(transfer, path);
+ r = pakfire_xfer_set_target(xfer, path);
if (r)
goto ERROR;
// Perform the transfer
- r = pakfire_downloader_transfer_run(transfer, PAKFIRE_TRANSFER_NO_PROGRESS);
+ r = pakfire_xfer_run(xfer, PAKFIRE_XFER_NO_PROGRESS);
if (r)
goto ERROR;
ERROR:
- if (transfer)
- pakfire_downloader_transfer_unref(transfer);
+ if (xfer)
+ pakfire_xfer_unref(xfer);
if (downloader)
pakfire_downloader_unref(downloader);
}
int pakfire_repo_download_package(struct pakfire_repo* repo, struct pakfire_downloader* downloader,
- struct pakfire_package* pkg, struct pakfire_transfer** transfer) {
- struct pakfire_transfer* t = NULL;
+ struct pakfire_package* pkg, struct pakfire_xfer** xfer) {
+ struct pakfire_xfer* x = NULL;
const unsigned char* digest = NULL;
const char* cache_path = NULL;
const char* nevra = NULL;
}
// Create a new transfer
- r = pakfire_repo_create_transfer(&t, repo, downloader, url);
+ r = pakfire_repo_create_xfer(&x, repo, downloader, url);
if (r)
goto ERROR;
// Set title
- r = pakfire_downloader_transfer_set_title(t, nevra);
+ r = pakfire_xfer_set_title(x, nevra);
if (r)
goto ERROR;
// Set size
if (downloadsize > 0) {
- r = pakfire_downloader_transfer_set_size(t, downloadsize);
+ r = pakfire_xfer_set_size(x, downloadsize);
if (r)
goto ERROR;
}
// Set target
- r = pakfire_downloader_transfer_set_target(t, cache_path);
+ r = pakfire_xfer_set_target(x, cache_path);
if (r)
goto ERROR;
// Set digest
- r = pakfire_downloader_transfer_verify_digest(t, digest_type, digest, digest_length);
+ r = pakfire_xfer_verify_digest(x, digest_type, digest, digest_length);
if (r)
goto ERROR;
// Success
- *transfer = t;
+ *xfer = x;
return 0;
ERROR:
- if (t)
- pakfire_downloader_transfer_unref(t);
+ if (x)
+ pakfire_xfer_unref(x);
return r;
}
static int pakfire_repo_download(struct pakfire_repo* repo, const char* url,
struct pakfire_package** package) {
struct pakfire_downloader* downloader = NULL;
- struct pakfire_transfer* transfer = NULL;
+ struct pakfire_xfer* xfer = NULL;
FILE* f = NULL;
int r;
goto ERROR;
// Create a new transfer
- r = pakfire_downloader_transfer_create(&transfer, downloader, url);
+ r = pakfire_downloader_create_xfer(&xfer, downloader, url);
if (r)
goto ERROR;
// Set destination
- r = pakfire_downloader_transfer_set_output(transfer, f);
+ r = pakfire_xfer_set_output(xfer, f);
if (r)
goto ERROR;
// Run the download
- r = pakfire_downloader_transfer_run(transfer, 0);
+ r = pakfire_xfer_run(xfer, 0);
if (r)
goto ERROR;
goto ERROR;
ERROR:
- if (transfer)
- pakfire_downloader_transfer_unref(transfer);
+ if (xfer)
+ pakfire_xfer_unref(xfer);
if (downloader)
pakfire_downloader_unref(downloader);
if (f)
static int pakfire_transaction_download_package(struct pakfire_transaction* transaction,
struct pakfire_downloader* downloader, struct pakfire_package* pkg) {
struct pakfire_repo* repo = NULL;
- struct pakfire_transfer* transfer = NULL;
+ struct pakfire_xfer* xfer = NULL;
int r = 1;
// Fetch the repository to download from
if (!repo)
goto ERROR;
- // Create a transfer for this package
- r = pakfire_repo_download_package(repo, downloader, pkg, &transfer);
+ // Create a xfer for this package
+ r = pakfire_repo_download_package(repo, downloader, pkg, &xfer);
if (r)
goto ERROR;
ERROR:
- if (transfer)
- pakfire_downloader_transfer_unref(transfer);
+ if (xfer)
+ pakfire_xfer_unref(xfer);
if (repo)
pakfire_repo_unref(repo);
--- /dev/null
+/*#############################################################################
+# #
+# Pakfire - The IPFire package management system #
+# Copyright (C) 2023 Pakfire development team #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+#############################################################################*/
+
+#include <errno.h>
+#include <limits.h>
+#include <sys/queue.h>
+#include <utime.h>
+
+#include <curl/curl.h>
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+
+#include <pakfire/ctx.h>
+#include <pakfire/mirrorlist.h>
+#include <pakfire/path.h>
+#include <pakfire/progress.h>
+#include <pakfire/string.h>
+#include <pakfire/util.h>
+#include <pakfire/xfer.h>
+
+struct pakfire_xfer {
+ struct pakfire_ctx* ctx;
+ int nrefs;
+
+ // Reference to the downloader
+ struct pakfire_downloader* downloader;
+
+ // Reference to the progress indicator
+ struct pakfire_progress* progress;
+
+ // cURL handle
+ CURL* handle;
+
+ // Headers
+ struct curl_slist* headers;
+
+ // URL
+ CURLU* fullurl;
+
+ char url[PATH_MAX];
+ char title[NAME_MAX];
+ char path[PATH_MAX];
+ pakfire_xfer_flags_t flags;
+ int tries;
+
+ // POST MIME Object
+ curl_mime* mime;
+
+ // Xfer direction
+ enum {
+ PAKFIRE_XFER_DOWNLOAD = 0,
+ PAKFIRE_XFER_UPLOAD = 1,
+ } direction;
+
+ // Size
+ size_t expected_size;
+ size_t xferred;
+
+ // Temporary file
+ char tempfile[PATH_MAX];
+
+ // File handles for streams
+ FILE* fin;
+ FILE* fout;
+
+ // Crypto Stuff
+ EVP_MD_CTX* evp;
+ const EVP_MD* md;
+ unsigned char computed_digest[EVP_MAX_MD_SIZE];
+ unsigned int computed_digest_length;
+ unsigned char expected_digest[EVP_MAX_MD_SIZE];
+ unsigned int expected_digest_length;
+
+ // Mirrors
+ char baseurl[PATH_MAX];
+ struct pakfire_mirrorlist* mirrors;
+ struct pakfire_mirror* mirror;
+
+ // Effective URL
+ const char* effective_url;
+
+ // Authentication
+ unsigned int auth;
+};
+
+static void pakfire_xfer_free(struct pakfire_xfer* xfer) {
+ if (xfer->handle)
+ curl_easy_cleanup(xfer->handle);
+
+ // Unlink the temporary file
+ if (*xfer->tempfile)
+ unlink(xfer->tempfile);
+
+ // Close any streams
+ if (xfer->fin)
+ fclose(xfer->fin);
+
+ // Free OpenSSL EVP context
+ if (xfer->evp)
+ EVP_MD_CTX_free(xfer->evp);
+
+ if (xfer->headers)
+ curl_slist_free_all(xfer->headers);
+ if (xfer->mime)
+ curl_mime_free(xfer->mime);
+ if (xfer->fullurl)
+ curl_url_cleanup(xfer->fullurl);
+
+ if (xfer->mirror)
+ pakfire_mirror_unref(xfer->mirror);
+ if (xfer->mirrors)
+ pakfire_mirrorlist_unref(xfer->mirrors);
+ if (xfer->progress)
+ pakfire_progress_unref(xfer->progress);
+ if (xfer->ctx)
+ pakfire_ctx_unref(xfer->ctx);
+
+ free(xfer);
+}
+
+#ifdef ENABLE_DEBUG
+static int pakfire_xfer_debug_callback(CURL *handle, curl_infotype type,
+ char* data, size_t size, void* private) {
+ struct pakfire_ctx* ctx = private;
+
+ switch (type) {
+ case CURLINFO_TEXT:
+ CTX_DEBUG(ctx, "cURL: %.*s", (int)size, data);
+ break;
+
+ // Log headers
+ case CURLINFO_HEADER_IN:
+ CTX_DEBUG(ctx, "cURL: < %.*s", (int)size, data);
+ break;
+
+ case CURLINFO_HEADER_OUT:
+ CTX_DEBUG(ctx, "cURL: > %.*s", (int)size, data);
+ break;
+
+ // Ignore everything else
+ default:
+ break;
+ }
+
+ return 0;
+}
+#endif
+
+static size_t pakfire_xfer_read(char* data, size_t size, size_t nmemb, void* p) {
+ struct pakfire_xfer* xfer = p;
+
+ return fread(data, size, nmemb, xfer->fout);
+}
+
+static int pakfire_xfer_seek(void* p, curl_off_t offset, int origin) {
+ struct pakfire_xfer* xfer = p;
+ int r;
+
+ // Perform the seek
+ r = fseek(xfer->fout, (long)offset, origin);
+ if (r < 0)
+ return CURL_SEEKFUNC_CANTSEEK;
+
+ return CURL_SEEKFUNC_OK;
+}
+
+static size_t pakfire_xfer_write(
+ char* data, size_t size, size_t nmemb, void* p) {
+ struct pakfire_xfer* xfer = p;
+ struct pakfire_ctx* ctx = xfer->ctx;
+ int r;
+
+ // Do not write empty blocks
+ if (!nmemb)
+ return nmemb;
+
+ // Update message digest
+ if (xfer->evp) {
+ r = EVP_DigestUpdate(xfer->evp, data, nmemb);
+ if (r != 1) {
+ CTX_ERROR(ctx, "EVP_DigestUpdate failed: %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ return 0;
+ }
+ }
+
+ // Write everything to the allocated file descriptor
+ return fwrite(data, size, nmemb, xfer->fin);
+}
+
+static int pakfire_xfer_setup(struct pakfire_xfer* xfer) {
+ struct pakfire_config* config = NULL;
+ const char* proxy = NULL;
+ int r;
+
+ CURLSH* share = pakfire_downloader_share(xfer->downloader);
+
+ // Configure the share handle
+ r = curl_easy_setopt(xfer->handle, CURLOPT_SHARE, share);
+ if (r) {
+ CTX_ERROR(xfer->ctx, "Could not configure cURL share handle: %s\n",
+ curl_easy_strerror(r));
+ return r;
+ }
+
+ // Fetch global configuration
+ config = pakfire_ctx_get_config(xfer->ctx);
+
+ // Set global configuration
+ if (config) {
+ proxy = pakfire_config_get(config, "general", "proxy", NULL);
+ if (proxy)
+ curl_easy_setopt(xfer->handle, CURLOPT_PROXY, proxy);
+ }
+
+ // Be a good net citizen and set a user agent
+ curl_easy_setopt(xfer->handle, CURLOPT_USERAGENT, PACKAGE_NAME "/" PACKAGE_VERSION);
+
+#ifdef ENABLE_DEBUG
+ // Enable logging/debugging
+ curl_easy_setopt(xfer->handle, CURLOPT_VERBOSE, 1L);
+
+ curl_easy_setopt(xfer->handle, CURLOPT_DEBUGFUNCTION, pakfire_xfer_debug_callback);
+ curl_easy_setopt(xfer->handle, CURLOPT_DEBUGDATA, xfer->ctx);
+#endif
+
+ // Limit protocols to HTTPS, HTTP, FTP and FILE
+ curl_easy_setopt(xfer->handle, CURLOPT_PROTOCOLS_STR, "HTTPS,HTTP,FTP,FILE");
+
+ // Reference back to this xfer
+ curl_easy_setopt(xfer->handle, CURLOPT_PRIVATE, xfer);
+
+ // Follow any redirects
+ curl_easy_setopt(xfer->handle, CURLOPT_FOLLOWLOCATION, 1);
+
+ // Only follow up to 30 redirects
+ curl_easy_setopt(xfer->handle, CURLOPT_MAXREDIRS, 30L);
+
+ // Read any data from a callback function
+ curl_easy_setopt(xfer->handle,
+ CURLOPT_READFUNCTION, pakfire_xfer_read);
+ curl_easy_setopt(xfer->handle, CURLOPT_READDATA, xfer);
+
+ // Write all data to the callback function
+ curl_easy_setopt(xfer->handle,
+ CURLOPT_WRITEFUNCTION, pakfire_xfer_write);
+ curl_easy_setopt(xfer->handle, CURLOPT_WRITEDATA, xfer);
+
+ // Register the seek callback
+ curl_easy_setopt(xfer->handle,
+ CURLOPT_SEEKFUNCTION, pakfire_xfer_seek);
+ curl_easy_setopt(xfer->handle, CURLOPT_SEEKDATA, xfer);
+
+ // Success
+ r = 0;
+
+ // Cleanup
+ if (config)
+ pakfire_config_unref(config);
+
+ return r;
+}
+
+int pakfire_xfer_create(struct pakfire_xfer** xfer, struct pakfire_ctx* ctx,
+ struct pakfire_downloader* downloader, const char* url) {
+ struct pakfire_xfer* x = NULL;
+ int r;
+
+ // Allocate a new xfer
+ x = calloc(1, sizeof(*x));
+ if (!x)
+ return -errno;
+
+ // Store a reference to the context
+ x->ctx = pakfire_ctx_ref(ctx);
+
+ // Initialize the reference counter
+ x->nrefs = 1;
+
+ // Store a reference to the downloader
+ x->downloader = pakfire_downloader_ref(downloader);
+
+ // Store the URL
+ r = pakfire_string_set(x->url, url);
+ if (r)
+ goto ERROR;
+
+ // Allocate a handle
+ x->handle = curl_easy_init();
+ if (!x->handle) {
+ r = 1;
+ goto ERROR;
+ }
+
+ // Allocate the full URL
+ x->fullurl = curl_url();
+ if (!x->fullurl) {
+ r = -errno;
+ goto ERROR;
+ }
+
+ // Setup the xfer
+ r = pakfire_xfer_setup(x);
+ if (r)
+ goto ERROR;
+
+ // Return the reference
+ *xfer = x;
+
+ return 0;
+
+ERROR:
+ if (x)
+ pakfire_xfer_unref(x);
+
+ return r;
+}
+
+struct pakfire_xfer* pakfire_xfer_ref(struct pakfire_xfer* xfer) {
+ ++xfer->nrefs;
+
+ return xfer;
+}
+
+struct pakfire_xfer* pakfire_xfer_unref(struct pakfire_xfer* xfer) {
+ if (--xfer->nrefs > 0)
+ return xfer;
+
+ pakfire_xfer_free(xfer);
+ return NULL;
+}
+
+CURL* pakfire_xfer_handle(struct pakfire_xfer* xfer) {
+ return xfer->handle;
+}
+
+int pakfire_xfer_set_method(struct pakfire_xfer* xfer,
+ const pakfire_xfer_method_t method) {
+ const char* m = NULL;
+
+ switch (method) {
+ case PAKFIRE_METHOD_DELETE:
+ m = "DELETE";
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return curl_easy_setopt(xfer->handle, CURLOPT_CUSTOMREQUEST, m);
+}
+
+
+const char* pakfire_xfer_get_title(struct pakfire_xfer* xfer) {
+ char title[PATH_MAX];
+ int r;
+
+ // Default to the filename if no title is set
+ if (!*xfer->title) {
+ // Only use the basename
+ r = pakfire_path_basename(title, xfer->url);
+ if (r)
+ return NULL;
+
+ // Store the title
+ r = pakfire_xfer_set_title(xfer, title);
+ if (r)
+ return NULL;
+ }
+
+ return xfer->title;
+}
+
+int pakfire_xfer_set_title(struct pakfire_xfer* xfer, const char* title) {
+ return pakfire_string_set(xfer->title, title);
+}
+
+int pakfire_xfer_set_baseurl(struct pakfire_xfer* xfer, const char* baseurl) {
+ return pakfire_string_set(xfer->baseurl, baseurl);
+}
+
+const char* pakfire_xfer_get_effective_url(struct pakfire_xfer* xfer) {
+ return xfer->effective_url;
+}
+
+int pakfire_xfer_set_mirrorlist(struct pakfire_xfer* xfer, struct pakfire_mirrorlist* mirrors) {
+ if (xfer->mirrors)
+ pakfire_mirrorlist_unref(xfer->mirrors);
+
+ xfer->mirrors = pakfire_mirrorlist_ref(mirrors);
+
+ return 0;
+}
+
+// Size
+
+size_t pakfire_xfer_get_size(struct pakfire_xfer* xfer) {
+ return xfer->expected_size;
+}
+
+int pakfire_xfer_set_size(struct pakfire_xfer* xfer, size_t size) {
+ xfer->expected_size = size;
+
+ return 0;
+}
+
+int pakfire_xfer_verify_digest(struct pakfire_xfer* xfer, const enum pakfire_digest_types md,
+ const unsigned char* expected_digest, const size_t expected_digest_length) {
+ // Check inputs
+ if (!expected_digest || !expected_digest_length)
+ return -EINVAL;
+
+ // Expected digest length cannot be too long
+ if (expected_digest_length > sizeof(xfer->expected_digest))
+ return -ENOBUFS;
+
+ // Store digest type
+ switch (md) {
+ case PAKFIRE_DIGEST_SHA3_512:
+ xfer->md = EVP_sha3_512();
+ break;
+
+ case PAKFIRE_DIGEST_SHA3_256:
+ xfer->md = EVP_sha3_256();
+ break;
+
+ case PAKFIRE_DIGEST_BLAKE2B512:
+ xfer->md = EVP_blake2b512();
+ break;
+
+ case PAKFIRE_DIGEST_BLAKE2S256:
+ xfer->md = EVP_blake2s256();
+ break;
+
+ case PAKFIRE_DIGEST_SHA2_512:
+ xfer->md = EVP_sha512();
+ break;
+
+ case PAKFIRE_DIGEST_SHA2_256:
+ xfer->md = EVP_sha256();
+ break;
+
+ default:
+ return -ENOTSUP;
+ }
+
+ // Store the expected digest and its length
+ memcpy(xfer->expected_digest, expected_digest, expected_digest_length);
+ xfer->expected_digest_length = expected_digest_length;
+
+ return 0;
+}
+
+int pakfire_xfer_add_param(struct pakfire_xfer* xfer,
+ const char* key, const char* format, ...) {
+ curl_mimepart* part = NULL;
+ char* buffer = NULL;
+ va_list args;
+ int r;
+
+ // Allocate the MIME object if not done, yet
+ if (!xfer->mime) {
+ xfer->mime = curl_mime_init(xfer->handle);
+
+ if (!xfer->mime) {
+ CTX_ERROR(xfer->ctx, "Could not allocate the MIME object: %s\n",
+ strerror(errno));
+ r = -errno;
+ goto ERROR;
+ }
+ }
+
+ // Format value
+ va_start(args, format);
+ r = vasprintf(&buffer, format, args);
+ va_end(args);
+
+ // Abort if we could not format the value
+ if (r < 0)
+ goto ERROR;
+
+ // Allocate another MIME part
+ part = curl_mime_addpart(xfer->mime);
+ if (!part) {
+ CTX_ERROR(xfer->ctx, "Could not allocate MIME part: %s\n",
+ strerror(errno));
+ r = errno;
+ goto ERROR;
+ }
+
+ // Set the key
+ r = curl_mime_name(part, key);
+ if (r) {
+ CTX_ERROR(xfer->ctx, "Could not set parameter key (%s): %s\n",
+ key, curl_easy_strerror(r));
+ goto ERROR;
+ }
+
+ // Set the data
+ r = curl_mime_data(part, buffer, CURL_ZERO_TERMINATED);
+ if (r) {
+ CTX_ERROR(xfer->ctx, "Could not set parameter data (%s): %s\n",
+ key, curl_easy_strerror(r));
+ goto ERROR;
+ }
+
+ERROR:
+ if (buffer)
+ free(buffer);
+
+ return r;
+}
+
+int pakfire_xfer_set_output(struct pakfire_xfer* xfer, FILE* f) {
+ xfer->fin = f;
+
+ return 0;
+}
+
+int pakfire_xfer_set_output_buffer(struct pakfire_xfer* xfer,
+ char** buffer, size_t* length) {
+ FILE* f = NULL;
+
+ // Open a memory stream
+ f = open_memstream(buffer, length);
+ if (!f) {
+ CTX_ERROR(xfer->ctx, "Could not open memory stream: %s\n", strerror(errno));
+ return -errno;
+ }
+
+ return pakfire_xfer_set_output(xfer, f);
+}
+
+int pakfire_xfer_set_input(struct pakfire_xfer* xfer, FILE* f) {
+ struct stat stat;
+ int r;
+
+ // Fetch the file descriptor
+ const int fd = fileno(f);
+
+ // Change direction
+ xfer->direction = PAKFIRE_XFER_UPLOAD;
+
+ // Store the file handle
+ xfer->fout = f;
+
+ // Try to find the upload size
+ if (fd > 0) {
+ r = fstat(fd, &stat);
+ if (r)
+ return 0;
+
+ // Store the expected filesize
+ xfer->expected_size = stat.st_size;
+ }
+
+ return 0;
+}
+
+int pakfire_xfer_set_target(
+ struct pakfire_xfer* xfer, const char* path) {
+ return pakfire_string_set(xfer->path, path);
+}
+
+int pakfire_xfer_auth(struct pakfire_xfer* xfer) {
+ // Enable authentication
+ xfer->auth = 1;
+
+ return 0;
+}
+
+static int pakfire_xfer_select_mirror(struct pakfire_xfer* xfer) {
+ // Choose the next mirror
+ if (xfer->mirror)
+ xfer->mirror = pakfire_mirrorlist_get_next(xfer->mirrors, xfer->mirror);
+
+ // If no mirror has been selected yet, choose the first one
+ else
+ xfer->mirror = pakfire_mirrorlist_get_first(xfer->mirrors);
+
+ // Skip this mirror if it is broken
+ while (xfer->mirror && pakfire_mirror_is_broken(xfer->mirror)) {
+ // Move on to the next mirror
+ xfer->mirror = pakfire_mirrorlist_get_next(xfer->mirrors, xfer->mirror);
+ }
+
+ // No mirror found
+ if (!xfer->mirror) {
+ CTX_ERROR(xfer->ctx, "No mirrors left to try\n");
+
+ // No mirrors left
+ return ENOENT;
+ }
+
+ CTX_DEBUG(xfer->ctx, "Selected mirror %s\n", pakfire_mirror_get_url(xfer->mirror));
+
+ return 0;
+}
+
+static const char* curl_http_version(long v) {
+ switch (v) {
+#ifdef CURL_HTTP_VERSION_3_0
+ case CURL_HTTP_VERSION_3_0:
+ return "HTTP/3.0";
+#endif
+
+ case CURL_HTTP_VERSION_2_0:
+ return "HTTP/2.0";
+
+ case CURL_HTTP_VERSION_1_1:
+ return "HTTP/1.1";
+
+ case CURL_HTTP_VERSION_1_0:
+ return "HTTP/1.0";
+ }
+
+ return "unknown";
+}
+
+static int pakfire_xfer_save(struct pakfire_xfer* xfer) {
+ struct utimbuf times = {
+ .actime = 0,
+ .modtime = 0,
+ };
+ int r;
+
+ // Flush any buffered data out to disk
+ if (xfer->fin)
+ fflush(xfer->fin);
+
+ // Nothing to do if path isn't set
+ if (!*xfer->path)
+ return 0;
+
+ CTX_DEBUG(xfer->ctx, "Download successful. Storing result in %s\n", xfer->path);
+
+ // Make sure the parent directory exists
+ r = pakfire_mkparentdir(xfer->path, 0755);
+ if (r)
+ return r;
+
+ // Unlink the destination file (if it exists)
+ unlink(xfer->path);
+
+ // Move the temporary file to its destination
+ r = link(xfer->tempfile, xfer->path);
+ if (r) {
+ CTX_ERROR(xfer->ctx, "Could not link destination file %s: %m\n",
+ xfer->path);
+ return r;
+ }
+
+ // Filetime
+ curl_easy_getinfo(xfer->handle, CURLINFO_FILETIME_T, ×.modtime);
+
+ if (times.modtime) {
+ r = utime(xfer->path, ×);
+ if (r)
+ CTX_ERROR(xfer->ctx, "Could not set mtime of %s: %m\n", xfer->path);
+ }
+
+ return 0;
+}
+
+int pakfire_xfer_fail(struct pakfire_xfer* xfer, int code) {
+ int r;
+
+ CTX_DEBUG(xfer->ctx, "Xfer failed\n");
+
+ // Get file descriptor
+ int fd = fileno(xfer->fin);
+
+ // Truncate downloaded data
+ r = ftruncate(fd, 0);
+ if (r)
+ return r;
+
+ // Did we use a mirror?
+ if (xfer->mirror) {
+ pakfire_mirror_xfer_failed(xfer->mirror);
+
+ // Try again with another mirror
+ return EAGAIN;
+ }
+
+ return 0;
+}
+
+int pakfire_xfer_done(struct pakfire_xfer* xfer, int code) {
+ CURL* h = xfer->handle;
+ int r;
+ char* scheme = NULL;
+ long response_code;
+ long http_version;
+ double total_time;
+
+ curl_off_t download_size = 0;
+ curl_off_t download_speed = 0;
+ curl_off_t upload_size = 0;
+ curl_off_t upload_speed = 0;
+
+ // Finish progress
+ r = pakfire_progress_finish(xfer->progress);
+ if (r)
+ return r;
+
+ CTX_DEBUG(xfer->ctx, "cURL xfer done: %d - %s\n",
+ code, curl_easy_strerror(code));
+
+ // Finish message digest computation
+ if (xfer->evp) {
+ r = EVP_DigestFinal_ex(xfer->evp, xfer->computed_digest, &xfer->computed_digest_length);
+ if (r != 1) {
+ CTX_ERROR(xfer->ctx, "Could not finish message digest computation: %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ return 1;
+ }
+ }
+
+ // Protocol
+ curl_easy_getinfo(h, CURLINFO_SCHEME, &scheme);
+
+ // Effective URL
+ curl_easy_getinfo(h, CURLINFO_EFFECTIVE_URL, &xfer->effective_url);
+ if (xfer->effective_url)
+ CTX_DEBUG(xfer->ctx, " Effective URL: %s\n", xfer->effective_url);
+
+ // Response code
+ curl_easy_getinfo(h, CURLINFO_RESPONSE_CODE, &response_code);
+ if (response_code)
+ CTX_DEBUG(xfer->ctx, " Response code: %ld\n", response_code);
+
+ // HTTP Version
+ curl_easy_getinfo(h, CURLINFO_HTTP_VERSION, &http_version);
+ if (http_version)
+ CTX_DEBUG(xfer->ctx, " HTTP Version: %s\n", curl_http_version(http_version));
+
+ // Total Times
+ curl_easy_getinfo(h, CURLINFO_TOTAL_TIME, &total_time);
+ CTX_DEBUG(xfer->ctx, " Total Time: %.2fs\n", total_time);
+
+ // Download Size
+ r = curl_easy_getinfo(h, CURLINFO_SIZE_DOWNLOAD_T, &download_size);
+ if (r)
+ return r;
+
+ if (download_size)
+ CTX_DEBUG(xfer->ctx, " Download Size: %ld bytes\n", download_size);
+
+ // Download Speed
+ r = curl_easy_getinfo(h, CURLINFO_SPEED_DOWNLOAD_T, &download_speed);
+ if (r)
+ return r;
+
+ if (download_speed)
+ CTX_DEBUG(xfer->ctx, " Download Speed: %ld bps\n", download_speed);
+
+ // Upload Size
+ r = curl_easy_getinfo(h, CURLINFO_SIZE_UPLOAD_T, &upload_size);
+ if (r)
+ return r;
+
+ if (upload_size)
+ CTX_DEBUG(xfer->ctx, " Upload Size: %ld bytes\n", upload_size);
+
+ // Upload Speed
+ r = curl_easy_getinfo(h, CURLINFO_SPEED_UPLOAD_T, &upload_speed);
+ if (r)
+ return r;
+
+ if (upload_speed)
+ CTX_DEBUG(xfer->ctx, " Upload Speed: %ld bps\n", upload_speed);
+
+ // Message Digest
+ char* hexdigest = __pakfire_hexlify(xfer->computed_digest, xfer->computed_digest_length);
+ if (hexdigest && *hexdigest) {
+ CTX_DEBUG(xfer->ctx, " Message Digest: %s\n", hexdigest);
+ free(hexdigest);
+ }
+
+ // Check if digests match
+ if (xfer->evp) {
+ r = CRYPTO_memcmp(xfer->computed_digest, xfer->expected_digest,
+ xfer->computed_digest_length);
+
+ // If they don't match, log an error and try again
+ if (r) {
+ char* computed_hexdigest = __pakfire_hexlify(xfer->computed_digest,
+ xfer->computed_digest_length);
+ char* expected_hexdigest = __pakfire_hexlify(xfer->expected_digest,
+ xfer->expected_digest_length);
+
+ CTX_ERROR(xfer->ctx, "Download checksum for %s didn't match:\n", xfer->effective_url);
+ CTX_ERROR(xfer->ctx, " Expected: %s\n", expected_hexdigest);
+ CTX_ERROR(xfer->ctx, " Computed: %s\n", computed_hexdigest);
+
+ if (computed_hexdigest)
+ free(computed_hexdigest);
+ if (expected_hexdigest)
+ free(expected_hexdigest);
+
+ // Make this download fail
+ r = pakfire_xfer_fail(xfer, 0);
+ if (r)
+ return r;
+
+ return 1;
+ }
+ }
+
+ // If we could not determine the scheme...
+ if (!scheme)
+ r = pakfire_xfer_fail(xfer, 0);
+
+ // FILE
+ else if (strcmp(scheme, "FILE") == 0) {
+ // Handle any errors
+ if (code)
+ r = pakfire_xfer_fail(xfer, code);
+ else
+ r = pakfire_xfer_save(xfer);
+
+ return r;
+
+ // HTTPS + HTTP
+ } else if ((strcmp(scheme, "HTTPS") == 0) || (strcmp(scheme, "HTTP") == 0)) {
+ switch (response_code) {
+ // 200 - OK
+ case 200:
+ r = pakfire_xfer_save(xfer);
+ if (r)
+ return r;
+ break;
+
+ // Treat all other response codes as an error
+ default:
+ r = pakfire_xfer_fail(xfer, code);
+ if (r)
+ return r;
+
+ // Error
+ return 1;
+ }
+
+ // FTP
+ } else if (strcmp(scheme, "FTP") == 0) {
+ if (response_code == 226)
+ r = pakfire_xfer_save(xfer);
+ else
+ r = pakfire_xfer_fail(xfer, code);
+
+ return r;
+ }
+
+ // Success
+ return 0;
+}
+
+static int pakfire_xfer_update(void* data,
+ curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
+ struct pakfire_xfer* xfer = data;
+
+ switch (xfer->direction) {
+ case PAKFIRE_XFER_DOWNLOAD:
+ // Update the expected size
+ xfer->expected_size = dltotal;
+
+ // Update the xferred counter
+ xfer->xferred = dlnow;
+ break;
+
+ case PAKFIRE_XFER_UPLOAD:
+ // Update the expected size
+ xfer->expected_size = ultotal;
+
+ // Update the xferred counter
+ xfer->xferred = ulnow;
+ break;
+ }
+
+ // Do nothing if no progress indicator has been set up
+ if (!xfer->progress)
+ return 0;
+
+ // Set maximum (because this might have changed)
+ pakfire_progress_set_max_value(xfer->progress, xfer->expected_size);
+
+ // Update current value
+ return pakfire_progress_update(xfer->progress, xfer->xferred);
+}
+
+
+static int pakfire_xfer_prepare_progress(struct pakfire_xfer* xfer,
+ struct pakfire_progress* parent, int flags) {
+ const char* title = NULL;
+ int progress_flags =
+ PAKFIRE_PROGRESS_SHOW_PERCENTAGE |
+ PAKFIRE_PROGRESS_SHOW_BYTES_TRANSFERRED |
+ PAKFIRE_PROGRESS_SHOW_TRANSFER_SPEED |
+ PAKFIRE_PROGRESS_SHOW_ETA;
+ int r;
+
+ // If this has already been set up, we skip it
+ if (xfer->progress)
+ return 0;
+
+ // Show no progress if requested
+ if (flags & PAKFIRE_XFER_NO_PROGRESS)
+ progress_flags |= PAKFIRE_PROGRESS_NO_PROGRESS;
+
+ // Show no progress if we have a parent progress
+ else if (parent)
+ progress_flags |= PAKFIRE_PROGRESS_NO_PROGRESS;
+
+ // Make a new progress meter
+ r = pakfire_progress_create(&xfer->progress, xfer->ctx, progress_flags, parent);
+ if (r)
+ return r;
+
+ // Set the title
+ title = pakfire_xfer_get_title(xfer);
+ if (title) {
+ r = pakfire_progress_set_title(xfer->progress, title);
+ if (r)
+ return r;
+ }
+
+ // Configure callbacks
+ curl_easy_setopt(xfer->handle, CURLOPT_XFERINFOFUNCTION,
+ pakfire_xfer_update);
+ curl_easy_setopt(xfer->handle, CURLOPT_XFERINFODATA, xfer);
+ curl_easy_setopt(xfer->handle, CURLOPT_NOPROGRESS, 0L);
+
+ // Start progress
+ r = pakfire_progress_start(xfer->progress, xfer->expected_size);
+ if (r)
+ return r;
+
+ return 0;
+}
+
+
+static int pakfire_xfer_prepare_tmpfile(struct pakfire_xfer* xfer) {
+ char path[PATH_MAX] = PAKFIRE_TMP_DIR "/pakfire-download.XXXXXX";
+ FILE* f = NULL;
+ int r;
+
+ // Allocate a temporary file
+ f = pakfire_mktemp(path, 0600);
+ if (!f)
+ return -errno;
+
+ // Set this as output
+ r = pakfire_xfer_set_output(xfer, f);
+ if (r)
+ return r;
+
+ // Store the path to the temporary file
+ return pakfire_string_set(xfer->tempfile, path);
+}
+
+static int pakfire_xfer_prepare_url(struct pakfire_xfer* xfer) {
+ int r;
+
+ // Simply set absolute URLs
+ if (pakfire_string_is_url(xfer->url)) {
+ r = curl_url_set(xfer->fullurl, CURLUPART_URL, xfer->url, 0);
+ if (r)
+ goto ERROR;
+
+ // Join path if we are using mirrors
+ } else if (xfer->mirrors && !pakfire_mirrorlist_empty(xfer->mirrors)) {
+ r = pakfire_xfer_select_mirror(xfer);
+ if (r)
+ goto ERROR;
+
+ // Set the mirror's base URL first
+ r = curl_url_set(xfer->fullurl, CURLUPART_URL,
+ pakfire_mirror_get_url(xfer->mirror), 0);
+ if (r)
+ goto ERROR;
+
+ // Then append our own part
+ r = curl_url_set(xfer->fullurl, CURLUPART_URL, xfer->url, 0);
+ if (r)
+ goto ERROR;
+
+ // Use baseurl
+ } else if (*xfer->baseurl) {
+ // Set the base URL first
+ r = curl_url_set(xfer->fullurl, CURLUPART_URL, xfer->baseurl, 0);
+ if (r)
+ goto ERROR;
+
+ // Then append our own part
+ r = curl_url_set(xfer->fullurl, CURLUPART_URL, xfer->url, 0);
+ if (r)
+ goto ERROR;
+
+ // Fail if we could not set the URL
+ } else {
+ CTX_ERROR(xfer->ctx, "Invalid xfer %s\n", xfer->url);
+ r = -EINVAL;
+ goto ERROR;
+ }
+
+ // Set the URL
+ r = curl_easy_setopt(xfer->handle, CURLOPT_CURLU, xfer->fullurl);
+ if (r) {
+ CTX_ERROR(xfer->ctx, "Could not set the URL: %s\n", curl_easy_strerror(r));
+ goto ERROR;
+ }
+
+ERROR:
+ return r;
+}
+
+int pakfire_xfer_prepare(struct pakfire_xfer* xfer, struct pakfire_progress* progress, int flags) {
+ int r;
+
+ // Increment tries
+ xfer->tries++;
+
+ // Set special options for direction
+ switch (xfer->direction) {
+ case PAKFIRE_XFER_DOWNLOAD:
+ break;
+
+ case PAKFIRE_XFER_UPLOAD:
+ // Let cURL know that we are uploading things
+ r = curl_easy_setopt(xfer->handle, CURLOPT_UPLOAD, 1L);
+ if (r) {
+ CTX_ERROR(xfer->ctx, "Could not enable upload\n");
+ return r;
+ }
+
+ // Tell it the expected upload size
+ if (xfer->expected_size) {
+ r = curl_easy_setopt(xfer->handle,
+ CURLOPT_INFILESIZE_LARGE, (curl_off_t)xfer->expected_size);
+ if (r) {
+ CTX_ERROR(xfer->ctx, "Could not set upload size\n");
+ return r;
+ }
+ }
+
+ // Upload files chunked
+ xfer->headers = curl_slist_append(xfer->headers, "Transfer-Encoding: chunked");
+ break;
+ }
+
+ // Compose the URL
+ r = pakfire_xfer_prepare_url(xfer);
+ if (r) {
+ CTX_ERROR(xfer->ctx, "Could not compose URL: %m\n");
+ return r;
+ }
+
+ // Add any headers
+ if (xfer->headers) {
+ r = curl_easy_setopt(xfer->handle, CURLOPT_HTTPHEADER, xfer->headers);
+ if (r) {
+ CTX_ERROR(xfer->ctx, "Could not set headers: %s\n", curl_easy_strerror(r));
+ return r;
+ }
+ }
+
+ // Add any payload
+ if (xfer->mime) {
+ r = curl_easy_setopt(xfer->handle, CURLOPT_MIMEPOST, xfer->mime);
+ if (r) {
+ CTX_ERROR(xfer->ctx, "Could not set POST payload: %s\n", curl_easy_strerror(r));
+ return r;
+ }
+ }
+
+ // If we do not have an output file, we will create a temporary file
+ if (!xfer->fout && !xfer->fin) {
+ r = pakfire_xfer_prepare_tmpfile(xfer);
+ if (r) {
+ CTX_ERROR(xfer->ctx, "Could not open a temporary file: %s\n", strerror(-r));
+ return r;
+ }
+ }
+
+ // Authentication
+ if (xfer->auth) {
+ // Request SPNEGO
+ r = curl_easy_setopt(xfer->handle, CURLOPT_HTTPAUTH, CURLAUTH_NEGOTIATE|CURLAUTH_ONLY);
+ if (r) {
+ CTX_ERROR(xfer->ctx, "Could not enable SPNEGO\n");
+ return r;
+ }
+
+ // Set an empty username
+ r = curl_easy_setopt(xfer->handle, CURLOPT_USERPWD, ":");
+ if (r) {
+ CTX_ERROR(xfer->ctx, "Could not set username\n");
+ return r;
+ }
+ }
+
+ // Drop any previously used EVP contexts
+ if (xfer->evp) {
+ EVP_MD_CTX_free(xfer->evp);
+ xfer->evp = NULL;
+ }
+
+ // Create a new EVP context
+ if (xfer->md) {
+ xfer->evp = EVP_MD_CTX_new();
+ if (!xfer->evp) {
+ CTX_ERROR(xfer->ctx, "Could not create EVP context: %m\n");
+ return 1;
+ }
+
+ // Initialize the EVP context
+ r = EVP_DigestInit_ex(xfer->evp, xfer->md, NULL);
+ if (r != 1) {
+ CTX_ERROR(xfer->ctx, "Could not initialize EVP context: %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ return 1;
+ }
+ }
+
+ // Setup progress
+ r = pakfire_xfer_prepare_progress(xfer, progress, flags);
+ if (r)
+ return r;
+
+ return 0;
+}
+
+int pakfire_xfer_run(struct pakfire_xfer* xfer, int flags) {
+ int r;
+
+AGAIN:
+ // Prepare the xfer
+ r = pakfire_xfer_prepare(xfer, NULL, flags);
+ if (r) {
+ CTX_ERROR(xfer->ctx, "Could not prepare xfer %s: %s\n",
+ xfer->url, strerror(-r));
+ return r;
+ }
+
+ // Perform the action
+ r = curl_easy_perform(xfer->handle);
+
+ // Handle the result
+ r = pakfire_xfer_done(xfer, r);
+
+ // Repeat the xfer if asked
+ switch (-r) {
+ case EAGAIN:
+ goto AGAIN;
+
+ default:
+ break;
+ }
+
+ return r;
+}