QXmpp  Version: 1.15.1
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 
22 class QDomElement;
23 class QXmlStreamWriter;
24 class QXmppNonza;
25 
26 namespace QXmpp::Private {
27 
28 class XmlWriter;
29 
30 // Helper for Q(Any)StringView overloads that were added later
31 inline auto toString60(QStringView s)
32 {
33  return s;
34 }
35 
36 template<typename T>
37 concept 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
43 QXMPP_PRIVATE_EXPORT extern QAtomicInt globalStanzaIdCounter;
44 
45 // Base64
46 std::optional<QByteArray> parseBase64(const QString &);
47 inline QString serializeBase64(const QByteArray &data) { return QString::fromUtf8(data.toBase64()); }
48 
49 // Integer parsing
50 template<typename Int = int>
51 std::optional<Int> parseInt(QStringView str);
52 template<typename Int>
53 inline QString serializeInt(Int value) { return QString::number(value); }
54 
55 // Double parsing
56 std::optional<double> parseDouble(QStringView str);
57 std::optional<float> parseFloat(QStringView str);
58 
59 // Booleans
60 std::optional<bool> parseBoolean(const QString &str);
61 QString serializeBoolean(bool);
62 
63 //
64 // DOM
65 //
66 
67 QXMPP_EXPORT QDomElement firstChildElement(const QDomElement &, QStringView tagName = {}, QStringView xmlNs = {});
68 QXMPP_EXPORT QDomElement nextSiblingElement(const QDomElement &, QStringView tagName = {}, QStringView xmlNs = {});
69 
70 template<typename T>
71 inline auto firstChildElement(const QDomElement &el)
72  requires HasXmlTag<T>
73 {
74  auto [tag, ns] = T::XmlTag;
75  return firstChildElement(el, tag, ns);
76 }
77 
78 template<typename T>
79 inline auto nextSiblingElement(const QDomElement &el)
80  requires HasXmlTag<T>
81 {
82  auto [tag, ns] = T::XmlTag;
83  return nextSiblingElement(el, tag, ns);
84 }
85 
86 inline auto hasChild(const QDomElement &el, QStringView tagName = {}, QStringView xmlns = {})
87 {
88  return !firstChildElement(el, tagName, xmlns).isNull();
89 }
90 
91 template<typename T>
92 inline auto hasChild(const QDomElement &el)
93  requires HasXmlTag<T>
94 {
95  return !firstChildElement<T>(el).isNull();
96 }
97 
98 struct 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 
122 inline DomChildElements iterChildElements(const QDomElement &el, QStringView tagName = {}, QStringView namespaceUri = {}) { return DomChildElements { el, tagName, namespaceUri }; }
123 
124 template<typename T>
125 inline auto iterChildElements(const QDomElement &el)
126  requires HasXmlTag<T>
127 {
128  auto [tag, ns] = T::XmlTag;
129  return iterChildElements(el, tag, ns);
130 }
131 
132 template<typename T>
133 concept DomParsableV1 = requires(T t) {
134  { t.parse(QDomElement()) } -> std::same_as<void>;
135 };
136 
137 template<typename T>
138 concept DomParsableV2 = requires(T t) {
139  { t.parse(QDomElement()) } -> std::same_as<bool>;
140 };
141 
142 template<typename T>
143 concept DomParsableV3 = requires {
144  { T::fromDom(QDomElement()) } -> std::same_as<std::optional<T>>;
145 };
146 
147 template<typename T>
148 concept DomParsable = DomParsableV1<T> || DomParsableV2<T> || DomParsableV3<T>;
149 
150 // Parse T from a QDomElement.
151 template<typename T>
152 auto 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).
171 template<typename T>
172 auto 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.
181 template<typename T>
182 auto parseOptionalChildElement(const QDomElement &parentEl, QStringView tagName, QStringView xmlns)
183  requires(!HasXmlTag<T>)
184 {
185  return parseOptionalElement<T>(firstChildElement(parentEl, tagName, xmlns));
186 }
187 
188 template<typename T>
189 auto 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.
197 template<typename Container>
198 auto 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 
213 template<typename Container>
214 auto 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 
229 template<typename Container = QList<QString>>
230 auto parseTextElements(const QDomElement &parent, QStringView tagName, QStringView xmlns)
231  -> Container
232 {
233  return transform<Container>(iterChildElements(parent, tagName, xmlns), &QDomElement::text);
234 }
235 
236 template<typename Container = QList<QString>>
237 auto 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 
244 QByteArray serializeXmlWriter(std::function<void(XmlWriter &)>);
245 QByteArray serializeQXmlStream(std::function<void(QXmlStreamWriter *)>);
246 
247 template<typename T>
248 concept 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 
256 template<typename T>
257 concept 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 
262 template<typename T>
263 inline 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 
277 QXMPP_EXPORT QByteArray generateRandomBytes(size_t minimumByteCount, size_t maximumByteCount);
278 QXMPP_EXPORT void generateRandomBytes(uint8_t *bytes, size_t byteCount);
279 float calculateProgress(qint64 transferred, qint64 total);
280 
281 QXMPP_EXPORT std::pair<QString, int> parseHostAddress(const QString &address);
282 
283 } // namespace QXmpp::Private
284 
285 #endif // QXMPPUTILS_P_H
Definition: QXmppNonza.h:13
Definition: Algorithms.h:14