mirror of
https://github.com/inspircd/inspircd.git
synced 2025-03-09 10:39:02 -04:00
Merge branch 'insp3' into master.
This commit is contained in:
commit
f602febac5
@ -43,6 +43,6 @@ InspIRCd is licensed under [version 2 of the GNU General Public License](https:/
|
||||
* [Website](https://www.inspircd.org)
|
||||
* [Documentation](https://docs.inspircd.org)
|
||||
* [GitHub](https://github.com/inspircd)
|
||||
* [Support IRC channel](https://kiwiirc.com/nextclient/irc.inspircd.org:+6697/inspircd?nick=Your%20nick%20here) — \#inspircd on irc.inspircd.org
|
||||
* [Development IRC channel](https://kiwiirc.com/nextclient/irc.inspircd.org:+6697/inspircd.dev?nick=Your%20nick%20here) — \#inspircd.dev on irc.inspircd.org
|
||||
* [InspIRCd test network](https://kiwiirc.com/nextclient/testnet.inspircd.org:+6697/?nick=Your%20nick%20here) — testnet.inspircd.org
|
||||
* Support IRC channel — \#inspircd on irc.inspircd.org
|
||||
* Development IRC channel — \#inspircd.dev on irc.inspircd.org
|
||||
* InspIRCd test network — testnet.inspircd.org
|
||||
|
2
configure
vendored
2
configure
vendored
@ -210,7 +210,7 @@ if (defined $opt_portable) {
|
||||
$config{LOG_DIR} = $opt_log_dir // catdir $config{BASE_DIR}, 'var/log/inspircd';
|
||||
$config{MANUAL_DIR} = $opt_manual_dir // catdir $config{BASE_DIR}, 'usr/share/man/man1';
|
||||
$config{MODULE_DIR} = $opt_module_dir // catdir $config{BASE_DIR}, 'usr/lib/inspircd';
|
||||
$config{RUNTIME_DIR} = $opt_runtime_dir // catdir $config{BASE_DIR}, 'var/run';
|
||||
$config{RUNTIME_DIR} = $opt_runtime_dir // catdir $config{BASE_DIR}, 'var/run/inspircd';
|
||||
$config{SCRIPT_DIR} = $opt_script_dir // catdir $config{BASE_DIR}, 'usr/share/inspircd';
|
||||
} else {
|
||||
$config{BASE_DIR} = rel2abs $opt_prefix // $config{BASE_DIR} // catdir $RealDir, 'run';
|
||||
|
@ -1706,7 +1706,7 @@
|
||||
#<permchanneldb filename="permchannels.conf"
|
||||
# listmodes="yes"
|
||||
# saveperiod="5s">
|
||||
#<include file="permchannels.conf">
|
||||
#<include file="permchannels.conf" missingokay="yes">
|
||||
#
|
||||
# You may also create channels on startup by using the <permchannels> block.
|
||||
#<permchannels channel="#opers" modes="isP" topic="Opers only.">
|
||||
|
@ -85,9 +85,8 @@ struct CoreExport ConnectClass
|
||||
*/
|
||||
unsigned int registration_timeout = 0;
|
||||
|
||||
/** Host mask for this line
|
||||
*/
|
||||
std::string host;
|
||||
/** Hosts that this connect class can be used by. */
|
||||
std::vector<std::string> hosts;
|
||||
|
||||
/** Number of seconds between pings for this line
|
||||
*/
|
||||
@ -152,16 +151,20 @@ struct CoreExport ConnectClass
|
||||
|
||||
/** Create a new connect class with no settings.
|
||||
*/
|
||||
ConnectClass(std::shared_ptr<ConfigTag> tag, char type, const std::string& mask);
|
||||
ConnectClass(std::shared_ptr<ConfigTag> tag, char type, const std::vector<std::string>& masks);
|
||||
|
||||
/** Create a new connect class with inherited settings.
|
||||
*/
|
||||
ConnectClass(std::shared_ptr<ConfigTag> tag, char type, const std::string& mask, std::shared_ptr<ConnectClass> parent);
|
||||
ConnectClass(std::shared_ptr<ConfigTag> tag, char type, const std::vector<std::string>& masks, std::shared_ptr<ConnectClass> parent);
|
||||
|
||||
/** Update the settings in this block to match the given block */
|
||||
void Update(const std::shared_ptr<ConnectClass> newSettings);
|
||||
|
||||
const std::string& GetName() { return name; }
|
||||
const std::string& GetHost() { return host; }
|
||||
/** Retrieves the name of this connect class. */
|
||||
const std::string& GetName() const { return name; }
|
||||
|
||||
/** Retrieves the hosts for this connect class. */
|
||||
const std::vector<std::string>& GetHosts() const { return hosts; }
|
||||
|
||||
/** Returns the registration timeout
|
||||
*/
|
||||
|
@ -38,7 +38,10 @@ enum ParseFlags
|
||||
FLAG_NO_INC = 4,
|
||||
|
||||
// &env.FOO; is disabled.
|
||||
FLAG_NO_ENV = 8
|
||||
FLAG_NO_ENV = 8,
|
||||
|
||||
// It's okay if an include doesn't exist.
|
||||
FLAG_MISSING_OKAY = 16
|
||||
};
|
||||
|
||||
// RAII wrapper for FILE* which closes the file when it goes out of scope.
|
||||
@ -370,11 +373,18 @@ void ParseStack::DoInclude(std::shared_ptr<ConfigTag> tag, int flags)
|
||||
{
|
||||
if (tag->getBool("noinclude", false))
|
||||
flags |= FLAG_NO_INC;
|
||||
|
||||
if (tag->getBool("noexec", false))
|
||||
flags |= FLAG_NO_EXEC;
|
||||
|
||||
if (tag->getBool("noenv", false))
|
||||
flags |= FLAG_NO_ENV;
|
||||
|
||||
if (tag->getBool("missingokay", false))
|
||||
flags |= FLAG_MISSING_OKAY;
|
||||
else
|
||||
flags &= ~FLAG_MISSING_OKAY;
|
||||
|
||||
if (!ParseFile(ServerInstance->Config->Paths.PrependConfig(name), flags, mandatorytag))
|
||||
throw CoreException("Included");
|
||||
}
|
||||
@ -459,7 +469,12 @@ bool ParseStack::ParseFile(const std::string& path, int flags, const std::string
|
||||
|
||||
FileWrapper file((isexec ? popen(path.c_str(), "r") : fopen(path.c_str(), "r")), isexec);
|
||||
if (!file)
|
||||
{
|
||||
if (flags & FLAG_MISSING_OKAY)
|
||||
return true;
|
||||
|
||||
throw CoreException("Could not read \"" + path + "\" for include");
|
||||
}
|
||||
|
||||
reading.push_back(path);
|
||||
Parser p(*this, flags, file, path, mandatory_tag);
|
||||
|
@ -164,11 +164,11 @@ void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current)
|
||||
{
|
||||
case CC_ALLOW:
|
||||
case CC_DENY:
|
||||
oldBlocksByMask[std::make_pair(c->host, c->type)] = c;
|
||||
oldBlocksByMask[std::make_pair(stdalgo::string::join(c->GetHosts()), c->type)] = c;
|
||||
break;
|
||||
|
||||
case CC_NAMED:
|
||||
oldBlocksByMask[std::make_pair(c->name, c->type)] = c;
|
||||
oldBlocksByMask[std::make_pair(c->GetName(), c->type)] = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -222,19 +222,14 @@ void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current)
|
||||
std::string mask;
|
||||
char type;
|
||||
|
||||
if (tag->readString("allow", mask, false))
|
||||
if (tag->readString("allow", mask, false) && !mask.empty())
|
||||
type = CC_ALLOW;
|
||||
else if (tag->readString("deny", mask, false))
|
||||
else if (tag->readString("deny", mask, false) && !mask.empty())
|
||||
type = CC_DENY;
|
||||
else if (!name.empty())
|
||||
{
|
||||
type = CC_NAMED;
|
||||
mask = name;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw CoreException("Connect class must have allow, deny, or name specified at " + tag->source.str());
|
||||
}
|
||||
|
||||
if (name.empty())
|
||||
name = "unnamed-" + ConvToStr(i);
|
||||
@ -243,9 +238,14 @@ void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current)
|
||||
throw CoreException("Two connect classes with name \"" + name + "\" defined!");
|
||||
names[name] = i;
|
||||
|
||||
std::vector<std::string> masks;
|
||||
irc::spacesepstream maskstream(mask);
|
||||
for (std::string maskentry; maskstream.GetToken(maskentry); )
|
||||
masks.push_back(maskentry);
|
||||
|
||||
auto me = parent
|
||||
? std::make_shared<ConnectClass>(tag, type, mask, parent)
|
||||
: std::make_shared<ConnectClass>(tag, type, mask);
|
||||
? std::make_shared<ConnectClass>(tag, type, masks, parent)
|
||||
: std::make_shared<ConnectClass>(tag, type, masks);
|
||||
|
||||
me->name = name;
|
||||
|
||||
|
@ -156,7 +156,7 @@ void CommandStats::DoStats(Stats::Context& stats)
|
||||
if (c->type == CC_NAMED)
|
||||
param.push_back('*');
|
||||
else
|
||||
param.append(c->host);
|
||||
param.append(stdalgo::string::join(c->GetHosts(), ','));
|
||||
|
||||
row.push(param).push(c->config->getString("port", "*", 1));
|
||||
row.push(ConvToStr(c->GetRecvqMax())).push(ConvToStr(c->GetSendqSoftMax())).push(ConvToStr(c->GetSendqHardMax())).push(ConvToStr(c->GetCommandRate()));
|
||||
@ -176,7 +176,8 @@ void CommandStats::DoStats(Stats::Context& stats)
|
||||
int idx = 0;
|
||||
for (const auto& c : ServerInstance->Config->Classes)
|
||||
{
|
||||
stats.AddRow(215, 'i', "NOMATCH", '*', c->GetHost(), (c->limit ? c->limit : SocketEngine::GetMaxFds()), idx, ServerInstance->Config->ServerName, '*');
|
||||
for (const auto& host : c->GetHosts())
|
||||
stats.AddRow(215, 'i', "NOMATCH", '*', host, (c->limit ? c->limit : SocketEngine::GetMaxFds()), idx, ServerInstance->Config->ServerName, '*');
|
||||
stats.AddRow(218, 'Y', idx, c->GetPingTime(), '0', c->GetSendqHardMax(), ConvToStr(c->GetRecvqMax())+" "+ConvToStr(c->GetRegTimeout()));
|
||||
idx++;
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
|
||||
#include "inspircd.h"
|
||||
#include "modules/ctctags.h"
|
||||
|
||||
// User mode +d - filter out channel messages and channel notices
|
||||
class DeafMode : public ModeHandler
|
||||
@ -67,7 +68,9 @@ class PrivDeafMode : public ModeHandler
|
||||
}
|
||||
};
|
||||
|
||||
class ModuleDeaf : public Module
|
||||
class ModuleDeaf
|
||||
: public Module
|
||||
, public CTCTags::EventListener
|
||||
{
|
||||
private:
|
||||
DeafMode deafmode;
|
||||
@ -76,9 +79,51 @@ class ModuleDeaf : public Module
|
||||
std::string deaf_bypasschars_service;
|
||||
bool privdeafservice;
|
||||
|
||||
ModResult HandleChannel(User* source, Channel* target, CUList& exemptions, bool is_bypasschar, bool is_bypasschar_service)
|
||||
{
|
||||
for (const auto& [member, _] : target->GetUsers())
|
||||
{
|
||||
// Allow if the user doesn't have the mode set.
|
||||
if (!member->IsModeSet(deafmode))
|
||||
continue;
|
||||
|
||||
// Allow if the message begins with a service char and the
|
||||
// user is on a services server.
|
||||
if (is_bypasschar_service && member->server->IsService())
|
||||
continue;
|
||||
|
||||
// Allow if the prefix begins with a normal char and the
|
||||
// user is not on a services server.
|
||||
if (is_bypasschar && !member->server->IsService())
|
||||
continue;
|
||||
|
||||
exemptions.insert(member);
|
||||
}
|
||||
|
||||
return MOD_RES_PASSTHRU;
|
||||
}
|
||||
|
||||
ModResult HandleUser(User* source, User* target)
|
||||
{
|
||||
// Allow if the mode is not set.
|
||||
if (!target->IsModeSet(privdeafmode))
|
||||
return MOD_RES_PASSTHRU;
|
||||
|
||||
// Reject if the source is a service and privdeafservice is disabled.
|
||||
if (!privdeafservice && source->server->IsService())
|
||||
return MOD_RES_DENY;
|
||||
|
||||
// Reject if the source doesn't have the right priv.
|
||||
if (!source->HasPrivPermission("users/ignore-privdeaf"))
|
||||
return MOD_RES_DENY;
|
||||
|
||||
return MOD_RES_ALLOW;
|
||||
}
|
||||
|
||||
public:
|
||||
ModuleDeaf()
|
||||
: Module(VF_VENDOR, "Adds user mode d (deaf) which prevents users from receiving channel messages.")
|
||||
, CTCTags::EventListener(this)
|
||||
, deafmode(this)
|
||||
, privdeafmode(this)
|
||||
{
|
||||
@ -88,8 +133,25 @@ class ModuleDeaf : public Module
|
||||
{
|
||||
auto tag = ServerInstance->Config->ConfValue("deaf");
|
||||
deaf_bypasschars = tag->getString("bypasschars");
|
||||
deaf_bypasschars_service = tag->getString("servicebypasschars", tag->getString("bypasscharsuline"));
|
||||
privdeafservice = tag->getBool("privdeafservice", tag->getBool("privdeafuline", true));
|
||||
deaf_bypasschars_service = tag->getString("servicebypasschars", tag->getString("bypasscharsservice"));
|
||||
privdeafservice = tag->getBool("privdeafservice", tag->getBool("privdeafservice", true));
|
||||
}
|
||||
|
||||
ModResult OnUserPreTagMessage(User* user, const MessageTarget& target, CTCTags::TagMessageDetails& details) override
|
||||
{
|
||||
switch (target.type)
|
||||
{
|
||||
case MessageTarget::TYPE_CHANNEL:
|
||||
return HandleChannel(user, target.Get<Channel>(), details.exemptions, false, false);
|
||||
|
||||
case MessageTarget::TYPE_USER:
|
||||
return HandleUser(user, target.Get<User>());
|
||||
|
||||
case MessageTarget::TYPE_SERVER:
|
||||
break;
|
||||
}
|
||||
|
||||
return MOD_RES_PASSTHRU;
|
||||
}
|
||||
|
||||
ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) override
|
||||
@ -98,52 +160,23 @@ class ModuleDeaf : public Module
|
||||
{
|
||||
case MessageTarget::TYPE_CHANNEL:
|
||||
{
|
||||
Channel* chan = target.Get<Channel>();
|
||||
bool is_bypasschar = (deaf_bypasschars.find(details.text[0]) != std::string::npos);
|
||||
bool is_bypasschar_service = (deaf_bypasschars_service.find(details.text[0]) != std::string::npos);
|
||||
|
||||
// If we have no bypasschars_service in config, and this is a bypasschar (regular)
|
||||
// Then it is obviously going to get through +d, no exemption list required
|
||||
bool is_bypasschar = (deaf_bypasschars.find(details.text[0]) != std::string::npos);
|
||||
if (deaf_bypasschars_service.empty() && is_bypasschar)
|
||||
return MOD_RES_PASSTHRU;
|
||||
|
||||
// If it matches both bypasschar and bypasschar_service, it will get through.
|
||||
bool is_bypasschar_service = (deaf_bypasschars_service.find(details.text[0]) != std::string::npos);
|
||||
if (is_bypasschar && is_bypasschar_service)
|
||||
return MOD_RES_PASSTHRU;
|
||||
|
||||
const Channel::MemberMap& ulist = chan->GetUsers();
|
||||
for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
|
||||
{
|
||||
// not +d
|
||||
if (!i->first->IsModeSet(deafmode))
|
||||
continue;
|
||||
|
||||
bool is_a_service = i->first->server->IsService();
|
||||
// matched a U-line only bypass
|
||||
if (is_bypasschar_service && is_a_service)
|
||||
continue;
|
||||
// matched a regular bypass
|
||||
if (is_bypasschar && !is_a_service)
|
||||
continue;
|
||||
|
||||
// don't deliver message!
|
||||
details.exemptions.insert(i->first);
|
||||
}
|
||||
break;
|
||||
return HandleChannel(user, target.Get<Channel>(), details.exemptions, is_bypasschar, is_bypasschar_service);
|
||||
}
|
||||
|
||||
case MessageTarget::TYPE_USER:
|
||||
{
|
||||
User* targ = target.Get<User>();
|
||||
if (!targ->IsModeSet(privdeafmode))
|
||||
return MOD_RES_PASSTHRU;
|
||||
return HandleUser(user, target.Get<User>());
|
||||
|
||||
if (!privdeafservice && user->server->IsService())
|
||||
return MOD_RES_DENY;
|
||||
|
||||
if (!user->HasPrivPermission("users/ignore-privdeaf"))
|
||||
return MOD_RES_DENY;
|
||||
|
||||
break;
|
||||
}
|
||||
case MessageTarget::TYPE_SERVER:
|
||||
break;
|
||||
}
|
||||
|
@ -945,10 +945,11 @@ bool ModuleFilter::Tick(time_t)
|
||||
return true;
|
||||
}
|
||||
|
||||
stream << "# This file is automatically generated by the filter module. Any changes will be overwritten." << std::endl
|
||||
stream
|
||||
<< "# This file was automatically generated by the " << INSPIRCD_VERSION << " filter module on " << InspIRCd::TimeString(ServerInstance->Time()) << "." << std::endl
|
||||
<< "# Any changes to this file will be automatically overwritten." << std::endl
|
||||
<< "# If you want to convert this to a normal config file you *MUST* remove the generated=\"yes\" keys!" << std::endl
|
||||
<< std::endl
|
||||
<< "<config format=\"xml\">" << std::endl;
|
||||
<< std::endl;
|
||||
|
||||
for (std::vector<FilterResult>::iterator i = filters.begin(); i != filters.end(); ++i)
|
||||
{
|
||||
|
@ -77,7 +77,9 @@ static bool WriteDatabase(PermChannel& permchanmode, Module* mod, bool save_list
|
||||
return false;
|
||||
}
|
||||
|
||||
stream << "# This file is automatically generated by m_permchannels. Any changes will be overwritten." << std::endl
|
||||
stream
|
||||
<< "# This file was automatically generated by the " << INSPIRCD_VERSION << " permchannels module on " << InspIRCd::TimeString(ServerInstance->Time()) << "." << std::endl
|
||||
<< "# Any changes to this file will be automatically overwritten." << std::endl
|
||||
<< std::endl;
|
||||
|
||||
const chan_hash& chans = ServerInstance->GetChans();
|
||||
|
@ -1133,12 +1133,20 @@ void LocalUser::SetClass(const std::string &explicit_name)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* check if host matches.. */
|
||||
if (!InspIRCd::MatchCIDR(this->GetIPString(), c->GetHost(), NULL) &&
|
||||
!InspIRCd::MatchCIDR(this->GetRealHost(), c->GetHost(), NULL))
|
||||
bool hostmatches = false;
|
||||
for (const auto& host : c->GetHosts())
|
||||
{
|
||||
if (InspIRCd::MatchCIDR(this->GetIPString(), host) || InspIRCd::MatchCIDR(this->GetRealHost(), host))
|
||||
{
|
||||
hostmatches = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hostmatches)
|
||||
{
|
||||
const std::string hosts = stdalgo::string::join(c->GetHosts());
|
||||
ServerInstance->Logs.Log("CONNECTCLASS", LOG_DEBUG, "The %s connect class is not suitable as neither the host (%s) nor the IP (%s) matches %s",
|
||||
c->GetName().c_str(), this->GetRealHost().c_str(), this->GetIPString().c_str(), c->GetHost().c_str());
|
||||
c->GetName().c_str(), this->GetRealHost().c_str(), this->GetIPString().c_str(), hosts.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1221,20 +1229,20 @@ const std::string& FakeUser::GetFullRealHost()
|
||||
return server->GetName();
|
||||
}
|
||||
|
||||
ConnectClass::ConnectClass(std::shared_ptr<ConfigTag> tag, char t, const std::string& mask)
|
||||
ConnectClass::ConnectClass(std::shared_ptr<ConfigTag> tag, char t, const std::vector<std::string>& masks)
|
||||
: config(tag)
|
||||
, type(t)
|
||||
, name("unnamed")
|
||||
, host(mask)
|
||||
, hosts(masks)
|
||||
{
|
||||
}
|
||||
|
||||
ConnectClass::ConnectClass(std::shared_ptr<ConfigTag> tag, char t, const std::string& mask, std::shared_ptr<ConnectClass> parent)
|
||||
ConnectClass::ConnectClass(std::shared_ptr<ConfigTag> tag, char t, const std::vector<std::string>& masks, std::shared_ptr<ConnectClass> parent)
|
||||
{
|
||||
Update(parent);
|
||||
name = "unnamed";
|
||||
type = t;
|
||||
host = mask;
|
||||
hosts = masks;
|
||||
|
||||
// Connect classes can inherit from each other but this is problematic for modules which can't use
|
||||
// ConnectClass::Update so we build a hybrid tag containing all of the values set on this class as
|
||||
@ -1265,7 +1273,7 @@ void ConnectClass::Update(const std::shared_ptr<ConnectClass> src)
|
||||
fakelag = src->fakelag;
|
||||
name = src->name;
|
||||
registration_timeout = src->registration_timeout;
|
||||
host = src->host;
|
||||
hosts = src->hosts;
|
||||
pingtime = src->pingtime;
|
||||
softsendqmax = src->softsendqmax;
|
||||
hardsendqmax = src->hardsendqmax;
|
||||
|
Loading…
x
Reference in New Issue
Block a user