1 #pragma once
2 
3 #include "biboumi.h"
4 
5 #include <xmpp/adhoc_commands_handler.hpp>
6 #include <network/tcp_client_socket_handler.hpp>
7 #include <database/database.hpp>
8 #include <xmpp/xmpp_parser.hpp>
9 #include <xmpp/body.hpp>
10 
11 #include <unordered_map>
12 #include <memory>
13 #include <string>
14 #include <ctime>
15 #include <map>
16 
17 #define STREAM_NS        "http://etherx.jabber.org/streams"
18 #define COMPONENT_NS     "jabber:component:accept"
19 #define MUC_NS           "http://jabber.org/protocol/muc"
20 #define MUC_USER_NS      MUC_NS"#user"
21 #define MUC_ADMIN_NS     MUC_NS"#admin"
22 #define MUC_OWNER_NS     MUC_NS"#owner"
23 #define DISCO_NS         "http://jabber.org/protocol/disco"
24 #define DISCO_ITEMS_NS   DISCO_NS"#items"
25 #define DISCO_INFO_NS    DISCO_NS"#info"
26 #define XHTMLIM_NS       "http://jabber.org/protocol/xhtml-im"
27 #define STANZA_NS        "urn:ietf:params:xml:ns:xmpp-stanzas"
28 #define STREAMS_NS       "urn:ietf:params:xml:ns:xmpp-streams"
29 #define VERSION_NS       "jabber:iq:version"
30 #define ADHOC_NS         "http://jabber.org/protocol/commands"
31 #define PING_NS          "urn:xmpp:ping"
32 #define DELAY_NS         "urn:xmpp:delay"
33 #define MAM_NS           "urn:xmpp:mam:2"
34 #define FORWARD_NS       "urn:xmpp:forward:0"
35 #define CLIENT_NS        "jabber:client"
36 #define DATAFORM_NS      "jabber:x:data"
37 #define RSM_NS           "http://jabber.org/protocol/rsm"
38 #define MUC_TRAFFIC_NS   "http://jabber.org/protocol/muc#traffic"
39 #define STABLE_ID_NS     "urn:xmpp:sid:0"
40 #define STABLE_MUC_ID_NS "http://jabber.org/protocol/muc#stable_id"
41 #define SELF_PING_FLAG   MUC_NS"#self-ping-optimization"
42 
43 /**
44  * An XMPP component, communicating with an XMPP server using the protocole
45  * described in XEP-0114: Jabber Component Protocol
46  *
47  * TODO: implement XEP-0225: Component Connections
48  */
49 class XmppComponent: public TCPClientSocketHandler
50 {
51 public:
52   explicit XmppComponent(std::shared_ptr<Poller>& poller, std::string hostname, std::string secret);
53   virtual ~XmppComponent() = default;
54 
55   XmppComponent(const XmppComponent&) = delete;
56   XmppComponent(XmppComponent&&) = delete;
57   XmppComponent& operator=(const XmppComponent&) = delete;
58   XmppComponent& operator=(XmppComponent&&) = delete;
59 
60   void on_connection_failed(const std::string& reason) override final;
61   void on_connected() override final;
62   void on_connection_close(const std::string& error) override final;
63   void parse_in_buffer(const size_t size) override final;
64 
65   /**
66    * Returns a unique id, to be used in the 'id' element of our iq stanzas.
67    */
68   static std::string next_id();
69   bool is_document_open() const;
70   /**
71    * Connect to the XMPP server.
72    */
73   void start();
74   /**
75    * Reset the component so we can use the component on a new XMPP stream
76    */
77   void reset();
78   /**
79    * Serialize the stanza and add it to the out_buf to be sent to the
80    * server.
81    */
82   void send_stanza(const Stanza& stanza);
83   /**
84    * Handle the opening of the remote stream
85    */
86   void on_remote_stream_open(const XmlNode& node);
87   /**
88    * Handle the closing of the remote stream
89    */
90   void on_remote_stream_close(const XmlNode& node);
91   /**
92    * Handle received stanzas
93    */
94   void on_stanza(const Stanza& stanza);
95   /**
96    * Send an error stanza. Message being the name of the element inside the
97    * stanza, and explanation being a short human-readable sentence
98    * describing the error.
99    */
100   void send_stream_error(const std::string& name, const std::string& explanation);
101   /**
102    * Send error stanza, described in http://xmpp.org/rfcs/rfc6120.html#stanzas-error
103    */
104   void send_stanza_error(const std::string& kind, const std::string& to, const std::string& from,
105                          const std::string& id, const std::string& error_type,
106                          const std::string& defined_condition, const std::string& text,
107                          const bool fulljid=true);
108   /**
109    * Send the closing signal for our document (not closing the connection though).
110    */
111   void close_document();
112   /**
113    * Send a message from from@served_hostname, with the given body
114    *
115    * If fulljid is false, the provided 'from' doesn't contain the
116    * server-part of the JID and must be added.
117    */
118   void send_message(const std::string& from, Xmpp::body&& body, const std::string& to,
119                     const std::string& type, const bool fulljid, const bool nocopy=false,
120                     const bool muc_private=false);
121   /**
122    * Send a join from a new participant
123    */
124   void send_user_join(const std::string& from,
125                       const std::string& nick,
126                       const std::string& realjid,
127                       const std::string& affiliation,
128                       const std::string& role,
129                       const std::string& to,
130                       const bool self);
131   /**
132    * Send the MUC topic to the user
133    */
134   void send_topic(const std::string& from, Xmpp::body&& xmpp_topic, const std::string& to, const std::string& who);
135   /**
136    * Send a (non-private) message to the MUC
137    */
138   Stanza make_muc_message(const std::string& muc_name, const std::string& nick, Xmpp::body&& xmpp_body, const std::string& jid_to,
139                           std::string uuid, std::string id);
140 #ifdef USE_DATABASE
141   /**
142    * Send a message, with a <delay/> element, part of a MUC history
143    */
144   void send_history_message(const std::string& muc_name, const std::string& nick, const std::string& body,
145                             const std::string& jid_to, Database::time_point::rep timestamp);
146 #endif
147   /**
148    * Send an unavailable presence for this nick
149    */
150   void send_muc_leave(const std::string& muc_name,
151                       const std::string& nick,
152                       Xmpp::body&& message,
153                       const std::string& jid_to,
154                       const bool self,
155                       const bool user_requested,
156                       const std::string& affiliation, const std::string& role);
157   /**
158    * Indicate that a participant changed his nick
159    */
160   void send_nick_change(const std::string& muc_name,
161                         const std::string& old_nick,
162                         const std::string& new_nick,
163                         const std::string& affiliation,
164                         const std::string& role,
165                         const std::string& jid_to,
166                         const bool self);
167   /**
168    * An user is kicked from a room
169    */
170   void kick_user(const std::string& muc_name, const std::string& target, const std::string& reason,
171                  const std::string& author, const std::string& jid_to, const bool self);
172   /**
173    * Send a generic presence error
174    */
175   void send_presence_error(const std::string& muc_name,
176                            const std::string& nickname,
177                            const std::string& jid_to,
178                            const std::string& type,
179                            const std::string& condition,
180                            const std::string& error_code,
181                            const std::string& text);
182   /**
183    * Send a presence from the MUC indicating a change in the role and/or
184    * affiliation of a participant
185    */
186   void send_affiliation_role_change(const std::string& muc_name,
187                                     const std::string& target,
188                                     const std::string& affiliation,
189                                     const std::string& role,
190                                     const std::string& jid_to);
191   /**
192    * Send a result IQ with the given version, or the gateway version if the
193    * passed string is empty.
194    */
195   void send_version(const std::string& id, const std::string& jid_to, const std::string& jid_from,
196                     const std::string& version="");
197   /**
198    * Send the list of all available ad-hoc commands to that JID. The list is
199    * different depending on what JID made the request.
200    */
201   void send_adhoc_commands_list(const std::string& id, const std::string& requester_jid, const std::string& from_jid,
202                                 const bool with_admin_only, const AdhocCommandsHandler& adhoc_handler);
203   /**
204    * Send an iq version request
205    */
206   void send_iq_version_request(const std::string& from,
207                                const std::string& jid_to);
208   /**
209    * Send an empty iq of type result
210    */
211   void send_iq_result(const std::string& id, const std::string& to_jid, const std::string& from);
212   void send_iq_result_full_jid(const std::string& id, const std::string& to_jid,
213                                const std::string& from_full_jid, std::unique_ptr<XmlNode> inner=nullptr);
214 
215   void handle_handshake(const Stanza& stanza);
216   void handle_error(const Stanza& stanza);
217 
after_handshake()218   virtual void after_handshake() {}
219 
get_served_hostname() const220   const std::string& get_served_hostname() const
221   { return this->served_hostname; }
222 
223   /**
224    * Whether or not we ever succeeded our authentication to the XMPP server
225    */
226   bool ever_auth;
227   /**
228    * Whether or not this is the first consecutive try on connecting to the
229    * XMPP server.  We use this to delay the connection attempt for a few
230    * seconds, if it is not the first try.
231    */
232   bool first_connection_try;
233 
234 private:
235   /**
236    * Return a buffer provided by the XML parser, to read data directly into
237    * it, and avoiding some unnecessary copy.
238    */
239   void* get_receive_buffer(const size_t size) const override final;
240   XmppParser parser;
241   std::string stream_id;
242   std::string secret;
243   bool authenticated;
244   /**
245    * Whether or not OUR XMPP document is open
246    */
247   bool doc_open;
248 protected:
249   std::string served_hostname;
250 
251   std::unordered_map<std::string, std::function<void(const Stanza&)>> stanza_handlers;
252   AdhocCommandsHandler adhoc_commands_handler;
253 };
254 
255 
256