Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-xmlsec for openSUSE:Factory 
checked in at 2022-05-12 23:00:23
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-xmlsec (Old)
 and      /work/SRC/openSUSE:Factory/.python-xmlsec.new.1538 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-xmlsec"

Thu May 12 23:00:23 2022 rev:6 rq:976452 version:1.3.12

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-xmlsec/python-xmlsec.changes      
2021-06-11 00:19:24.193392967 +0200
+++ /work/SRC/openSUSE:Factory/.python-xmlsec.new.1538/python-xmlsec.changes    
2022-05-12 23:00:48.528832929 +0200
@@ -1,0 +2,14 @@
+Wed May 11 12:39:21 UTC 2022 - Matej Cepl <[email protected]>
+
+- Update to 1.3.12:
+  - Added support for registering custom xmlsec IO callbacks
+  - Added support for building without MD5 transforms
+  - Added support for PEP 539 for Python 3.7 and newer
+  - Using lxml-stubs package instead of custom LXML stubs
+- Add avoid_lxml_tests_failing.patch (help working
+  around the lxml issue lp#1880251 and lp#1887848, from
+  gh#mehcode/python-xmlsec#84).
+- Switch off building on Python 3.10 and %ix86
+  (gh#mehcode/python-xmlsec#204).
+
+-------------------------------------------------------------------

Old:
----
  xmlsec-1.3.11.tar.gz

New:
----
  avoid_lxml_tests_failing.patch
  xmlsec-1.3.12.tar.gz

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

Other differences:
------------------
++++++ python-xmlsec.spec ++++++
--- /var/tmp/diff_new_pack.0jiwT2/_old  2022-05-12 23:00:49.380834073 +0200
+++ /var/tmp/diff_new_pack.0jiwT2/_new  2022-05-12 23:00:49.388834084 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-xmlsec
 #
-# Copyright (c) 2021 SUSE LLC
+# Copyright (c) 2022 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -17,22 +17,29 @@
 
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
+# gh#mehcode/python-xmlsec#204 and gh#mehcode/python-xmlsec#210
+%define skip_python310 1
 Name:           python-xmlsec
-Version:        1.3.11
+Version:        1.3.12
 Release:        0
 Summary:        Python bindings for the XML Security Library
 License:        MIT
 URL:            https://github.com/mehcode/python-xmlsec
 Source:         
https://files.pythonhosted.org/packages/source/x/xmlsec/xmlsec-%{version}.tar.gz
+# PATCH-FIX-UPSTREAM avoid_lxml_tests_failing.patch 
gh#mehcode/python-xmlsec#84 [email protected]
+# work around the lxml issue
+Patch0:         avoid_lxml_tests_failing.patch
 BuildRequires:  %{python_module devel}
 BuildRequires:  %{python_module hypothesis}
 BuildRequires:  %{python_module lxml >= 3.0}
 BuildRequires:  %{python_module lxml-devel}
+BuildRequires:  %{python_module pip}
 BuildRequires:  %{python_module pkgconfig}
 BuildRequires:  %{python_module pytest}
 BuildRequires:  %{python_module setuptools_scm}
 BuildRequires:  %{python_module setuptools}
 BuildRequires:  %{python_module toml}
+BuildRequires:  %{python_module wheel}
 BuildRequires:  fdupes
 BuildRequires:  libtool
 BuildRequires:  pkgconfig
@@ -50,23 +57,33 @@
 Python bindings for the XML Security Library
 
 %prep
-%setup -q -n xmlsec-%{version}
+%autosetup -p1 -n xmlsec-%{version}
 
 %build
 export CFLAGS="%{optflags}"
-%python_build
+%pyproject_wheel
 
 %install
-%python_install
+%pyproject_install
 %python_expand %fdupes %{buildroot}%{$python_sitearch}
 
 %check
-# Tests coredump gh#mehcode/python-xmlsec#183
 # %%pytest_arch tests/
+%ifarch %ix86
+export skip_tests="not test_reinitialize_module"
+%else
+export skip_tests=""
+%endif
+%{python_expand export PYTHONPATH=%{buildroot}%{$python_sitearch} 
PYTHONDONTWRITEBYTECODE=1
+rm -rf .hypothesis/ .pytest_cache/
+$python -mpytest --ignore=_build.python39 --ignore=_build.python310 
--ignore=_build.python38 -v -k "$skip_tests" tests/
+}
 
 %files %{python_files}
 %doc README.rst
 %license LICENSE
-%{python_sitearch}/*
+%{python_sitearch}/xmlsec
+%{python_sitearch}/xmlsec-%{version}*-info
+%{python_sitearch}/xmlsec*.so
 
 %changelog

++++++ avoid_lxml_tests_failing.patch ++++++
---
 tests/base.py              |    1 +
 tests/test_doc_examples.py |    2 ++
 2 files changed, 3 insertions(+)

--- a/tests/base.py
+++ b/tests/base.py
@@ -99,6 +99,7 @@ class TestMemoryLeaks(unittest.TestCase)
 
     def load_xml(self, name, xpath=None):
         """returns xml.etree"""
+        etree.set_default_parser(parser=etree.XMLParser())
         root = etree.parse(self.path(name)).getroot()
         if xpath is None:
             return root
--- a/tests/test_doc_examples.py
+++ b/tests/test_doc_examples.py
@@ -42,3 +42,5 @@ def test_doc_example(example):
     """
     with cd(example.parent):
         runpy.run_path(str(example))
+    from lxml import etree
+    etree.set_default_parser(parser=etree.XMLParser())

++++++ xmlsec-1.3.11.tar.gz -> xmlsec-1.3.12.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlsec-1.3.11/PKG-INFO new/xmlsec-1.3.12/PKG-INFO
--- old/xmlsec-1.3.11/PKG-INFO  2021-05-28 23:18:26.535471700 +0200
+++ new/xmlsec-1.3.12/PKG-INFO  2021-09-05 18:54:29.396132200 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: xmlsec
-Version: 1.3.11
+Version: 1.3.12
 Summary: Python bindings for the XML Security Library
 Home-page: https://github.com/mehcode/python-xmlsec
 Author: Bulat Gaifullin
@@ -10,6 +10,7 @@
 License: MIT
 Project-URL: Documentation, https://xmlsec.readthedocs.io
 Project-URL: Source, https://github.com/mehcode/python-xmlsec
+Project-URL: Changelog, https://github.com/mehcode/python-xmlsec/releases
 Keywords: xmlsec
 Platform: UNKNOWN
 Classifier: Development Status :: 5 - Production/Stable
@@ -84,7 +85,7 @@
 
 .. code-block:: bash
 
-   apt-get install libxml2-dev libxmlsec1-dev libxmlsec1-openssl
+   apt-get install pkg-config libxml2-dev libxmlsec1-dev libxmlsec1-openssl
 
 
 Note: There is no required version of LibXML2 for Ubuntu Precise,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlsec-1.3.11/README.rst new/xmlsec-1.3.12/README.rst
--- old/xmlsec-1.3.11/README.rst        2021-05-28 23:18:21.000000000 +0200
+++ new/xmlsec-1.3.12/README.rst        2021-09-05 18:54:21.000000000 +0200
@@ -53,7 +53,7 @@
 
 .. code-block:: bash
 
-   apt-get install libxml2-dev libxmlsec1-dev libxmlsec1-openssl
+   apt-get install pkg-config libxml2-dev libxmlsec1-dev libxmlsec1-openssl
 
 
 Note: There is no required version of LibXML2 for Ubuntu Precise,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlsec-1.3.11/mypy.ini new/xmlsec-1.3.12/mypy.ini
--- old/xmlsec-1.3.11/mypy.ini  2021-05-28 23:18:21.000000000 +0200
+++ new/xmlsec-1.3.12/mypy.ini  2021-09-05 18:54:21.000000000 +0200
@@ -1,6 +1,5 @@
 [mypy]
 files = src
-mypy_path = typeshed/
 ignore_missing_imports = False
 warn_unused_configs = True
 disallow_subclassing_any = True
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlsec-1.3.11/setup.cfg new/xmlsec-1.3.12/setup.cfg
--- old/xmlsec-1.3.11/setup.cfg 2021-05-28 23:18:26.535471700 +0200
+++ new/xmlsec-1.3.12/setup.cfg 2021-09-05 18:54:29.396132200 +0200
@@ -1,9 +1,9 @@
 [metadata]
-description-file = README.rst
+description_file = README.rst
 
 [bdist_rpm]
 release = 1
-build-requires = pkg-config xmlsec1-devel libxml2-devel xmlsec1-openssl-devel
+build_requires = pkg-config xmlsec1-devel libxml2-devel xmlsec1-openssl-devel
 group = Development/Libraries
 requires = xmlsec1 xmlsec1-openssl
 
@@ -13,7 +13,7 @@
 all_files = 1
 
 [upload_docs]
-upload-dir = doc/build/html
+upload_dir = doc/build/html
 
 [flake8]
 max-line-length = 130
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlsec-1.3.11/setup.py new/xmlsec-1.3.12/setup.py
--- old/xmlsec-1.3.11/setup.py  2021-05-28 23:18:21.000000000 +0200
+++ new/xmlsec-1.3.12/setup.py  2021-09-05 18:54:21.000000000 +0200
@@ -415,6 +415,7 @@
     project_urls={
         'Documentation': 'https://xmlsec.readthedocs.io',
         'Source': 'https://github.com/mehcode/python-xmlsec',
+        'Changelog': 'https://github.com/mehcode/python-xmlsec/releases',
     },
     license='MIT',
     keywords=['xmlsec'],
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlsec-1.3.11/src/constants.c 
new/xmlsec-1.3.12/src/constants.c
--- old/xmlsec-1.3.11/src/constants.c   2021-05-28 23:18:21.000000000 +0200
+++ new/xmlsec-1.3.12/src/constants.c   2021-09-05 18:54:21.000000000 +0200
@@ -507,7 +507,10 @@
     PYXMLSEC_ADD_TRANSFORM_CONSTANT(TransformEcdsaSha512, "ECDSA_SHA512");
 #endif
 
+#ifndef XMLSEC_NO_MD5
     PYXMLSEC_ADD_TRANSFORM_CONSTANT(TransformHmacMd5, "HMAC_MD5");
+#endif
+
 #ifndef XMLSEC_NO_RIPEMD160
     PYXMLSEC_ADD_TRANSFORM_CONSTANT(TransformHmacRipemd160, "HMAC_RIPEMD160");
 #endif
@@ -517,7 +520,10 @@
     PYXMLSEC_ADD_TRANSFORM_CONSTANT(TransformHmacSha384, "HMAC_SHA384");
     PYXMLSEC_ADD_TRANSFORM_CONSTANT(TransformHmacSha512, "HMAC_SHA512");
 
+#ifndef XMLSEC_NO_MD5
     PYXMLSEC_ADD_TRANSFORM_CONSTANT(TransformRsaMd5, "RSA_MD5");
+#endif
+
 #ifndef XMLSEC_NO_RIPEMD160
     PYXMLSEC_ADD_TRANSFORM_CONSTANT(TransformRsaRipemd160, "RSA_RIPEMD160");
 #endif
@@ -529,7 +535,10 @@
     PYXMLSEC_ADD_TRANSFORM_CONSTANT(TransformRsaPkcs1, "RSA_PKCS1");
     PYXMLSEC_ADD_TRANSFORM_CONSTANT(TransformRsaOaep, "RSA_OAEP");
 
+#ifndef XMLSEC_NO_MD5
     PYXMLSEC_ADD_TRANSFORM_CONSTANT(TransformMd5, "MD5");
+#endif
+
 #ifndef XMLSEC_NO_RIPEMD160
     PYXMLSEC_ADD_TRANSFORM_CONSTANT(TransformRipemd160, "RIPEMD160");
 #endif
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlsec-1.3.11/src/exception.c 
new/xmlsec-1.3.12/src/exception.c
--- old/xmlsec-1.3.11/src/exception.c   2021-05-28 23:18:21.000000000 +0200
+++ new/xmlsec-1.3.12/src/exception.c   2021-09-05 18:54:21.000000000 +0200
@@ -23,7 +23,11 @@
 PyObject* PyXmlSec_InternalError;
 PyObject* PyXmlSec_VerificationError;
 
+#if PY_MINOR_VERSION >= 7
+static Py_tss_t PyXmlSec_LastErrorKey;
+#else
 static int PyXmlSec_LastErrorKey = 0;
+#endif
 
 static int PyXmlSec_PrintErrorMessage = 0;
 
@@ -71,16 +75,26 @@
     PyXmlSec_ErrorHolder* v;
     int r;
 
+    #if PY_MINOR_VERSION >= 7
+    if (PyThread_tss_is_created(&PyXmlSec_LastErrorKey) == 0) {
+    #else
     if (PyXmlSec_LastErrorKey == 0) {
+    #endif
         PYXMLSEC_DEBUG("WARNING: There is no error key.");
         PyXmlSec_ErrorHolderFree(e);
         return NULL;
     }
 
     // get_key_value and set_key_value are gil free
+    #if PY_MINOR_VERSION >= 7
+    v = (PyXmlSec_ErrorHolder*)PyThread_tss_get(&PyXmlSec_LastErrorKey);
+    //PyThread_tss_delete(&PyXmlSec_LastErrorKey);
+    r = PyThread_tss_set(&PyXmlSec_LastErrorKey, (void*)e);
+    #else
     v = (PyXmlSec_ErrorHolder*)PyThread_get_key_value(PyXmlSec_LastErrorKey);
     PyThread_delete_key_value(PyXmlSec_LastErrorKey);
     r = PyThread_set_key_value(PyXmlSec_LastErrorKey, (void*)e);
+    #endif
     PYXMLSEC_DEBUGF("set_key_value returns %d", r);
     return v;
 }
@@ -166,7 +180,11 @@
 }
 
 void PyXmlSec_InstallErrorCallback() {
+    #if PY_MINOR_VERSION >= 7
+    if (PyThread_tss_is_created(&PyXmlSec_LastErrorKey) != 0) {
+    #else
     if (PyXmlSec_LastErrorKey != 0) {
+    #endif
         xmlSecErrorsSetCallback(PyXmlSec_ErrorCallback);
     }
 }
@@ -190,8 +208,14 @@
     if (PyModule_AddObject(package, "InternalError", PyXmlSec_InternalError) < 
0) goto ON_FAIL;
     if (PyModule_AddObject(package, "VerificationError", 
PyXmlSec_VerificationError) < 0) goto ON_FAIL;
 
+    #if PY_MINOR_VERSION >= 7
+    if (PyThread_tss_create(&PyXmlSec_LastErrorKey) == 0) {
+        PyXmlSec_InstallErrorCallback();
+    }
+    #else
     PyXmlSec_LastErrorKey = PyThread_create_key();
     PyXmlSec_InstallErrorCallback();
+    #endif
 
     return 0;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlsec-1.3.11/src/keys.c new/xmlsec-1.3.12/src/keys.c
--- old/xmlsec-1.3.11/src/keys.c        2021-05-28 23:18:21.000000000 +0200
+++ new/xmlsec-1.3.12/src/keys.c        2021-09-05 18:54:21.000000000 +0200
@@ -453,7 +453,7 @@
     }
 
     if (value == NULL) {
-        if (xmlSecKeySetName(key->handle, value) < 0) {
+        if (xmlSecKeySetName(key->handle, NULL) < 0) {
             PyXmlSec_SetLastError("cannot delete name");
             return -1;
         }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlsec-1.3.11/src/main.c new/xmlsec-1.3.12/src/main.c
--- old/xmlsec-1.3.11/src/main.c        2021-05-28 23:18:21.000000000 +0200
+++ new/xmlsec-1.3.12/src/main.c        2021-09-05 18:54:21.000000000 +0200
@@ -15,6 +15,7 @@
 #include <xmlsec/crypto.h>
 #include <xmlsec/errors.h>
 #include <xmlsec/base64.h>
+#include <xmlsec/io.h>
 
 #define _PYXMLSEC_FREE_NONE 0
 #define _PYXMLSEC_FREE_XMLSEC 1
@@ -87,6 +88,11 @@
         PyXmlSec_Free(_PYXMLSEC_FREE_ALL);
         return -1;
     }
+    // xmlsec will install default callback in xmlSecCryptoInit,
+    // overwriting any custom callbacks.
+    // We thus reinstall our callback now.
+    PyXmlSec_InstallErrorCallback();
+
     free_mode = _PYXMLSEC_FREE_ALL;
     return 0;
 }
@@ -128,6 +134,211 @@
     Py_RETURN_NONE;
 }
 
+// NB: This whole thing assumes that the `xmlsec` callbacks are not re-entrant
+// (i.e. that xmlsec won't come across a link in the reference it's processing
+// and try to open that with these callbacks too).
+typedef struct CbList {
+  PyObject* match_cb;
+  PyObject* open_cb;
+  PyObject* read_cb;
+  PyObject* close_cb;
+  struct CbList* next;
+} CbList;
+
+static CbList* registered_callbacks = NULL;
+
+static void RCBListCons(CbList* cb_list_item) {
+    cb_list_item->next = registered_callbacks;
+    registered_callbacks = cb_list_item;
+}
+
+static void RCBListClear() {
+    CbList* cb_list_item = registered_callbacks;
+    while (cb_list_item) {
+        Py_DECREF(cb_list_item->match_cb);
+        Py_DECREF(cb_list_item->open_cb);
+        Py_DECREF(cb_list_item->read_cb);
+        Py_DECREF(cb_list_item->close_cb);
+        CbList* next = cb_list_item->next;
+        free(cb_list_item);
+        cb_list_item = next;
+    }
+    registered_callbacks = NULL;
+}
+
+// The currently executing set of Python callbacks:
+static CbList* cur_cb_list_item;
+
+static int PyXmlSec_MatchCB(const char* filename) {
+    cur_cb_list_item = registered_callbacks;
+    PyGILState_STATE state = PyGILState_Ensure();
+    PyObject* args = Py_BuildValue("(y)", filename);
+    while (cur_cb_list_item) {
+        PyObject* result = PyObject_CallObject(cur_cb_list_item->match_cb, 
args);
+        if (result && PyObject_IsTrue(result)) {
+            Py_DECREF(result);
+            Py_DECREF(args);
+            PyGILState_Release(state);
+            return 1;
+        }
+        Py_XDECREF(result);
+        cur_cb_list_item = cur_cb_list_item->next;
+    }
+    Py_DECREF(args);
+    PyGILState_Release(state);
+    return 0;
+}
+
+static void* PyXmlSec_OpenCB(const char* filename) {
+    PyGILState_STATE state = PyGILState_Ensure();
+
+    // NB: Assumes the match callback left the current callback list item in 
the
+    // right place:
+    PyObject* args = Py_BuildValue("(y)", filename);
+    PyObject* result = PyObject_CallObject(cur_cb_list_item->open_cb, args);
+    Py_DECREF(args);
+
+    PyGILState_Release(state);
+    return result;
+}
+
+static int PyXmlSec_ReadCB(void* context, char* buffer, int len) {
+    PyGILState_STATE state = PyGILState_Ensure();
+
+    // NB: Assumes the match callback left the current callback list item in 
the
+    // right place:
+    PyObject* py_buffer = PyMemoryView_FromMemory(buffer, (Py_ssize_t) len, 
PyBUF_WRITE);
+    PyObject* args = Py_BuildValue("(OO)", context, py_buffer);
+    PyObject* py_bytes_read = PyObject_CallObject(cur_cb_list_item->read_cb, 
args);
+    Py_DECREF(args);
+    Py_DECREF(py_buffer);
+    int result;
+    if (py_bytes_read && PyLong_Check(py_bytes_read)) {
+        result = (int)PyLong_AsLong(py_bytes_read);
+    } else {
+        result = EOF;
+    }
+    Py_XDECREF(py_bytes_read);
+
+    PyGILState_Release(state);
+    return result;
+}
+
+static int PyXmlSec_CloseCB(void* context) {
+    PyGILState_STATE state = PyGILState_Ensure();
+
+    PyObject* args = Py_BuildValue("(O)", context);
+    PyObject* result = PyObject_CallObject(cur_cb_list_item->close_cb, args);
+    Py_DECREF(args);
+    Py_DECREF(context);
+    Py_DECREF(result);
+
+    PyGILState_Release(state);
+    return 0;
+}
+
+static char PyXmlSec_PyIOCleanupCallbacks__doc__[] = \
+    "Unregister globally all sets of IO callbacks from xmlsec.";
+static PyObject* PyXmlSec_PyIOCleanupCallbacks(PyObject *self) {
+    xmlSecIOCleanupCallbacks();
+    // We always have callbacks registered to delegate to any Python callbacks
+    // we have registered within these bindings:
+    if (xmlSecIORegisterCallbacks(
+            PyXmlSec_MatchCB, PyXmlSec_OpenCB, PyXmlSec_ReadCB,
+            PyXmlSec_CloseCB) < 0) {
+        return NULL;
+    }
+    RCBListClear();
+    Py_RETURN_NONE;
+}
+
+static char PyXmlSec_PyIORegisterDefaultCallbacks__doc__[] = \
+    "Register globally xmlsec's own default set of IO callbacks.";
+static PyObject* PyXmlSec_PyIORegisterDefaultCallbacks(PyObject *self) {
+    // NB: The default callbacks (specifically libxml2's `xmlFileMatch`) always
+    // match, and callbacks are called in the reverse order to that which they
+    // were added. So, there's no point in holding onto any previously 
registered
+    // callbacks, because they will never be run:
+    xmlSecIOCleanupCallbacks();
+    RCBListClear();
+    if (xmlSecIORegisterDefaultCallbacks() < 0) {
+        return NULL;
+    }
+    // We need to make sure we can continue trying to match any newly added
+    // Python callbacks:
+    if (xmlSecIORegisterCallbacks(
+            PyXmlSec_MatchCB, PyXmlSec_OpenCB, PyXmlSec_ReadCB,
+            PyXmlSec_CloseCB) < 0) {
+        return NULL;
+    };
+    Py_RETURN_NONE;
+}
+
+static char PyXmlSec_PyIORegisterCallbacks__doc__[] = \
+    "Register globally a custom set of IO callbacks with xmlsec.\n\n"
+    ":param callable input_match_callback: A callable that takes a filename 
`bytestring` and "
+    "returns a boolean as to whether the other callbacks in this set can 
handle that name.\n"
+    ":param callable input_open_callback: A callable that takes a filename and 
returns some "
+    "context object (e.g. a file object) that the remaining callables in this 
set will be passed "
+    "during handling.\n"
+    // FIXME: How do we handle failures in ^^ (e.g. can't find the file)?
+    ":param callable input_read_callback: A callable that that takes the 
context object from the "
+    "open callback and a buffer, and should fill the buffer with data (e.g. 
BytesIO.readinto()). "
+    "xmlsec will call this function several times until there is no more data 
returned.\n"
+    ":param callable input_close_callback: A callable that takes the context 
object from the "
+    "open callback and can do any resource cleanup necessary.\n"
+    ;
+static PyObject* PyXmlSec_PyIORegisterCallbacks(PyObject *self, PyObject 
*args, PyObject *kwargs) {
+    static char *kwlist[] = {
+        "input_match_callback",
+        "input_open_callback",
+        "input_read_callback",
+        "input_close_callback",
+        NULL
+    };
+    CbList* cb_list_item = malloc(sizeof(CbList));
+    if (cb_list_item == NULL) {
+      return NULL;
+    }
+    if (!PyArg_ParseTupleAndKeywords(
+            args, kwargs, "OOOO:register_callbacks", kwlist,
+            &cb_list_item->match_cb, &cb_list_item->open_cb, 
&cb_list_item->read_cb,
+            &cb_list_item->close_cb)) {
+        free(cb_list_item);
+        return NULL;
+    }
+    if (!PyCallable_Check(cb_list_item->match_cb)) {
+        PyErr_SetString(PyExc_TypeError, "input_match_callback must be a 
callable");
+        free(cb_list_item);
+        return NULL;
+    }
+    if (!PyCallable_Check(cb_list_item->open_cb)) {
+        PyErr_SetString(PyExc_TypeError, "input_open_callback must be a 
callable");
+        free(cb_list_item);
+        return NULL;
+    }
+    if (!PyCallable_Check(cb_list_item->read_cb)) {
+        PyErr_SetString(PyExc_TypeError, "input_read_callback must be a 
callable");
+        free(cb_list_item);
+        return NULL;
+    }
+    if (!PyCallable_Check(cb_list_item->close_cb)) {
+        PyErr_SetString(PyExc_TypeError, "input_close_callback must be a 
callable");
+        free(cb_list_item);
+        return NULL;
+    }
+    Py_INCREF(cb_list_item->match_cb);
+    Py_INCREF(cb_list_item->open_cb);
+    Py_INCREF(cb_list_item->read_cb);
+    Py_INCREF(cb_list_item->close_cb);
+    cb_list_item->next = NULL;
+    RCBListCons(cb_list_item);
+    // NB: We don't need to register the callbacks with `xmlsec` here, because
+    // we've already registered our helper functions that will trawl through 
our
+    // list of callbacks.
+    Py_RETURN_NONE;
+}
+
 static char PyXmlSec_PyBase64DefaultLineSize__doc__[] = \
     "base64_default_line_size(size = None)\n"
     "Configures the default maximum columns size for base64 encoding.\n\n"
@@ -177,6 +388,24 @@
         PyXmlSec_PyEnableDebugOutput__doc__
     },
     {
+        "cleanup_callbacks",
+        (PyCFunction)PyXmlSec_PyIOCleanupCallbacks,
+        METH_NOARGS,
+        PyXmlSec_PyIOCleanupCallbacks__doc__
+    },
+    {
+        "register_default_callbacks",
+        (PyCFunction)PyXmlSec_PyIORegisterDefaultCallbacks,
+        METH_NOARGS,
+        PyXmlSec_PyIORegisterDefaultCallbacks__doc__
+    },
+    {
+        "register_callbacks",
+        (PyCFunction)PyXmlSec_PyIORegisterCallbacks,
+        METH_VARARGS|METH_KEYWORDS,
+        PyXmlSec_PyIORegisterCallbacks__doc__
+    },
+    {
         "base64_default_line_size",
         (PyCFunction)PyXmlSec_PyBase64DefaultLineSize,
         METH_VARARGS|METH_KEYWORDS,
@@ -239,11 +468,6 @@
 
     if (PyXmlSec_Init() < 0) goto ON_FAIL;
 
-    // xmlsec will install default callback in PyXmlSec_Init,
-    // overwriting any custom callbacks.
-    // We thus install our callback again now.
-    PyXmlSec_InstallErrorCallback();
-
     if (PyModule_AddStringConstant(module, "__version__", 
STRINGIFY(MODULE_VERSION)) < 0) goto ON_FAIL;
 
     if (PyXmlSec_InitLxmlModule() < 0) goto ON_FAIL;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlsec-1.3.11/src/xmlsec/__init__.pyi 
new/xmlsec-1.3.12/src/xmlsec/__init__.pyi
--- old/xmlsec-1.3.11/src/xmlsec/__init__.pyi   2021-05-28 23:18:21.000000000 
+0200
+++ new/xmlsec-1.3.12/src/xmlsec/__init__.pyi   2021-09-05 18:54:21.000000000 
+0200
@@ -1,5 +1,7 @@
 import sys
-from typing import AnyStr, IO, Iterable, Optional, Type, TypeVar, Union, 
overload
+from typing import (
+    Any, AnyStr, Callable, IO, Iterable, Optional, Type, TypeVar, Union,
+    overload)
 
 from lxml.etree import _Element
 
@@ -24,6 +26,14 @@
 def enable_debug_trace(enabled: bool = ...) -> None: ...
 def init() -> None: ...
 def shutdown() -> None: ...
+def cleanup_callbacks() -> None: ...
+def register_default_callbacks() -> None: ...
+def register_callbacks(
+        input_match_callback: Callable[[bytes], bool],
+        input_open_callback: Callable[[bytes], Any],
+        input_read_callback: Callable[[Any, memoryview], int],
+        input_close_callback: Callable[[Any], None],
+) -> None: ...
 @overload
 def base64_default_line_size() -> int: ...
 @overload
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlsec-1.3.11/src/xmlsec/constants.pyi 
new/xmlsec-1.3.12/src/xmlsec/constants.pyi
--- old/xmlsec-1.3.11/src/xmlsec/constants.pyi  2021-05-28 23:18:21.000000000 
+0200
+++ new/xmlsec-1.3.12/src/xmlsec/constants.pyi  2021-09-05 18:54:21.000000000 
+0200
@@ -1,5 +1,5 @@
 import sys
-from typing import NamedTuple
+from typing import NamedTuple, Optional
 
 if sys.version_info >= (3, 8):
     from typing import Final
@@ -7,12 +7,12 @@
     from typing_extensions import Final
 
 class __KeyData(NamedTuple):  # __KeyData type
-    href: str
     name: str
+    href: Optional[str]
 
 class __Transform(NamedTuple):  # __Transform type
-    href: str
     name: str
+    href: Optional[str]
     usage: int
 
 DSigNs: Final = 'http://www.w3.org/2000/09/xmldsig#'
@@ -109,7 +109,9 @@
     'c14n11-with-comments', 
'http://www.w3.org/2006/12/xml-c14n11#WithComments', 3
 )
 TransformInclC14NWithComments: Final = __Transform(
-    'c14n-with-comments', 
'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments', 3
+    'c14n-with-comments',
+    'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments',
+    3,
 )
 TransformKWAes128: Final = __Transform('kw-aes128', 
'http://www.w3.org/2001/04/xmlenc#kw-aes128', 16)
 TransformKWAes192: Final = __Transform('kw-aes192', 
'http://www.w3.org/2001/04/xmlenc#kw-aes192', 16)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlsec-1.3.11/src/xmlsec.egg-info/PKG-INFO 
new/xmlsec-1.3.12/src/xmlsec.egg-info/PKG-INFO
--- old/xmlsec-1.3.11/src/xmlsec.egg-info/PKG-INFO      2021-05-28 
23:18:26.000000000 +0200
+++ new/xmlsec-1.3.12/src/xmlsec.egg-info/PKG-INFO      2021-09-05 
18:54:29.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: xmlsec
-Version: 1.3.11
+Version: 1.3.12
 Summary: Python bindings for the XML Security Library
 Home-page: https://github.com/mehcode/python-xmlsec
 Author: Bulat Gaifullin
@@ -10,6 +10,7 @@
 License: MIT
 Project-URL: Documentation, https://xmlsec.readthedocs.io
 Project-URL: Source, https://github.com/mehcode/python-xmlsec
+Project-URL: Changelog, https://github.com/mehcode/python-xmlsec/releases
 Keywords: xmlsec
 Platform: UNKNOWN
 Classifier: Development Status :: 5 - Production/Stable
@@ -84,7 +85,7 @@
 
 .. code-block:: bash
 
-   apt-get install libxml2-dev libxmlsec1-dev libxmlsec1-openssl
+   apt-get install pkg-config libxml2-dev libxmlsec1-dev libxmlsec1-openssl
 
 
 Note: There is no required version of LibXML2 for Ubuntu Precise,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlsec-1.3.11/src/xmlsec.egg-info/SOURCES.txt 
new/xmlsec-1.3.12/src/xmlsec.egg-info/SOURCES.txt
--- old/xmlsec-1.3.11/src/xmlsec.egg-info/SOURCES.txt   2021-05-28 
23:18:26.000000000 +0200
+++ new/xmlsec-1.3.12/src/xmlsec.egg-info/SOURCES.txt   2021-09-05 
18:54:29.000000000 +0200
@@ -85,6 +85,4 @@
 tests/data/sign5-out.xml
 tests/data/sign6-in.bin
 tests/data/sign6-out.bin
-tests/data/sign_template.xml
-typeshed/lxml/__init__.pyi
-typeshed/lxml/etree.pyi
\ No newline at end of file
+tests/data/sign_template.xml
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlsec-1.3.11/tests/test_main.py 
new/xmlsec-1.3.12/tests/test_main.py
--- old/xmlsec-1.3.11/tests/test_main.py        2021-05-28 23:18:21.000000000 
+0200
+++ new/xmlsec-1.3.12/tests/test_main.py        2021-09-05 18:54:21.000000000 
+0200
@@ -1,5 +1,10 @@
+import sys
+from io import BytesIO
+from unittest import skipIf
+
 import xmlsec
 from tests import base
+from xmlsec import constants as consts
 
 
 class TestBase64LineSize(base.TestMemoryLeaks):
@@ -30,3 +35,126 @@
         with self.assertRaises(ValueError):
             xmlsec.base64_default_line_size(-1)
         self.assertEqual(xmlsec.base64_default_line_size(), size)
+
+
+class TestCallbacks(base.TestMemoryLeaks):
+    def setUp(self):
+        super().setUp()
+        xmlsec.cleanup_callbacks()
+
+    def _sign_doc(self):
+        root = self.load_xml("doc.xml")
+        sign = xmlsec.template.create(root, 
c14n_method=consts.TransformExclC14N, sign_method=consts.TransformRsaSha1)
+        xmlsec.template.add_reference(sign, consts.TransformSha1, 
uri="cid:123456")
+
+        ctx = xmlsec.SignatureContext()
+        ctx.key = xmlsec.Key.from_file(self.path("rsakey.pem"), 
format=consts.KeyDataFormatPem)
+        ctx.sign(sign)
+        return sign
+
+    def _expect_sign_failure(self):
+        with self.assertRaisesRegex(xmlsec.Error, 'failed to sign'):
+            self._sign_doc()
+
+    def _mismatch_callbacks(self, match_cb=lambda filename: False):
+        return [
+            match_cb,
+            lambda filename: None,
+            lambda none, buf: 0,
+            lambda none: None,
+        ]
+
+    def _register_mismatch_callbacks(self, match_cb=lambda filename: False):
+        xmlsec.register_callbacks(*self._mismatch_callbacks(match_cb))
+
+    def _register_match_callbacks(self):
+        xmlsec.register_callbacks(
+            lambda filename: filename == b'cid:123456',
+            lambda filename: BytesIO(b'<html><head/><body/></html>'),
+            lambda bio, buf: bio.readinto(buf),
+            lambda bio: bio.close(),
+        )
+
+    def _find(self, elem, *tags):
+        try:
+            return elem.xpath(
+                './' + '/'.join('xmldsig:{}'.format(tag) for tag in tags),
+                namespaces={
+                    'xmldsig': 'http://www.w3.org/2000/09/xmldsig#',
+                },
+            )[0]
+        except IndexError as e:
+            raise KeyError(tags) from e
+
+    def _verify_external_data_signature(self):
+        signature = self._sign_doc()
+        digest = self._find(signature, 'SignedInfo', 'Reference', 
'DigestValue').text
+        self.assertEqual(digest, 'VihZwVMGJ48NsNl7ertVHiURXk8=')
+
+    def test_sign_external_data_no_callbacks_fails(self):
+        self._expect_sign_failure()
+
+    def test_sign_external_data_default_callbacks_fails(self):
+        xmlsec.register_default_callbacks()
+        self._expect_sign_failure()
+
+    def test_sign_external_data_no_matching_callbacks_fails(self):
+        self._register_mismatch_callbacks()
+        self._expect_sign_failure()
+
+    def test_sign_data_from_callbacks(self):
+        self._register_match_callbacks()
+        self._verify_external_data_signature()
+
+    def test_sign_data_not_first_callback(self):
+        bad_match_calls = 0
+
+        def match_cb(filename):
+            nonlocal bad_match_calls
+            bad_match_calls += 1
+            False
+
+        for _ in range(2):
+            self._register_mismatch_callbacks(match_cb)
+
+        self._register_match_callbacks()
+
+        for _ in range(2):
+            self._register_mismatch_callbacks()
+
+        self._verify_external_data_signature()
+        self.assertEqual(bad_match_calls, 0)
+
+    @skipIf(sys.platform == "win32", "unclear behaviour on windows")
+    def test_failed_sign_because_default_callbacks(self):
+        mismatch_calls = 0
+
+        def mismatch_cb(filename):
+            nonlocal mismatch_calls
+            mismatch_calls += 1
+            False
+
+        # NB: These first two sets of callbacks should never get called,
+        # because the default callbacks always match beforehand:
+        self._register_match_callbacks()
+        self._register_mismatch_callbacks(mismatch_cb)
+        xmlsec.register_default_callbacks()
+        self._register_mismatch_callbacks(mismatch_cb)
+        self._register_mismatch_callbacks(mismatch_cb)
+        self._expect_sign_failure()
+        self.assertEqual(mismatch_calls, 2)
+
+    def test_register_non_callables(self):
+        for idx in range(4):
+            cbs = self._mismatch_callbacks()
+            cbs[idx] = None
+            self.assertRaises(TypeError, xmlsec.register_callbacks, *cbs)
+
+    def test_sign_external_data_fails_on_read_callback_wrong_returns(self):
+        xmlsec.register_callbacks(
+            lambda filename: filename == b'cid:123456',
+            lambda filename: BytesIO(b'<html><head/><body/></html>'),
+            lambda bio, buf: None,
+            lambda bio: bio.close(),
+        )
+        self._expect_sign_failure()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlsec-1.3.11/tests/test_xmlsec.py 
new/xmlsec-1.3.12/tests/test_xmlsec.py
--- old/xmlsec-1.3.11/tests/test_xmlsec.py      2021-05-28 23:18:21.000000000 
+0200
+++ new/xmlsec-1.3.12/tests/test_xmlsec.py      2021-09-05 18:54:21.000000000 
+0200
@@ -10,5 +10,5 @@
         tests don't fail, we know that the ``init()``/``shutdown()``
         function pair doesn't break anything.
         """
-        # xmlsec.shutdown()
-        # xmlsec.init()
+        xmlsec.shutdown()
+        xmlsec.init()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlsec-1.3.11/typeshed/lxml/etree.pyi 
new/xmlsec-1.3.12/typeshed/lxml/etree.pyi
--- old/xmlsec-1.3.11/typeshed/lxml/etree.pyi   2021-05-28 23:18:21.000000000 
+0200
+++ new/xmlsec-1.3.12/typeshed/lxml/etree.pyi   1970-01-01 01:00:00.000000000 
+0100
@@ -1,6 +0,0 @@
-from typing import Any
-
-def __getattr__(name: str) -> Any: ...  # incomplete
-
-class _Element:
-    def __getattr__(self, name: str) -> Any: ...  # incomplete

Reply via email to