QXmpp Version: 1.11.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, QXmppError>
193{
195 using Result = std::variant<Payload, 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 QXmppError { error->text(), std::move(*error) };
212 } else {
213 return QXmppError {
214 u"Failed to parse error response"_s,
216 };
217 }
218 } else {
219 return QXmppError {
220 u"Received unexpected IQ type"_s,
222 };
223 }
224 },
225 std::move(sendResult));
226}
227
228// For usage with QXmppClient::sendIq()
229template<typename Payload>
230class CompatIq : public QXmppIq
231{
232public:
233 std::optional<Payload> payload;
234
235 template<IqType Type>
236 requires(Type == IqType::Get || Type == IqType::Set)
237 CompatIq(Iq<Type, Payload> &&iq) : QXmppIq(), payload(std::move(iq.payload))
238 {
239 setId(iq.id);
240 setFrom(iq.from);
241 setTo(iq.to);
242 setLang(iq.lang);
243 switch (Type) {
244 case IqType::Get:
246 break;
247 case IqType::Set:
249 break;
250 case IqType::Result:
252 break;
253 case IqType::Error:
255 break;
256 }
257 }
258
259 void parseElementFromChild(const QDomElement &el) override
260 {
261 payload = parseElement<Payload>(el.firstChildElement());
262 }
263 void toXmlElementFromChild(QXmlStreamWriter *writer) const override
264 {
265 XmlWriter(writer).write(payload);
266 }
267};
268
269} // namespace QXmpp::Private
270
271#endif // IQ_H
The QXmppIq class is the base class for all IQs.
Definition QXmppIq.h:23
@ 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:96
QString text() const
Definition QXmppStanza.cpp:219
@ Modify
The request needs to be changed and resent.
Definition QXmppStanza.h:106
@ Cancel
The error is not temporary.
Definition QXmppStanza.h:104
@ UndefinedCondition
An undefined condition was hit.
Definition QXmppStanza.h:138
@ UnexpectedRequest
The request was unexpected.
Definition QXmppStanza.h:139
void setLang(const QString &)
Definition QXmppStanza.cpp:739
void setFrom(const QString &)
Definition QXmppStanza.cpp:703
void setTo(const QString &)
Definition QXmppStanza.cpp:685
void setId(const QString &)
Definition QXmppStanza.cpp:721
Definition QXmppError.h:17