bridge.cpp 7.72 KB
Newer Older
1
#include <bridge/bridge.hpp>
2
#include <bridge/colors.hpp>
3 4
#include <xmpp/xmpp_component.hpp>
#include <network/poller.hpp>
5
#include <utils/encoding.hpp>
louiz’'s avatar
louiz’ committed
6
#include <logger/logger.hpp>
louiz’'s avatar
louiz’ committed
7
#include <utils/split.hpp>
louiz’'s avatar
louiz’ committed
8 9
#include <iostream>

10 11 12
static const char* action_prefix = "\01ACTION ";
static const size_t action_prefix_len = 8;

13 14 15 16 17 18 19 20 21 22 23
Bridge::Bridge(const std::string& user_jid, XmppComponent* xmpp, Poller* poller):
  user_jid(user_jid),
  xmpp(xmpp),
  poller(poller)
{
}

Bridge::~Bridge()
{
}

louiz’'s avatar
louiz’ committed
24 25 26 27 28 29 30 31
void Bridge::shutdown()
{
  for (auto it = this->irc_clients.begin(); it != this->irc_clients.end(); ++it)
  {
    it->second->send_quit_command();
  }
}

louiz’'s avatar
louiz’ committed
32 33 34 35 36 37 38 39 40 41 42 43 44
void Bridge::clean()
{
  auto it = this->irc_clients.begin();
  while (it != this->irc_clients.end())
  {
    IrcClient* client = it->second.get();
    if (!client->is_connected())
      it = this->irc_clients.erase(it);
    else
      ++it;
  }
}

45
Xmpp::body Bridge::make_xmpp_body(const std::string& str)
46 47 48 49 50 51
{
  std::string res;
  if (utils::is_valid_utf8(str.c_str()))
    res = str;
  else
    res = utils::convert_to_utf8(str, "ISO-8859-1");
52
  return irc_format_to_xhtmlim(res);
53 54
}

55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
IrcClient* Bridge::get_irc_client(const std::string& hostname, const std::string& username)
{
  try
    {
      return this->irc_clients.at(hostname).get();
    }
  catch (const std::out_of_range& exception)
    {
      this->irc_clients.emplace(hostname, std::make_shared<IrcClient>(hostname, username, this));
      std::shared_ptr<IrcClient> irc = this->irc_clients.at(hostname);
      this->poller->add_socket_handler(irc);
      return irc.get();
    }
}

louiz’'s avatar
louiz’ committed
70 71 72 73 74 75 76 77 78 79 80 81
IrcClient* Bridge::get_irc_client(const std::string& hostname)
{
  try
    {
      return this->irc_clients.at(hostname).get();
    }
  catch (const std::out_of_range& exception)
    {
      return nullptr;
    }
}

louiz’'s avatar
louiz’ committed
82
bool Bridge::join_irc_channel(const Iid& iid, const std::string& username)
83 84
{
  IrcClient* irc = this->get_irc_client(iid.server, username);
louiz’'s avatar
louiz’ committed
85 86 87 88 89 90
  if (irc->is_channel_joined(iid.chan) == false)
    {
      irc->send_join_command(iid.chan);
      return true;
    }
  return false;
91 92
}

louiz’'s avatar
louiz’ committed
93 94
void Bridge::send_channel_message(const Iid& iid, const std::string& body)
{
95 96 97 98
  std::vector<std::string> lines = utils::split(body, '\n', true);
  if (lines.empty())
    return ;
  const std::string first_line = lines[0];
louiz’'s avatar
louiz’ committed
99 100
  if (iid.chan.empty() || iid.server.empty())
    {
louiz’'s avatar
louiz’ committed
101
      log_warning("Cannot send message to channel: [" << iid.chan << "] on server [" << iid.server << "]");
louiz’'s avatar
louiz’ committed
102 103 104 105 106
      return;
    }
  IrcClient* irc = this->get_irc_client(iid.server);
  if (!irc)
    {
louiz’'s avatar
louiz’ committed
107
      log_warning("Cannot send message: no client exist for server " << iid.server);
louiz’'s avatar
louiz’ committed
108 109
      return;
    }
110
  if (first_line.substr(0, 6) == "/mode ")
louiz’'s avatar
louiz’ committed
111
    {
112
      std::vector<std::string> args = utils::split(first_line.substr(6), ' ', false);
louiz’'s avatar
louiz’ committed
113 114 115
      irc->send_mode_command(iid.chan, args);
      return;
    }
116 117
  if (first_line.substr(0, 4) == "/me ")
    irc->send_channel_message(iid.chan, action_prefix + first_line.substr(4) + "\01");
118
  else
119 120 121 122
    irc->send_channel_message(iid.chan, first_line);
  // Send each of the other lines of the message as a separate IRC message
  for (std::vector<std::string>::const_iterator it = lines.begin() + 1; it != lines.end(); ++it)
    irc->send_channel_message(iid.chan, *it);
123 124
  // We do not need to convert body to utf-8: it comes from our XMPP server,
  // so it's ok to send it back
125 126
  this->xmpp->send_muc_message(iid.chan + "%" + iid.server, irc->get_own_nick(),
                               this->make_xmpp_body(body), this->user_jid);
louiz’'s avatar
louiz’ committed
127 128
}

louiz’'s avatar
louiz’ committed
129 130 131 132 133 134 135 136 137
void Bridge::send_private_message(const Iid& iid, const std::string& body)
{
  if (iid.chan.empty() || iid.server.empty())
    return ;
  IrcClient* irc = this->get_irc_client(iid.server);
  if (irc)
    irc->send_private_message(iid.chan, body);
}

louiz’'s avatar
louiz’ committed
138 139 140 141 142 143 144
void Bridge::leave_irc_channel(Iid&& iid, std::string&& status_message)
{
  IrcClient* irc = this->get_irc_client(iid.server);
  if (irc)
    irc->send_part_command(iid.chan, status_message);
}

louiz’'s avatar
louiz’ committed
145 146 147 148 149 150 151
void Bridge::send_irc_nick_change(const Iid& iid, const std::string& new_nick)
{
  IrcClient* irc = this->get_irc_client(iid.server);
  if (irc)
    irc->send_nick_command(new_nick);
}

152 153 154 155 156 157 158
void Bridge::send_irc_kick(const Iid& iid, const std::string& target, const std::string& reason)
{
  IrcClient* irc = this->get_irc_client(iid.server);
  if (irc)
    irc->send_kick_command(iid.chan, target, reason);
}

159 160 161 162 163 164 165
void Bridge::set_channel_topic(const Iid& iid, const std::string& subject)
{
  IrcClient* irc = this->get_irc_client(iid.server);
  if (irc)
    irc->send_topic_command(iid.chan, subject);
}

louiz’'s avatar
louiz’ committed
166
void Bridge::send_message(const Iid& iid, const std::string& nick, const std::string& body, const bool muc)
louiz’'s avatar
louiz’ committed
167
{
louiz’'s avatar
louiz’ committed
168
  if (muc)
169 170
    this->xmpp->send_muc_message(iid.chan + "%" + iid.server, nick,
                                 this->make_xmpp_body(body), this->user_jid);
louiz’'s avatar
louiz’ committed
171
  else
172 173
    this->xmpp->send_message(iid.chan + "%" + iid.server,
                             this->make_xmpp_body(body), this->user_jid);
louiz’'s avatar
louiz’ committed
174 175
}

louiz’'s avatar
louiz’ committed
176
void Bridge::send_muc_leave(Iid&& iid, std::string&& nick, const std::string& message, const bool self)
louiz’'s avatar
louiz’ committed
177
{
178
  this->xmpp->send_muc_leave(std::move(iid.chan) + "%" + std::move(iid.server), std::move(nick), this->make_xmpp_body(message), this->user_jid, self);
louiz’'s avatar
louiz’ committed
179 180
}

louiz’'s avatar
louiz’ committed
181 182 183 184 185 186
void Bridge::send_nick_change(Iid&& iid, const std::string& old_nick, const std::string& new_nick, const bool self)
{
  this->xmpp->send_nick_change(std::move(iid.chan) + "%" + std::move(iid.server),
                               old_nick, new_nick, this->user_jid, self);
}

187 188
void Bridge::send_xmpp_message(const std::string& from, const std::string& author, const std::string& msg)
{
louiz’'s avatar
louiz’ committed
189 190 191 192 193
  std::string body;
  if (!author.empty())
    body = std::string("[") + author + std::string("] ") + msg;
  else
    body = msg;
194
  this->xmpp->send_message(from, this->make_xmpp_body(body), this->user_jid);
195 196
}

197 198
void Bridge::send_user_join(const std::string& hostname,
                            const std::string& chan_name,
199 200
                            const IrcUser* user,
                            const bool self)
201
{
202 203 204 205 206 207 208 209 210
  std::string affiliation = "participant";
  std::string role = "none";
  if (user->modes.find('o') != user->modes.end())
    {
      affiliation = "admin";
      role = "moderator";
    }
  this->xmpp->send_user_join(chan_name + "%" + hostname, user->nick, user->host,
                             affiliation, role, this->user_jid, self);
211 212 213 214
}

void Bridge::send_topic(const std::string& hostname, const std::string& chan_name, const std::string topic)
{
215
  this->xmpp->send_topic(chan_name + "%" + hostname, this->make_xmpp_body(topic), this->user_jid);
216
}
louiz’'s avatar
louiz’ committed
217 218 219 220 221 222 223 224

std::string Bridge::get_own_nick(const Iid& iid)
{
  IrcClient* irc = this->get_irc_client(iid.server);
  if (irc)
    return irc->get_own_nick();
  return "";
}
225 226 227 228 229

void Bridge::kick_muc_user(Iid&& iid, const std::string& target, const std::string& reason, const std::string& author)
{
  this->xmpp->kick_user(iid.chan + "%" + iid.server, target, reason, author, this->user_jid);
}
230 231 232 233 234

void Bridge::send_nickname_conflict_error(const Iid& iid, const std::string& nickname)
{
  this->xmpp->send_nickname_conflict_error(iid.chan + "%" + iid.server, nickname, this->user_jid);
}
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

void Bridge::send_affiliation_role_change(const Iid& iid, const std::string& target, const char mode)
{
  std::string role;
  std::string affiliation;
  if (mode == 0)
    {
      role = "participant";
      affiliation = "none";
    }
  else if (mode == 'a')
    {
      role = "moderator";
      affiliation = "owner";
    }
  else if (mode == 'o')
    {
      role = "moderator";
      affiliation = "admin";
    }
  else if (mode == 'h')
    {
      role = "moderator";
      affiliation = "member";
    }
  else if (mode == 'v')
    {
      role = "participant";
      affiliation = "member";
    }
  this->xmpp->send_affiliation_role_change(iid.chan + "%" + iid.server, target, affiliation, role, this->user_jid);
}