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