QXmpp: Iq.h Source File

QXmpp Version: 1.14.3

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 "XmlWriter.h"
14
15#include <optional>
16
17#include <QString>
18
19namespace QXmpp::Private {
20
21enum class IqType {
22 Get,
23 Set,
24 Result,
25 Error,
26};
27
28template<>
29struct 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
39template<IqType, typename Payload = void>
40struct Iq;
41
42template<typename Payload = void>
43using GetIq = Iq<IqType::Get, Payload>;
44
45template<typename Payload = void>
46using SetIq = Iq<IqType::Set, Payload>;
47
48template<typename Payload = void>
49using ResultIq = Iq<IqType::Result, Payload>;
50
51template<typename Payload = void>
52using ErrorIq = Iq<IqType::Error, Payload>;
53
54template<IqType Type, typename Payload = void>
55auto iqFromDomImpl(const QDomElement &el) -> std::optional<Iq<Type, Payload>>;
56
57template<IqType Type, typename Payload>
58 requires((Type == IqType::Get || Type == IqType::Set) && !std::is_void_v<Payload>)
59struct 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
84template<typename Payload>
85struct 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
109template<typename Payload>
110struct 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
136template<IqType Type, typename Payload>
137auto 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
191template<typename Payload>
192auto parseIqResponse(std::variant<QDomElement, QXmppError> &&sendResult) -> std::variant<Payload, QXmppStanza::Error, QXmppError>
193{
194 using StanzaError = QXmppStanza::Error;
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,
206 StanzaError(StanzaError::Cancel, StanzaError::UndefinedCondition),
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
222template<typename Payload>
223auto 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()
232template<typename Payload>
233class CompatIq : public QXmppIq
234{
235public:
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) {
247 setType(QXmppIq::Get);
248 } else if constexpr (Type == IqType::Set) {
249 setType(QXmppIq::Set);
250 } else if constexpr (Type == IqType::Result) {
251 setType(QXmppIq::Result);
252 } else if constexpr (Type == IqType::Error) {
253 setType(QXmppIq::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
272template<IqType Type, typename Payload>
273CompatIq(Iq<Type, Payload> &&) -> CompatIq<Payload>;
274
275template<typename Payload>
276CompatIq(QXmppIq::Type, Payload) -> CompatIq<Payload>;
277
278} // namespace QXmpp::Private
279
280#endif // IQ_H
QXmppIq::QXmppIq
QXmppIq(QXmppIq::Type type=QXmppIq::Get)
Definition QXmppIq.cpp:39
QXmppIq::Type
Type
This enum describes the type of IQ.
Definition QXmppIq.h:26
QXmppIq::Error
@ Error
Error response.
Definition QXmppIq.h:27
QXmppIq::Set
@ Set
Set request.
Definition QXmppIq.h:29
QXmppIq::Get
@ Get
Get request.
Definition QXmppIq.h:28
QXmppIq::Result
@ Result
Result.
Definition QXmppIq.h:30
QXmppIq::setType
void setType(QXmppIq::Type)
Definition QXmppIq.cpp:70
QXmppIq::type
QXmppIq::Type type() const
Definition QXmppIq.cpp:60
QXmppStanza::Error::Cancel
@ Cancel
The error is not temporary.
Definition QXmppStanza.h:120
QXmppStanza::Error::UndefinedCondition
@ UndefinedCondition
An undefined condition was hit.
Definition QXmppStanza.h:154
QXmppStanza::setLang
void setLang(const QString &)
Definition QXmppStanza.cpp:778
QXmppStanza::setFrom
void setFrom(const QString &)
Definition QXmppStanza.cpp:742
QXmppStanza::setTo
void setTo(const QString &)
Definition QXmppStanza.cpp:724
QXmppStanza::setId
void setId(const QString &)
Definition QXmppStanza.cpp:760
QXmpp::Result
std::variant< T, QXmppError > Result
Definition QXmppGlobal.h:209