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