diff options
| author | 2022-10-16 23:24:44 +0100 | |
|---|---|---|
| committer | 2022-10-18 10:59:57 +0100 | |
| commit | 1736775c766cddf9e1dc436aff6060bd8553c8c3 (patch) | |
| tree | 4ee3a286930f57882e02f755c73f10f2230f8cc9 /src/modules/m_account.cpp | |
| parent | Split out channel and user mode +r into a separate module. (diff) | |
| download | inspircd++-1736775c766cddf9e1dc436aff6060bd8553c8c3.tar.gz inspircd++-1736775c766cddf9e1dc436aff6060bd8553c8c3.tar.bz2 inspircd++-1736775c766cddf9e1dc436aff6060bd8553c8c3.zip | |
Rename the services_account module to account.
Diffstat (limited to 'src/modules/m_account.cpp')
| -rw-r--r-- | src/modules/m_account.cpp | 353 |
1 files changed, 353 insertions, 0 deletions
diff --git a/src/modules/m_account.cpp b/src/modules/m_account.cpp new file mode 100644 index 000000000..dd696f5fa --- /dev/null +++ b/src/modules/m_account.cpp @@ -0,0 +1,353 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2019 linuxdaemon <linuxdaemon.irc@gmail.com> + * Copyright (C) 2013, 2017-2022 Sadie Powell <sadie@witchery.services> + * Copyright (C) 2012-2015 Attila Molnar <attilamolnar@hush.com> + * Copyright (C) 2012, 2019 Robby <robby@chatbelgie.be> + * Copyright (C) 2012 Shawn Smith <ShawnSmith0828@gmail.com> + * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org> + * Copyright (C) 2009 Uli Schlachter <psychon@inspircd.org> + * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org> + * Copyright (C) 2006, 2008 Robin Burchell <robin+git@viroteck.net> + * Copyright (C) 2006, 2008 Craig Edwards <brain@inspircd.org> + * + * This file is part of InspIRCd. InspIRCd is free software: you can + * redistribute it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "inspircd.h" +#include "modules/account.h" +#include "modules/callerid.h" +#include "modules/ctctags.h" +#include "modules/extban.h" +#include "modules/exemption.h" +#include "modules/who.h" +#include "modules/whois.h" + +enum +{ + // From ircd-hybrid? + ERR_NEEDREGGEDNICK = 477, + + // From IRCv3 sasl-3.1. + RPL_LOGGEDIN = 900, + RPL_LOGGEDOUT = 901 +}; + +class AccountExtItemImpl final + : public StringExtItem +{ + Events::ModuleEventProvider eventprov; + +public: + AccountExtItemImpl(Module* mod) + : StringExtItem(mod, "accountname", ExtensionType::USER, true) + , eventprov(mod, "event/account") + { + } + + void FromNetwork(Extensible* container, const std::string& value) noexcept override + { + if (container->extype != this->extype) + return; + + StringExtItem::FromNetwork(container, value); + + User* user = static_cast<User*>(container); + if (IS_LOCAL(user)) + { + if (value.empty()) + { + // Logged out. + user->WriteNumeric(RPL_LOGGEDOUT, user->GetFullHost(), "You are now logged out"); + } + else + { + // Logged in. + user->WriteNumeric(RPL_LOGGEDIN, user->GetFullHost(), value, InspIRCd::Format("You are now logged in as %s", value.c_str())); + } + } + + eventprov.Call(&Account::EventListener::OnAccountChange, user, value); + } +}; + + +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") + { + } + + std::string* GetAccountId(const User* user) const override + { + return accountidext.Get(user); + } + + std::string* GetAccountName(const User* user) const override + { + 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 + : public ExtBan::MatchingBase +{ +private: + AccountAPIImpl& accountapi; + +public: + AccountExtBan(Module* Creator, AccountAPIImpl& AccountAPI) + : ExtBan::MatchingBase(Creator, "account", 'R') + , accountapi(AccountAPI) + { + } + + bool IsMatch(User* user, Channel* channel, const std::string& text) override + { + const std::string* account = accountapi.GetAccountName(user); + return account && InspIRCd::Match(*account, text); + } +}; + +class UnauthedExtBan final + : public ExtBan::MatchingBase +{ +private: + AccountAPIImpl& accountapi; + +public: + UnauthedExtBan(Module* Creator, AccountAPIImpl& AccountAPI) + : ExtBan::MatchingBase(Creator, "unauthed", 'U') + , accountapi(AccountAPI) + { + } + + bool IsMatch(User* user, Channel* channel, const std::string& text) override + { + const std::string* account = accountapi.GetAccountName(user); + return !account && channel->CheckBan(user, text); + } +}; + +class ModuleAccount final + : public Module + , public CTCTags::EventListener + , public Who::EventListener + , public Whois::EventListener +{ +private: + CallerID::API calleridapi; + CheckExemption::EventProvider exemptionprov; + SimpleChannelMode reginvitemode; + SimpleChannelMode regmoderatedmode; + SimpleUserMode regdeafmode; + AccountAPIImpl accountapi; + AccountExtBan accountextban; + UnauthedExtBan unauthedextban; + +public: + ModuleAccount() + : Module(VF_VENDOR | VF_OPTCOMMON, "Adds various channel and user modes relating to accounts.") + , CTCTags::EventListener(this) + , Who::EventListener(this) + , Whois::EventListener(this) + , calleridapi(this) + , exemptionprov(this) + , reginvitemode(this, "reginvite", 'R') + , regmoderatedmode(this, "regmoderated", 'M') + , regdeafmode(this, "regdeaf", 'R') + , accountapi(this) + , accountextban(this, accountapi) + , unauthedextban(this, accountapi) + { + } + + ModResult OnWhoLine(const Who::Request& request, LocalUser* source, User* user, Membership* memb, Numeric::Numeric& numeric) override + { + size_t flag_index; + if (!request.GetFieldIndex('f', flag_index)) + return MOD_RES_PASSTHRU; + + if (accountapi.IsIdentifiedToNick(user)) + numeric.GetParams()[flag_index].push_back('r'); + + return MOD_RES_PASSTHRU; + } + + 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 (accountapi.IsIdentifiedToNick(whois.GetTarget())) + whois.SendLine(RPL_WHOISREGNICK, "is a registered nick"); + } + + ModResult HandleMessage(User* user, const MessageTarget& target) + { + if (!IS_LOCAL(user)) + return MOD_RES_PASSTHRU; + + + const std::string* account = accountapi.GetAccountName(user); + switch (target.type) + { + case MessageTarget::TYPE_CHANNEL: + { + Channel* targetchan = target.Get<Channel>(); + + if (!targetchan->IsModeSet(regmoderatedmode) || account) + return MOD_RES_PASSTHRU; + + if (CheckExemption::Call(exemptionprov, user, targetchan, "regmoderated") == MOD_RES_ALLOW) + return MOD_RES_PASSTHRU; + + // User is messaging a +M channel and is not registered or exempt. + user->WriteNumeric(ERR_NEEDREGGEDNICK, targetchan->name, "You need to be identified to a registered account to message this channel"); + return MOD_RES_DENY; + } + case MessageTarget::TYPE_USER: + { + User* targetuser = target.Get<User>(); + if (!targetuser->IsModeSet(regdeafmode) || account) + return MOD_RES_PASSTHRU; + + if (calleridapi && calleridapi->IsOnAcceptList(user, targetuser)) + return MOD_RES_PASSTHRU; + + // User is messaging a +R user and is not registered or on an accept list. + user->WriteNumeric(ERR_NEEDREGGEDNICK, targetuser->nick, "You need to be identified to a registered account to message this user"); + return MOD_RES_DENY; + } + case MessageTarget::TYPE_SERVER: + break; + } + return MOD_RES_PASSTHRU; + } + + ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) override + { + return HandleMessage(user, target); + } + + ModResult OnUserPreTagMessage(User* user, const MessageTarget& target, CTCTags::TagMessageDetails& details) override + { + return HandleMessage(user, target); + } + + ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven, bool override) override + { + if (override) + return MOD_RES_PASSTHRU; + + + const std::string* account = accountapi.GetAccountName(user); + if (chan) + { + if (chan->IsModeSet(reginvitemode)) + { + if (!account) + { + // joining a +R channel and not identified + user->WriteNumeric(ERR_NEEDREGGEDNICK, chan->name, "You need to be identified to a registered account to join this channel"); + return MOD_RES_DENY; + } + } + } + return MOD_RES_PASSTHRU; + } + + ModResult OnSetConnectClass(LocalUser* user, ConnectClass::Ptr myclass) override + { + if (myclass->config->getBool("requireaccount") && !accountapi.GetAccountName(user)) + { + ServerInstance->Logs.Debug("CONNECTCLASS", "The %s connect class is not suitable as it requires the user to be logged into an account", + myclass->GetName().c_str()); + return MOD_RES_DENY; + } + return MOD_RES_PASSTHRU; + } +}; + +MODULE_INIT(ModuleAccount) |
