biboumi_component.cpp 27.7 KB
Newer Older
1
2
3
4
5
6
7
#include <xmpp/biboumi_component.hpp>

#include <utils/timed_events.hpp>
#include <utils/scopeguard.hpp>
#include <utils/tolower.hpp>
#include <logger/logger.hpp>
#include <xmpp/adhoc_command.hpp>
8
#include <xmpp/biboumi_adhoc_commands.hpp>
9
10
11
#include <bridge/list_element.hpp>
#include <config/config.hpp>
#include <utils/sha1.hpp>
12
13
#include <utils/time.hpp>
#include <xmpp/jid.hpp>
14
15
16
17
18
19
20

#include <stdexcept>
#include <iostream>

#include <stdio.h>

#include <louloulibs.h>
21
#include <biboumi.h>
22

23
#include <uuid/uuid.h>
24
25
26
27
28

#ifdef SYSTEMD_FOUND
# include <systemd/sd-daemon.h>
#endif

29
30
#include <database/database.hpp>

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using namespace std::string_literals;

static std::set<std::string> kickable_errors{
    "gone",
    "internal-server-error",
    "item-not-found",
    "jid-malformed",
    "recipient-unavailable",
    "redirect",
    "remote-server-not-found",
    "remote-server-timeout",
    "service-unavailable",
    "malformed-error"
    };


BiboumiComponent::BiboumiComponent(std::shared_ptr<Poller> poller, const std::string& hostname, const std::string& secret):
48
  XmppComponent(poller, hostname, secret),
49
50
  irc_server_adhoc_commands_handler(*this),
  irc_channel_adhoc_commands_handler(*this)
51
52
53
54
55
56
57
58
{
  this->stanza_handlers.emplace("presence",
                                std::bind(&BiboumiComponent::handle_presence, this,std::placeholders::_1));
  this->stanza_handlers.emplace("message",
                                std::bind(&BiboumiComponent::handle_message, this,std::placeholders::_1));
  this->stanza_handlers.emplace("iq",
                                std::bind(&BiboumiComponent::handle_iq, this,std::placeholders::_1));

59
60
61
62
63
  this->adhoc_commands_handler.add_command("ping", {{&PingStep1}, "Do a ping", false});
  this->adhoc_commands_handler.add_command("hello", {{&HelloStep1, &HelloStep2}, "Receive a custom greeting", false});
  this->adhoc_commands_handler.add_command("disconnect-user", {{&DisconnectUserStep1, &DisconnectUserStep2}, "Disconnect selected users from the gateway", true});
  this->adhoc_commands_handler.add_command("hello", {{&HelloStep1, &HelloStep2}, "Receive a custom greeting", false});
  this->adhoc_commands_handler.add_command("reload", {{&Reload}, "Reload biboumi’s configuration", true});
64

65
66
#ifdef USE_DATABASE
  AdhocCommand configure_server_command({&ConfigureIrcServerStep1, &ConfigureIrcServerStep2}, "Configure a few settings for that IRC server", false);
67
  AdhocCommand configure_global_command({&ConfigureGlobalStep1, &ConfigureGlobalStep2}, "Configure a few settings", false);
68

69
  if (!Config::get("fixed_irc_server", "").empty())
70
    this->adhoc_commands_handler.add_command("configure", configure_server_command);
71
  else
72
    this->adhoc_commands_handler.add_command("configure", configure_global_command);
73

74
75
  this->irc_server_adhoc_commands_handler.add_command("configure", configure_server_command);
  this->irc_channel_adhoc_commands_handler.add_command("configure", {{&ConfigureIrcChannelStep1, &ConfigureIrcChannelStep2}, "Configure a few settings for that IRC channel", false});
76
#endif
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
}

void BiboumiComponent::shutdown()
{
  for (auto it = this->bridges.begin(); it != this->bridges.end(); ++it)
  {
    it->second->shutdown("Gateway shutdown");
  }
}

void BiboumiComponent::clean()
{
  auto it = this->bridges.begin();
  while (it != this->bridges.end())
  {
    it->second->clean();
    if (it->second->active_clients() == 0)
      it = this->bridges.erase(it);
    else
      ++it;
  }
}

void BiboumiComponent::handle_presence(const Stanza& stanza)
{
102
  std::string from_str = stanza.get_tag("from");
103
104
105
106
107
  std::string id = stanza.get_tag("id");
  std::string to_str = stanza.get_tag("to");
  std::string type = stanza.get_tag("type");

  // Check for mandatory tags
108
  if (from_str.empty())
109
110
111
112
113
114
    {
      log_warning("Received an invalid presence stanza: tag 'from' is missing.");
      return;
    }
  if (to_str.empty())
    {
115
      this->send_stanza_error("presence", from_str, this->served_hostname, id,
116
117
118
119
                              "modify", "bad-request", "Missing 'to' tag");
      return;
    }

120
  Bridge* bridge = this->get_user_bridge(from_str);
121
  Jid to(to_str);
122
  Jid from(from_str);
123
  Iid iid(to.local, bridge);
124
125
126
127
128
129
130
131
132
133

  // An error stanza is sent whenever we exit this function without
  // disabling this scopeguard.  If error_type and error_name are not
  // changed, the error signaled is internal-server-error. Change their
  // value to signal an other kind of error. For example
  // feature-not-implemented, etc.  Any non-error process should reach the
  // stanza_error.disable() call at the end of the function.
  std::string error_type("cancel");
  std::string error_name("internal-server-error");
  utils::ScopeGuard stanza_error([&](){
134
      this->send_stanza_error("presence", from_str, to_str, id,
135
136
137
                              error_type, error_name, "");
        });

138
  try {
139
  if (iid.type == Iid::Type::Channel && !iid.get_server().empty())
140
141
142
143
144
145
146
    { // presence toward a MUC that corresponds to an irc channel, or a
      // dummy channel if iid.chan is empty
      if (type.empty())
        {
          const std::string own_nick = bridge->get_own_nick(iid);
          if (!own_nick.empty() && own_nick != to.resource)
            bridge->send_irc_nick_change(iid, to.resource);
147
148
          const XmlNode* x = stanza.get_child("x", MUC_NS);
          const XmlNode* password = x ? x->get_child("password", MUC_NS): nullptr;
149
150
          bridge->join_irc_channel(iid, to.resource, password ? password->get_inner(): "",
                                   from.resource);
151
152
153
        }
      else if (type == "unavailable")
        {
154
          const XmlNode* status = stanza.get_child("status", COMPONENT_NS);
155
          bridge->leave_irc_channel(std::move(iid), status ? status->get_inner() : "", from.resource);
156
157
158
159
        }
    }
  else
    {
louiz’'s avatar
louiz’ committed
160
      // A user wants to join an invalid IRC channel, return a presence error to him/her
161
      if (type.empty())
162
        this->send_invalid_room_error(to.local, to.resource, from_str);
163
    }
164
165
166
  }
  catch (const IRCNotConnected& ex)
    {
167
      this->send_stanza_error("presence", from_str, to_str, id,
168
169
170
171
                              "cancel", "remote-server-not-found",
                              "Not connected to IRC server "s + ex.hostname,
                              true);
    }
172
173
174
175
176
  stanza_error.disable();
}

void BiboumiComponent::handle_message(const Stanza& stanza)
{
177
  std::string from_str = stanza.get_tag("from");
178
179
180
181
  std::string id = stanza.get_tag("id");
  std::string to_str = stanza.get_tag("to");
  std::string type = stanza.get_tag("type");

182
  if (from_str.empty())
183
184
185
    return;
  if (type.empty())
    type = "normal";
186
187
  Bridge* bridge = this->get_user_bridge(from_str);
  Jid from(from_str);
188
  Jid to(to_str);
189
  Iid iid(to.local, bridge);
190
191
192
193

  std::string error_type("cancel");
  std::string error_name("internal-server-error");
  utils::ScopeGuard stanza_error([&](){
194
      this->send_stanza_error("message", from_str, to_str, id,
195
196
                              error_type, error_name, "");
    });
197
  const XmlNode* body = stanza.get_child("body", COMPONENT_NS);
198
199

  try {                         // catch IRCNotConnected exceptions
200
  if (type == "groupchat" && iid.type == Iid::Type::Channel)
201
202
203
204
205
    {
      if (body && !body->get_inner().empty())
        {
          bridge->send_channel_message(iid, body->get_inner());
        }
206
      const XmlNode* subject = stanza.get_child("subject", COMPONENT_NS);
207
208
209
210
211
212
213
      if (subject)
        bridge->set_channel_topic(iid, subject->get_inner());
    }
  else if (type == "error")
    {
      const XmlNode* error = stanza.get_child("error", COMPONENT_NS);
      // Only a set of errors are considered “fatal”. If we encounter one of
214
      // them, we purge (we disconnect that resource from all the IRC servers)
215
216
217
218
219
220
221
222
223
224
      // We consider this to be true, unless the error condition is
      // specified and is not in the kickable_errors set
      bool kickable_error = true;
      if (error && error->has_children())
        {
          const XmlNode* condition = error->get_last_child();
          if (kickable_errors.find(condition->get_name()) == kickable_errors.end())
            kickable_error = false;
        }
      if (kickable_error)
225
        bridge->remove_resource(from.resource, "Error from remote client");
226
227
228
229
230
231
    }
  else if (type == "chat")
    {
      if (body && !body->get_inner().empty())
        {
          // a message for nick!server
232
          if (iid.type == Iid::Type::User && !iid.get_local().empty())
233
234
235
236
            {
              bridge->send_private_message(iid, body->get_inner());
              bridge->remove_preferred_from_jid(iid.get_local());
            }
237
          else if (iid.type != Iid::Type::User && !to.resource.empty())
238
239
240
            { // a message for chan%server@biboumi/Nick or
              // server@biboumi/Nick
              // Convert that into a message to nick!server
241
              Iid user_iid(utils::tolower(to.resource), iid.get_server(), Iid::Type::User);
242
243
244
              bridge->send_private_message(user_iid, body->get_inner());
              bridge->set_preferred_from_jid(user_iid.get_local(), to_str);
            }
245
          else if (iid.type == Iid::Type::Server)
louiz’'s avatar
louiz’ committed
246
247
248
249
            { // Message sent to the server JID
              // Convert the message body into a raw IRC message
              bridge->send_raw_message(iid.get_server(), body->get_inner());
            }
250
251
        }
    }
252
253
254
255
256
257
258
259
260
261
262
263
264
  else if (type == "normal" && iid.type == Iid::Type::Channel)
    {
      if (const XmlNode* x = stanza.get_child("x", MUC_USER_NS))
        if (const XmlNode* invite = x->get_child("invite", MUC_USER_NS))
          {
            const auto invite_to = invite->get_tag("to");
            if (!invite_to.empty())
              {
                bridge->send_irc_invitation(iid, invite_to);
              }
          }

    }
265
  else if (iid.type == Iid::Type::User)
266
    this->send_invalid_user_error(to.local, from_str);
267
268
  } catch (const IRCNotConnected& ex)
    {
269
      this->send_stanza_error("message", from_str, to_str, id,
270
271
272
273
                              "cancel", "remote-server-not-found",
                              "Not connected to IRC server "s + ex.hostname,
                              true);
    }
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
  stanza_error.disable();
}

// We MUST return an iq, whatever happens, except if the type is
// "result".
// To do this, we use a scopeguard. If an exception is raised somewhere, an
// iq of type error "internal-server-error" is sent. If we handle the
// request properly (by calling a function that registers an iq to be sent
// later, or that directly sends an iq), we disable the ScopeGuard. If we
// reach the end of the function without having disabled the scopeguard, we
// send a "feature-not-implemented" iq as a result.  If an other kind of
// error is found (for example the feature is implemented in biboumi, but
// the request is missing some attribute) we can just change the values of
// error_type and error_name and return from the function (without disabling
// the scopeguard); an iq error will be sent
void BiboumiComponent::handle_iq(const Stanza& stanza)
{
  std::string id = stanza.get_tag("id");
  std::string from = stanza.get_tag("from");
  std::string to_str = stanza.get_tag("to");
  std::string type = stanza.get_tag("type");

296
297
  if (from.empty()) {
    log_warning("Received an iq without a 'from'. Ignoring.");
298
    return;
299
  }
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
  if (id.empty() || to_str.empty() || type.empty())
    {
      this->send_stanza_error("iq", from, this->served_hostname, id,
                              "modify", "bad-request", "");
      return;
    }

  Bridge* bridge = this->get_user_bridge(from);
  Jid to(to_str);

  // These two values will be used in the error iq sent if we don't disable
  // the scopeguard.
  std::string error_type("cancel");
  std::string error_name("internal-server-error");
  utils::ScopeGuard stanza_error([&](){
      this->send_stanza_error("iq", from, to_str, id,
                              error_type, error_name, "");
    });
318
  try {
319
320
  if (type == "set")
    {
321
      const XmlNode* query;
322
323
324
325
326
327
328
329
330
331
      if ((query = stanza.get_child("query", MUC_ADMIN_NS)))
        {
          const XmlNode* child = query->get_child("item", MUC_ADMIN_NS);
          if (child)
            {
              std::string nick = child->get_tag("nick");
              std::string role = child->get_tag("role");
              std::string affiliation = child->get_tag("affiliation");
              if (!nick.empty())
                {
332
                  Iid iid(to.local, {});
333
334
335
                  if (role == "none")
                    {               // This is a kick
                      std::string reason;
336
                      const XmlNode* reason_el = child->get_child("reason", MUC_ADMIN_NS);
337
338
339
340
341
342
343
344
345
346
347
348
349
350
                      if (reason_el)
                        reason = reason_el->get_inner();
                      bridge->send_irc_kick(iid, nick, reason, id, from);
                    }
                  else
                    bridge->forward_affiliation_role_change(iid, nick, affiliation, role);
                  stanza_error.disable();
                }
            }
        }
      else if ((query = stanza.get_child("command", ADHOC_NS)))
        {
          Stanza response("iq");
          response["to"] = from;
351
          response["from"] = to_str;
352
          response["id"] = id;
353
354
355

          // Depending on the 'to' jid in the request, we use one adhoc
          // command handler or an other
356
          Iid iid(to.local, {});
357
          AdhocCommandsHandler* adhoc_handler;
358
          if (to.local.empty())
359
            adhoc_handler = &this->adhoc_commands_handler;
360
361
362
363
364
365
366
          else
          {
            if (iid.type == Iid::Type::Server)
              adhoc_handler = &this->irc_server_adhoc_commands_handler;
            else
              adhoc_handler = &this->irc_channel_adhoc_commands_handler;
          }
367
368
369
          // Execute the command, if any, and get a result XmlNode that we
          // insert in our response
          XmlNode inner_node = adhoc_handler->handle_request(from, to_str, *query);
370
371
372
373
374
375
376
377
          if (inner_node.get_child("error", ADHOC_NS))
            response["type"] = "error";
          else
            response["type"] = "result";
          response.add_child(std::move(inner_node));
          this->send_stanza(response);
          stanza_error.disable();
        }
378
379
380
381
382
383
384
#ifdef USE_DATABASE
      else if ((query = stanza.get_child("query", MAM_NS)))
        {
          if (this->handle_mam_request(stanza))
            stanza_error.disable();
        }
#endif
385
386
387
    }
  else if (type == "get")
    {
388
      const XmlNode* query;
389
390
      if ((query = stanza.get_child("query", DISCO_INFO_NS)))
        { // Disco info
391
392
          Iid iid(to.local, {});
          const std::string node = query->get_tag("node");
393
394
395
396
397
398
399
400
401
          if (to_str == this->served_hostname)
            {
              if (node.empty())
                {
                  // On the gateway itself
                  this->send_self_disco_info(id, from);
                  stanza_error.disable();
                }
            }
402
403
404
405
406
407
408
409
          else if (iid.type == Iid::Type::Server)
            {
                if (node.empty())
                {
                    this->send_irc_server_disco_info(id, from, to_str);
                    stanza_error.disable();
                }
            }
410
411
412
        }
      else if ((query = stanza.get_child("query", VERSION_NS)))
        {
413
414
          Iid iid(to.local, bridge);
          if (iid.type != Iid::Type::Server && !to.resource.empty())
415
416
417
            {
              // Get the IRC user version
              std::string target;
418
              if (iid.type == Iid::Type::User)
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
                target = iid.get_local();
              else
                target = to.resource;
              bridge->send_irc_version_request(iid.get_server(), target, id,
                                               from, to_str);
            }
          else
            {
              // On the gateway itself or on a channel
              this->send_version(id, from, to_str);
            }
          stanza_error.disable();
        }
      else if ((query = stanza.get_child("query", DISCO_ITEMS_NS)))
        {
434
          Iid iid(to.local, bridge);
435
436
437
          const std::string node = query->get_tag("node");
          if (node == ADHOC_NS)
            {
438
              Jid from_jid(from);
439
440
441
442
              if (to.local.empty())
                {               // Get biboumi's adhoc commands
                  this->send_adhoc_commands_list(id, from, this->served_hostname,
                                                 (Config::get("admin", "") ==
443
                                                  from_jid.bare()),
444
445
446
                                                 this->adhoc_commands_handler);
                  stanza_error.disable();
                }
447
              else if (iid.type == Iid::Type::Server)
448
449
450
                {               // Get the server's adhoc commands
                  this->send_adhoc_commands_list(id, from, to_str,
                                                 (Config::get("admin", "") ==
451
                                                  from_jid.bare()),
452
453
454
                                                 this->irc_server_adhoc_commands_handler);
                  stanza_error.disable();
                }
455
              else if (iid.type == Iid::Type::Channel)
456
457
458
459
460
461
462
                {               // Get the channel's adhoc commands
                  this->send_adhoc_commands_list(id, from, to_str,
                                                 (Config::get("admin", "") ==
                                                  from_jid.bare()),
                                                 this->irc_channel_adhoc_commands_handler);
                  stanza_error.disable();
                }
463
            }
464
          else if (node.empty() && iid.type == Iid::Type::Server)
465
466
467
468
469
470
471
            { // Disco on an IRC server: get the list of channels
              bridge->send_irc_channel_list_request(iid, id, from);
              stanza_error.disable();
            }
        }
      else if ((query = stanza.get_child("ping", PING_NS)))
        {
472
473
          Iid iid(to.local, bridge);
          if (iid.type == Iid::Type::User)
474
475
476
477
            { // Ping any user (no check on the nick done ourself)
              bridge->send_irc_user_ping_request(iid.get_server(),
                                                 iid.get_local(), id, from, to_str);
            }
478
          else if (iid.type == Iid::Type::Channel && !to.resource.empty())
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
            { // Ping a room participant (we check if the nick is in the room)
              bridge->send_irc_participant_ping_request(iid,
                                                        to.resource, id, from, to_str);
            }
          else
            { // Ping a channel, a server or the gateway itself
              bridge->on_gateway_ping(iid.get_server(),
                                     id, from, to_str);
            }
          stanza_error.disable();
        }
    }
  else if (type == "result")
    {
      stanza_error.disable();
494
      const XmlNode* query;
495
496
      if ((query = stanza.get_child("query", VERSION_NS)))
        {
497
498
499
          const XmlNode* name_node = query->get_child("name", VERSION_NS);
          const XmlNode* version_node = query->get_child("version", VERSION_NS);
          const XmlNode* os_node = query->get_child("os", VERSION_NS);
500
501
502
503
504
505
506
507
508
          std::string name;
          std::string version;
          std::string os;
          if (name_node)
            name = name_node->get_inner() + " (through the biboumi gateway)";
          if (version_node)
            version = version_node->get_inner();
          if (os_node)
            os = os_node->get_inner();
509
          const Iid iid(to.local, bridge);
510
511
512
513
514
515
516
517
518
519
520
521
          bridge->send_xmpp_version_to_irc(iid, name, version, os);
        }
      else
        {
          const auto it = this->waiting_iq.find(id);
          if (it != this->waiting_iq.end())
            {
              it->second(bridge, stanza);
              this->waiting_iq.erase(it);
            }
        }
    }
522
523
524
525
526
527
528
529
530
531
  }
  catch (const IRCNotConnected& ex)
    {
      this->send_stanza_error("iq", from, to_str, id,
                              "cancel", "remote-server-not-found",
                              "Not connected to IRC server "s + ex.hostname,
                              true);
      stanza_error.disable();
      return;
    }
532
533
534
535
  error_type = "cancel";
  error_name = "feature-not-implemented";
}

536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
#ifdef USE_DATABASE
bool BiboumiComponent::handle_mam_request(const Stanza& stanza)
{
    std::string id = stanza.get_tag("id");
    Jid from(stanza.get_tag("from"));
    Jid to(stanza.get_tag("to"));

    const XmlNode* query = stanza.get_child("query", MAM_NS);
    std::string query_id;
    if (query)
      query_id = query->get_tag("queryid");

    Iid iid(to.local, {'#', '&'});
    if (iid.type == Iid::Type::Channel && to.resource.empty())
      {
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
        std::string start;
        std::string end;
        const XmlNode* x = query->get_child("x", DATAFORM_NS);
        if (x)
          {
            const XmlNode* value;
            const auto fields = x->get_children("field", DATAFORM_NS);
            for (const auto& field: fields)
              {
                if (field->get_tag("var") == "start")
                  {
                    value = field->get_child("value", DATAFORM_NS);
                    if (value)
                      start = value->get_inner();
                  }
                else if (field->get_tag("var") == "end")
                  {
                    value = field->get_child("value", DATAFORM_NS);
                    if (value)
                      end = value->get_inner();
                  }
              }
          }
        const auto lines = Database::get_muc_logs(from.bare(), iid.get_local(), iid.get_server(), -1, start, end);
        for (const db::MucLogLine& line: lines)
        {
          const auto queryid = query->get_tag("queryid");
          if (!line.nick.value().empty())
            this->send_archived_message(line, to.full(), from.full(), queryid);
        }
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
        this->send_iq_result_full_jid(id, from.full(), to.full());
        return true;
      }
  return false;
}

void BiboumiComponent::send_archived_message(const db::MucLogLine& log_line, const std::string& from, const std::string& to,
                                             const std::string& queryid)
{
    Stanza message("message");
    message["from"] = from;
    message["to"] = to;

    XmlNode result("result");
    result["xmlns"] = MAM_NS;
    result["queryid"] = queryid;
    result["id"] = log_line.uuid.value();

    XmlNode forwarded("forwarded");
    forwarded["xmlns"] = FORWARD_NS;

    XmlNode delay("delay");
    delay["xmlns"] = DELAY_NS;
    delay["stamp"] = utils::to_string(log_line.date.value().timeStamp());

    forwarded.add_child(std::move(delay));

    XmlNode submessage("message");
    submessage["xmlns"] = CLIENT_NS;
    submessage["from"] = from + "/" + log_line.nick.value();
    submessage["type"] = "groupchat";

    XmlNode body("body");
    body.set_inner(log_line.body.value());
    submessage.add_child(std::move(body));

    forwarded.add_child(std::move(submessage));
    result.add_child(std::move(forwarded));
    message.add_child(std::move(result));

    this->send_stanza(message);
}

#endif

626
627
Bridge* BiboumiComponent::get_user_bridge(const std::string& user_jid)
{
628
  auto bare_jid = Jid{user_jid}.bare();
629
630
  try
    {
631
      return this->bridges.at(bare_jid).get();
632
633
634
    }
  catch (const std::out_of_range& exception)
    {
635
636
      this->bridges.emplace(bare_jid, std::make_unique<Bridge>(bare_jid, *this, this->poller));
      return this->bridges.at(bare_jid).get();
637
638
639
    }
}

640
Bridge* BiboumiComponent::find_user_bridge(const std::string& full_jid)
641
{
642
  auto bare_jid = Jid{full_jid}.bare();
643
644
  try
    {
645
      return this->bridges.at(bare_jid).get();
646
647
648
649
650
651
652
    }
  catch (const std::out_of_range& exception)
    {
      return nullptr;
    }
}

louiz’'s avatar
louiz’ committed
653
std::vector<Bridge*> BiboumiComponent::get_bridges() const
654
{
louiz’'s avatar
louiz’ committed
655
  std::vector<Bridge*> res;
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
  for (auto it = this->bridges.begin(); it != this->bridges.end(); ++it)
    res.push_back(it->second.get());
  return res;
}

void BiboumiComponent::send_self_disco_info(const std::string& id, const std::string& jid_to)
{
  Stanza iq("iq");
  iq["type"] = "result";
  iq["id"] = id;
  iq["to"] = jid_to;
  iq["from"] = this->served_hostname;
  XmlNode query("query");
  query["xmlns"] = DISCO_INFO_NS;
  XmlNode identity("identity");
  identity["category"] = "conference";
  identity["type"] = "irc";
  identity["name"] = "Biboumi XMPP-IRC gateway";
  query.add_child(std::move(identity));
675
  for (const char* ns: {DISCO_INFO_NS, MUC_NS, ADHOC_NS, PING_NS, MAM_NS, VERSION_NS})
676
677
678
679
680
681
682
683
684
    {
      XmlNode feature("feature");
      feature["var"] = ns;
      query.add_child(std::move(feature));
    }
  iq.add_child(std::move(query));
  this->send_stanza(iq);
}

685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
void BiboumiComponent::send_irc_server_disco_info(const std::string& id, const std::string& jid_to, const std::string& jid_from)
{
  Jid from(jid_from);
  Stanza iq("iq");
  iq["type"] = "result";
  iq["id"] = id;
  iq["to"] = jid_to;
  iq["from"] = jid_from;
  XmlNode query("query");
  query["xmlns"] = DISCO_INFO_NS;
  XmlNode identity("identity");
  identity["category"] = "conference";
  identity["type"] = "irc";
  identity["name"] = "IRC server "s + from.local + " over Biboumi";
  query.add_child(std::move(identity));
  for (const char* ns: {DISCO_INFO_NS, ADHOC_NS, PING_NS, VERSION_NS})
    {
      XmlNode feature("feature");
      feature["var"] = ns;
      query.add_child(std::move(feature));
    }
  iq.add_child(std::move(query));
  this->send_stanza(iq);
}

710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
void BiboumiComponent::send_iq_version_request(const std::string& from,
                                            const std::string& jid_to)
{
  Stanza iq("iq");
  iq["type"] = "get";
  iq["id"] = "version_"s + this->next_id();
  iq["from"] = from + "@" + this->served_hostname;
  iq["to"] = jid_to;
  XmlNode query("query");
  query["xmlns"] = VERSION_NS;
  iq.add_child(std::move(query));
  this->send_stanza(iq);
}

void BiboumiComponent::send_ping_request(const std::string& from,
725
726
                                         const std::string& jid_to,
                                         const std::string& id)
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
{
  Stanza iq("iq");
  iq["type"] = "get";
  iq["id"] = id;
  iq["from"] = from + "@" + this->served_hostname;
  iq["to"] = jid_to;
  XmlNode ping("ping");
  ping["xmlns"] = PING_NS;
  iq.add_child(std::move(ping));
  this->send_stanza(iq);

  auto result_cb = [from, id](Bridge* bridge, const Stanza& stanza)
    {
      Jid to(stanza.get_tag("to"));
      if (to.local != from)
        {
          log_error("Received a corresponding ping result, but the 'to' from "
                    "the response mismatches the 'from' of the request");
        }
      else
747
        bridge->send_irc_ping_result({from, bridge}, id);
748
749
750
751
752
    };
  this->waiting_iq[id] = result_cb;
}

void BiboumiComponent::send_iq_room_list_result(const std::string& id,
753
                                             const std::string& to_jid,
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
                                             const std::string& from,
                                             const std::vector<ListElement>& rooms_list)
{
  Stanza iq("iq");
  iq["from"] = from + "@" + this->served_hostname;
  iq["to"] = to_jid;
  iq["id"] = id;
  iq["type"] = "result";
  XmlNode query("query");
  query["xmlns"] = DISCO_ITEMS_NS;
  for (const auto& room: rooms_list)
    {
      XmlNode item("item");
      item["jid"] = room.channel + "%" + from + "@" + this->served_hostname;
      query.add_child(std::move(item));
    }
  iq.add_child(std::move(query));
  this->send_stanza(iq);
}
louiz’'s avatar
louiz’ committed
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788

void BiboumiComponent::send_invitation(const std::string& room_target,
                                       const std::string& jid_to,
                                       const std::string& author_nick)
{
  Stanza message("message");
  message["from"] = room_target + "@" + this->served_hostname;
  message["to"] = jid_to;
  XmlNode x("x");
  x["xmlns"] = MUC_USER_NS;
  XmlNode invite("invite");
  invite["from"] = room_target + "@" + this->served_hostname + "/" + author_nick;
  x.add_child(std::move(invite));
  message.add_child(std::move(x));
  this->send_stanza(message);
}