Add the accountnicks metadata as a replacement for user mode +r.

This isn't used much currently but in the future will allow doing
a lot of behaviour which is currently implemented in services in
the IRCd instead e.g. killing ghost clients, nickname enforcement.
This commit is contained in:
Sadie Powell 2022-10-16 22:47:24 +01:00
parent 65fe0fc67d
commit 4dfb70a936
2 changed files with 77 additions and 8 deletions

View File

@ -26,6 +26,9 @@ namespace Account
class API;
class APIBase;
class EventListener;
/** Encapsulates a list of nicknames associated with an account. */
typedef insp::flat_set<std::string, irc::insensitive_swo> NickList;
}
/** Defines the interface for the account API. */
@ -49,6 +52,18 @@ public:
* @return If the user is logged in to an account then the account name; otherwise, nullptr.
*/
virtual std::string* GetAccountName(const User* user) const = 0;
/** Retrieves the account nicks of the specified user.
* @param user The user to retrieve the account nicks of.
* @return If the user is logged in to an account then the account nicks; otherwise, nullptr.
*/
virtual NickList* GetAccountNicks(const User* user) const = 0;
/** Determines whether a user is identified to their nickname.
* @param user The user to check the identification status of.
* @return If the user is identified to their nickname then true; otherwise, false.
*/
virtual bool IsIdentifiedToNick(const User* user) = 0;
};
/** Allows modules to access information regarding user accounts. */

View File

@ -130,18 +130,63 @@ public:
}
};
class AccountNicksExtItem final
: public SimpleExtItem<Account::NickList>
{
public:
AccountNicksExtItem(Module* mod)
: SimpleExtItem<Account::NickList>(mod, "accountnicks", ExtensionType::USER, true)
{
}
void FromInternal(Extensible* container, const std::string& value) noexcept override
{
if (container->extype != this->extype)
return;
auto list = new Account::NickList();
irc::spacesepstream nickstream(value);
for (std::string nick; nickstream.GetToken(nick); )
list->insert(nick);
if (list->empty())
{
// The remote sent an empty list of nicknames for some reason.
delete list;
Unset(container);
}
else
{
// The remote sent a non-zero list of nicks; set it.
Set(container, list);
}
}
std::string ToInternal(const Extensible* container, void* item) const noexcept override
{
auto list = static_cast<Account::NickList*>(item);
return stdalgo::string::join(*list);
}
};
class AccountAPIImpl final
: public Account::APIBase
{
private:
AccountExtItemImpl accountext;
StringExtItem accountidext;
AccountNicksExtItem accountnicksext;
UserModeReference identifiedmode;
public:
AccountAPIImpl(Module* mod)
: Account::APIBase(mod)
, accountext(mod)
, accountidext(mod, "accountid", ExtensionType::USER, true)
, accountnicksext(mod)
, identifiedmode(mod, "u_registered")
{
}
@ -154,6 +199,21 @@ public:
{
return accountext.Get(user);
}
Account::NickList* GetAccountNicks(const User* user) const override
{
return accountnicksext.Get(user);
}
bool IsIdentifiedToNick(const User* user) override
{
if (user->IsModeSet(identifiedmode))
return true; // User has +r set.
// Check whether their current nick is in their nick list.
Account::NickList* nicks = accountnicksext.Get(user);
return nicks && stdalgo::isin(*nicks, user->nick);
}
};
class AccountExtBan final
@ -240,26 +300,20 @@ public:
if (!request.GetFieldIndex('f', flag_index))
return MOD_RES_PASSTHRU;
if (user->IsModeSet(userregmode))
if (accountapi.IsIdentifiedToNick(user))
numeric.GetParams()[flag_index].push_back('r');
return MOD_RES_PASSTHRU;
}
/* <- :twisted.oscnet.org 330 w00t2 w00t2 w00t :is logged in as */
void OnWhois(Whois::Context& whois) override
{
const std::string* account = accountapi.GetAccountName(whois.GetTarget());
if (account)
{
whois.SendLine(RPL_WHOISACCOUNT, *account, "is logged in as");
}
if (whois.GetTarget()->IsModeSet(userregmode))
{
/* user is registered */
if (accountapi.IsIdentifiedToNick(whois.GetTarget()))
whois.SendLine(RPL_WHOISREGNICK, "is a registered nick");
}
}
void OnUserPostNick(User* user, const std::string& oldnick) override