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