Skip to content

Commit c442a83

Browse files
metsmaklemensn
authored andcommitted
Use libxml2 for configuration parsing (open-eid#584)
IB-7946 Signed-off-by: Raul Metsma <raul@metsma.ee>
1 parent 42f5b0c commit c442a83

File tree

15 files changed

+456
-209
lines changed

15 files changed

+456
-209
lines changed

.github/workflows/build.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ jobs:
2727
brew unlink python@3.10 || true
2828
brew unlink python@3.11 || true
2929
brew unlink python@3.12 || true
30+
brew unlink xz
3031
- name: Cache
3132
uses: actions/cache@v4
3233
id: cache
@@ -45,6 +46,9 @@ jobs:
4546
- name: Build xml-security-c
4647
if: steps.cache.outputs.cache-hit != 'true'
4748
run: ./prepare_osx_build_environment.sh xmlsec ${{ matrix.target }}
49+
- name: Build libxml2
50+
if: steps.cache.outputs.cache-hit != 'true'
51+
run: ./prepare_osx_build_environment.sh libxml2 ${{ matrix.target }}
4852
- name: Move to cache
4953
if: steps.cache.outputs.cache-hit != 'true'
5054
run: |
@@ -84,7 +88,7 @@ jobs:
8488
- name: Install Deps
8589
run: |
8690
dnf install -y --setopt=install_weak_deps=False \
87-
git gcc-c++ cmake rpm-build xml-security-c-devel zlib-devel vim-common doxygen boost-test swig python3-devel java-1.8.0-openjdk-devel xsd minizip-devel
91+
git gcc-c++ cmake rpm-build xml-security-c-devel libxml2-devel zlib-devel vim-common doxygen boost-test swig python3-devel java-1.8.0-openjdk-devel xsd minizip-devel
8892
- name: Install CMake
8993
if: matrix.container == 39
9094
run: |
@@ -118,7 +122,7 @@ jobs:
118122
DEBEMAIL: github-actions@github.com
119123
steps:
120124
- name: Install dependencies
121-
run: apt update -qq && apt install --no-install-recommends -y git lsb-release build-essential devscripts debhelper cmake xxd xsdcxx libxml-security-c-dev zlib1g-dev doxygen swig openjdk-8-jdk-headless libpython3-dev python3-setuptools libboost-test-dev lintian
125+
run: apt update -qq && apt install --no-install-recommends -y git lsb-release build-essential devscripts debhelper cmake xxd xsdcxx libxml-security-c-dev libxml2-dev zlib1g-dev doxygen swig openjdk-8-jdk-headless libpython3-dev python3-setuptools libboost-test-dev lintian
122126
- name: Checkout
123127
uses: actions/checkout@v4
124128
with:

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ find_package(OpenSSL 1.1.1 REQUIRED)
4949
find_package(PKCS11)
5050
#find_package(PoDoFo)
5151
find_package(Threads)
52+
find_package(LibXml2 REQUIRED)
5253
find_package(XmlSecurityC REQUIRED)
5354
find_package(XSD 4.0 REQUIRED)
5455
find_package(ZLIB REQUIRED)

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
1. Install dependencies
1717

1818
# Ubuntu
19-
sudo apt install cmake xxd libxml-security-c-dev xsdcxx libssl-dev zlib1g-dev
19+
sudo apt install cmake xxd libxml-security-c-dev xsdcxx libxml2-dev libssl-dev zlib1g-dev
2020
# Fedora
21-
sudo dnf install cmake gcc-c++ openssl-devel xerces-c-devel xml-security-c-devel xsd zlib-devel vim-common
21+
sudo dnf install cmake gcc-c++ openssl-devel xerces-c-devel xml-security-c-devel xsd libxml2-devel zlib-devel vim-common
2222

2323
* doxygen - Optional, for API documentation
2424
* libboost-test-dev - Optional, for unittests

debian/control

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Build-Depends:
77
cmake,
88
libxml-security-c-dev,
99
xsdcxx (>= 4.0) | xsd (>= 4.0),
10+
libxml2-dev,
1011
xxd,
1112
doxygen,
1213
swig,

libdigidocpp.wxs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
<File Name="Xalan-C_1_12.dll" />
5555
<File Name="XalanMessages_1_12.dll" />
5656
<File Name="xsec_2_0.dll" />
57+
<File Name="libxml2.dll" />
5758
</ComponentGroup>
5859

5960
<ComponentGroup Id="Libraries" Source="$(var.libdigidocpp)\bin">
@@ -106,7 +107,7 @@
106107
<ComponentGroup Id="Headers" Directory="INSTALLFOLDER" Subdirectory="include">
107108
<Files Include="$(var.libdigidocpp)\include\**" />
108109
</ComponentGroup>
109-
110+
110111
<?ifdef var.docLocation ?>
111112
<ComponentGroup Id="Documentation" Directory="INSTALLFOLDER" Subdirectory="documentation">
112113
<Files Include="$(var.docLocation)\**" />

prepare_osx_build_environment.sh

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ XALAN_DIR=xalan_c-1.12
66
XMLSEC_DIR=xml-security-c-2.0.4
77
XSD=xsd-4.0.0-i686-macosx
88
OPENSSL_DIR=openssl-3.0.13
9-
LIBXML2_DIR=libxml2-2.11.5
9+
LIBXML2_DIR=libxml2-2.12.5
1010
ANDROID_NDK=android-ndk-r26b
1111
FREETYPE_DIR=freetype-2.10.1
1212
FONTCONFIG_DIR=fontconfig-2.13.1
@@ -232,11 +232,11 @@ function libxml2 {
232232
return 0
233233
;;
234234
esac
235-
if [ ! -f ${LIBXML2_DIR}.tar.gz ]; then
236-
curl -O -L http://xmlsoft.org/sources/${LIBXML2_DIR}.tar.gz
235+
if [ ! -f ${LIBXML2_DIR}.tar.xz ]; then
236+
curl -O -L https://download.gnome.org/sources/libxml2/2.12/${LIBXML2_DIR}.tar.xz
237237
fi
238238
rm -rf ${LIBXML2_DIR}
239-
tar xf ${LIBXML2_DIR}.tar.gz
239+
tar xf ${LIBXML2_DIR}.tar.xz
240240
cd ${LIBXML2_DIR}
241241
./configure --prefix=${TARGET_PATH} ${CONFIGURE} --without-python
242242
# Android is missing glob.h
@@ -429,6 +429,7 @@ case "$@" in
429429
openssl
430430
xalan
431431
xml_security
432+
libxml2
432433
;;
433434
*)
434435
echo "Usage:"

src/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ configure_file( ${CMAKE_SOURCE_DIR}/etc/digidocpp.conf.cmake digidocpp.conf )
1414

1515
set(SCHEMA_DIR ${CMAKE_SOURCE_DIR}/etc/schema)
1616
set(XML_DIR ${CMAKE_CURRENT_BINARY_DIR}/xml)
17-
XSD_SCHEMA( xsd_SRCS IGNORE ${XML_DIR} ${SCHEMA_DIR}/conf.xsd
18-
--root-element configuration )
1917
XSD_SCHEMA( xsd_SRCS IGNORE ${XML_DIR} ${SCHEMA_DIR}/OpenDocument_manifest.xsd
2018
--root-element manifest
2119
--namespace-map urn:oasis:names:tc:opendocument:xmlns:manifest:1.0=digidoc::manifest )
@@ -161,6 +159,7 @@ add_library(digidocpp_priv STATIC
161159
xml/SecureDOMParser.cpp
162160
xml/UnsignedSignaturePropertiesType.cpp
163161
xml/URIResolver.cpp
162+
XMLDocument.h
164163
)
165164

166165
set_target_properties(digidocpp_util digidocpp_priv PROPERTIES
@@ -183,6 +182,7 @@ target_link_libraries(digidocpp_priv
183182
digidocpp_util
184183
XmlSecurityC::XmlSecurityC
185184
ZLIB::ZLIB
185+
LibXml2::LibXml2
186186
$<$<C_COMPILER_ID:MSVC>:Ws2_32>
187187
)
188188

src/Container.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include "util/File.h"
3131
#include "util/log.h"
3232

33+
#include <libxml/parser.h>
34+
3335
DIGIDOCPP_WARNING_PUSH
3436
DIGIDOCPP_WARNING_DISABLE_CLANG("-Wnull-conversion")
3537
DIGIDOCPP_WARNING_DISABLE_MSVC(4005)
@@ -186,6 +188,7 @@ void digidoc::terminate()
186188
} catch (...) {
187189
// Don't throw on terminate
188190
}
191+
xmlCleanupParser();
189192
m_createList.clear();
190193
m_openList.clear();
191194
m_appName.clear();

src/XMLDocument.h

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
/*
2+
* libdigidocpp
3+
*
4+
* This library is free software; you can redistribute it and/or
5+
* modify it under the terms of the GNU Lesser General Public
6+
* License as published by the Free Software Foundation; either
7+
* version 2.1 of the License, or (at your option) any later version.
8+
*
9+
* This library is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
* Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public
15+
* License along with this library; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17+
*
18+
*/
19+
20+
#pragma once
21+
22+
#include "util/log.h"
23+
24+
#include <libxml/parser.h>
25+
#include <libxml/xmlschemas.h>
26+
27+
#include <memory>
28+
29+
namespace digidoc {
30+
31+
template<typename> struct unique_xml;
32+
template<class T>
33+
struct unique_xml<void(T *)>
34+
{
35+
using type = std::unique_ptr<T,void(*)(T *)>;
36+
};
37+
38+
template<typename T>
39+
using unique_xml_t = typename unique_xml<T>::type;
40+
41+
template<class T, typename D>
42+
constexpr std::unique_ptr<T, D> make_unique_ptr(T *p, D d) noexcept
43+
{
44+
return {p, d};
45+
}
46+
47+
template<class T>
48+
struct XMLElem
49+
{
50+
using value_type = T;
51+
using pointer = value_type*;
52+
using sv = std::string_view;
53+
using pcxmlChar = const xmlChar *;
54+
55+
template<class C>
56+
constexpr static C find(C n, xmlElementType type) noexcept
57+
{
58+
for(; n; n = n->next)
59+
if(n->type == type)
60+
return n;
61+
return {};
62+
}
63+
64+
constexpr static sv to_string_view(const xmlChar *str) noexcept
65+
{
66+
return str ? sv(sv::const_pointer(str)) : sv();
67+
}
68+
69+
constexpr sv name() const noexcept
70+
{
71+
return to_string_view(d ? d->name : nullptr);
72+
}
73+
74+
constexpr sv ns() const noexcept
75+
{
76+
return to_string_view(d && d->ns ? d->ns->href : nullptr);
77+
}
78+
79+
constexpr operator bool() const noexcept
80+
{
81+
return bool(d);
82+
}
83+
84+
constexpr auto& operator++() noexcept
85+
{
86+
d = d ? find(d->next, d->type) : nullptr;
87+
return *this;
88+
}
89+
90+
constexpr operator sv() const noexcept
91+
{
92+
auto text = find(d ? d->children : nullptr, XML_TEXT_NODE);
93+
return to_string_view(text ? text->content : nullptr);
94+
}
95+
96+
pointer d{};
97+
};
98+
99+
struct XMLNode: public XMLElem<xmlNode>
100+
{
101+
struct Name_NS {
102+
sv name, ns;
103+
};
104+
105+
struct iterator: XMLElem<xmlNode>
106+
{
107+
using iterator_category = std::forward_iterator_tag;
108+
using difference_type = std::ptrdiff_t;
109+
110+
constexpr XMLNode operator*() const noexcept { return {d}; }
111+
};
112+
113+
constexpr iterator begin() const noexcept
114+
{
115+
return {find(d ? d->children : nullptr, XML_ELEMENT_NODE)};
116+
}
117+
118+
constexpr iterator end() const noexcept
119+
{
120+
return {};
121+
}
122+
123+
XMLNode addChild(sv name, sv ns = {}) const noexcept
124+
{
125+
return {xmlNewChild(d, searchNS(ns), pcxmlChar(name.data()), nullptr)};
126+
}
127+
128+
xmlNsPtr addNS(sv href, sv prefix = {}) const noexcept
129+
{
130+
return xmlNewNs(d, pcxmlChar(href.data()), prefix.empty() ? nullptr : pcxmlChar(prefix.data()));
131+
}
132+
133+
xmlNsPtr searchNS(sv ns) const noexcept
134+
{
135+
return xmlSearchNsByHref(nullptr, d, ns.empty() ? nullptr : pcxmlChar(ns.data()));
136+
}
137+
138+
constexpr sv property(sv name, sv ns = {}) const noexcept
139+
{
140+
for(XMLElem<xmlAttr> a{d ? d->properties : nullptr}; a; ++a)
141+
{
142+
if(a.name() == name && a.ns() == ns)
143+
return a;
144+
}
145+
return {};
146+
}
147+
148+
void setProperty(sv name, sv value, xmlNsPtr ns = {}) const noexcept
149+
{
150+
xmlSetNsProp(d, ns, pcxmlChar(name.data()), pcxmlChar(value.data()));
151+
}
152+
153+
static iterator erase(iterator pos) noexcept
154+
{
155+
iterator next = pos;
156+
++next;
157+
xmlUnlinkNode(pos.d);
158+
xmlFreeNode(pos.d);
159+
return next;
160+
}
161+
162+
XMLNode& operator=(sv text) noexcept
163+
{
164+
if(!d)
165+
return *this;
166+
xmlChar *content = xmlEncodeSpecialChars(d->doc, pcxmlChar(text.data()));
167+
xmlNodeSetContent(d, content);
168+
xmlFree(content);
169+
return *this;
170+
}
171+
};
172+
173+
struct XMLDocument: public unique_xml_t<decltype(xmlFreeDoc)>, public XMLNode
174+
{
175+
using XMLNode::operator bool;
176+
177+
XMLDocument(element_type *ptr, std::string_view _name = {}, std::string_view _ns = {}) noexcept
178+
: std::unique_ptr<element_type, deleter_type>(ptr, xmlFreeDoc)
179+
, XMLNode{xmlDocGetRootElement(get())}
180+
{
181+
if(d && !_name.empty() && _name != name() && !_ns.empty() && _ns != ns())
182+
d = {};
183+
}
184+
185+
XMLDocument(std::string_view path, std::string_view name = {}) noexcept
186+
: XMLDocument(xmlParseFile(path.data()), name)
187+
{}
188+
189+
static XMLDocument create(std::string_view name = {}, std::string_view href = {}, std::string_view prefix = {}) noexcept
190+
{
191+
XMLDocument doc(xmlNewDoc(nullptr));
192+
if(!name.empty())
193+
{
194+
doc.d = xmlNewNode(nullptr, pcxmlChar(name.data()));
195+
if(!href.empty())
196+
xmlSetNs(doc.d, doc.addNS(href, prefix));
197+
xmlDocSetRootElement(doc.get(), doc.d);
198+
}
199+
return doc;
200+
}
201+
202+
bool save(std::string_view path) const noexcept
203+
{
204+
return xmlSaveFormatFileEnc(path.data(), get(), "UTF-8", 1) > 0;
205+
}
206+
207+
bool validateSchema(const std::string &schemaPath) const noexcept
208+
{
209+
auto parser = make_unique_ptr(xmlSchemaNewParserCtxt(schemaPath.c_str()), xmlSchemaFreeParserCtxt);
210+
if(!parser)
211+
return false;
212+
xmlSchemaSetParserErrors(parser.get(), schemaValidationError, schemaValidationWarning, nullptr);
213+
auto schema = make_unique_ptr(xmlSchemaParse(parser.get()), xmlSchemaFree);
214+
if(!schema)
215+
return false;
216+
auto validate = make_unique_ptr(xmlSchemaNewValidCtxt(schema.get()), xmlSchemaFreeValidCtxt);
217+
if(!validate)
218+
return false;
219+
xmlSchemaSetValidErrors(validate.get(), schemaValidationError, schemaValidationWarning, nullptr);
220+
return xmlSchemaValidateDoc(validate.get(), get()) == 0;
221+
}
222+
223+
static void schemaValidationError(void */*ctx*/, const char *msg, ...) noexcept
224+
{
225+
va_list args{};
226+
va_start(args, msg);
227+
std::string m = Log::formatArgList(msg, args);
228+
va_end(args);
229+
ERR("Schema validation error: %s", m.c_str());
230+
}
231+
232+
static void schemaValidationWarning(void */*ctx*/, const char *msg, ...) noexcept
233+
{
234+
va_list args{};
235+
va_start(args, msg);
236+
std::string m = Log::formatArgList(msg, args);
237+
va_end(args);
238+
WARN("Schema validation warning: %s", m.c_str());
239+
}
240+
};
241+
242+
}

0 commit comments

Comments
 (0)