~singpolyma/biboumi

ref: e0c50dd99a6343000e147711d4a75dacd3853341 biboumi/src/xmpp/xmpp_component.hpp -rw-r--r-- 9.5 KiB
e0c50dd9Stephen Paul Weber Add gateway/irc and caps 6 months ago
                                                                                
81f8f45b louiz’
58fd6891 louiz’
e1a7114c Florent Le Coz
0c8adc85 louiz’
58fd6891 louiz’
e1a7114c Florent Le Coz
5e59cc51 louiz’
e1a7114c Florent Le Coz
e1a7114c Florent Le Coz
8cf0b833 louiz’
e1a7114c Florent Le Coz
7536a1b3 louiz’
a3844c1d louiz’
7536a1b3 louiz’
3047bd41 louiz’
76a8189b louiz’
1d197ff2 louiz’
7f2127a7 louiz’
0de282a1 louiz’
b68f3605 Jonas Schäfer
e1a7114c Florent Le Coz
0c8adc85 louiz’
e1a7114c Florent Le Coz
1a09c965 louiz’
e1a7114c Florent Le Coz
af420738 louiz’
e1a7114c Florent Le Coz
1a09c965 louiz’
e1a7114c Florent Le Coz
dfc0793e louiz’
37340e59 louiz’
e1a7114c Florent Le Coz
04d28f96 louiz’
e1a7114c Florent Le Coz
e9670889 louiz’
58fd6891 louiz’
e1a7114c Florent Le Coz
d1626c92 louiz’
61de6b1d louiz’
58fd6891 louiz’
d1626c92 louiz’
e1a7114c Florent Le Coz
9fa1852c louiz’
bb596582 louiz’
e1a7114c Florent Le Coz
8ac8d2b2 louiz’
e1a7114c Florent Le Coz
45e8fe56 Florent Le Coz
e1a7114c Florent Le Coz
b2e7edee louiz’
131ef994 louiz’
e1a7114c Florent Le Coz
76a8189b louiz’
e1a7114c Florent Le Coz
81f8f45b louiz’
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
#pragma once

#include "biboumi.h"

#include <xmpp/adhoc_commands_handler.hpp>
#include <network/tcp_client_socket_handler.hpp>
#include <database/database.hpp>
#include <xmpp/xmpp_parser.hpp>
#include <xmpp/body.hpp>

#include <unordered_map>
#include <memory>
#include <string>
#include <ctime>
#include <map>

#define STREAM_NS        "http://etherx.jabber.org/streams"
#define COMPONENT_NS     "jabber:component:accept"
#define CAPS_NS          "http://jabber.org/protocol/caps"
#define MUC_NS           "http://jabber.org/protocol/muc"
#define MUC_USER_NS      MUC_NS"#user"
#define MUC_ADMIN_NS     MUC_NS"#admin"
#define MUC_OWNER_NS     MUC_NS"#owner"
#define DISCO_NS         "http://jabber.org/protocol/disco"
#define DISCO_ITEMS_NS   DISCO_NS"#items"
#define DISCO_INFO_NS    DISCO_NS"#info"
#define XHTMLIM_NS       "http://jabber.org/protocol/xhtml-im"
#define STANZA_NS        "urn:ietf:params:xml:ns:xmpp-stanzas"
#define STREAMS_NS       "urn:ietf:params:xml:ns:xmpp-streams"
#define VERSION_NS       "jabber:iq:version"
#define ADHOC_NS         "http://jabber.org/protocol/commands"
#define PING_NS          "urn:xmpp:ping"
#define DELAY_NS         "urn:xmpp:delay"
#define MAM_NS           "urn:xmpp:mam:2"
#define FORWARD_NS       "urn:xmpp:forward:0"
#define CLIENT_NS        "jabber:client"
#define DATAFORM_NS      "jabber:x:data"
#define RSM_NS           "http://jabber.org/protocol/rsm"
#define MUC_TRAFFIC_NS   "http://jabber.org/protocol/muc#traffic"
#define STABLE_ID_NS     "urn:xmpp:sid:0"
#define STABLE_MUC_ID_NS "http://jabber.org/protocol/muc#stable_id"
#define SELF_PING_FLAG   MUC_NS"#self-ping-optimization"

/**
 * An XMPP component, communicating with an XMPP server using the protocole
 * described in XEP-0114: Jabber Component Protocol
 *
 * TODO: implement XEP-0225: Component Connections
 */
class XmppComponent: public TCPClientSocketHandler
{
public:
  explicit XmppComponent(std::shared_ptr<Poller>& poller, std::string hostname, std::string secret);
  virtual ~XmppComponent() = default;

  XmppComponent(const XmppComponent&) = delete;
  XmppComponent(XmppComponent&&) = delete;
  XmppComponent& operator=(const XmppComponent&) = delete;
  XmppComponent& operator=(XmppComponent&&) = delete;

  void on_connection_failed(const std::string& reason) override final;
  void on_connected() override final;
  void on_connection_close(const std::string& error) override final;
  void parse_in_buffer(const size_t size) override final;

  /**
   * Returns a unique id, to be used in the 'id' element of our iq stanzas.
   */
  static std::string next_id();
  bool is_document_open() const;
  /**
   * Connect to the XMPP server.
   */
  void start();
  /**
   * Reset the component so we can use the component on a new XMPP stream
   */
  void reset();
  /**
   * Serialize the stanza and add it to the out_buf to be sent to the
   * server.
   */
  void send_stanza(const Stanza& stanza);
  /**
   * Handle the opening of the remote stream
   */
  void on_remote_stream_open(const XmlNode& node);
  /**
   * Handle the closing of the remote stream
   */
  void on_remote_stream_close(const XmlNode& node);
  /**
   * Handle received stanzas
   */
  void on_stanza(const Stanza& stanza);
  /**
   * Send an error stanza. Message being the name of the element inside the
   * stanza, and explanation being a short human-readable sentence
   * describing the error.
   */
  void send_stream_error(const std::string& name, const std::string& explanation);
  /**
   * Send error stanza, described in http://xmpp.org/rfcs/rfc6120.html#stanzas-error
   */
  void send_stanza_error(const std::string& kind, const std::string& to, const std::string& from,
                         const std::string& id, const std::string& error_type,
                         const std::string& defined_condition, const std::string& text,
                         const bool fulljid=true);
  /**
   * Send the closing signal for our document (not closing the connection though).
   */
  void close_document();
  /**
   * Send a message from from@served_hostname, with the given body
   *
   * If fulljid is false, the provided 'from' doesn't contain the
   * server-part of the JID and must be added.
   */
  void send_message(const std::string& from, Xmpp::body&& body, const std::string& to,
                    const std::string& type, const bool fulljid, const bool nocopy=false,
                    const bool muc_private=false);
  /**
   * Send a join from a new participant
   */
  void send_user_join(const std::string& from,
                      const std::string& nick,
                      const std::string& realjid,
                      const std::string& affiliation,
                      const std::string& role,
                      const std::string& to,
                      const bool self);
  /**
   * Send the MUC topic to the user
   */
  void send_topic(const std::string& from, Xmpp::body&& xmpp_topic, const std::string& to, const std::string& who);
  /**
   * Send a (non-private) message to the MUC
   */
  Stanza make_muc_message(const std::string& muc_name, const std::string& nick, Xmpp::body&& xmpp_body, const std::string& jid_to,
                          std::string uuid, std::string id);
#ifdef USE_DATABASE
  /**
   * Send a message, with a <delay/> element, part of a MUC history
   */
  void send_history_message(const std::string& muc_name, const std::string& nick, const std::string& body,
                            const std::string& jid_to, Database::time_point::rep timestamp);
#endif
  /**
   * Send an unavailable presence for this nick
   */
  void send_muc_leave(const std::string& muc_name,
                      const std::string& nick,
                      Xmpp::body&& message,
                      const std::string& jid_to,
                      const bool self,
                      const bool user_requested,
                      const std::string& affiliation, const std::string& role);
  /**
   * Indicate that a participant changed his nick
   */
  void send_nick_change(const std::string& muc_name,
                        const std::string& old_nick,
                        const std::string& new_nick,
                        const std::string& affiliation,
                        const std::string& role,
                        const std::string& jid_to,
                        const bool self);
  /**
   * An user is kicked from a room
   */
  void kick_user(const std::string& muc_name, const std::string& target, const std::string& reason,
                 const std::string& author, const std::string& jid_to, const bool self);
  /**
   * Send a generic presence error
   */
  void send_presence_error(const std::string& muc_name,
                           const std::string& nickname,
                           const std::string& jid_to,
                           const std::string& type,
                           const std::string& condition,
                           const std::string& error_code,
                           const std::string& text);
  /**
   * Send a presence from the MUC indicating a change in the role and/or
   * affiliation of a participant
   */
  void send_affiliation_role_change(const std::string& muc_name,
                                    const std::string& target,
                                    const std::string& affiliation,
                                    const std::string& role,
                                    const std::string& jid_to);
  /**
   * Send a result IQ with the given version, or the gateway version if the
   * passed string is empty.
   */
  void send_version(const std::string& id, const std::string& jid_to, const std::string& jid_from,
                    const std::string& version="");
  /**
   * Send the list of all available ad-hoc commands to that JID. The list is
   * different depending on what JID made the request.
   */
  void send_adhoc_commands_list(const std::string& id, const std::string& requester_jid, const std::string& from_jid,
                                const bool with_admin_only, const AdhocCommandsHandler& adhoc_handler);
  /**
   * Send an iq version request
   */
  void send_iq_version_request(const std::string& from,
                               const std::string& jid_to);
  /**
   * Send an empty iq of type result
   */
  void send_iq_result(const std::string& id, const std::string& to_jid, const std::string& from);
  void send_iq_result_full_jid(const std::string& id, const std::string& to_jid,
                               const std::string& from_full_jid, std::unique_ptr<XmlNode> inner=nullptr);

  void handle_handshake(const Stanza& stanza);
  void handle_error(const Stanza& stanza);

  virtual void after_handshake() {}

  const std::string& get_served_hostname() const
  { return this->served_hostname; }

  /**
   * Whether or not we ever succeeded our authentication to the XMPP server
   */
  bool ever_auth;
  /**
   * Whether or not this is the first consecutive try on connecting to the
   * XMPP server.  We use this to delay the connection attempt for a few
   * seconds, if it is not the first try.
   */
  bool first_connection_try;

private:
  /**
   * Return a buffer provided by the XML parser, to read data directly into
   * it, and avoiding some unnecessary copy.
   */
  void* get_receive_buffer(const size_t size) const override final;
  XmppParser parser;
  std::string stream_id;
  std::string secret;
  bool authenticated;
  /**
   * Whether or not OUR XMPP document is open
   */
  bool doc_open;
protected:
  std::string served_hostname;

  std::unordered_map<std::string, std::function<void(const Stanza&)>> stanza_handlers;
  AdhocCommandsHandler adhoc_commands_handler;
};