]> git.ipfire.org Git - pakfire.git/commitdiff
xfer: Split off transfers from the downloader
authorMichael Tremer <michael.tremer@ipfire.org>
Fri, 20 Oct 2023 15:43:41 +0000 (15:43 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Sat, 21 Oct 2023 11:11:51 +0000 (11:11 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
12 files changed:
Makefile.am
src/libpakfire/buildservice.c
src/libpakfire/dist.c
src/libpakfire/downloader.c
src/libpakfire/include/pakfire/downloader.h
src/libpakfire/include/pakfire/mirror.h
src/libpakfire/include/pakfire/repo.h
src/libpakfire/include/pakfire/xfer.h [new file with mode: 0644]
src/libpakfire/mirror.c
src/libpakfire/repo.c
src/libpakfire/transaction.c
src/libpakfire/xfer.c [new file with mode: 0644]

index ed71e64e8d8928e9ecb5b24ae586d43902c5bec1..422ab6adca8b741439764dc5034977bd575a78d8 100644 (file)
@@ -250,7 +250,8 @@ libpakfire_la_SOURCES = \
        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 \
@@ -295,7 +296,8 @@ pkginclude_HEADERS += \
        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) \
index 0ebb5091192a4fa7e777f9e7425ec29a0c306871..f70c001b4f2c2ba2fcd1171b3a4a36864b761f55 100644 (file)
@@ -34,6 +34,7 @@
 #include <pakfire/private.h>
 #include <pakfire/string.h>
 #include <pakfire/util.h>
+#include <pakfire/xfer.h>
 
 #include <krb5/krb5.h>
 
@@ -183,40 +184,40 @@ PAKFIRE_EXPORT struct pakfire_buildservice* pakfire_buildservice_unref(
        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))
@@ -248,7 +249,7 @@ static int pakfire_buildservice_handle_error(struct pakfire_buildservice* servic
        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;
@@ -281,7 +282,7 @@ static int pakfire_buildservice_parse_response(struct pakfire_buildservice* serv
        // 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;
        }
 
@@ -301,36 +302,36 @@ 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;
                }
@@ -338,31 +339,31 @@ int pakfire_buildservice_build(struct pakfire_buildservice* service, const char*
 
        // 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)
@@ -375,7 +376,7 @@ ERROR:
 
 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;
@@ -408,48 +409,48 @@ static int pakfire_buildservice_create_upload(struct pakfire_buildservice* servi
        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;
@@ -482,8 +483,8 @@ static int pakfire_buildservice_create_upload(struct pakfire_buildservice* servi
        r = 0;
 
 ERROR:
-       if (transfer)
-               pakfire_downloader_transfer_unref(transfer);
+       if (xfer)
+               pakfire_xfer_unref(xfer);
        if (response)
                json_object_put(response);
        if (hexdigest)
@@ -496,7 +497,7 @@ ERROR:
 
 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;
@@ -508,41 +509,41 @@ static int pakfire_buildservice_upload_payload(struct pakfire_buildservice* serv
        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);
 
@@ -598,35 +599,35 @@ ERROR:
 
 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;
@@ -644,8 +645,8 @@ PAKFIRE_EXPORT int pakfire_buildservice_list_uploads(
        *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)
@@ -656,7 +657,7 @@ ERROR:
 
 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;
@@ -668,41 +669,41 @@ PAKFIRE_EXPORT int pakfire_buildservice_delete_upload(
        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)
@@ -715,7 +716,7 @@ ERROR:
 
 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];
@@ -732,28 +733,28 @@ PAKFIRE_EXPORT int pakfire_buildservice_list_repos(struct pakfire_buildservice*
        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;
@@ -770,8 +771,8 @@ PAKFIRE_EXPORT int pakfire_buildservice_list_repos(struct pakfire_buildservice*
        *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)
@@ -782,7 +783,7 @@ ERROR:
 
 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;
@@ -798,28 +799,28 @@ PAKFIRE_EXPORT int pakfire_buildservice_get_repo(struct pakfire_buildservice* se
        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;
@@ -829,8 +830,8 @@ PAKFIRE_EXPORT int pakfire_buildservice_get_repo(struct pakfire_buildservice* se
        *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)
@@ -841,7 +842,7 @@ ERROR:
 
 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;
@@ -857,40 +858,40 @@ PAKFIRE_EXPORT int pakfire_buildservice_create_repo(struct pakfire_buildservice*
        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;
@@ -901,8 +902,8 @@ PAKFIRE_EXPORT int pakfire_buildservice_create_repo(struct pakfire_buildservice*
                *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)
@@ -913,7 +914,7 @@ ERROR:
 
 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;
@@ -925,41 +926,41 @@ PAKFIRE_EXPORT int pakfire_buildservice_delete_repo(struct pakfire_buildservice*
        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)
index ccc18452ea82573b90e93a070ed414a4b108239d..989fd3386e62f664386eaf05679e28863c09c642 100644 (file)
@@ -42,6 +42,7 @@
 #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"
@@ -278,7 +279,7 @@ ERROR:
 
 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
@@ -286,28 +287,28 @@ static int pakfire_dist_download_source(struct pakfire_downloader* downloader,
                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;
 }
index 3137df5aad7345d49cb80a01e7e85e49779a7b55..4809cd408dd28b393c5591096d9b5651fb2f4193 100644 (file)
 #############################################################################*/
 
 #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 {
@@ -124,9 +51,9 @@ 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) {
@@ -164,66 +91,64 @@ 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)
@@ -261,9 +186,9 @@ int pakfire_downloader_create(
        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);
@@ -295,1065 +220,38 @@ struct pakfire_downloader* pakfire_downloader_unref(struct pakfire_downloader* 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, &times.modtime);
-
-       if (times.modtime) {
-               r = utime(transfer->path, &times);
-               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)
@@ -1365,75 +263,66 @@ static size_t pakfire_downloader_total_downloadsize(struct pakfire_downloader* d
        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;
@@ -1452,7 +341,7 @@ int pakfire_downloader_run(struct pakfire_downloader* downloader, const char* ti
        }
 
        // 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);
@@ -1467,14 +356,14 @@ int pakfire_downloader_run(struct pakfire_downloader* downloader, const char* ti
        }
 
        // 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;
                }
@@ -1495,33 +384,39 @@ int pakfire_downloader_run(struct pakfire_downloader* downloader, const char* ti
                        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:
@@ -1533,7 +428,7 @@ int pakfire_downloader_run(struct pakfire_downloader* downloader, const char* ti
                // 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);
@@ -1542,12 +437,12 @@ int pakfire_downloader_run(struct pakfire_downloader* downloader, const char* ti
 
 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)
index cddb8d3fe30bb4340d3411850b96f2f00c683743..7909229f6a616f870c055e0d0af84e4645c63772 100644 (file)
 
 #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 */
index 3796671d6b67c446bc6cdd8c4882a9491042032b..ab2d4615da3bd1f26e2fa2debe8193d597ad7f2d 100644 (file)
@@ -38,6 +38,6 @@ const char* pakfire_mirror_get_url(struct pakfire_mirror* mirror);
 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 */
index 82c7b10ed30f80eed32cf4b43ec35ae089a1e699..ce2e0fbab58ab37c625554352f86e148aa19ea47 100644 (file)
@@ -101,6 +101,7 @@ int pakfire_repo_compose(struct pakfire* pakfire, const char* path,
 #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"
@@ -127,7 +128,7 @@ int pakfire_repo_download_package(
        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);
diff --git a/src/libpakfire/include/pakfire/xfer.h b/src/libpakfire/include/pakfire/xfer.h
new file mode 100644 (file)
index 0000000..332451b
--- /dev/null
@@ -0,0 +1,93 @@
+/*#############################################################################
+#                                                                             #
+# 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 */
index 6cf40692a6131e0fde5d04a4e57385e2c6ca1c55..c643599b762808faeba8085a6388eb75fd20ef7f 100644 (file)
@@ -111,7 +111,7 @@ void pakfire_mirror_mark_as_broken(struct pakfire_mirror* mirror) {
        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);
 }
index 6b163d2d1056c9ba6fa63788358a79df0f489af8..b4f3dc414e1c36cb983c5258477de502aa37cd2b 100644 (file)
@@ -49,6 +49,7 @@
 #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
@@ -221,25 +222,25 @@ static int __pakfire_repo_path(struct pakfire_repo* repo,
 }
 
 /*
-       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;
        }
@@ -247,20 +248,20 @@ static int pakfire_repo_create_transfer(struct pakfire_transfer** transfer,
        // 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)
@@ -431,7 +432,7 @@ struct pakfire_mirrorlist* pakfire_repo_get_mirrorlist(struct pakfire_repo* repo
 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;
@@ -460,28 +461,28 @@ static int pakfire_repo_download_database(struct pakfire_repo* repo,
                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);
 
@@ -581,7 +582,7 @@ ERROR:
 
 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;
 
@@ -626,19 +627,19 @@ static int pakfire_repo_refresh_mirrorlist(struct pakfire_repo* repo, const int
        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)
@@ -649,7 +650,7 @@ ERROR:
 
 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
@@ -687,23 +688,23 @@ static int pakfire_repo_download_metadata(struct pakfire_repo* repo, const char*
                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);
 
@@ -1183,8 +1184,8 @@ PAKFIRE_EXPORT int pakfire_repo_is_installed_repo(struct pakfire_repo* repo) {
 }
 
 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;
@@ -1221,12 +1222,12 @@ int pakfire_repo_download_package(struct pakfire_repo* repo, struct pakfire_down
        }
 
        // 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;
 
@@ -1235,29 +1236,29 @@ int pakfire_repo_download_package(struct pakfire_repo* repo, struct pakfire_down
 
        // 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;
 }
@@ -1265,7 +1266,7 @@ ERROR:
 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;
 
@@ -1284,17 +1285,17 @@ static int pakfire_repo_download(struct pakfire_repo* repo, const char* url,
                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;
 
@@ -1304,8 +1305,8 @@ static int pakfire_repo_download(struct pakfire_repo* repo, const char* url,
                goto ERROR;
 
 ERROR:
-       if (transfer)
-               pakfire_downloader_transfer_unref(transfer);
+       if (xfer)
+               pakfire_xfer_unref(xfer);
        if (downloader)
                pakfire_downloader_unref(downloader);
        if (f)
index 2f22c2c60cd5f291f9decfaa7238e8537ace3e4f..d26da2ac5ae4fe476df3e1b48b14597c325e595d 100644 (file)
@@ -1820,7 +1820,7 @@ ERROR:
 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
@@ -1828,14 +1828,14 @@ static int pakfire_transaction_download_package(struct pakfire_transaction* tran
        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);
 
diff --git a/src/libpakfire/xfer.c b/src/libpakfire/xfer.c
new file mode 100644 (file)
index 0000000..5b66afd
--- /dev/null
@@ -0,0 +1,1180 @@
+/*#############################################################################
+#                                                                             #
+# 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, &times.modtime);
+
+       if (times.modtime) {
+               r = utime(xfer->path, &times);
+               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;
+}