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
257
258
259
260
261
262
263
264
265
266
267
|
#ifndef BRIDGE_INCLUDED
# define BRIDGE_INCLUDED
#include <irc/irc_message.hpp>
#include <irc/irc_client.hpp>
#include <bridge/colors.hpp>
#include <irc/irc_user.hpp>
#include <irc/iid.hpp>
#include <unordered_map>
#include <functional>
#include <exception>
#include <string>
#include <memory>
class BiboumiComponent;
class Poller;
/**
* A callback called for each IrcMessage we receive. If the message triggers
* a response, it must send ore or more iq and return true (in that case it
* is removed from the list), otherwise it must do nothing and just return
* false.
*/
using irc_responder_callback_t = std::function<bool(const std::string& irc_hostname, const IrcMessage& message)>;
/**
* One bridge is spawned for each XMPP user that uses the component. The
* bridge spawns IrcClients when needed (when the user wants to join a
* channel on a new server) and does the translation between the two
* protocols.
*/
class Bridge
{
public:
explicit Bridge(const std::string& user_jid, BiboumiComponent& xmpp, std::shared_ptr<Poller> poller);
~Bridge() = default;
Bridge(const Bridge&) = delete;
Bridge(Bridge&& other) = delete;
Bridge& operator=(const Bridge&) = delete;
Bridge& operator=(Bridge&&) = delete;
/**
* QUIT all connected IRC servers.
*/
void shutdown(const std::string& exit_message);
/**
* Remove all inactive IrcClients
*/
void clean();
/**
* Return the jid of the XMPP user using this bridge
*/
const std::string& get_jid() const;
std::string get_bare_jid() const;
static Xmpp::body make_xmpp_body(const std::string& str, const std::string& encodin = "ISO-8859-1");
/***
**
** From XMPP to IRC.
**
**/
/**
* Try to join an irc_channel, does nothing and return true if the channel
* was already joined.
*/
bool join_irc_channel(const Iid& iid, const std::string& nickname, const std::string& password);
void send_channel_message(const Iid& iid, const std::string& body);
void send_private_message(const Iid& iid, const std::string& body, const std::string& type="PRIVMSG");
void send_raw_message(const std::string& hostname, const std::string& body);
void leave_irc_channel(Iid&& iid, std::string&& status_message);
void send_irc_nick_change(const Iid& iid, const std::string& new_nick);
void send_irc_kick(const Iid& iid, const std::string& target, const std::string& reason,
const std::string& iq_id, const std::string& to_jid);
void set_channel_topic(const Iid& iid, const std::string& subject);
void send_xmpp_version_to_irc(const Iid& iid, const std::string& name, const std::string& version,
const std::string& os);
void send_irc_ping_result(const Iid& iid, const std::string& id);
void send_irc_version_request(const std::string& irc_hostname, const std::string& target,
const std::string& iq_id, const std::string& to_jid,
const std::string& from_jid);
void send_irc_channel_list_request(const Iid& iid, const std::string& iq_id,
const std::string& to_jid);
void forward_affiliation_role_change(const Iid& iid, const std::string& nick,
const std::string& affiliation, const std::string& role);
/**
* Directly send a CTCP PING request to the IRC user
*/
void send_irc_user_ping_request(const std::string& irc_hostname, const std::string& nick,
const std::string& iq_id, const std::string& to_jid,
const std::string& from_jid);
/**
* First check if the participant is in the room, before sending a direct
* CTCP PING request to the IRC user
*/
void send_irc_participant_ping_request(const Iid& iid, const std::string& nick,
const std::string& iq_id, const std::string& to_jid,
const std::string& from_jid);
/**
* Directly send back a result if it's a gateway ping or if we are
* connected to the given IRC server, an error otherwise.
*/
void on_gateway_ping(const std::string& irc_hostname, const std::string& iq_id, const std::string& to_jid,
const std::string& from_jid);
/***
**
** From IRC to XMPP.
**
**/
/**
* Send a message corresponding to a server NOTICE, the from attribute
* should be juste the server hostname.
*/
void send_xmpp_message(const std::string& from, const std::string& author, const std::string& msg);
/**
* Send the presence of a new user in the MUC.
*/
void send_user_join(const std::string& hostname,
const std::string& chan_name,
const IrcUser* user,
const char user_mode,
const bool self);
/**
* Send the topic of the MUC to the user
*/
void send_topic(const std::string& hostname, const std::string& chan_name, const std::string& topic, const std::string& who);
/**
* Send a MUC message from some participant
*/
void send_message(const Iid& iid, const std::string& nick, const std::string& body, const bool muc);
/**
* Send a presence of type error, from a room.
*/
void send_presence_error(const Iid& iid, const std::string& nick, const std::string& type, const std::string& condition, const std::string& error_code, const std::string& text);
/**
* Send an unavailable presence from this participant
*/
void send_muc_leave(Iid&& iid, std::string&& nick, const std::string& message, const bool self);
/**
* Send presences to indicate that an user old_nick (ourself if self ==
* true) changed his nick to new_nick. The user_mode is needed because
* the xmpp presence needs ton contain the role and affiliation of the
* user.
*/
void send_nick_change(Iid&& iid,
const std::string& old_nick,
const std::string& new_nick,
const char user_mode,
const bool self);
void kick_muc_user(Iid&& iid, const std::string& target, const std::string& reason, const std::string& author);
void send_nickname_conflict_error(const Iid& iid, const std::string& nickname);
/**
* Send a role/affiliation change, matching the change of mode for that user
*/
void send_affiliation_role_change(const Iid& iid, const std::string& target, const char mode);
/**
* Send an iq version request coming from nick!hostname@
*/
void send_iq_version_request(const std::string& nick, const std::string& hostname);
/**
* Send an iq ping request coming from nick!hostname@
*/
void send_xmpp_ping_request(const std::string& nick, const std::string& hostname,
const std::string& id);
/**
* Misc
*/
std::string get_own_nick(const Iid& iid);
/**
* Get the number of server to which this bridge is connected or connecting.
*/
size_t active_clients() const;
/**
* Add (or replace the existing) <nick, jid> into the preferred_user_from map
*/
void set_preferred_from_jid(const std::string& nick, const std::string& full_jid);
/**
* Remove the preferred jid for the given IRC nick
*/
void remove_preferred_from_jid(const std::string& nick);
/**
* Add a callback to the waiting list of irc callbacks.
*/
void add_waiting_irc(irc_responder_callback_t&& callback);
/**
* Iter over all the waiting_iq, call the iq_responder_filter_t for each,
* whenever one of them returns true: call the corresponding
* iq_responder_callback_t and remove the callback from the list.
*/
void trigger_on_irc_message(const std::string& irc_hostname, const IrcMessage& message);
std::unordered_map<std::string, std::shared_ptr<IrcClient>>& get_irc_clients();
/**
* Manage which resource is in which channel
*/
void add_resource_to_chan(const std::string& channel, const std::string& resource);
void remove_resource_from_chan(const std::string& channel, const std::string& resource);
bool is_resource_in_chan(const std::string& channel, const std::string& resource) const;
private:
/**
* Returns the client for the given hostname, create one (and use the
* username in this case) if none is found, and connect that newly-created
* client immediately.
*/
IrcClient* make_irc_client(const std::string& hostname, const std::string& nickname);
/**
* This version does not create the IrcClient if it does not exist, throws
* a IRCServerNotConnected error in that case.
*/
IrcClient* get_irc_client(const std::string& hostname);
/**
* Idem, but returns nullptr if the server does not exist.
*/
IrcClient* find_irc_client(const std::string& hostname);
/**
* The JID of the user associated with this bridge. Messages from/to this
* JID are only managed by this bridge.
*/
const std::string user_jid;
/**
* One IrcClient for each IRC server we need to be connected to.
* The pointer is shared by the bridge and the poller.
*/
std::unordered_map<std::string, std::shared_ptr<IrcClient>> irc_clients;
/**
* To communicate back with the XMPP component
*/
BiboumiComponent& xmpp;
/**
* Poller, to give it the IrcClients that we spawn, to make it manage
* their sockets.
*/
std::shared_ptr<Poller> poller;
/**
* A map of <nick, full_jid>. For example if this map contains <"toto",
* "#somechan%server@biboumi/ToTo">, whenever a private message is
* received from the user "toto", instead of forwarding it to XMPP with
* from='toto!server@biboumi', we use instead
* from='#somechan%server@biboumi/ToTo'
*/
std::unordered_map<std::string, std::string> preferred_user_from;
/**
* A list of callbacks that are waiting for some IrcMessage to trigger a
* response. We add callbacks in this list whenever we received an IQ
* request and we need a response from IRC to be able to provide the
* response iq.
*/
std::vector<irc_responder_callback_t> waiting_irc;
/**
* Keep track of which resource is in which channel.
*/
std::map<std::string, std::set<std::string>> resources_in_chan;
};
struct IRCNotConnected: public std::exception
{
IRCNotConnected(const std::string& hostname):
hostname(hostname) {}
const std::string hostname;
};
#endif // BRIDGE_INCLUDED
|