QXmpp Version: 1.11.3
Loading...
Searching...
No Matches
QXmppUtils_p.h
1// SPDX-FileCopyrightText: 2022 Linus Jahn <lnj@kaidan.im>
2// SPDX-FileCopyrightText: 2022 Melvin Keskin <melvo@olomono.de>
3//
4// SPDX-License-Identifier: LGPL-2.1-or-later
5
6#ifndef QXMPPUTILS_P_H
7#define QXMPPUTILS_P_H
8
9#include "QXmppGlobal.h"
10#include "QXmppXmlTags_p.h"
11
12#include "Algorithms.h"
13
14#include <functional>
15#include <optional>
16#include <stdint.h>
17#include <type_traits>
18
19#include <QByteArray>
20#include <QDomElement>
21
22class QDomElement;
23class QXmlStreamWriter;
24class QXmppNonza;
25
26namespace QXmpp::Private {
27
28class XmlWriter;
29
30// Helper for Q(Any)StringView overloads that were added later
31inline auto toString60(QStringView s)
32{
33#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
34 return s;
35#else
36 return s.toString();
37#endif
38}
39
40template<typename T>
41concept IsStdOptional = requires {
42 typename std::remove_cvref_t<T>::value_type;
43 requires std::same_as<std::remove_cvref_t<T>, std::optional<typename std::remove_cvref_t<T>::value_type>>;
44};
45
46// sequential stanza IDs
47QXMPP_PRIVATE_EXPORT extern QAtomicInt globalStanzaIdCounter;
48
49// Base64
50std::optional<QByteArray> parseBase64(const QString &);
51inline QString serializeBase64(const QByteArray &data) { return QString::fromUtf8(data.toBase64()); }
52
53// Integer parsing
54template<typename Int = int>
55std::optional<Int> parseInt(QStringView str);
56template<typename Int>
57inline QString serializeInt(Int value) { return QString::number(value); }
58
59// Double parsing
60std::optional<double> parseDouble(QStringView str);
61std::optional<float> parseFloat(QStringView str);
62
63// Booleans
64std::optional<bool> parseBoolean(const QString &str);
65QString serializeBoolean(bool);
66
67//
68// DOM
69//
70
71QXMPP_EXPORT QDomElement firstChildElement(const QDomElement &, QStringView tagName = {}, QStringView xmlNs = {});
72QXMPP_EXPORT QDomElement nextSiblingElement(const QDomElement &, QStringView tagName = {}, QStringView xmlNs = {});
73
74template<typename T>
75inline auto firstChildElement(const QDomElement &el)
76 requires HasXmlTag<T>
77{
78 auto [tag, ns] = T::XmlTag;
79 return firstChildElement(el, tag, ns);
80}
81
82template<typename T>
83inline auto nextSiblingElement(const QDomElement &el)
84 requires HasXmlTag<T>
85{
86 auto [tag, ns] = T::XmlTag;
87 return nextSiblingElement(el, tag, ns);
88}
89
90inline auto hasChild(const QDomElement &el, QStringView tagName = {}, QStringView xmlns = {})
91{
92 return !firstChildElement(el, tagName, xmlns).isNull();
93}
94
95template<typename T>
96inline auto hasChild(const QDomElement &el)
97 requires HasXmlTag<T>
98{
99 return !firstChildElement<T>(el).isNull();
100}
101
102struct DomChildElements {
103 QDomElement parent;
104 QStringView tagName;
105 QStringView namespaceUri;
106
107 struct EndIterator { };
108 struct Iterator {
109 Iterator operator++()
110 {
111 el = nextSiblingElement(el, tagName, namespaceUri);
112 return *this;
113 }
114 bool operator!=(EndIterator) const { return !el.isNull(); }
115 const QDomElement &operator*() const { return el; }
116
117 QDomElement el;
118 QStringView tagName;
119 QStringView namespaceUri;
120 };
121
122 Iterator begin() const { return { firstChildElement(parent, tagName, namespaceUri), tagName, namespaceUri }; }
123 EndIterator end() const { return {}; }
124};
125
126inline DomChildElements iterChildElements(const QDomElement &el, QStringView tagName = {}, QStringView namespaceUri = {}) { return DomChildElements { el, tagName, namespaceUri }; }
127
128template<typename T>
129inline auto iterChildElements(const QDomElement &el)
130 requires HasXmlTag<T>
131{
132 auto [tag, ns] = T::XmlTag;
133 return iterChildElements(el, tag, ns);
134}
135
136template<typename T>
137concept DomParsableV1 = requires(T t) {
138 { t.parse(QDomElement()) } -> std::same_as<void>;
139};
140
141template<typename T>
142concept DomParsableV2 = requires(T t) {
143 { t.parse(QDomElement()) } -> std::same_as<bool>;
144};
145
146template<typename T>
147concept DomParsableV3 = requires {
148 { T::fromDom(QDomElement()) } -> std::same_as<std::optional<T>>;
149};
150
151template<typename T>
152concept DomParsable = DomParsableV1<T> || DomParsableV2<T> || DomParsableV3<T>;
153
154// Parse T from a QDomElement.
155template<typename T>
156auto parseElement(const QDomElement &el) -> std::optional<T>
157 requires DomParsable<T>
158{
159 if constexpr (DomParsableV3<T>) {
160 return T::fromDom(el);
161 } else if constexpr (DomParsableV2<T>) {
162 T element;
163 if (!element.parse(el)) {
164 return {};
165 }
166 return element;
167 } else if constexpr (DomParsableV1<T>) {
168 T element;
169 element.parse(el);
170 return element;
171 }
172}
173
174// Parse T with T::parse() if DOM element is not null (no namespace check).
175template<typename T>
176auto parseOptionalElement(const QDomElement &domEl) -> std::optional<T>
177{
178 if (domEl.isNull()) {
179 return {};
180 }
181 return parseElement<T>(domEl);
182}
183
184// Parse T with T::parse() with first child element with the correct tag name and namespace.
185template<typename T>
186auto parseOptionalChildElement(const QDomElement &parentEl, QStringView tagName, QStringView xmlns)
187 requires(!HasXmlTag<T>)
188{
189 return parseOptionalElement<T>(firstChildElement(parentEl, tagName, xmlns));
190}
191
192template<typename T>
193auto parseOptionalChildElement(const QDomElement &parentEl)
194 requires HasXmlTag<T>
195{
196 auto [tag, ns] = T::XmlTag;
197 return parseOptionalElement<T>(firstChildElement(parentEl, tag, ns));
198}
199
200// Parse all child elements matching the tag name and namespace into a container.
201template<typename Container>
202auto parseChildElements(const QDomElement &parentEl, QStringView tagName, QStringView xmlns)
203 -> Container
204 requires(!HasXmlTag<typename Container::value_type>)
205{
206 using T = typename Container::value_type;
207
208 Container elements;
209 for (const auto &childEl : iterChildElements(parentEl, tagName, xmlns)) {
210 if (auto parsedEl = parseElement<T>(childEl)) {
211 elements.push_back(std::move(*parsedEl));
212 }
213 }
214 return elements;
215}
216
217template<typename Container>
218auto parseChildElements(const QDomElement &parentEl) -> Container
219 requires HasXmlTag<typename Container::value_type>
220{
221 using T = typename Container::value_type;
222 auto [tag, ns] = T::XmlTag;
223
224 Container elements;
225 for (const auto &childEl : iterChildElements(parentEl, tag, ns)) {
226 if (auto parsedEl = parseElement<T>(childEl)) {
227 elements.push_back(std::move(*parsedEl));
228 }
229 }
230 return elements;
231}
232
233template<typename Container = QList<QString>>
234auto parseTextElements(const QDomElement &parent, QStringView tagName, QStringView xmlns)
235 -> Container
236{
237 return transform<Container>(iterChildElements(parent, tagName, xmlns), &QDomElement::text);
238}
239
240template<typename Container = QList<QString>>
241auto parseSingleAttributeElements(const QDomElement &parent, QStringView tagName, QStringView xmlns, const QString &attribute)
242{
243 return transform<Container>(iterChildElements(parent, tagName, xmlns), [=](const QDomElement &el) {
244 return el.attribute(attribute);
245 });
246}
247
248QByteArray serializeXmlWriter(std::function<void(XmlWriter &)>);
249QByteArray serializeQXmlStream(std::function<void(QXmlStreamWriter *)>);
250
251template<typename T>
252concept XmlWriterSerializeable =
253 std::same_as<
254 decltype(static_cast<void (std::remove_cvref_t<T>::*)(XmlWriter &)>(&std::remove_cvref_t<T>::toXml)),
255 void (std::remove_cvref_t<T>::*)(XmlWriter &)> ||
256 std::same_as<
257 decltype(static_cast<void (std::remove_cvref_t<T>::*)(XmlWriter &) const>(&std::remove_cvref_t<T>::toXml)),
258 void (std::remove_cvref_t<T>::*)(XmlWriter &) const>;
259
260template<typename T>
261concept QXmlStreamSerializeable =
262 std::same_as<
263 decltype(static_cast<void (std::remove_cvref_t<T>::*)(QXmlStreamWriter *) const>(&std::remove_cvref_t<T>::toXml)),
264 void (std::remove_cvref_t<T>::*)(QXmlStreamWriter *) const>;
265
266template<typename T>
267inline QByteArray serializeXml(const T &packet)
268 requires XmlWriterSerializeable<T> || QXmlStreamSerializeable<T>
269{
270 if constexpr (XmlWriterSerializeable<T>) {
271 return serializeXmlWriter([&](XmlWriter &w) {
272 packet.toXml(w);
273 });
274 } else if constexpr (QXmlStreamSerializeable<T>) {
275 return serializeQXmlStream([&](QXmlStreamWriter *w) {
276 packet.toXml(w);
277 });
278 }
279}
280
281QXMPP_EXPORT QByteArray generateRandomBytes(size_t minimumByteCount, size_t maximumByteCount);
282QXMPP_EXPORT void generateRandomBytes(uint8_t *bytes, size_t byteCount);
283float calculateProgress(qint64 transferred, qint64 total);
284
285QXMPP_EXPORT std::pair<QString, int> parseHostAddress(const QString &address);
286
287} // namespace QXmpp::Private
288
289#endif // QXMPPUTILS_P_H
Definition QXmppNonza.h:14