Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package librepo for openSUSE:Factory checked 
in at 2022-12-30 11:08:13
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/librepo (Old)
 and      /work/SRC/openSUSE:Factory/.librepo.new.1563 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "librepo"

Fri Dec 30 11:08:13 2022 rev:18 rq:1045671 version:1.15.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/librepo/librepo.changes  2022-10-17 
14:57:19.242049025 +0200
+++ /work/SRC/openSUSE:Factory/.librepo.new.1563/librepo.changes        
2022-12-30 11:08:15.589004242 +0100
@@ -1,0 +2,9 @@
+Wed Dec 28 16:50:53 UTC 2022 - Andreas Stieger <andreas.stie...@gmx.de>
+
+- update to 1.15.1:
+  * Add API support for waiting on network in an event driven
+    manner
+  * OpenPGP API extension and fixes
+- lincense updated to LGPL-2.1-or-later
+
+-------------------------------------------------------------------

Old:
----
  librepo-1.14.5.tar.gz

New:
----
  librepo-1.15.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ librepo.spec ++++++
--- /var/tmp/diff_new_pack.X57fFf/_old  2022-12-30 11:08:17.485015576 +0100
+++ /var/tmp/diff_new_pack.X57fFf/_new  2022-12-30 11:08:17.513015744 +0100
@@ -37,10 +37,10 @@
 %define devname %{name}-devel
 
 Name:           librepo
-Version:        1.14.5
+Version:        1.15.1
 Release:        0
 Summary:        Repodata downloading library
-License:        LGPL-2.0-or-later
+License:        LGPL-2.1-or-later
 Group:          Development/Libraries/C and C++
 
 URL:            https://github.com/rpm-software-management/librepo

++++++ librepo-1.14.5.tar.gz -> librepo-1.15.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/librepo-1.14.5/CMakeLists.txt 
new/librepo-1.15.1/CMakeLists.txt
--- old/librepo-1.14.5/CMakeLists.txt   2022-09-09 12:32:23.000000000 +0200
+++ new/librepo-1.15.1/CMakeLists.txt   2022-12-09 13:47:26.000000000 +0100
@@ -28,7 +28,7 @@
 # Find necessare libraries
 
 FIND_PACKAGE(PkgConfig)
-PKG_CHECK_MODULES(GLIB2 glib-2.0>=2.28 REQUIRED)
+PKG_CHECK_MODULES(GLIB2 glib-2.0>=2.28 gio-2.0 REQUIRED)
 PKG_SEARCH_MODULE(LIBCRYPTO REQUIRED libcrypto openssl)
 PKG_CHECK_MODULES(LIBXML2 libxml-2.0 REQUIRED)
 FIND_PACKAGE(CURL 7.52.0 REQUIRED)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/librepo-1.14.5/VERSION.cmake 
new/librepo-1.15.1/VERSION.cmake
--- old/librepo-1.14.5/VERSION.cmake    2022-09-09 12:32:23.000000000 +0200
+++ new/librepo-1.15.1/VERSION.cmake    2022-12-09 13:47:26.000000000 +0100
@@ -1,3 +1,3 @@
 SET(LIBREPO_MAJOR "1")
-SET(LIBREPO_MINOR "14")
-SET(LIBREPO_PATCH "5")
+SET(LIBREPO_MINOR "15")
+SET(LIBREPO_PATCH "1")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/librepo-1.14.5/librepo/gpg.c 
new/librepo-1.15.1/librepo/gpg.c
--- old/librepo-1.14.5/librepo/gpg.c    2022-09-09 12:32:23.000000000 +0200
+++ new/librepo-1.15.1/librepo/gpg.c    2022-12-09 13:47:26.000000000 +0100
@@ -1,5 +1,6 @@
 /* librepo - A library providing (libcURL like) API to downloading repository
  * Copyright (C) 2012  Tomas Mlcoch
+ * Copyright (C) 2022  Jaroslav Rohel <jro...@redhat.com>
  *
  * Licensed under the GNU Lesser General Public License Version 2.1
  *
@@ -19,12 +20,10 @@
  */
 
 #include <assert.h>
-#include <glib.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <errno.h>
-#include <string.h>
 #include <gpgme.h>
 #include <unistd.h>
 
@@ -32,6 +31,21 @@
 #include "util.h"
 #include "gpg.h"
 
+struct tLrGpgSubkey {
+    gboolean has_next;   // FALSE if this is the last subkey in the list
+    char *id;            // subkey id
+    char *fingerprint;   // fingerprint of the subkey in hex digit form
+    long int timestamp;  // creation timestamp, -1 if invalid, 0 if not 
available
+    gboolean can_sign;   // TRUE if subkey can be used for signing
+};
+
+struct tLrGpgKey {
+    gboolean has_next;     // FALSE if this is the last subkey in the list
+    char **uids;           // NULL terminated array of user IDs strings
+    LrGpgSubkey *subkeys;  // list of subkeys associated with the key. The 
first subkey is the primary key
+    char *raw_key;         // key in ACII-Armor format
+};
+
 /*
  * Creates the '/run/user/$UID' directory if it doesn't exist. If this
  * directory exists, gpgagent will create its sockets under
@@ -52,7 +66,9 @@
  * [2] https://bugzilla.redhat.com/show_bug.cgi?id=1769831
  * [3] https://github.com/rpm-software-management/microdnf/issues/50
  */
-void ensure_socket_dir_exists() {
+static void
+lr_gpg_ensure_socket_dir_exists()
+{
     char dirname[32];
     snprintf(dirname, sizeof(dirname), "/run/user/%u", getuid());
     int res = mkdir(dirname, 0700);
@@ -61,22 +77,16 @@
     }
 }
 
-gboolean
-lr_gpg_check_signature_fd(int signature_fd,
-                          int data_fd,
-                          const char *home_dir,
-                          GError **err)
+static gpgme_ctx_t
+lr_gpg_context_init(const char *home_dir, GError **err)
 {
-    gpgme_error_t gpgerr;
-    gpgme_ctx_t context;
-    gpgme_data_t signature_data;
-    gpgme_data_t data_data;
-    gpgme_verify_result_t result;
-    gpgme_signature_t sig;
-
     assert(!err || *err == NULL);
 
-    // Initialization
+    lr_gpg_ensure_socket_dir_exists();
+
+    gpgme_ctx_t context;
+    gpgme_error_t gpgerr;
+
     gpgme_check_version(NULL);
     gpgerr = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
     if (gpgerr != GPG_ERR_NO_ERROR) {
@@ -85,7 +95,7 @@
         g_set_error(err, LR_GPG_ERROR, LRE_GPGNOTSUPPORTED,
                     "gpgme_engine_check_version() error: %s",
                     gpgme_strerror(gpgerr));
-        return FALSE;
+        return NULL;
     }
 
     gpgerr = gpgme_new(&context);
@@ -93,7 +103,7 @@
         g_debug("%s: gpgme_new: %s", __func__, gpgme_strerror(gpgerr));
         g_set_error(err, LR_GPG_ERROR, LRE_GPGERROR,
                     "gpgme_new() error: %s", gpgme_strerror(gpgerr));
-        return FALSE;
+        return NULL;
     }
 
     gpgerr = gpgme_set_protocol(context, GPGME_PROTOCOL_OpenPGP);
@@ -102,25 +112,44 @@
         g_set_error(err, LR_GPG_ERROR, LRE_GPGERROR,
                     "gpgme_set_protocol() error: %s", gpgme_strerror(gpgerr));
         gpgme_release(context);
-        return FALSE;
+        return NULL;
     }
 
     if (home_dir) {
         gpgerr = gpgme_ctx_set_engine_info(context, GPGME_PROTOCOL_OpenPGP,
                                         NULL, home_dir);
         if (gpgerr != GPG_ERR_NO_ERROR) {
-            g_debug("%s: gpgme_ctx_set_engine_info: %s", __func__,
-                    gpgme_strerror(gpgerr));
+            g_debug("%s: gpgme_ctx_set_engine_info: %s", __func__, 
gpgme_strerror(gpgerr));
             g_set_error(err, LR_GPG_ERROR, LRE_GPGERROR,
                         "gpgme_ctx_set_engine_info() error: %s",
                         gpgme_strerror(gpgerr));
             gpgme_release(context);
-            return FALSE;
+            return NULL;
         }
     }
 
     gpgme_set_armor(context, 1);
 
+    return context;
+}
+
+gboolean
+lr_gpg_check_signature_fd(int signature_fd,
+                          int data_fd,
+                          const char *home_dir,
+                          GError **err)
+{
+    gpgme_error_t gpgerr;
+    gpgme_data_t signature_data;
+    gpgme_data_t data_data;
+    gpgme_verify_result_t result;
+    gpgme_signature_t sig;
+
+    gpgme_ctx_t context = lr_gpg_context_init(home_dir, err);
+    if (!context) {
+        return FALSE;
+    }
+
     gpgerr = gpgme_data_new_from_fd(&signature_data, signature_fd);
     if (gpgerr != GPG_ERR_NO_ERROR) {
         g_debug("%s: gpgme_data_new_from_fd: %s",
@@ -235,73 +264,54 @@
 }
 
 gboolean
-lr_gpg_import_key(const char *key_fn, const char *home_dir, GError **err)
+lr_gpg_import_key_from_memory(const char *key, size_t key_len, const char 
*home_dir, GError **err)
 {
+    gpgme_ctx_t context = lr_gpg_context_init(home_dir, err);
+    if (!context) {
+        return FALSE;
+    }
+
     gpgme_error_t gpgerr;
-    int key_fd;
-    gpgme_ctx_t context;
     gpgme_data_t key_data;
 
-    assert(!err || *err == NULL);
-
-    ensure_socket_dir_exists();
-
-    // Initialization
-    gpgme_check_version(NULL);
-    gpgerr = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
+    gpgerr = gpgme_data_new_from_mem(&key_data, key, key_len, 0);
     if (gpgerr != GPG_ERR_NO_ERROR) {
-        g_debug("%s: gpgme_engine_check_version: %s",
+        g_debug("%s: gpgme_data_new_from_mem: %s",
                  __func__, gpgme_strerror(gpgerr));
-        g_set_error(err, LR_GPG_ERROR, LRE_GPGNOTSUPPORTED,
-                    "gpgme_engine_check_version() error: %s",
-                    gpgme_strerror(gpgerr));
-        return FALSE;
-    }
-
-    gpgerr = gpgme_new(&context);
-    if (gpgerr != GPG_ERR_NO_ERROR) {
-        g_debug("%s: gpgme_new: %s", __func__, gpgme_strerror(gpgerr));
         g_set_error(err, LR_GPG_ERROR, LRE_GPGERROR,
-                    "gpgme_new() error: %s", gpgme_strerror(gpgerr));
+                    "gpgme_data_new_from_mem(_, _, %ld, 0) error: %s",
+                    (unsigned long)key_len, gpgme_strerror(gpgerr));
+        gpgme_release(context);
         return FALSE;
     }
 
-    gpgerr = gpgme_set_protocol(context, GPGME_PROTOCOL_OpenPGP);
+    gpgerr = gpgme_op_import(context, key_data);
     if (gpgerr != GPG_ERR_NO_ERROR) {
-        g_debug("%s: gpgme_set_protocol: %s", __func__, 
gpgme_strerror(gpgerr));
+        g_debug("%s: gpgme_op_import: %s", __func__, gpgme_strerror(gpgerr));
         g_set_error(err, LR_GPG_ERROR, LRE_GPGERROR,
-                    "gpgme_set_protocol() error: %s", gpgme_strerror(gpgerr));
+                    "gpgme_op_import() error: %s", gpgme_strerror(gpgerr));
+        gpgme_data_release(key_data);
         gpgme_release(context);
         return FALSE;
     }
 
-    if (home_dir) {
-        gpgerr = gpgme_ctx_set_engine_info(context, GPGME_PROTOCOL_OpenPGP,
-                                        NULL, home_dir);
-        if (gpgerr != GPG_ERR_NO_ERROR) {
-            g_debug("%s: gpgme_ctx_set_engine_info: %s", __func__, 
gpgme_strerror(gpgerr));
-            g_set_error(err, LR_GPG_ERROR, LRE_GPGERROR,
-                        "gpgme_ctx_set_engine_info() error: %s",
-                        gpgme_strerror(gpgerr));
-            gpgme_release(context);
-            return FALSE;
-        }
-    }
-
-    gpgme_set_armor(context, 1);
+    gpgme_data_release(key_data);
+    gpgme_release(context);
 
-    // Key import
+    return TRUE;
+}
 
-    key_fd = open(key_fn, O_RDONLY);
-    if (key_fd == -1) {
-        g_debug("%s: Opening key: %s", __func__, g_strerror(errno));
-        g_set_error(err, LR_GPG_ERROR, LRE_IO,
-                    "Error while opening key %s: %s",
-                    key_fn, g_strerror(errno));
-        gpgme_release(context);
+gboolean
+lr_gpg_import_key_from_fd(int key_fd, const char *home_dir, GError **err)
+{
+    gpgme_ctx_t context = lr_gpg_context_init(home_dir, err);
+    if (!context) {
         return FALSE;
     }
 
+    gpgme_error_t gpgerr;
+    gpgme_data_t key_data;
+
     gpgerr = gpgme_data_new_from_fd(&key_data, key_fd);
     if (gpgerr != GPG_ERR_NO_ERROR) {
         g_debug("%s: gpgme_data_new_from_fd: %s",
@@ -310,24 +320,267 @@
                     "gpgme_data_new_from_fd(_, %d) error: %s",
                     key_fd, gpgme_strerror(gpgerr));
         gpgme_release(context);
-        close(key_fd);
         return FALSE;
     }
 
     gpgerr = gpgme_op_import(context, key_data);
-    gpgme_data_release(key_data);
     if (gpgerr != GPG_ERR_NO_ERROR) {
         g_debug("%s: gpgme_op_import: %s", __func__, gpgme_strerror(gpgerr));
         g_set_error(err, LR_GPG_ERROR, LRE_GPGERROR,
                     "gpgme_op_import() error: %s", gpgme_strerror(gpgerr));
+        gpgme_data_release(key_data);
         gpgme_release(context);
-        close(key_fd);
         return FALSE;
     }
 
+    gpgme_data_release(key_data);
+    gpgme_release(context);
+
+    return TRUE;
+}
+
+gboolean
+lr_gpg_import_key(const char *key_fn, const char *home_dir, GError **err)
+{
+    assert(!err || *err == NULL);
+
+    int key_fd = open(key_fn, O_RDONLY);
+    if (key_fd == -1) {
+        g_debug("%s: Opening key: %s", __func__, g_strerror(errno));
+        g_set_error(err, LR_GPG_ERROR, LRE_IO,
+                    "Error while opening key %s: %s",
+                    key_fn, g_strerror(errno));
+        return FALSE;
+    }
+
+    gboolean ret = lr_gpg_import_key_from_fd(key_fd, home_dir, err);
+
     close(key_fd);
 
+    return ret;
+}
+
+LrGpgKey *
+lr_gpg_list_keys(gboolean export_keys, const char *home_dir, GError **err)
+{
+    gpgme_error_t gpgerr;
+
+    gpgme_ctx_t context = lr_gpg_context_init(home_dir, err);
+    if (!context) {
+        return NULL;
+    }
+
+    GArray * keys = g_array_new(FALSE, FALSE, sizeof(LrGpgKey));
+
+    gpgerr = gpgme_op_keylist_start(context, NULL, 0);
+    while (gpg_err_code(gpgerr) == GPG_ERR_NO_ERROR) {
+        gpgme_key_t key;
+        gpgerr = gpgme_op_keylist_next(context, &key);
+        if (gpgerr) {
+            break;
+        }
+
+        GArray * subkeys = g_array_new(FALSE, FALSE, sizeof(LrGpgSubkey));
+        gpgme_subkey_t subkey = key->subkeys;
+        while (subkey) {
+            LrGpgSubkey lr_subkey;
+            lr_subkey.has_next = FALSE;
+            lr_subkey.id = g_strdup(subkey->keyid);
+            lr_subkey.fingerprint = g_strdup(subkey->fpr);
+            lr_subkey.timestamp = subkey->timestamp;
+            lr_subkey.can_sign = subkey->can_sign;
+            g_array_append_val(subkeys, lr_subkey);
+            subkey = subkey->next;
+        }
+         // All subkeys in the list except the last one are followed by 
another subkey
+        if (subkeys->len > 0) {
+            for (guint i = 0; i < subkeys->len - 1; ++i) {
+                g_array_index(subkeys, LrGpgSubkey, i).has_next = TRUE;
+            }
+        }
+
+        LrGpgKey lr_key;
+        lr_key.has_next = FALSE;
+
+        GPtrArray * uid_strings = g_ptr_array_new();
+        for (gpgme_user_id_t uids = key->uids; uids; uids = uids->next) {
+            if (!uids->uid) {
+                continue;
+            }
+            g_ptr_array_add(uid_strings, g_strdup(uids->uid));
+        }
+
+        gpgme_key_release(key);
+
+        g_ptr_array_add(uid_strings, NULL);  // add terminating NULL
+        lr_key.uids = (char **)g_ptr_array_free(uid_strings, FALSE);
+
+        lr_key.subkeys = (LrGpgSubkey *)(subkeys->len > 0 ? 
g_array_free(subkeys, FALSE) : g_array_free(subkeys, TRUE));
+        lr_key.raw_key = NULL;
+        g_array_append_val(keys, lr_key);
+    }
+    // All keys in the list except the last one are followed by another key
+    for (guint i = 0; i < keys->len - 1; ++i) {
+        g_array_index(keys, LrGpgKey, i).has_next = TRUE;
+    }
+
+    if (gpg_err_code(gpgerr) != GPG_ERR_EOF) {
+        g_debug("%s: gpgme_op_keylist_: %s",
+                 __func__, gpgme_strerror(gpgerr));
+        g_set_error(err, LR_GPG_ERROR, LRE_GPGERROR,
+                    "gpgme_op_keylist_ error: %s",
+                    gpgme_strerror(gpgerr));
+        lr_gpg_keys_free((LrGpgKey *)g_array_free(keys, FALSE));
+        gpgme_release(context);
+        return NULL;
+    }
+
+    gpgme_op_keylist_end(context);
+
+    LrGpgKey *lr_keys = (LrGpgKey *)(keys->len > 0 ? g_array_free(keys, FALSE) 
: g_array_free(keys, TRUE));
+
+    if (export_keys) {
+        for (LrGpgKey *lr_key = lr_keys; lr_key; ++lr_key) {
+            LrGpgSubkey *lr_subkey = lr_key->subkeys;
+            if (!lr_subkey) {
+                g_info("%s: Missing data to export key. Damaged key? Skipping 
the key", __func__);
+                if (!lr_key->has_next) {
+                    break;
+                }
+                continue;
+            }
+
+            gpgme_data_t key_data;
+            gpgerr = gpgme_data_new(&key_data);
+            if (gpgerr != GPG_ERR_NO_ERROR) {
+                g_debug("%s: gpgme_data_new: %s", __func__, 
gpgme_strerror(gpgerr));
+                g_set_error(err, LR_GPG_ERROR, LRE_GPGERROR,
+                            "gpgme_data_new() error: %s", 
gpgme_strerror(gpgerr));
+                lr_gpg_keys_free(lr_keys);
+                gpgme_release(context);
+                return NULL;
+            }
+
+            gpgerr = gpgme_op_export(context, lr_subkey->fingerprint, 0, 
key_data);
+            if (gpgerr != GPG_ERR_NO_ERROR) {
+                g_debug("%s: gpgme_op_export: %s", __func__, 
gpgme_strerror(gpgerr));
+                g_set_error(err, LR_GPG_ERROR, LRE_GPGERROR,
+                            "gpgme_op_export() error: %s", 
gpgme_strerror(gpgerr));
+                gpgme_data_release(key_data);
+                lr_gpg_keys_free(lr_keys);
+                gpgme_release(context);
+                return NULL;
+            }
+
+            off_t key_size = gpgme_data_seek(key_data, 0, SEEK_CUR);
+            gpgerr = gpgme_data_rewind(key_data);
+            if (gpgerr != GPG_ERR_NO_ERROR) {
+                g_debug("%s: gpgme_data_rewind: %s", __func__, 
gpgme_strerror(gpgerr));
+                g_set_error(err, LR_GPG_ERROR, LRE_GPGERROR,
+                            "gpgme_data_rewind() error: %s", 
gpgme_strerror(gpgerr));
+                gpgme_data_release(key_data);
+                lr_gpg_keys_free(lr_keys);
+                gpgme_release(context);
+                return NULL;
+            }
+
+            lr_key->raw_key = g_malloc0(key_size + 1);
+            ssize_t readed = gpgme_data_read(key_data, lr_key->raw_key, 
key_size);
+            if (readed == -1) {
+                g_debug("%s: gpgme_data_read: %s", __func__, 
gpgme_strerror(gpgerr));
+                g_set_error(err, LR_GPG_ERROR, LRE_GPGERROR,
+                            "gpgme_data_read() error: %s", 
gpgme_strerror(gpgerr));
+                gpgme_data_release(key_data);
+                lr_gpg_keys_free(lr_keys);
+                gpgme_release(context);
+                return NULL;
+            }
+            if (readed != key_size) {
+                g_warning("%s: Error exporting key \"%s\": gpgme_data_read: 
Key size is %ld but readed %ld. "
+                          "Skipping the key",
+                          __func__, lr_key->subkeys->fingerprint, 
(long)key_size, (long)readed);
+                g_free(lr_key->raw_key);
+                lr_key->raw_key = NULL;
+            }
+
+            gpgme_data_release(key_data);
+
+            if (!lr_key->has_next) {
+                break;
+            }
+        }
+    }
+
     gpgme_release(context);
+    return lr_keys;
+}
 
-    return TRUE;
+const LrGpgKey *
+lr_gpg_key_get_next(const LrGpgKey *key) {
+    return key->has_next ? ++key : NULL;
+}
+
+char * const *
+lr_gpg_key_get_userids(const LrGpgKey *key) {
+    return key->uids;
+}
+
+const char *
+lr_gpg_key_get_raw_key(const LrGpgKey *key) {
+    return key->raw_key;
+}
+
+const LrGpgSubkey *
+lr_gpg_key_get_subkeys(const LrGpgKey *key) {
+    return key->subkeys;
+}
+
+static void
+lr_gpg_subkeys_free(LrGpgSubkey *subkeys) {
+    for (LrGpgSubkey *item = subkeys; item; ++item) {
+        g_free(item->fingerprint);
+        g_free(item->id);
+        if (!item->has_next) {
+            break;
+        }
+    }
+    g_free(subkeys);
+}
+
+void
+lr_gpg_keys_free(LrGpgKey *keys) {
+    for (LrGpgKey *item = keys; item; ++item) {
+        g_free(item->raw_key);
+        lr_gpg_subkeys_free(item->subkeys);
+        g_strfreev(item->uids);
+        if (!item->has_next) {
+            break;
+        }
+    }
+    g_free(keys);
+}
+
+const LrGpgSubkey *
+lr_gpg_subkey_get_next(const LrGpgSubkey *subkey) {
+    return subkey->has_next ? ++subkey : NULL;
+}
+
+const char *
+lr_gpg_subkey_get_id(const LrGpgSubkey *subkey) {
+    return subkey->id;
+}
+
+const char *
+lr_gpg_subkey_get_fingerprint(const LrGpgSubkey *subkey) {
+    return subkey->fingerprint;
+}
+
+long int
+lr_gpg_subkey_get_timestamp(const LrGpgSubkey *subkey) {
+    return subkey->timestamp;
+}
+
+gboolean
+lr_gpg_subkey_get_can_sign(const LrGpgSubkey *subkey) {
+    return subkey->can_sign;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/librepo-1.14.5/librepo/gpg.h 
new/librepo-1.15.1/librepo/gpg.h
--- old/librepo-1.14.5/librepo/gpg.h    2022-09-09 12:32:23.000000000 +0200
+++ new/librepo-1.15.1/librepo/gpg.h    2022-12-09 13:47:26.000000000 +0100
@@ -1,5 +1,6 @@
 /* librepo - A library providing (libcURL like) API to downloading repository
  * Copyright (C) 2012  Tomas Mlcoch
+ * Copyright (C) 2022  Jaroslav Rohel <jro...@redhat.com>
  *
  * Licensed under the GNU Lesser General Public License Version 2.1
  *
@@ -30,6 +31,14 @@
  *  @{
  */
 
+/** A structure containing information about subkey.
+ */
+typedef struct tLrGpgSubkey LrGpgSubkey;
+
+/** A structure containing information about a key with subkeys.
+ */
+typedef struct tLrGpgKey LrGpgKey;
+
 /** Check detached signature of data.
  * @param signature_fd  File descriptor of signature file.
  * @param data_fd       File descriptor of data to verify.
@@ -60,6 +69,35 @@
                        const char *home_dir,
                        GError **err);
 
+
+/** Import key into the keyring.
+ * @param key           Pointer to memory buffer with key.
+ * @param key_len       Length of the key.
+ * @param home_dir      Configuration directory of OpenPGP engine
+ *                      (e.g. "/home/user/.gnupg/"), if NULL default
+ *                      config directory is used.
+ * @param err           GError **
+ * @return              returns TRUE if error is not set and FALSE if it is.
+ */
+gboolean
+lr_gpg_import_key_from_memory(const char *key,
+                              size_t key_len,
+                              const char *home_dir,
+                              GError **err);
+
+/** Import key into the keyring.
+ * @param key_fd        Filedescriptor of key file.
+ * @param home_dir      Configuration directory of OpenPGP engine
+ *                      (e.g. "/home/user/.gnupg/"), if NULL default
+ *                      config directory is used.
+ * @param err           GError **
+ * @return              returns TRUE if error is not set and FALSE if it is.
+ */
+gboolean
+lr_gpg_import_key_from_fd(int key_fd,
+                          const char *home_dir,
+                          GError **err);
+
 /** Import key into the keyring.
  * @param key_fn        Filename (path) of key file.
  * @param home_dir      Configuration directory of OpenPGP engine
@@ -73,6 +111,90 @@
                   const char *home_dir,
                   GError **err);
 
+
+/** List/export keys (and subkeys) from the keyring.
+ * @param export_keys   If TRUE, the list also contains the exported keys.
+ *                      Export is in ASCII-Armor format.
+ * @param home_dir      Configuration directory of OpenPGP engine
+ *                      (e.g. "/home/user/.gnupg/"), if NULL default
+ *                      config directory is used.
+ * @param err           GError **
+ * @return              returns list of keys (and subkeys), or NULL if keyring 
is empty or an error occured.
+ */
+LrGpgKey *
+lr_gpg_list_keys(gboolean export_keys,
+                 const char *home_dir,
+                 GError **err);
+
+/** Get the next key from the list obtained from lr_gpg_list_keys.
+ * @param key           Input key.
+ * @return              returns next kye.
+ */
+const LrGpgKey *
+lr_gpg_key_get_next(const LrGpgKey *key);
+
+/** Get NULL terminated array of user IDs strings.
+ * @param key           Input key.
+ * @return              returns NULL terminated array of user IDs strings.
+ */
+char * const *
+lr_gpg_key_get_userids(const LrGpgKey *key);
+
+/** Get key in ASCII-Armor format (only if `key` was obtained from 
lr_gpg_list_keys with `export = TRUE`).
+ * @param key           Input key.
+ * @return              returns key in ACII-Armor format.
+ */
+const char *
+lr_gpg_key_get_raw_key(const LrGpgKey *key);
+
+/** Get a list of subkeys associated with the key. The first subkey is the 
primary key.
+ * @param key           Input key.
+ * @return              returns list of subkeys associated with the key.
+ */
+const LrGpgSubkey *
+lr_gpg_key_get_subkeys(const LrGpgKey *key);
+
+/** Release the list of keys obtained from lr_gpg_list_keys.
+ * @param key           Input array of keys.
+ */
+void
+lr_gpg_keys_free(LrGpgKey *key_array);
+
+/** Get the next subkey from the list obtained from lr_gpg_key_get_subkeys.
+ * @param key           Input subkey.
+ * @return              returns next subkye.
+ */
+const LrGpgSubkey *
+lr_gpg_subkey_get_next(const LrGpgSubkey *subkey);
+
+/** Get subkey ID.
+ * @param key           Input subkey.
+ * @return              returns key ID.
+ */
+const char *
+lr_gpg_subkey_get_id(const LrGpgSubkey *subkey);
+
+/** Get fingerprint of the subkey in hex digit form.
+ * @param key           Input subkey.
+ * @return              returns fingerprint of the subkey in hex digit form.
+ */
+const char *
+lr_gpg_subkey_get_fingerprint(const LrGpgSubkey *subkey);
+
+/** Get the creation timestamp.
+ * @param key           Input subkey.
+ * @return              returns Get the creation timestamp, -1 if invalid, 0 
if not available..
+ */
+long int
+lr_gpg_subkey_get_timestamp(const LrGpgSubkey *subkey);
+
+/** Get information if the subkey can be used for signing.
+ * @param key           Input subkey.
+ * @return              returns TRUE if subkey can be used for signing.
+ */
+gboolean
+lr_gpg_subkey_get_can_sign(const LrGpgSubkey *subkey);
+
 /** @} */
 
 G_END_DECLS
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/librepo-1.14.5/librepo/handle.c 
new/librepo-1.15.1/librepo/handle.c
--- old/librepo-1.14.5/librepo/handle.c 2022-09-09 12:32:23.000000000 +0200
+++ new/librepo-1.15.1/librepo/handle.c 2022-12-09 13:47:26.000000000 +0100
@@ -32,6 +32,8 @@
 #include <unistd.h>
 #include <sys/stat.h>
 #include <fcntl.h>
+#include <time.h>
+#include <gio/gio.h>
 
 
 #include "handle_internal.h"
@@ -264,6 +266,51 @@
     }
 }
 
+struct callback_data {
+    GMainLoop *loop; 
+    guint64 deadline_millis;
+    guint timeout_id; 
+    GNetworkMonitor *monitor;
+    GSocketConnectable *connectable; 
+    GCancellable *cancellable;
+};
+
+gboolean 
+timeout_callback(gpointer data)
+{ 
+    struct callback_data *dt = (struct callback_data*)data;
+
+    //if past deadline, exit
+    if (g_get_monotonic_time() >= dt->deadline_millis) {
+        g_main_loop_quit(dt->loop);
+        return G_SOURCE_REMOVE;
+    }
+
+    //if no internet, remove source
+    if (!g_network_monitor_get_network_available(dt->monitor)) {
+        return G_SOURCE_REMOVE;
+    }
+  
+    //if url reached, quit loop and remove source
+    if (g_network_monitor_can_reach (dt->monitor, dt->connectable, 
dt->cancellable, NULL)) {
+        g_main_loop_quit(dt->loop);
+        return G_SOURCE_REMOVE;
+    }
+
+    //Still waiting on the URL to be availbale, keep polling
+    return G_SOURCE_CONTINUE;
+}
+
+void 
+on_network_available(GObject *object, GParamSpec *pspec, gpointer data)
+{
+    struct callback_data *dt = (struct callback_data*)data;
+    if (dt->timeout_id == 0 && 
g_network_monitor_get_network_available(dt->monitor)) {
+        dt->timeout_id = g_timeout_add(200, timeout_callback, dt);
+    }
+    g_main_loop_run(dt->loop);
+}
+
 gboolean
 lr_handle_setopt(LrHandle *handle,
                  GError **err,
@@ -841,6 +888,60 @@
     return ret;
 }
 
+gboolean
+lr_handle_network_wait(LrHandle *handle, GError **err, guint seconds, 
GCancellable *cancellable)
+{
+    assert(!err || *err == NULL);
+
+    if (!handle) {
+        g_set_error(err, LR_HANDLE_ERROR, LRE_BADFUNCARG,
+                    "No handle specified");
+        return FALSE;
+    }
+
+    GNetworkMonitor *monitor = g_network_monitor_get_default();
+
+    struct callback_data data_struct;
+    data_struct.cancellable = cancellable;
+    data_struct.monitor = monitor;
+
+    const gchar *baseurl;
+    if (handle->metalinkurl)
+        baseurl = handle->metalinkurl;
+    else if (handle->mirrorlisturl)
+        baseurl = handle->mirrorlisturl;
+    else if (handle->urls)
+        baseurl = handle->urls[0];
+    assert(baseurl);
+
+    g_autoptr(GUri) uri = g_uri_parse(baseurl, G_URI_FLAGS_NONE, NULL);
+    if (uri == NULL) {
+        return FALSE;
+    }
+    const gchar* scheme = g_uri_get_scheme(uri);
+    if (!g_strcmp0(scheme, "file")) {
+        return TRUE; 
+    }
+    const gchar* host = g_uri_get_host(uri);
+    guint16 port = g_uri_get_port(uri);
+    GSocketConnectable *connectable =  g_network_address_new(host, port);
+    data_struct.connectable = connectable;
+    data_struct.deadline_millis = g_get_monotonic_time() + seconds * 
G_USEC_PER_SEC;
+    g_autoptr(GMainLoop) loop;
+    loop = g_main_loop_new(NULL, FALSE);
+    data_struct.loop = loop;
+    data_struct.timeout_id = 0; 
+
+    if (g_network_monitor_get_network_available(data_struct.monitor)) {
+        data_struct.timeout_id = g_timeout_add(200, timeout_callback, 
&data_struct);
+        g_main_loop_run(data_struct.loop);
+    }
+    else{
+        g_signal_connect(monitor, "notify::network-available", 
G_CALLBACK(on_network_available), &data_struct);    
+    }
+    return TRUE;
+}
+
 static gboolean
 lr_handle_prepare_urls(LrHandle *handle, GError **err)
 {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/librepo-1.14.5/librepo/handle.h 
new/librepo-1.15.1/librepo/handle.h
--- old/librepo-1.14.5/librepo/handle.h 2022-09-09 12:32:23.000000000 +0200
+++ new/librepo-1.15.1/librepo/handle.h 2022-12-09 13:47:26.000000000 +0100
@@ -22,6 +22,7 @@
 #define __LR_HANDLE_H__
 
 #include <glib.h>
+#include <gio/gio.h>
 
 #include "result.h"
 
@@ -560,6 +561,16 @@
 gboolean
 lr_handle_perform(LrHandle *handle, LrResult *result, GError **err);
 
+/** Handle waiting on network for LRO_URLS.
+ * @param handle        Librepo handle.
+ * @param seconds       Network timeout seconds 
+ * @param err           GError **
+ * @param cancellable   GCancellable *
+ * @return              TRUE if everything is ok, FALSE if err is set.
+ */
+gboolean
+lr_handle_network_wait(LrHandle *handle, GError **err, guint seconds, 
GCancellable *cancellable);
+
 /** @} */
 
 G_END_DECLS
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/librepo-1.14.5/librepo.spec 
new/librepo-1.15.1/librepo.spec
--- old/librepo-1.14.5/librepo.spec     2022-09-09 12:32:23.000000000 +0200
+++ new/librepo-1.15.1/librepo.spec     2022-12-09 13:47:26.000000000 +0100
@@ -11,11 +11,11 @@
 %global dnf_conflict 2.8.8
 
 Name:           librepo
-Version:        1.14.5
+Version:        1.15.1
 Release:        1%{?dist}
 Summary:        Repodata downloading library
 
-License:        LGPLv2+
+License:        LGPL-2.1-or-later
 URL:            https://github.com/rpm-software-management/librepo
 Source0:        %{url}/archive/%{version}/%{name}-%{version}.tar.gz
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/librepo-1.14.5/tests/test_gpg.c 
new/librepo-1.15.1/tests/test_gpg.c
--- old/librepo-1.14.5/tests/test_gpg.c 2022-09-09 12:32:23.000000000 +0200
+++ new/librepo-1.15.1/tests/test_gpg.c 2022-12-09 13:47:26.000000000 +0100
@@ -38,6 +38,7 @@
     _signature_path = lr_pathconcat(test_globals.testdata_dir,
                              "repo_yum_01/repodata/repomd.xml_bad.asc", NULL);
 
+    // Import the first key directly from the file
     ret = lr_gpg_import_key(key_path, tmp_home_path, &tmp_err);
     ck_assert(ret);
     ck_assert_ptr_null(tmp_err);
@@ -57,8 +58,7 @@
                                  &tmp_err);
     ck_assert(!ret);
     ck_assert_ptr_nonnull(tmp_err);
-    g_error_free(tmp_err);
-    tmp_err = NULL;
+    g_clear_error(&tmp_err);
 
     // Bad data
     ret = lr_gpg_check_signature(signature_path,
@@ -67,13 +67,19 @@
                                  &tmp_err);
     ck_assert(!ret);
     ck_assert_ptr_nonnull(tmp_err);
-    g_error_free(tmp_err);
-    tmp_err = NULL;
+    g_clear_error(&tmp_err);
+
+    // Load the second key into memory and import it from memory
+    gchar *contents;
+    gsize length;
+    ret = g_file_get_contents(_key_path, &contents, &length, &tmp_err);
+    ck_assert(ret);
+    ck_assert_ptr_null(tmp_err);
 
-    // Import the 2nd key
-    ret = lr_gpg_import_key(_key_path, tmp_home_path, &tmp_err);
+    ret = lr_gpg_import_key_from_memory(contents, length, tmp_home_path, 
&tmp_err);
     ck_assert(ret);
     ck_assert_ptr_null(tmp_err);
+    g_free(contents);
 
     // Valid key and data
     ret = lr_gpg_check_signature(_signature_path,
@@ -90,7 +96,7 @@
                                  &tmp_err);
     ck_assert(!ret);
     ck_assert_ptr_nonnull(tmp_err);
-    g_error_free(tmp_err);
+    g_clear_error(&tmp_err);
     tmp_err = NULL;
 
     // Bad data 2
@@ -100,7 +106,7 @@
                                  &tmp_err);
     ck_assert(!ret);
     ck_assert_ptr_nonnull(tmp_err);
-    g_error_free(tmp_err);
+    g_clear_error(&tmp_err);
     tmp_err = NULL;
 
     lr_remove_dir(tmp_home_path);
@@ -114,12 +120,117 @@
 }
 END_TEST
 
+START_TEST(test_gpg_check_key_export)
+{
+    gboolean ret;
+    char *key_path;
+    char *tmp_home_path;
+    GError *tmp_err = NULL;
+
+    tmp_home_path = lr_gettmpdir();
+    key_path = lr_pathconcat(test_globals.testdata_dir,
+                             "repo_yum_01/repodata/repomd.xml.key", NULL);
+
+    // Import the key from file descriptor
+    int key_fd = open(key_path, O_RDONLY);
+    ck_assert(key_fd != -1);
+    ret = lr_gpg_import_key_from_fd(key_fd, tmp_home_path, &tmp_err);
+    ck_assert(ret);
+    ck_assert_ptr_null(tmp_err);
+    ck_assert(close(key_fd) != -1);
+
+    // Export the keys
+    LrGpgKey *keys = lr_gpg_list_keys(TRUE, tmp_home_path, &tmp_err);
+    ck_assert_ptr_nonnull(keys);
+    ck_assert_ptr_null(tmp_err);
+
+    // Test key user ids
+    char * const *uids = lr_gpg_key_get_userids(keys);
+    ck_assert_ptr_nonnull(uids);
+    ck_assert(g_strcmp0(uids[0], "Tomas Mlcoch (test key) 
<tmlc...@redhat.com>") == 0);
+    ck_assert_ptr_null(uids[1]);
+
+    // Get subkeys
+    const LrGpgSubkey *subkeys = lr_gpg_key_get_subkeys(keys);
+    ck_assert_ptr_nonnull(subkeys);
+
+    // Test first subkey
+    const char *id = lr_gpg_subkey_get_id(subkeys);
+    ck_assert(g_strcmp0(id, "46AF958A22F2C4E9") == 0);
+    const char *fingerprint = lr_gpg_subkey_get_fingerprint(subkeys);
+    ck_assert(g_strcmp0(fingerprint, 
"55B80C4944D8938E94980B6D46AF958A22F2C4E9") == 0);
+    long timestamp = lr_gpg_subkey_get_timestamp(subkeys);
+    ck_assert(timestamp == 1347882156);
+    gboolean can_sign = lr_gpg_subkey_get_can_sign(subkeys);
+    ck_assert(can_sign);
+
+    // Get second subkey
+    subkeys = lr_gpg_subkey_get_next(subkeys);
+    ck_assert_ptr_nonnull(subkeys);
+
+    // Test second subkey
+    id = lr_gpg_subkey_get_id(subkeys);
+    ck_assert(g_strcmp0(id, "7AE6F6EF026AF38A") == 0);
+    fingerprint = lr_gpg_subkey_get_fingerprint(subkeys);
+    ck_assert(g_strcmp0(fingerprint, 
"D855DBAA43DB343EC4F214EB7AE6F6EF026AF38A") == 0);
+    timestamp = lr_gpg_subkey_get_timestamp(subkeys);
+    ck_assert(timestamp == 1347882156);
+    can_sign = lr_gpg_subkey_get_can_sign(subkeys);
+    ck_assert(!can_sign);
+
+    // There are no other subkeys for the key
+    subkeys = lr_gpg_subkey_get_next(subkeys);
+    ck_assert_ptr_null(subkeys);
+
+    // Test exported raw key
+    const char *raw_key = lr_gpg_key_get_raw_key(keys);
+    ck_assert_ptr_nonnull(raw_key);
+    ck_assert(strstr(raw_key,
+                     
"mQENBFBXDKwBCADA5jpCwpb/JKOG8mcFyIanNojDwpHwKoyjGNpZNPNUDJguvkRa\n"
+                     
"IO3NdoyXYd5QVTOsnyKBaRaiLLJWI/VJxTOT3fwOPprrzUlkHwoWl+sYuSdXHASu\n"
+                     
"m4lkBiXHsa5oiXPdrY6hoh5vsF8ASwCHXOwpR9yyvGEaUUMBl2GpJAX/cGVcL4Dy\n"
+                     
"Z0pyJMLO4qrIPoX+wd1ZSFSc8JcAC4UtA82HCGTmesgialpwKdoQyt+em94oIM1f\n"
+                     
"D6v7zzcRX/zLKKEzpFnU458WBA+JACkde3ohX//0fDCeaLqMzs++FCgwm/HMCszw\n"
+                     
"RnINr+K8ENfMYBoeM7a7tnhiae+rkxWmvWz/ABEBAAG0LFRvbWFzIE1sY29jaCAo\n"
+                     
"dGVzdCBrZXkpIDx0bWxjb2NoQHJlZGhhdC5jb20+iQE4BBMBAgAiBQJQVwysAhsD\n"
+                     
"BgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRBGr5WKIvLE6fdyB/9OzDczaqGy\n"
+                     
"1rzk7Wp2C9S5QatFUFNWt6FIFPITbixT4jrDo/LyUJVWLw4ng7ldg79vmrzhpP8h\n"
+                     
"yFVvuvGvSEMn5sgnZ83SEd4vRJ2O8K5RuVs5Kcj7ayLlxPpqbYOYmrmTaLwYTwdv\n"
+                     
"0wDnNU9IkkMSK752RQes4J+4XGikd8CNm5lw8cRQ7bcQd8s2rnCoiyGt7PIdl13z\n"
+                     
"8hO9KA52iUP06AbbIusbQ1jzsViEny+xQH7SZ53Ga4eRr0mW2iA20Mkp4Ieb+dNo\n"
+                     
"47Q8aHUqI9O4HTF/3Fzt7KmNxXCpCOhxTWx0IkqPGoZ0W63Aut/CVh0LXsBF2TUD\n"
+                     
"Ym9P/IjRgJLhuQENBFBXDKwBCACvhlMcgjLJ4PtMTtauF1OXVTfODQSHo+qwKt4S\n"
+                     
"GyDlTGayQ76pOqYkkzIRqmNYl1ThmcfzpmJ3O+hFQQ7OdguYcfkbfgIMjEJEbKG3\n"
+                     
"wsR5pm9zjwStzYHedwkct1nyROgBz70o16FfdiWOguw58jQZOSO/I2S3JpLsLgI8\n"
+                     
"KqdIk/0WuoOfzt+KcvL52lX94O2hBpRI0v6lDgSm7KkPGQrVFnSIUR5r73ceageL\n"
+                     
"5LmGm1TlEjWHwA9iYIvBcjnE26/l8u58IYQ/sUmn0u4jBcBNc7iqdWvlSLZLlMmi\n"
+                     
"qnzDNhUup9neKGxgr4hGAblxiSxXlmOoFv0jEW81b4VximSJABEBAAGJAR8EGAEC\n"
+                     
"AAkFAlBXDKwCGwwACgkQRq+ViiLyxOnf6Qf+PIG//12qp3hXZsvB7JQuQ4nUNwp6\n"
+                     
"Ufm6W9pFm3DOqnI9H9ZNzGbkoS5WwRp0B1NLfNKipQVORnDs6qve298ReRrmLKnk\n"
+                     
"BPZqxFpPqLQ6X83Or2bqKiJS1axonIgqkImFLfxxqKoukvhn328Z2FVlrvkKSMU8\n"
+                     
"eHi/iDF/TCHoPE9WtnVSzsNU9i+9j8j//GO+bMC5AGNOxcBKlChFpLYpE/pfITL/\n"
+                     
"icS7wB9MrMLNvjlN1EKszQFxJrFVBGTt8hUqRH3CCUFRwbpE1QJ1WAzJ0Vzk5nWR\n"
+                     
"rVZQiiLe03B8hC7/qRiB4bya5nbWcwe9ltPFja4/tTe92ivScFfCLyALVQ==\n"
+                     "=G300") != NULL);
+
+    // Only one key is in keyring
+    ck_assert_ptr_null(lr_gpg_key_get_next(keys));
+
+    lr_gpg_keys_free(keys);
+    lr_remove_dir(tmp_home_path);
+    lr_free(key_path);
+    g_free(tmp_home_path);
+}
+END_TEST
+
+
 Suite *
 gpg_suite(void)
 {
     Suite *s = suite_create("gpg");
     TCase *tc = tcase_create("Main");
     tcase_add_test(tc, test_gpg_check_signature);
+    tcase_add_test(tc, test_gpg_check_key_export);
     suite_add_tcase(s, tc);
     return s;
 }

Reply via email to