Commit b7789fe5 authored by louiz’'s avatar louiz’

Add a Persistent option on channels

fix #3230
parent 1017e8f4
......@@ -46,6 +46,8 @@
<field name="maxHistoryLength" type="integer" default="20"/>
<field name="persistent" type="boolean" default="false"/>
<index unique="true">
<indexfield name="owner"/>
<indexfield name="server"/>
......
......@@ -564,6 +564,13 @@ On a channel JID (e.g on the JID #test%chat.freenode.org@biboumi.example.com)
* In encoding: see the option with the same name in the server configuration
form.
* Out encoding: Currently ignored.
* Persistent: If set to true, biboumi will stay in this channel even when
all the XMPP resources have left the room. I.e. it will not send a PART
command, and will stay idle in the channel until the connection is
forcibly closed. If a resource comes back in the room again, and if
the archiving of messages is enabled for this room, the client will
receive the messages that where sent in this channel. This option can be
used to make biboumi act as an IRC bouncer.
Raw IRC messages
----------------
......
......@@ -62,7 +62,7 @@ void Bridge::shutdown(const std::string& exit_message)
for (auto it = this->irc_clients.begin(); it != this->irc_clients.end(); ++it)
{
it->second->send_quit_command(exit_message);
it->second->leave_dummy_channel(exit_message);
it->second->leave_dummy_channel(exit_message, {});
}
}
......@@ -422,33 +422,48 @@ void Bridge::leave_irc_channel(Iid&& iid, const std::string& status_message, con
if (!this->is_resource_in_chan(key, resource))
return ;
IrcChannel* channel = irc->get_channel(iid.get_local());
auto nick = channel->get_self()->nick;
const auto resources = this->number_of_resources_in_chan(key);
if (resources == 1)
{
// Do not send a PART message if we actually are not in that channel
// or if we already sent a PART but we are just waiting for the
// acknowledgment from the server
IrcChannel* channel = irc->get_channel(iid.get_local());
if (channel->joined && !channel->parting)
irc->send_part_command(iid.get_local(), status_message);
bool persistent = false;
#ifdef USE_DATABASE
const auto coptions = Database::get_irc_channel_options_with_server_default(this->user_jid,
iid.get_server(), iid.get_local());
persistent = coptions.persistent.value();
#endif
if (channel->joined && !channel->parting && !persistent)
{
const auto chan_name = iid.get_local();
if (chan_name.empty())
irc->leave_dummy_channel(status_message, resource);
else
irc->send_part_command(iid.get_local(), status_message);
}
else
{
this->send_muc_leave(std::move(iid), std::move(nick), "", true, resource);
}
// Since there are no resources left in that channel, we don't
// want to receive private messages using this room's JID
this->remove_all_preferred_from_jid_of_room(iid.get_local());
}
else
{
IrcChannel* chan = irc->get_channel(iid.get_local());
if (chan)
{
auto nick = chan->get_self()->nick;
this->remove_resource_from_chan(key, resource);
this->send_muc_leave(std::move(iid), std::move(nick),
"Biboumi note: "s + std::to_string(resources - 1) + " resources are still in this channel.",
true, resource);
if (this->number_of_channels_the_resource_is_in(iid.get_server(), resource) == 0)
this->remove_resource_from_server(iid.get_server(), resource);
}
if (channel)
this->send_muc_leave(std::move(iid), std::move(nick),
"Biboumi note: "s + std::to_string(resources - 1) + " resources are still in this channel.",
true, resource);
this->remove_resource_from_chan(key, resource);
if (this->number_of_channels_the_resource_is_in(iid.get_server(), resource) == 0)
this->remove_resource_from_server(iid.get_server(), resource);
}
}
void Bridge::send_irc_nick_change(const Iid& iid, const std::string& new_nick, const std::string& requesting_resource)
......@@ -862,9 +877,13 @@ void Bridge::send_muc_leave(Iid&& iid, std::string&& nick, const std::string& me
this->xmpp.send_muc_leave(std::to_string(iid), std::move(nick), this->make_xmpp_body(message),
this->user_jid + "/" + resource, self);
else
for (const auto& res: this->resources_in_chan[iid.to_tuple()])
this->xmpp.send_muc_leave(std::to_string(iid), std::move(nick), this->make_xmpp_body(message),
this->user_jid + "/" + res, self);
{
for (const auto &res: this->resources_in_chan[iid.to_tuple()])
this->xmpp.send_muc_leave(std::to_string(iid), std::move(nick), this->make_xmpp_body(message),
this->user_jid + "/" + res, self);
this->remove_all_resources_from_chan(iid.to_tuple());
}
IrcClient* irc = this->find_irc_client(iid.get_server());
if (irc && irc->number_of_joined_channels() == 0)
this->quit_or_start_linger_timer(iid.get_server());
......@@ -1137,6 +1156,11 @@ bool Bridge::is_resource_in_chan(const Bridge::ChannelKey& channel, const std::s
return false;
}
void Bridge::remove_all_resources_from_chan(const Bridge::ChannelKey& channel_key)
{
this->resources_in_chan.erase(channel_key);
}
void Bridge::add_resource_to_server(const Bridge::IrcHostname& irc_hostname, const std::string& resource)
{
auto it = this->resources_in_server.find(irc_hostname);
......
......@@ -312,6 +312,7 @@ private:
void add_resource_to_chan(const ChannelKey& channel_key, const std::string& resource);
void remove_resource_from_chan(const ChannelKey& channel_key, const std::string& resource);
bool is_resource_in_chan(const ChannelKey& channel_key, const std::string& resource) const;
void remove_all_resources_from_chan(const ChannelKey& channel_key);
std::size_t number_of_resources_in_chan(const ChannelKey& channel_key) const;
void add_resource_to_server(const IrcHostname& irc_hostname, const std::string& resource);
......
......@@ -501,15 +501,7 @@ void IrcClient::send_private_message(const std::string& username, const std::str
void IrcClient::send_part_command(const std::string& chan_name, const std::string& status_message)
{
IrcChannel* channel = this->get_channel(chan_name);
if (channel->joined == true)
{
if (chan_name.empty())
this->leave_dummy_channel(status_message);
else
this->send_message(IrcMessage("PART", {chan_name, status_message}));
channel->parting = true;
}
this->send_message(IrcMessage("PART", {chan_name, status_message}));
}
void IrcClient::send_mode_command(const std::string& chan_name, const std::vector<std::string>& arguments)
......@@ -1160,14 +1152,14 @@ DummyIrcChannel& IrcClient::get_dummy_channel()
return this->dummy_channel;
}
void IrcClient::leave_dummy_channel(const std::string& exit_message)
void IrcClient::leave_dummy_channel(const std::string& exit_message, const std::string& resource)
{
if (!this->dummy_channel.joined)
return;
this->dummy_channel.joined = false;
this->dummy_channel.joining = false;
this->dummy_channel.remove_all_users();
this->bridge.send_muc_leave(Iid("%"s + this->hostname, this->chantypes), std::string(this->current_nick), exit_message, true);
this->bridge.send_muc_leave(Iid("%"s + this->hostname, this->chantypes), std::string(this->current_nick), exit_message, true, resource);
}
#ifdef BOTAN_FOUND
......
......@@ -283,7 +283,7 @@ public:
* Leave the dummy channel: forward a message to the user to indicate that
* he left it, and mark it as not joined.
*/
void leave_dummy_channel(const std::string& exit_message);
void leave_dummy_channel(const std::string& exit_message, const std::string& resource);
const std::string& get_hostname() const { return this->hostname; }
std::string get_nick() const { return this->current_nick; }
......
......@@ -256,7 +256,8 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com
XmlSubNode pass(x, "field");
pass["var"] = "pass";
pass["type"] = "text-private";
pass["label"] = "Server password (to be used in a PASS command when connecting)";
pass["label"] = "Server password";
pass["desc"] = "Will be used in a PASS command when connecting";
if (!options.pass.value().empty())
{
XmlSubNode pass_value(pass, "value");
......@@ -463,6 +464,20 @@ void ConfigureIrcChannelStep1(XmppComponent&, AdhocSession& session, XmlNode& co
XmlSubNode encoding_in_value(encoding_in, "value");
encoding_in_value.set_inner(options.encodingIn.value());
}
XmlSubNode persistent(x, "field");
persistent["var"] = "persistent";
persistent["type"] = "boolean";
persistent["desc"] = "If set to true, when all XMPP clients have left this channel, biboumi will stay idle in it, without sending a PART command.";
persistent["label"] = "Persistent";
{
XmlSubNode value(persistent, "value");
value.set_name("value");
if (options.persistent.value())
value.set_inner("true");
else
value.set_inner("false");
}
}
void ConfigureIrcChannelStep2(XmppComponent&, AdhocSession& session, XmlNode& command_node)
......@@ -486,6 +501,10 @@ void ConfigureIrcChannelStep2(XmppComponent&, AdhocSession& session, XmlNode& co
else if (field->get_tag("var") == "encoding_in" &&
value && !value->get_inner().empty())
options.encodingIn = value->get_inner();
else if (field->get_tag("var") == "persistent" &&
value)
options.persistent = to_bool(value->get_inner());
}
options.update();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment