xmpp_component.cpp 22.3 KB
Newer Older
1
#include <utils/make_unique.hpp>
2
#include <utils/scopeguard.hpp>
louiz’'s avatar
louiz’ committed
3
#include <logger/logger.hpp>
4

5
#include <xmpp/xmpp_component.hpp>
6
#include <xmpp/jid.hpp>
7

8
9
#include <utils/sha1.hpp>

louiz’'s avatar
louiz’ committed
10
#include <stdexcept>
11
12
#include <iostream>

13
#include <stdio.h>
14

louiz’'s avatar
louiz’ committed
15
16
17
18
19
20
#include <config.h>

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

21
22
23
24
#define STREAM_NS        "http://etherx.jabber.org/streams"
#define COMPONENT_NS     "jabber:component:accept"
#define MUC_NS           "http://jabber.org/protocol/muc"
#define MUC_USER_NS      MUC_NS"#user"
25
#define MUC_ADMIN_NS     MUC_NS"#admin"
26
27
28
#define DISCO_NS         "http://jabber.org/protocol/disco"
#define DISCO_ITEMS_NS   DISCO_NS"#items"
#define DISCO_INFO_NS    DISCO_NS"#info"
29
#define XHTMLIM_NS       "http://jabber.org/protocol/xhtml-im"
30
#define STANZA_NS        "urn:ietf:params:xml:ns:xmpp-stanzas"
louiz’'s avatar
louiz’ committed
31
#define STREAMS_NS       "urn:ietf:params:xml:ns:xmpp-streams"
32

33
XmppComponent::XmppComponent(const std::string& hostname, const std::string& secret):
34
35
  ever_auth(false),
  last_auth(false),
36
37
  served_hostname(hostname),
  secret(secret),
louiz’'s avatar
louiz’ committed
38
39
  authenticated(false),
  doc_open(false)
40
41
42
43
44
45
46
{
  this->parser.add_stream_open_callback(std::bind(&XmppComponent::on_remote_stream_open, this,
                                                  std::placeholders::_1));
  this->parser.add_stanza_callback(std::bind(&XmppComponent::on_stanza, this,
                                                  std::placeholders::_1));
  this->parser.add_stream_close_callback(std::bind(&XmppComponent::on_remote_stream_close, this,
                                                  std::placeholders::_1));
47
  this->stanza_handlers.emplace(COMPONENT_NS":handshake",
48
                                std::bind(&XmppComponent::handle_handshake, this,std::placeholders::_1));
49
  this->stanza_handlers.emplace(COMPONENT_NS":presence",
50
                                std::bind(&XmppComponent::handle_presence, this,std::placeholders::_1));
51
  this->stanza_handlers.emplace(COMPONENT_NS":message",
louiz’'s avatar
louiz’ committed
52
                                std::bind(&XmppComponent::handle_message, this,std::placeholders::_1));
53
54
  this->stanza_handlers.emplace(COMPONENT_NS":iq",
                                std::bind(&XmppComponent::handle_iq, this,std::placeholders::_1));
louiz’'s avatar
louiz’ committed
55
56
  this->stanza_handlers.emplace(STREAM_NS":error",
                                std::bind(&XmppComponent::handle_error, this,std::placeholders::_1));
57
58
59
60
61
62
}

XmppComponent::~XmppComponent()
{
}

63
void XmppComponent::start()
64
{
65
  this->connect("127.0.0.1", "5347");
66
67
}

louiz’'s avatar
louiz’ committed
68
69
70
71
72
bool XmppComponent::is_document_open() const
{
  return this->doc_open;
}

73
74
void XmppComponent::send_stanza(const Stanza& stanza)
{
louiz’'s avatar
louiz’ committed
75
76
77
  std::string str = stanza.to_string();
  log_debug("XMPP SENDING: " << str);
  this->send_data(std::move(str));
78
79
}

80
81
82
void XmppComponent::on_connection_failed(const std::string& reason)
{
  log_error("Failed to connect to the XMPP server: " << reason);
louiz’'s avatar
louiz’ committed
83
84
85
#ifdef SYSTEMDDAEMON_FOUND
  sd_notifyf(0, "STATUS=Failed to connect to the XMPP server: %s", reason.data());
#endif
86
87
}

88
89
void XmppComponent::on_connected()
{
louiz’'s avatar
louiz’ committed
90
  log_info("connected to XMPP server");
91
  XmlNode node("stream:stream", nullptr);
92
93
  node["xmlns"] = COMPONENT_NS;
  node["xmlns:stream"] = STREAM_NS;
94
  node["to"] = this->served_hostname;
95
  this->send_stanza(node);
louiz’'s avatar
louiz’ committed
96
  this->doc_open = true;
97
98
99
  // We may have some pending data to send: this happens when we try to send
  // some data before we are actually connected.  We send that data right now, if any
  this->send_pending_data();
100
101
102
103
}

void XmppComponent::on_connection_close()
{
louiz’'s avatar
louiz’ committed
104
  log_info("XMPP server closed connection");
105
106
}

107
void XmppComponent::parse_in_buffer(const size_t size)
108
{
109
110
111
112
113
114
115
116
117
118
119
120
  if (!this->in_buf.empty())
    { // This may happen if the parser could not allocate enough space for
      // us. We try to feed it the data that was read into our in_buf
      // instead. If this fails again we are in trouble.
      this->parser.feed(this->in_buf.data(), this->in_buf.size(), false);
      this->in_buf.clear();
    }
  else
    { // Just tell the parser to parse the data that was placed into the
      // buffer it provided to us with GetBuffer
      this->parser.parse(size, false);
    }
121
122
}

louiz’'s avatar
louiz’ committed
123
124
125
126
127
128
129
130
void XmppComponent::shutdown()
{
  for (auto it = this->bridges.begin(); it != this->bridges.end(); ++it)
  {
    it->second->shutdown();
  }
}

louiz’'s avatar
louiz’ committed
131
132
void XmppComponent::clean()
{
louiz’'s avatar
louiz’ committed
133
134
  auto it = this->bridges.begin();
  while (it != this->bridges.end())
louiz’'s avatar
louiz’ committed
135
136
  {
    it->second->clean();
137
    if (it->second->active_clients() == 0)
louiz’'s avatar
louiz’ committed
138
139
140
      it = this->bridges.erase(it);
    else
      ++it;
louiz’'s avatar
louiz’ committed
141
142
143
  }
}

144
145
void XmppComponent::on_remote_stream_open(const XmlNode& node)
{
louiz’'s avatar
louiz’ committed
146
  log_debug("XMPP DOCUMENT OPEN: " << node.to_string());
147
148
  this->stream_id = node.get_tag("id");
  if (this->stream_id.empty())
149
    {
louiz’'s avatar
louiz’ committed
150
      log_error("Error: no attribute 'id' found");
151
152
153
154
155
      this->send_stream_error("bad-format", "missing 'id' attribute");
      this->close_document();
      return ;
    }

156
  this->last_auth = false;
157
  // Try to authenticate
158
159
160
161
162
163
164
165
166
167
168
  char digest[HASH_LENGTH * 2 + 1];
  sha1nfo sha1;
  sha1_init(&sha1);
  sha1_write(&sha1, this->stream_id.data(), this->stream_id.size());
  sha1_write(&sha1, this->secret.data(),  this->secret.size());
  const uint8_t* result = sha1_result(&sha1);
  for (int i=0; i < HASH_LENGTH; i++)
    sprintf(digest + (i*2), "%02x", result[i]);
  digest[HASH_LENGTH * 2] = '\0';

  Stanza handshake("handshake");
169
170
171
172
173
174
175
  handshake.set_inner(digest);
  handshake.close();
  this->send_stanza(handshake);
}

void XmppComponent::on_remote_stream_close(const XmlNode& node)
{
louiz’'s avatar
louiz’ committed
176
  log_debug("XMPP DOCUMENT CLOSE " << node.to_string());
177
  this->doc_open = false;
178
179
}

180
181
182
183
184
void XmppComponent::reset()
{
  this->parser.reset();
}

185
186
void XmppComponent::on_stanza(const Stanza& stanza)
{
louiz’'s avatar
louiz’ committed
187
  log_debug("XMPP RECEIVING: " << stanza.to_string());
188
  std::function<void(const Stanza&)> handler;
189
190
  try
    {
191
      handler = this->stanza_handlers.at(stanza.get_name());
192
193
194
    }
  catch (const std::out_of_range& exception)
    {
louiz’'s avatar
louiz’ committed
195
      log_warning("No handler for stanza of type " << stanza.get_name());
196
197
      return;
    }
198
  handler(stanza);
199
200
201
202
203
204
}

void XmppComponent::send_stream_error(const std::string& name, const std::string& explanation)
{
  XmlNode node("stream:error", nullptr);
  XmlNode error(name, nullptr);
205
  error["xmlns"] = STREAM_NS;
206
207
208
209
210
211
212
213
  if (!explanation.empty())
    error.set_inner(explanation);
  error.close();
  node.add_child(std::move(error));
  node.close();
  this->send_stanza(node);
}

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
void XmppComponent::send_stanza_error(const std::string& kind, const std::string& to, const std::string& from,
                       const std::string& id, const std::string& error_type,
                       const std::string& defined_condition, const std::string& text)
{
  Stanza node(kind);
  if (!to.empty())
    node["to"] = to;
  if (!from.empty())
    node["from"] = from;
  if (!id.empty())
    node["id"] = id;
  node["type"] = "error";
  XmlNode error("error");
  error["type"] = error_type;
  XmlNode inner_error(defined_condition);
  inner_error["xmlns"] = STANZA_NS;
  inner_error.close();
  error.add_child(std::move(inner_error));
  if (!text.empty())
    {
      XmlNode text_node("text");
      text_node["xmlns"] = STANZA_NS;
      text_node.set_inner(text);
      text_node.close();
      error.add_child(std::move(text_node));
    }
  error.close();
  node.add_child(std::move(error));
  node.close();
  this->send_stanza(node);
}

246
247
void XmppComponent::close_document()
{
louiz’'s avatar
louiz’ committed
248
  log_debug("XMPP SENDING: </stream:stream>");
249
  this->send_data("</stream:stream>");
louiz’'s avatar
louiz’ committed
250
  this->doc_open = false;
251
252
253
254
}

void XmppComponent::handle_handshake(const Stanza& stanza)
{
255
  (void)stanza;
256
  this->authenticated = true;
257
258
  this->ever_auth = true;
  this->last_auth = true;
louiz’'s avatar
louiz’ committed
259
  log_info("Authenticated with the XMPP server");
louiz’'s avatar
louiz’ committed
260
261
262
#ifdef SYSTEMDDAEMON_FOUND
  sd_notify(0, "READY=1");
#endif
263
}
264
265
266

void XmppComponent::handle_presence(const Stanza& stanza)
{
267
268
269
270
271
272
  std::string from = stanza.get_tag("from");
  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
273
  if (from.empty())
274
275
276
277
278
279
    {
      log_warning("Received an invalid presence stanza: tag 'from' is missing.");
      return;
    }
  if (to_str.empty())
    {
280
      this->send_stanza_error("presence", from, this->served_hostname, id,
281
282
283
284
285
286
                              "modify", "bad-request", "Missing 'to' tag");
      return;
    }

  Bridge* bridge = this->get_user_bridge(from);
  Jid to(to_str);
287
  Iid iid(to.local);
288
289
290
291
292
293
294
295
296

  // 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");
297
  utils::ScopeGuard stanza_error([&](){
298
      this->send_stanza_error("presence", from, to_str, id,
299
300
                              error_type, error_name, "");
        });
louiz’'s avatar
louiz’ committed
301

louiz’'s avatar
louiz’ committed
302
303
304
  if (!iid.server.empty())
    { // presence toward a MUC that corresponds to an irc channel, or a
      // dummy channel if iid.chan is empty
louiz’'s avatar
louiz’ committed
305
      if (type.empty())
louiz’'s avatar
louiz’ committed
306
307
308
309
        {
          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);
310
          bridge->join_irc_channel(iid, to.resource);
louiz’'s avatar
louiz’ committed
311
        }
louiz’'s avatar
louiz’ committed
312
313
      else if (type == "unavailable")
        {
314
          XmlNode* status = stanza.get_child(COMPONENT_NS":status");
louiz’'s avatar
louiz’ committed
315
316
317
          bridge->leave_irc_channel(std::move(iid), status ? std::move(status->get_inner()) : "");
        }
    }
318
319
320
321
  else
    {
      // An user wants to join an invalid IRC channel, return a presence error to him
      if (type.empty())
322
        this->send_invalid_room_error(to.local, to.resource, from);
323
    }
324
  stanza_error.disable();
325
326
}

louiz’'s avatar
louiz’ committed
327
328
void XmppComponent::handle_message(const Stanza& stanza)
{
329
330
331
332
333
  std::string from = stanza.get_tag("from");
  std::string id = stanza.get_tag("id");
  std::string to_str = stanza.get_tag("to");
  std::string type = stanza.get_tag("type");

334
335
  if (from.empty())
    return;
336
  if (type.empty())
337
    type = "normal";
338
339
340
  Bridge* bridge = this->get_user_bridge(from);
  Jid to(to_str);
  Iid iid(to.local);
341
342
343

  std::string error_type("cancel");
  std::string error_name("internal-server-error");
344
  utils::ScopeGuard stanza_error([&](){
345
      this->send_stanza_error("message", from, to_str, id,
346
347
                              error_type, error_name, "");
        });
348
  XmlNode* body = stanza.get_child(COMPONENT_NS":body");
349
  if (type == "groupchat")
louiz’'s avatar
louiz’ committed
350
351
352
353
    {
      if (to.resource.empty())
        if (body && !body->get_inner().empty())
          bridge->send_channel_message(iid, body->get_inner());
354
355
356
      XmlNode* subject = stanza.get_child(COMPONENT_NS":subject");
      if (subject)
        bridge->set_channel_topic(iid, subject->get_inner());
louiz’'s avatar
louiz’ committed
357
    }
louiz’'s avatar
louiz’ committed
358
359
360
361
362
  else
    {
      if (body && !body->get_inner().empty())
        bridge->send_private_message(iid, body->get_inner());
    }
363
  stanza_error.disable();
louiz’'s avatar
louiz’ committed
364
365
}

366
367
void XmppComponent::handle_iq(const Stanza& stanza)
{
368
369
370
371
372
  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");

373
374
  if (from.empty())
    return;
375
376
  if (id.empty() || to_str.empty() || type.empty())
    {
377
      this->send_stanza_error("iq", from, this->served_hostname, id,
378
                              "modify", "bad-request", "");
379
380
381
382
383
      return;
    }

  Bridge* bridge = this->get_user_bridge(from);
  Jid to(from);
384
385
386

  std::string error_type("cancel");
  std::string error_name("internal-server-error");
387
  utils::ScopeGuard stanza_error([&](){
388
      this->send_stanza_error("iq", from, to_str, id,
389
390
                              error_type, error_name, "");
        });
391
392
393
394
395
  if (type == "set")
    {
      XmlNode* query;
      if ((query = stanza.get_child(MUC_ADMIN_NS":query")))
        {
396
397
          const XmlNode* child = query->get_child(MUC_ADMIN_NS":item");
          if (child)
398
            {
399
400
              std::string nick = child->get_tag("nick");
              std::string role = child->get_tag("role");
401
402
403
404
405
406
407
408
409
              if (!nick.empty() && role == "none")
                {
                  std::string reason;
                  XmlNode* reason_el = child->get_child(MUC_ADMIN_NS":reason");
                  if (reason_el)
                    reason = reason_el->get_inner();
                  Iid iid(to.local);
                  bridge->send_irc_kick(iid, nick, reason);
                }
410
411
412
413
414
415
              else
                {
                  error_type = "cancel";
                  error_name = "feature-not-implemented";
                  return;
                }
416
417
418
            }
        }
    }
419
420
421
422
423
424
425
  else
    {
      error_type = "cancel";
      error_name = "feature-not-implemented";
      return;
    }
  stanza_error.disable();
426
427
}

louiz’'s avatar
louiz’ committed
428
429
430
431
432
433
434
void XmppComponent::handle_error(const Stanza& stanza)
{
  XmlNode* text = stanza.get_child(STREAMS_NS":text");
  std::string error_message("Unspecified error");
  if (text)
    error_message = text->get_inner();
  log_error("Stream error received from the XMPP server: " << error_message);
louiz’'s avatar
louiz’ committed
435
436
437
438
439
#ifdef SYSTEMDDAEMON_FOUND
  if (!this->ever_auth)
    sd_notifyf(0, "STATUS=Failed to authenticate to the XMPP server: %s", error_message.data());
#endif

louiz’'s avatar
louiz’ committed
440
441
}

442
443
444
445
446
447
448
449
450
451
452
453
454
Bridge* XmppComponent::get_user_bridge(const std::string& user_jid)
{
  try
    {
      return this->bridges.at(user_jid).get();
    }
  catch (const std::out_of_range& exception)
    {
      this->bridges.emplace(user_jid, std::make_unique<Bridge>(user_jid, this, this->poller));
      return this->bridges.at(user_jid).get();
    }
}

455
456
457
458
459
void* XmppComponent::get_receive_buffer(const size_t size) const
{
  return this->parser.get_buffer(size);
}

460
void XmppComponent::send_message(const std::string& from, Xmpp::body&& body, const std::string& to, const std::string& type)
461
462
463
464
{
  XmlNode node("message");
  node["to"] = to;
  node["from"] = from + "@" + this->served_hostname;
465
466
  if (!type.empty())
    node["type"] = type;
467
  XmlNode body_node("body");
468
  body_node.set_inner(std::get<0>(body));
469
470
471
472
473
474
  body_node.close();
  node.add_child(std::move(body_node));
  node.close();
  this->send_stanza(node);
}

475
476
477
void XmppComponent::send_user_join(const std::string& from,
                                   const std::string& nick,
                                   const std::string& realjid,
478
479
480
481
                                   const std::string& affiliation,
                                   const std::string& role,
                                   const std::string& to,
                                   const bool self)
482
483
484
485
486
487
{
  XmlNode node("presence");
  node["to"] = to;
  node["from"] = from + "@" + this->served_hostname + "/" + nick;

  XmlNode x("x");
488
  x["xmlns"] = MUC_USER_NS;
489
490

  XmlNode item("item");
491
492
493
494
  if (!affiliation.empty())
    item["affiliation"] = affiliation;
  if (!role.empty())
    item["role"] = role;
495
496
497
498
499
500
  if (!realjid.empty())
    {
      const std::string preped_jid = jidprep(realjid);
      if (!preped_jid.empty())
        item["jid"] = preped_jid;
    }
501
  item.close();
502
  x.add_child(std::move(item));
503

504
505
506
507
508
509
510
  if (self)
    {
      XmlNode status("status");
      status["code"] = "110";
      status.close();
      x.add_child(std::move(status));
    }
511
512
513
514
515
516
  x.close();
  node.add_child(std::move(x));
  node.close();
  this->send_stanza(node);
}

517
518
519
520
521
522
523
524
525
526
527
528
529
530
void XmppComponent::send_invalid_room_error(const std::string& muc_name,
                                            const std::string& nick,
                                            const std::string& to)
{
  Stanza presence("presence");
  presence["from"] = muc_name + "@" + this->served_hostname + "/" + nick;
  presence["to"] = to;
  presence["type"] = "error";
  XmlNode x("x");
  x["xmlns"] = MUC_NS;
  x.close();
  presence.add_child(std::move(x));
  XmlNode error("error");
  error["by"] = muc_name + "@" + this->served_hostname;
louiz’'s avatar
Idem    
louiz’ committed
531
  error["type"] = "cancel";
louiz’'s avatar
louiz’ committed
532
533
534
535
  XmlNode item_not_found("item-not-found");
  item_not_found["xmlns"] = STANZA_NS;
  item_not_found.close();
  error.add_child(std::move(item_not_found));
536
537
538
539
540
541
542
543
544
545
546
547
548
549
  XmlNode text("text");
  text["xmlns"] = STANZA_NS;
  text["xml:lang"] = "en";
  text.set_inner(muc_name +
                 " is not a valid IRC channel name. A correct room jid is of the form: #<chan>%<server>@" +
                 this->served_hostname);
  text.close();
  error.add_child(std::move(text));
  error.close();
  presence.add_child(std::move(error));
  presence.close();
  this->send_stanza(presence);
}

550
void XmppComponent::send_topic(const std::string& from, Xmpp::body&& topic, const std::string& to)
551
552
553
554
555
556
{
  XmlNode message("message");
  message["to"] = to;
  message["from"] = from + "@" + this->served_hostname;
  message["type"] = "groupchat";
  XmlNode subject("subject");
557
  subject.set_inner(std::get<0>(topic));
558
559
560
561
562
  subject.close();
  message.add_child(std::move(subject));
  message.close();
  this->send_stanza(message);
}
louiz’'s avatar
louiz’ committed
563

564
void XmppComponent::send_muc_message(const std::string& muc_name, const std::string& nick, Xmpp::body&& xmpp_body, const std::string& jid_to)
louiz’'s avatar
louiz’ committed
565
566
567
{
  Stanza message("message");
  message["to"] = jid_to;
louiz’'s avatar
louiz’ committed
568
569
570
571
  if (!nick.empty())
    message["from"] = muc_name + "@" + this->served_hostname + "/" + nick;
  else // Message from the room itself
    message["from"] = muc_name + "@" + this->served_hostname;
louiz’'s avatar
louiz’ committed
572
573
  message["type"] = "groupchat";
  XmlNode body("body");
574
  body.set_inner(std::get<0>(xmpp_body));
louiz’'s avatar
louiz’ committed
575
576
  body.close();
  message.add_child(std::move(body));
577
578
579
580
581
582
583
584
585
  if (std::get<1>(xmpp_body))
    {
      XmlNode html("html");
      html["xmlns"] = XHTMLIM_NS;
      // Pass the ownership of the pointer to this xmlnode
      html.add_child(std::get<1>(xmpp_body).release());
      html.close();
      message.add_child(std::move(html));
    }
louiz’'s avatar
louiz’ committed
586
587
588
  message.close();
  this->send_stanza(message);
}
louiz’'s avatar
louiz’ committed
589

590
void XmppComponent::send_muc_leave(std::string&& muc_name, std::string&& nick, Xmpp::body&& message, const std::string& jid_to, const bool self)
louiz’'s avatar
louiz’ committed
591
592
593
594
595
{
  Stanza presence("presence");
  presence["to"] = jid_to;
  presence["from"] = muc_name + "@" + this->served_hostname + "/" + nick;
  presence["type"] = "unavailable";
596
  const std::string message_str = std::get<0>(message);
louiz’'s avatar
louiz’ committed
597
598
599
600
601
602
603
604
605
606
607
608
  XmlNode x("x");
  x["xmlns"] = MUC_USER_NS;
  if (self)
    {
      XmlNode status("status");
      status["code"] = "110";
      status.close();
      x.add_child(std::move(status));
    }
  x.close();
  presence.add_child(std::move(x));
  if (!message_str.empty())
louiz’'s avatar
louiz’ committed
609
610
    {
      XmlNode status("status");
louiz’'s avatar
louiz’ committed
611
      status.set_inner(message_str);
louiz’'s avatar
louiz’ committed
612
613
614
615
616
617
      status.close();
      presence.add_child(std::move(status));
    }
  presence.close();
  this->send_stanza(presence);
}
louiz’'s avatar
louiz’ committed
618

619
620
621
622
623
624
625
void XmppComponent::send_nick_change(const std::string& muc_name,
                                     const std::string& old_nick,
                                     const std::string& new_nick,
                                     const std::string& affiliation,
                                     const std::string& role,
                                     const std::string& jid_to,
                                     const bool self)
louiz’'s avatar
louiz’ committed
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
{
  Stanza presence("presence");
  presence["to"] = jid_to;
  presence["from"] = muc_name + "@" + this->served_hostname + "/" + old_nick;
  presence["type"] = "unavailable";
  XmlNode x("x");
  x["xmlns"] = MUC_USER_NS;
  XmlNode item("item");
  item["nick"] = new_nick;
  item.close();
  x.add_child(std::move(item));
  XmlNode status("status");
  status["code"] = "303";
  status.close();
  x.add_child(std::move(status));
  if (self)
    {
      XmlNode status2("status");
      status2["code"] = "110";
      status2.close();
      x.add_child(std::move(status2));
    }
  x.close();
  presence.add_child(std::move(x));
  presence.close();
  this->send_stanza(presence);

653
  this->send_user_join(muc_name, new_nick, "", affiliation, role, jid_to, self);
louiz’'s avatar
louiz’ committed
654
}
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690

void XmppComponent::kick_user(const std::string& muc_name,
                                  const std::string& target,
                                  const std::string& txt,
                                  const std::string& author,
                                  const std::string& jid_to)
{
  Stanza presence("presence");
  presence["from"] = muc_name + "@" + this->served_hostname + "/" + target;
  presence["to"] = jid_to;
  presence["type"] = "unavailable";
  XmlNode x("x");
  x["xmlns"] = MUC_USER_NS;
  XmlNode item("item");
  item["affiliation"] = "none";
  item["role"] = "none";
  XmlNode actor("actor");
  actor["nick"] = author;
  actor["jid"] = author; // backward compatibility with old clients
  actor.close();
  item.add_child(std::move(actor));
  XmlNode reason("reason");
  reason.set_inner(txt);
  reason.close();
  item.add_child(std::move(reason));
  item.close();
  x.add_child(std::move(item));
  XmlNode status("status");
  status["code"] = "307";
  status.close();
  x.add_child(std::move(status));
  x.close();
  presence.add_child(std::move(x));
  presence.close();
  this->send_stanza(presence);
}
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715

void XmppComponent::send_nickname_conflict_error(const std::string& muc_name,
                                                 const std::string& nickname,
                                                 const std::string& jid_to)
{
  Stanza presence("presence");
  presence["from"] = muc_name + "@" + this->served_hostname + "/" + nickname;
  presence["to"] = jid_to;
  XmlNode x("x");
  x["xmlns"] = MUC_NS;
  x.close();
  presence.add_child(std::move(x));
  XmlNode error("error");
  error["by"] = muc_name + "@" + this->served_hostname;
  error["type"] = "cancel";
  error["code"] = "409";
  XmlNode conflict("conflict");
  conflict["xmlns"] = STANZA_NS;
  conflict.close();
  error.add_child(std::move(conflict));
  error.close();
  presence.add_child(std::move(error));
  presence.close();
  this->send_stanza(presence);
}
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737

void XmppComponent::send_affiliation_role_change(const std::string& muc_name,
                                                 const std::string& target,
                                                 const std::string& affiliation,
                                                 const std::string& role,
                                                 const std::string& jid_to)
{
  Stanza presence("presence");
  presence["from"] = muc_name + "@" + this->served_hostname + "/" + target;
  presence["to"] = jid_to;
  XmlNode x("x");
  x["xmlns"] = MUC_USER_NS;
  XmlNode item("item");
  item["affiliation"] = affiliation;
  item["role"] = role;
  item.close();
  x.add_child(std::move(item));
  x.close();
  presence.add_child(std::move(x));
  presence.close();
  this->send_stanza(presence);
}