Commit e840704b authored by louiz’'s avatar louiz’

Convert received modes into roles and affiliations

parent baf03a7e
...@@ -184,17 +184,21 @@ notified of this XMPP event as well. For example if a mode “+o toto” is ...@@ -184,17 +184,21 @@ notified of this XMPP event as well. For example if a mode “+o toto” is
received, then toto’s role will be changed to moderator. The mapping received, then toto’s role will be changed to moderator. The mapping
between IRC modes and XMPP features is as follow: between IRC modes and XMPP features is as follow:
`+a`
Sets the participant’s role to `moderator` and its affiliation to `owner`.
`+o` `+o`
Sets the participant’s role to `moderator`. Sets the participant’s role to `moderator` and its affiliation to `admin`.
`+a` `+h`
Sets the participant’s role to `admin`. Sets the participant’s role to `moderator` and its affiliation to `member`.
`+v` `+v`
Sets the participant’s affiliation to `member`. Sets the participant’s role to `participant` and its affiliation to `member`.
SECURITY SECURITY
-------- --------
......
...@@ -225,3 +225,35 @@ void Bridge::send_nickname_conflict_error(const Iid& iid, const std::string& nic ...@@ -225,3 +225,35 @@ void Bridge::send_nickname_conflict_error(const Iid& iid, const std::string& nic
{ {
this->xmpp->send_nickname_conflict_error(iid.chan + "%" + iid.server, nickname, this->user_jid); this->xmpp->send_nickname_conflict_error(iid.chan + "%" + iid.server, nickname, this->user_jid);
} }
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);
}
...@@ -87,6 +87,10 @@ public: ...@@ -87,6 +87,10 @@ public:
void send_nick_change(Iid&& iid, const std::string& old_nick, const std::string& new_nick, const bool self); void send_nick_change(Iid&& iid, const std::string& old_nick, const std::string& new_nick, const bool self);
void kick_muc_user(Iid&& iid, const std::string& target, const std::string& reason, const std::string& author); 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); 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);
/** /**
* Misc * Misc
......
...@@ -24,7 +24,7 @@ IrcUser* IrcChannel::get_self() const ...@@ -24,7 +24,7 @@ IrcUser* IrcChannel::get_self() const
return this->self.get(); return this->self.get();
} }
IrcUser* IrcChannel::find_user(const std::string& name) IrcUser* IrcChannel::find_user(const std::string& name) const
{ {
IrcUser user(name); IrcUser user(name);
for (const auto& u: this->users) for (const auto& u: this->users)
......
...@@ -22,7 +22,7 @@ public: ...@@ -22,7 +22,7 @@ public:
IrcUser* get_self() const; IrcUser* get_self() const;
IrcUser* add_user(const std::string& name, IrcUser* add_user(const std::string& name,
const std::map<char, char> prefix_to_mode); const std::map<char, char> prefix_to_mode);
IrcUser* find_user(const std::string& name); IrcUser* find_user(const std::string& name) const;
void remove_user(const IrcUser* user); void remove_user(const IrcUser* user);
private: private:
......
...@@ -218,7 +218,10 @@ void IrcClient::on_isupport_message(const IrcMessage& message) ...@@ -218,7 +218,10 @@ void IrcClient::on_isupport_message(const IrcMessage& message)
j++; j++;
j++; j++;
while (j < token.size() && token[i] != ')') while (j < token.size() && token[i] != ')')
this->prefix_to_mode[token[j++]] = token[i++]; {
this->sorted_user_modes.push_back(token[i]);
this->prefix_to_mode[token[j++]] = token[i++];
}
} }
} }
} }
...@@ -508,6 +511,76 @@ void IrcClient::on_channel_mode(const IrcMessage& message) ...@@ -508,6 +511,76 @@ void IrcClient::on_channel_mode(const IrcMessage& message)
this->bridge->send_message(iid, "", std::string("Mode ") + iid.chan + this->bridge->send_message(iid, "", std::string("Mode ") + iid.chan +
" [" + mode_arguments + "] by " + user.nick, " [" + mode_arguments + "] by " + user.nick,
true); true);
const IrcChannel* channel = this->get_channel(iid.chan);
if (!channel)
return;
// parse the received modes, we need to handle things like "+m-oo coucou toutou"
const std::string modes = message.arguments[1];
// a list of modified IrcUsers. When we applied all modes, we check the
// modes that now applies to each of them, and send a notification for
// each one. This is to disallow sending two notifications or more when a
// single MODE command changes two or more modes on the same participant
std::set<const IrcUser*> modified_users;
// If it is true, the modes are added, if it’s false they are
// removed. When we encounter the '+' char, the value is changed to true,
// and with '-' it is changed to false.
bool add = true;
bool use_arg;
size_t arg_pos = 2;
for (const char c: modes)
{
if (c == '+')
add = true;
else if (c == '-')
add = false;
else
{ // lookup the mode symbol in the 4 chanmodes lists, depending on
// the list where it is found, it takes an argument or not
size_t type;
for (type = 0; type < 4; ++type)
if (this->chanmodes[type].find(c) != std::string::npos)
break;
if (type == 4) // if mode was not found
{
// That mode can also be of type B if it is present in the
// prefix_to_mode map
for (const std::pair<char, char>& pair: this->prefix_to_mode)
if (pair.second == c)
{
type = 1;
break;
}
}
// modes of type A, B or C (but only with add == true)
if (type == 0 || type == 1 ||
(type == 2 && add == true))
use_arg = true;
else // modes of type C (but only with add == false), D, or unknown
use_arg = false;
if (use_arg == true && message.arguments.size() > arg_pos)
{
const std::string target = message.arguments[arg_pos++];
IrcUser* user = channel->find_user(target);
if (!user)
{
log_warning("Trying to set mode for non-existing user '" << target
<< "' in channel" << iid.chan);
return;
}
if (add)
user->add_mode(c);
else
user->remove_mode(c);
modified_users.insert(user);
}
}
}
for (const IrcUser* u: modified_users)
{
char most_significant_mode = u->get_most_significant_mode(this->sorted_user_modes);
this->bridge->send_affiliation_role_change(iid, u->nick, most_significant_mode);
}
} }
void IrcClient::on_user_mode(const IrcMessage& message) void IrcClient::on_user_mode(const IrcMessage& message)
......
...@@ -229,9 +229,14 @@ private: ...@@ -229,9 +229,14 @@ private:
/** /**
* See http://www.irc.org/tech_docs/draft-brocklesby-irc-isupport-03.txt section 3.14 * See http://www.irc.org/tech_docs/draft-brocklesby-irc-isupport-03.txt section 3.14
* The example given would be transformed into * The example given would be transformed into
* modes_to_prefix = {{'a', '&'}, {'b', '*'}} * modes_to_prefix = {{'&', 'a'}, {'*', 'b'}}
*/ */
std::map<char, char> prefix_to_mode; std::map<char, char> prefix_to_mode;
/**
* Available user modes, sorted from most significant to least significant
* (for example 'ahov' is a common order).
*/
std::vector<char> sorted_user_modes;
IrcClient(const IrcClient&) = delete; IrcClient(const IrcClient&) = delete;
IrcClient(IrcClient&&) = delete; IrcClient(IrcClient&&) = delete;
......
...@@ -25,3 +25,23 @@ IrcUser::IrcUser(const std::string& name): ...@@ -25,3 +25,23 @@ IrcUser::IrcUser(const std::string& name):
IrcUser(name, {}) IrcUser(name, {})
{ {
} }
void IrcUser::add_mode(const char mode)
{
this->modes.insert(mode);
}
void IrcUser::remove_mode(const char mode)
{
this->modes.erase(mode);
}
char IrcUser::get_most_significant_mode(const std::vector<char>& modes) const
{
for (const char mode: modes)
{
if (this->modes.find(mode) != this->modes.end())
return mode;
}
return 0;
}
#ifndef IRC_USER_INCLUDED #ifndef IRC_USER_INCLUDED
# define IRC_USER_INCLUDED # define IRC_USER_INCLUDED
#include <vector>
#include <string> #include <string>
#include <map> #include <map>
#include <set> #include <set>
...@@ -14,6 +15,9 @@ public: ...@@ -14,6 +15,9 @@ public:
explicit IrcUser(const std::string& name, explicit IrcUser(const std::string& name,
const std::map<char, char>& prefix_to_mode); const std::map<char, char>& prefix_to_mode);
explicit IrcUser(const std::string& name); explicit IrcUser(const std::string& name);
void add_mode(const char mode);
void remove_mode(const char mode);
char get_most_significant_mode(const std::vector<char>& sorted_user_modes) const;
std::string nick; std::string nick;
std::string host; std::string host;
std::set<char> modes; std::set<char> modes;
......
...@@ -484,3 +484,25 @@ void XmppComponent::send_nickname_conflict_error(const std::string& muc_name, ...@@ -484,3 +484,25 @@ void XmppComponent::send_nickname_conflict_error(const std::string& muc_name,
presence.close(); presence.close();
this->send_stanza(presence); this->send_stanza(presence);
} }
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);
}
...@@ -112,6 +112,15 @@ public: ...@@ -112,6 +112,15 @@ public:
void send_nickname_conflict_error(const std::string& muc_name, void send_nickname_conflict_error(const std::string& muc_name,
const std::string& nickname, const std::string& nickname,
const std::string& jid_to); const std::string& jid_to);
/**
* Send a presence from the MUC indicating a change in the role and/or
* affiliation of a participant
*/
void 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);
/** /**
* Handle the various stanza types * Handle the various stanza types
*/ */
......
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