QXmpp  Version: 1.15.1
Iq.h
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2025 Linus Jahn <lnj@kaidan.im>
4 
5 #ifndef IQ_H
6 #define IQ_H
7 
8 #include "QXmppError.h"
9 #include "QXmppIq.h"
10 #include "QXmppStanza.h"
11 #include "QXmppVisitHelper_p.h"
12 
13 #include "XmlWriter.h"
14 
15 #include <optional>
16 
17 #include <QString>
18 
19 namespace QXmpp::Private {
20 
21 enum class IqType {
22  Get,
23  Set,
24  Result,
25  Error,
26 };
27 
28 template<>
29 struct Enums::Data<IqType> {
30  using enum IqType;
31  static constexpr auto Values = makeValues<IqType>({
32  { Get, u"get" },
33  { Set, u"set" },
34  { Result, u"result" },
35  { Error, u"error" },
36  });
37 };
38 
39 template<IqType, typename Payload = void>
40 struct Iq;
41 
42 template<typename Payload = void>
43 using GetIq = Iq<IqType::Get, Payload>;
44 
45 template<typename Payload = void>
46 using SetIq = Iq<IqType::Set, Payload>;
47 
48 template<typename Payload = void>
49 using ResultIq = Iq<IqType::Result, Payload>;
50 
51 template<typename Payload = void>
52 using ErrorIq = Iq<IqType::Error, Payload>;
53 
54 template<IqType Type, typename Payload = void>
55 auto iqFromDomImpl(const QDomElement &el) -> std::optional<Iq<Type, Payload>>;
56 
57 template<IqType Type, typename Payload>
58  requires((Type == IqType::Get || Type == IqType::Set) && !std::is_void_v<Payload>)
59 struct Iq<Type, Payload> {
60  QString id;
61  QString from;
62  QString to;
63  QString lang;
64  Payload payload;
65 
66  using PayloadType = Payload;
67  static constexpr auto IqType = Type;
68 
69  static std::optional<Iq> fromDom(const QDomElement &el) { return iqFromDomImpl<Type, Payload>(el); }
70  void toXml(XmlWriter &w) const
71  {
72  w.write(Element {
73  u"iq",
74  Attribute { u"id", id },
75  OptionalAttribute { u"from", from },
76  OptionalAttribute { u"to", to },
77  Attribute { u"type", Type },
78  OptionalAttribute { u"xml:lang", lang },
79  payload,
80  });
81  }
82 };
83 
84 template<typename Payload>
85 struct Iq<IqType::Result, Payload> {
86  QString id;
87  QString from;
88  QString to;
89  QString lang;
90  std::conditional_t<std::is_void_v<Payload>, std::monostate, Payload> payload;
91 
92  using PayloadType = Payload;
93 
94  static std::optional<Iq> fromDom(const QDomElement &el) { return iqFromDomImpl<IqType::Result, Payload>(el); }
95  void toXml(XmlWriter &w) const
96  {
97  w.write(Element {
98  u"iq",
99  Attribute { u"id", id },
100  OptionalAttribute { u"from", from },
101  OptionalAttribute { u"to", to },
102  Attribute { u"type", IqType::Result },
103  OptionalAttribute { u"xml:lang", lang },
104  payload,
105  });
106  }
107 };
108 
109 template<typename Payload>
110 struct Iq<IqType::Error, Payload> {
111  QString id;
112  QString from;
113  QString to;
114  QString lang;
115  std::conditional_t<std::is_void_v<Payload>, std::monostate, std::optional<Payload>> payload;
116  QXmppStanza::Error error;
117 
118  using PayloadType = Payload;
119 
120  static std::optional<Iq> fromDom(const QDomElement &el) { return iqFromDomImpl<IqType::Error, Payload>(el); }
121  void toXml(XmlWriter &w) const
122  {
123  w.write(Element {
124  u"iq",
125  Attribute { u"id", id },
126  OptionalAttribute { u"from", from },
127  OptionalAttribute { u"to", to },
128  Attribute { u"type", IqType::Error },
129  OptionalAttribute { u"xml:lang", lang },
130  payload,
131  error,
132  });
133  }
134 };
135 
136 template<IqType Type, typename Payload>
137 auto iqFromDomImpl(const QDomElement &el) -> std::optional<Iq<Type, Payload>>
138 {
139  using IqT = Iq<Type, Payload>;
140 
141  if (el.tagName() == u"iq" && el.hasAttribute(u"id"_s) && el.attribute(u"type"_s) == Type) {
142  auto id = el.attribute(u"id"_s);
143  auto from = el.attribute(u"from"_s);
144  auto to = el.attribute(u"to"_s);
145  auto lang = el.attributeNS(ns_xml.toString(), u"lang"_s);
146 
147  if constexpr (Type == IqType::Get || Type == IqType::Set) {
148  if (auto payload = parseOptionalElement<Payload>(el.firstChildElement())) {
149  return IqT {
150  std::move(id),
151  std::move(from),
152  std::move(to),
153  std::move(lang),
154  std::move(*payload),
155  };
156  }
157  } else if constexpr (Type == IqType::Result) {
158  if constexpr (std::is_void_v<Payload>) {
159  return IqT { std::move(id), std::move(from), std::move(to), std::move(lang) };
160  } else {
161  if (auto payload = parseOptionalElement<Payload>(el.firstChildElement())) {
162  return IqT {
163  std::move(id),
164  std::move(from),
165  std::move(to),
166  std::move(lang),
167  std::move(*payload),
168  };
169  }
170  }
171  } else if constexpr (Type == IqType::Error) {
172  auto errorEl = el.lastChildElement();
173  auto payloadEl = errorEl.previousSiblingElement();
174 
175  if (auto error = parseOptionalElement<QXmppStanza::Error>(errorEl)) {
176  return IqT {
177  std::move(id),
178  std::move(from),
179  std::move(to),
180  std::move(lang),
181  parseOptionalElement<Payload>(payloadEl),
182  std::move(*error),
183  };
184  }
185  }
186  }
187  return {};
188 }
189 
190 // parse Iq type from QDomElement or pass error
191 template<typename Payload>
192 auto parseIqResponse(std::variant<QDomElement, QXmppError> &&sendResult) -> std::variant<Payload, QXmppStanza::Error, QXmppError>
193 {
195  using Result = std::variant<Payload, QXmppStanza::Error, QXmppError>;
196 
197  return map<Result>(
198  [](QDomElement &&el) -> Result {
199  auto type = el.attribute(u"type"_s);
200  if (type == IqType::Result) {
201  if (auto payload = parseElement<Payload>(el.firstChildElement())) {
202  return std::move(*payload);
203  } else {
204  return QXmppError {
205  u"Failed to parse IQ result payload"_s,
207  };
208  }
209  } else if (type == IqType::Error) {
210  if (auto error = parseElement<StanzaError>(el.lastChildElement())) {
211  return std::move(*error);
212  } else {
213  return QXmppError { u"Failed to parse error response"_s, {} };
214  }
215  } else {
216  return QXmppError { u"Received unexpected IQ type"_s, {} };
217  }
218  },
219  std::move(sendResult));
220 }
221 
222 template<typename Payload>
223 auto parseIqResponseFlat(std::variant<QDomElement, QXmppError> &&sendResult)
224 {
225  using Result = std::variant<Payload, QXmppError>;
226  return map<Result>(
227  [](QXmppStanza::Error &&e) -> Result { return QXmppError { e.text(), std::move(e) }; },
228  parseIqResponse<Payload>(std::move(sendResult)));
229 }
230 
231 // For usage with QXmppClient::sendIq()
232 template<typename Payload>
233 class CompatIq : public QXmppIq
234 {
235 public:
236  std::optional<Payload> payload;
237 
238  template<IqType Type>
239  requires(Type == IqType::Get || Type == IqType::Set || Type == IqType::Result)
240  CompatIq(Iq<Type, Payload> &&iq) : QXmppIq(), payload(std::move(iq.payload))
241  {
242  setId(iq.id);
243  setFrom(iq.from);
244  setTo(iq.to);
245  setLang(iq.lang);
246  if constexpr (Type == IqType::Get) {
248  } else if constexpr (Type == IqType::Set) {
250  } else if constexpr (Type == IqType::Result) {
252  } else if constexpr (Type == IqType::Error) {
254  }
255  }
256 
257  CompatIq(QXmppIq::Type type, std::optional<Payload> &&payload = {})
258  : QXmppIq(type), payload(std::move(payload))
259  {
260  }
261 
262  void parseElementFromChild(const QDomElement &el) override
263  {
264  payload = parseElement<Payload>(el.firstChildElement());
265  }
266  void toXmlElementFromChild(QXmlStreamWriter *writer) const override
267  {
268  XmlWriter(writer).write(payload);
269  }
270 };
271 
272 template<IqType Type, typename Payload>
273 CompatIq(Iq<Type, Payload> &&) -> CompatIq<Payload>;
274 
275 template<typename Payload>
276 CompatIq(QXmppIq::Type, Payload) -> CompatIq<Payload>;
277 
278 } // namespace QXmpp::Private
279 
280 #endif // IQ_H
The error is not temporary.
Definition: QXmppStanza.h:120
void setId(const QString &)
Definition: QXmppStanza.cpp:760
void setTo(const QString &)
Definition: QXmppStanza.cpp:724
Definition: QXmppError.h:17
Definition: QXmppTask.h:646
Type
This enum describes the type of IQ.
Definition: QXmppIq.h:26
Result.
Definition: QXmppIq.h:30
An undefined condition was hit.
Definition: QXmppStanza.h:154
Set request.
Definition: QXmppIq.h:29
Get request.
Definition: QXmppIq.h:28
The QXmppIq class is the base class for all IQs.
Definition: QXmppIq.h:22
void setType(QXmppIq::Type)
Definition: QXmppIq.cpp:70
void setFrom(const QString &)
Definition: QXmppStanza.cpp:742
Definition: Algorithms.h:14
The Error class represents a stanza error.
Definition: QXmppStanza.h:111
void setLang(const QString &)
Definition: QXmppStanza.cpp:778
std::variant< T, QXmppError > Result
Definition: QXmppGlobal.h:209