QXmpp  Version: 1.15.1
QXmppOutgoingClient_p.h
1 // SPDX-FileCopyrightText: 2024 Linus Jahn <lnj@kaidan.im>
2 //
3 // SPDX-License-Identifier: LGPL-2.1-or-later
4 
5 #ifndef QXMPPOUTGOINGCLIENT_P_H
6 #define QXMPPOUTGOINGCLIENT_P_H
7 
8 #include "QXmppOutgoingClient.h"
9 #include "QXmppPromise.h"
10 #include "QXmppSaslManager_p.h"
11 #include "QXmppSasl_p.h"
12 #include "QXmppStreamManagement_p.h"
13 
14 #include "StreamError.h"
15 #include "XmppSocket.h"
16 
17 #include <QDnsLookup>
18 #include <QDomElement>
19 
20 class QTimer;
21 class QXmppPacket;
22 
23 // this leaks into other files, maybe better put QXmppOutgoingClientPrivate into QXmpp::Private
24 using namespace QXmpp::Private;
25 
26 namespace QXmpp::Private {
27 
28 using LegacyError = std::variant<QAbstractSocket::SocketError, QXmpp::TimeoutError, QXmppStanza::Error::Condition>;
29 
30 // STARTTLS
31 class StarttlsManager
32 {
33 public:
34  static constexpr QStringView TaskName = u"STARTTLS";
35  QXmppTask<void> task() { return m_promise.task(); }
36  HandleElementResult handleElement(const QDomElement &el);
37 
38 private:
39  QXmppPromise<void> m_promise;
40 };
41 
42 struct ProtocolError {
43  QString text;
44 };
45 
46 struct BoundAddress {
47  QString user;
48  QString domain;
49  QString resource;
50 };
51 
52 // Resource Binding
53 class BindManager
54 {
55 public:
56  using Result = std::variant<BoundAddress, QXmppStanza::Error, ProtocolError>;
57  static constexpr QStringView TaskName = u"resource binding";
58 
59  explicit BindManager(SendDataInterface *socket) : m_socket(socket) { }
60 
61  QXmppTask<Result> bindAddress(const QString &resource);
62  HandleElementResult handleElement(const QDomElement &el);
63 
64 private:
65  SendDataInterface *m_socket;
66  QString m_iqId;
67  std::optional<QXmppPromise<Result>> m_promise;
68 };
69 
70 struct NonSaslAuthOptions {
71  bool plain;
72  bool digest;
73 };
74 
75 // Authentication using Non-SASL auth
76 class NonSaslAuthManager
77 {
78 public:
79  using OptionsResult = std::variant<NonSaslAuthOptions, QXmppError>;
80  using AuthResult = std::variant<Success, QXmppError>;
81  static constexpr QStringView TaskName = u"Non-SASL authentication";
82 
83  explicit NonSaslAuthManager(SendDataInterface *socket) : m_socket(socket) { }
84 
85  QXmppTask<OptionsResult> queryOptions(const QString &streamFrom, const QString &username);
86  QXmppTask<AuthResult> authenticate(bool plainText, const QString &username, const QString &password, const QString &resource, const QString &streamId);
87  HandleElementResult handleElement(const QDomElement &el);
88 
89 private:
90  struct NoQuery {
91  };
92  struct OptionsQuery {
94  };
95  struct AuthQuery {
97  QString id;
98  };
99 
100  SendDataInterface *m_socket;
101  std::variant<NoQuery, OptionsQuery, AuthQuery> m_query;
102 };
103 
104 // XEP-0199: XMPP Ping
105 class PingManager
106 {
107 public:
108  explicit PingManager(QXmppOutgoingClient *q);
109 
110  void onDataReceived();
111 
112 private:
113  void sendPing();
114 
115  QXmppOutgoingClient *q;
116  QTimer *pingTimer;
117  QTimer *timeoutTimer;
118 };
119 
120 using IqResult = QXmppOutgoingClient::IqResult;
121 
122 struct IqState {
123  QXmppPromise<IqResult> interface;
124  QString jid;
125 
126  explicit IqState(QString jid) : jid(std::move(jid)) { }
127  IqState(IqState &&) = default;
128  IqState &operator=(IqState &&) = default;
129  IqState(const IqState &) = delete;
130  IqState &operator=(const IqState &) = delete;
131 };
132 
133 // Manager for creating tasks for outgoing IQ requests
134 class OutgoingIqManager
135 {
136 public:
137  OutgoingIqManager(QXmppLoggable *l, StreamAckManager &streamAckMananger);
138  ~OutgoingIqManager();
139 
140  QXmppTask<IqResult> sendIq(QXmppIq &&, const QString &to);
141  QXmppTask<IqResult> sendIq(QXmppPacket &&, const QString &id, const QString &to);
142 
143  bool hasId(const QString &id) const;
144  bool isIdValid(const QString &id) const;
145 
146  QXmppTask<IqResult> start(const QString &id, const QString &to);
147  void finish(const QString &id, IqResult &&result);
148  void cancelAll();
149 
150  void onSessionOpened(const SessionBegin &);
151  void onSessionClosed(const SessionEnd &);
152  bool handleStanza(const QDomElement &stanza);
153 
154 private:
155  void warning(const QString &message);
156 
157  QXmppLoggable *l;
158  StreamAckManager &m_streamAckManager;
159  std::unordered_map<QString, IqState> m_requests;
160 };
161 
162 } // namespace QXmpp::Private
163 
164 class QXmppOutgoingClientPrivate
165 {
166 public:
167  struct Error {
168  QString text;
169  QXmppOutgoingClient::ConnectionError details;
170  LegacyError legacyError;
171  };
172 
173  explicit QXmppOutgoingClientPrivate(QXmppOutgoingClient *q);
174  void connectToHost(const ServerAddress &);
175  void connectToAddressList(std::vector<ServerAddress> &&);
176  void connectToNextAddress();
177 
178  // This object provides the configuration
179  // required for connecting to the XMPP server.
180  QXmppConfiguration config;
181  std::optional<Error> error;
182 
183  // Core stream
184  XmppSocket socket;
185  StreamAckManager streamAckManager;
186  OutgoingIqManager iqManager;
187 
188  // DNS
189  std::vector<ServerAddress> serverAddresses;
190  std::size_t nextServerAddressIndex = 0;
191  enum {
192  Current,
193  TryNext,
194  } nextAddressState = Current;
195 
196  QString streamId;
197 
198  // Redirection
199  std::optional<StreamErrorElement::SeeOtherHost> redirect;
200 
201  // Authentication & Session
202  bool isAuthenticated = false;
203  bool bindModeAvailable = false;
204  bool sessionStarted = false;
205  AuthenticationMethod authenticationMethod = AuthenticationMethod::Sasl;
206  std::optional<Bind2Bound> bind2Bound;
207 
208  std::variant<QXmppOutgoingClient *, StarttlsManager, NonSaslAuthManager, SaslManager, Sasl2Manager, C2sStreamManager *, BindManager> listener;
209  // Incremented every time setListener() installs a new listener to detect a replacements.
210  uint listenerGeneration = 0;
211  FastTokenManager fastTokenManager;
212  C2sStreamManager c2sStreamManager;
213  CarbonManager carbonManager;
214  CsiManager csiManager;
215  PingManager pingManager;
216 
217  template<typename T, typename... Args>
218  T &setListener(Args... args)
219  {
220  listener = T { args... };
221  ++listenerGeneration;
222  return std::get<T>(listener);
223  }
224 
225 private:
226  QXmppOutgoingClient *q;
227 };
228 
229 #endif // QXMPPOUTGOINGCLIENT_P_H
The QXmppConfiguration class holds configuration options.
Definition: QXmppConfiguration.h:36
Definition: QXmppTask.h:646
The QXmppLoggable class represents a source of logging messages.
Definition: QXmppLogger.h:108
Definition: QXmppTask.h:67
The QXmppIq class is the base class for all IQs.
Definition: QXmppIq.h:22
Definition: Algorithms.h:14
std::variant< T, QXmppError > Result
Definition: QXmppGlobal.h:209