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