QXmpp  Version: 1.15.1
QXmppOutgoingClient.h
1 // SPDX-FileCopyrightText: 2010 Manjeet Dahiya <manjeetdahiya@gmail.com>
2 // SPDX-FileCopyrightText: 2010 Jeremy LainĂ© <jeremy.laine@m4x.org>
3 // SPDX-FileCopyrightText: 2020 Linus Jahn <lnj@kaidan.im>
4 //
5 // SPDX-License-Identifier: LGPL-2.1-or-later
6 
7 #ifndef QXMPPOUTGOINGCLIENT_H
8 #define QXMPPOUTGOINGCLIENT_H
9 
10 #include "QXmppAuthenticationError.h"
11 #include "QXmppBindError.h"
12 #include "QXmppClient.h"
13 #include "QXmppPromise.h"
14 #include "QXmppStanza.h"
15 #include "QXmppStreamError.h"
16 
17 #include <QAbstractSocket>
18 
19 class QDomElement;
20 class QSslError;
21 class QSslSocket;
22 
23 class QXmppConfiguration;
24 class QXmppPresence;
25 class QXmppIq;
26 class QXmppMessage;
28 class QXmppOutgoingClientPrivate;
29 class TestClient;
30 
31 namespace QXmpp::Private {
32 class C2sStreamManager;
33 class CarbonManager;
34 class CsiManager;
35 class OutgoingIqManager;
36 class PingManager;
37 class SendDataInterface;
38 class StreamAckManager;
39 class XmppSocket;
40 struct Bind2Request;
41 struct Bind2Bound;
42 struct SmEnabled;
43 struct SmFailed;
44 struct SmResumed;
45 struct StreamErrorElement;
46 struct StreamOpen;
47 
48 enum HandleElementResult {
49  Accepted,
50  Rejected,
51  Finished,
52 };
53 
54 enum class AuthenticationMethod {
55  NonSasl,
56  Sasl,
57  Sasl2,
58 };
59 
60 struct SessionBegin {
61  bool smEnabled;
62  bool smResumed;
63  bool bind2Used;
64  bool fastTokenChanged;
65  AuthenticationMethod authenticationMethod;
66 };
67 
68 struct SessionEnd {
69  bool smCanResume;
70 };
71 } // namespace QXmpp::Private
72 
73 namespace QXmpp::Private::Sasl2 {
74 struct Authenticate;
75 struct StreamFeature;
76 struct Success;
77 } // namespace QXmpp::Private::Sasl2
78 
79 // The QXmppOutgoingClient class represents an outgoing XMPP stream to an XMPP server.
80 class QXMPP_EXPORT QXmppOutgoingClient : public QXmppLoggable
81 {
82  Q_OBJECT
83 
84 public:
85  using IqResult = std::variant<QDomElement, QXmppError>;
86  using ConnectionError = std::variant<QAbstractSocket::SocketError, QXmpp::TimeoutError, QXmpp::StreamError, QXmpp::AuthenticationError, QXmpp::BindError>;
87  static constexpr QStringView TaskName = u"client session";
88 
89  explicit QXmppOutgoingClient(QObject *parent);
90  ~QXmppOutgoingClient() override;
91 
92  void connectToHost();
93  void disconnectFromHost();
94  bool isAuthenticated() const;
95  bool isConnected() const;
96  QXmppTask<IqResult> sendIq(QXmppIq &&);
97 
98  QXmppStanza::Error::Condition xmppStreamError();
99 
100  QXmppConfiguration &configuration();
101 
102  QXmpp::Private::XmppSocket &xmppSocket() const;
103  QXmpp::Private::StreamAckManager &streamAckManager() const;
104  QXmpp::Private::OutgoingIqManager &iqManager() const;
105  QXmpp::Private::C2sStreamManager &c2sStreamManager() const;
106  QXmpp::Private::CarbonManager &carbonManager() const;
107  QXmpp::Private::CsiManager &csiManager() const;
108 
110  Q_SIGNAL void connected(const QXmpp::Private::SessionBegin &);
111 
113  Q_SIGNAL void disconnected(const QXmpp::Private::SessionEnd &);
114 
116  Q_SIGNAL void errorOccurred(const QString &text, const QXmppOutgoingClient::ConnectionError &details, QXmppClient::Error oldError);
117 
119  Q_SIGNAL void elementReceived(const QDomElement &element, bool &handled);
120 
122  Q_SIGNAL void presenceReceived(const QXmppPresence &);
123 
125  Q_SIGNAL void messageReceived(const QXmppMessage &);
126 
129  Q_SIGNAL void iqReceived(const QXmppIq &);
130 
132  Q_SIGNAL void sslErrors(const QList<QSslError> &errors);
133 
134 private:
135  void handleStart();
136  void handleStream(const QXmpp::Private::StreamOpen &stream);
137  void handlePacketReceived(const QDomElement &element);
138  QXmpp::Private::HandleElementResult handleElement(const QDomElement &nodeRecv);
139  void handleStreamFeatures(const QXmppStreamFeatures &features);
140  void handleStreamError(const QXmpp::Private::StreamErrorElement &streamError);
141  bool handleStanza(const QDomElement &);
142  bool handleStarttls(const QXmppStreamFeatures &features);
143 
144  void handleSocketDisconnected();
145  void handleSocketError(const QString &text, std::variant<QXmpp::StreamError, QAbstractSocket::SocketError>);
146  void handleSocketSslErrors(const QList<QSslError> &);
147 
148  void startSasl2Auth(const QXmpp::Private::Sasl2::StreamFeature &sasl2Feature);
149  void startNonSaslAuth();
150  void startSmResume();
151  void startSmEnable();
152  void startResourceBinding();
153  void openSession();
154  void closeSession();
155  void setError(const QString &text, ConnectionError &&details);
156  void throwKeepAliveError();
157 
158  // for unit tests, see TestClient
159  void enableStreamManagement(bool resetSequenceNumber);
160  bool handleIqResponse(const QDomElement &);
161 
162  friend class QXmppOutgoingClientPrivate;
163  friend class QXmpp::Private::PingManager;
164  friend class QXmpp::Private::C2sStreamManager;
165  friend class QXmppRegistrationManager;
166  friend class TestClient;
167 
168  const std::unique_ptr<QXmppOutgoingClientPrivate> d;
169 };
170 
171 namespace QXmpp::Private {
172 
173 class C2sStreamManager
174 {
175 public:
176  using Result = std::variant<Success, QXmppError>;
177  static constexpr QStringView TaskName = u"stream management";
178 
179  explicit C2sStreamManager(QXmppOutgoingClient *q);
180 
181  HandleElementResult handleElement(const QDomElement &);
182  bool hasResumeAddress() const { return m_canResume && !m_resumeHost.isEmpty() && m_resumePort; }
183  std::pair<QString, quint16> resumeAddress() const { return { m_resumeHost, m_resumePort }; }
184  void onStreamStart();
185  void onStreamFeatures(const QXmppStreamFeatures &);
186  void onStreamClosed();
187  void onSasl2Authenticate(Sasl2::Authenticate &auth, const Sasl2::StreamFeature &feature);
188  void onSasl2Success(const Sasl2::Success &success);
189  void onBind2Request(Bind2Request &request, const std::vector<QString> &bind2Features);
190  void onBind2Bound(const Bind2Bound &);
191  bool canResume() const { return m_canResume; }
192  bool enabled() const { return m_enabled; }
193  bool streamResumed() const { return m_streamResumed; }
194  bool canRequestResume() const { return m_smAvailable && !m_enabled && m_canResume; }
195  QXmppTask<void> requestResume();
196  bool canRequestEnable() const { return m_smAvailable && !m_enabled; }
197  QXmppTask<void> requestEnable();
198  bool hasOngoingRequest() const { return !std::holds_alternative<NoRequest>(m_request); }
199 
200 private:
201  friend class ::TestClient;
202 
203  void onEnabled(const SmEnabled &enabled);
204  void onEnableFailed(const SmFailed &failed);
205  void onResumed(const SmResumed &resumed);
206  void onResumeFailed(const SmFailed &failed);
207  bool setResumeAddress(const QString &address);
208  void setEnabled(bool enabled) { m_enabled = enabled; }
209  void setResumed(bool resumed) { m_streamResumed = resumed; }
210 
211  struct NoRequest { };
212  struct ResumeRequest {
214  };
215  struct EnableRequest {
217  };
218 
219  QXmppOutgoingClient *q;
220 
221  std::variant<NoRequest, ResumeRequest, EnableRequest> m_request;
222  bool m_smAvailable = false;
223  QString m_smId;
224  bool m_canResume = false;
225  QString m_resumeHost;
226  quint16 m_resumePort = 0;
227  bool m_enabled = false;
228  bool m_streamResumed = false;
229 };
230 
231 // XEP-0280: Message Carbons
232 class CarbonManager
233 {
234 public:
235  void setEnableViaBind2(bool enable) { m_enableViaBind2 = enable; }
236  bool enabled() const { return m_enabled; }
237  void onBind2Request(Bind2Request &request, const std::vector<QString> &bind2Features);
238  void onSessionOpened(const SessionBegin &session);
239 
240 private:
241  // whether to enable carbons via bind2 if available
242  bool m_enableViaBind2 = false;
243  // whether carbons have been enabled via bind2
244  bool m_enabled = false;
245  bool m_requested = false;
246 };
247 
248 // XEP-0352: Client State Indication
249 class CsiManager
250 {
251 public:
252  enum State {
253  Active,
254  Inactive,
255  };
256 
257  explicit CsiManager(QXmppOutgoingClient *client);
258 
259  State state() const { return m_state; }
260  void setState(State);
261  void onSessionOpened(const SessionBegin &);
262  void onStreamFeatures(const QXmppStreamFeatures &);
263  void onBind2Request(Bind2Request &request, const std::vector<QString> &bind2Features);
264 
265 private:
266  void sendState();
267 
268  QXmppOutgoingClient *m_client;
269  State m_state = Active;
270  bool m_synced = true;
271  bool m_featureAvailable = false;
272  bool m_bind2InactiveSet = false;
273 };
274 
275 } // namespace QXmpp::Private
276 
277 #endif // QXMPPOUTGOINGCLIENT_H
The QXmppConfiguration class holds configuration options.
Definition: QXmppConfiguration.h:36
The QXmppStreamFeatures class represents the features returned by an XMPP server or client...
Definition: QXmppStreamFeatures.h:22
The QXmppLoggable class represents a source of logging messages.
Definition: QXmppLogger.h:108
Definition: QXmppTask.h:67
Error
Definition: QXmppClient.h:76
Definition: QXmppSasl_p.h:175
The QXmppPresence class represents an XMPP presence stanza.
Definition: QXmppPresence.h:21
Condition
A detailed condition of the error.
Definition: QXmppStanza.h:128
The QXmppRegistrationManager class manages in-band registration and account management tasks like cha...
Definition: QXmppRegistrationManager.h:212
The QXmppIq class is the base class for all IQs.
Definition: QXmppIq.h:22
The QXmppMessage class represents an XMPP message.
Definition: QXmppMessage.h:63
Definition: Algorithms.h:14
std::variant< T, QXmppError > Result
Definition: QXmppGlobal.h:209