/*
* InspIRCd -- Internet Relay Chat Daemon
*
* Copyright (C) 2018 linuxdaemon <linuxdaemon.irc@gmail.com>
* Copyright (C) 2013-2014, 2016-2025 Sadie Powell <sadie@witchery.services>
* Copyright (C) 2012-2014 Attila Molnar <attilamolnar@hush.com>
* Copyright (C) 2012 Robby <robby@chatbelgie.be>
* Copyright (C) 2009-2010 Daniel De Graaf <danieldg@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 <cinttypes>
#include <filesystem>
#include <fstream>
#include "inspircd.h"
#include "configparser.h"
#include "timeutils.h"
#include "utility/container.h"
#ifdef _WIN32
# define pclose _pclose
# define popen _popen
#else
# include <unistd.h>
#endif
namespace
{
std::string GetPrintable(int chr)
{
if (isprint(chr))
return FMT::format("{} (0x{:x})", (char)chr, chr);
else
return FMT::format("0x{:x}", chr);
}
}
enum ParseFlags
{
// Executable includes are disabled.
FLAG_NO_EXEC = 2,
// All includes are disabled.
FLAG_NO_INC = 4,
// &env.FOO; is disabled.
FLAG_NO_ENV = 8,
// It's okay if an include doesn't exist.
FLAG_MISSING_OKAY = 16
};
struct Parser final
{
ParseStack& stack;
int flags;
FilePtr file;
FilePosition current;
FilePosition last_tag;
std::shared_ptr<ConfigTag> tag;
int ungot = -1;
std::string mandatory_tag;
Parser(ParseStack& me, int myflags, FilePtr conf, const std::string& name, const std::string& mandatorytag)
: stack(me)
, flags(myflags)
, file(std::move(conf))
, current(name, 1, 0)
, last_tag(name, 0, 0)
, mandatory_tag(mandatorytag)
{
}
int next(bool eof_ok = false)
{
if (ungot != -1)
{
int ch = ungot;
ungot = -1;
return ch;
}
int ch = fgetc(file.get());
if (ch == EOF && !eof_ok)
{
throw CoreException("Unexpected end-of-file");
}
else if (ch == '\n')
{
current.line++;
current.column = 1;
}
else
{
current.column++;
}
return ch;
}
void unget(int ch)
{
if (ungot != -1)
throw CoreException("INTERNAL ERROR: cannot unget twice");
ungot = ch;
}
void comment()
{
while (true)
{
int ch = next(true);
if (ch == '\n')
return;
if (ch == EOF)
{
unget(ch);
return;
}
}
}
static bool wordchar(int ch)
{
return isalnum(ch)
|| ch == '-'
|| ch == '.'
|| ch == '_';
}
void nextword(std::string& rv)
{
int ch = next();
while (isspace(ch))
ch = next();
while (wordchar(ch))
{
rv.push_back(ch);
ch = next();
}
unget(ch);
}
bool kv()
{
std::string key;
nextword(key);
int ch = next();
if (ch == '>' && key.empty())
{
return false;
}
else if (ch == '#' && key.empty())
{
comment();
return true;
}
else if (ch != '=')
{
throw CoreException("Invalid character in key " + key + ": " + GetPrintable(ch));
}
std::string value;
ch = next();
if (ch != '"')
{
throw CoreException("Invalid character in value of <" + tag->name + ":" + key + ">: " + GetPrintable(ch));
}
while (true)
{
ch = next();
if (ch == '&')
{
std::string varname;
while (true)
{
ch = next();
if (wordchar(ch) || (varname.empty() && ch == '#'))
varname.push_back(ch);
else if (ch == ';')
break;
else
{
stack.errors.push_back(FMT::format("Invalid XML entity name in value of <{}:{}>", tag->name, key));
stack.errors.push_back("To include an ampersand or quote, use & or "");
throw CoreException("Parse error");
}
}
if (varname.empty())
throw CoreException("Empty XML entity reference");
else if (varname[0] == '#' && (varname.size() == 1 || (varname.size() == 2 && varname[1] == 'x')))
throw CoreException("Empty numeric character reference");
else if (varname[0] == '#')
{
const char* cvarname = varname.c_str();
char* endptr;
unsigned long lvalue;
if (cvarname[1] == 'x')
lvalue = strtoul(cvarname + 2, &endptr, 16);
else
lvalue = strtoul(cvarname + 1, &endptr, 10);
if (*endptr != '\0' || lvalue > 255)
throw CoreException("Invalid numeric character reference '&" + varname + ";'");
value.push_back(static_cast<char>(lvalue));
}
else if (varname.compare(0, 4, "env.") == 0)
{
if (flags & FLAG_NO_ENV)
throw CoreException("XML environment entity reference in file included with noenv=\"yes\"");
const char* envstr = getenv(varname.c_str() + 4);
if (!envstr)
throw CoreException("Undefined XML environment entity reference '&" + varname + ";'");
value.append(envstr);
}
else
{
insp::flat_map<std::string, std::string>::iterator var = stack.vars.find(varname);
if (var == stack.vars.end())
throw CoreException("Undefined XML entity reference '&" + varname + ";'");
value.append(var->second);
}
}
else if (ch == &a/*
* InspIRCd -- Internet Relay Chat Daemon
*
* Copyright (C) 2013, 2015, 2019-2020 Sadie Powell <sadie@witchery.services>
* Copyright (C) 2013 Adam <Adam@anope.org>
* Copyright (C) 2012-2013, 2015 Attila Molnar <attilamolnar@hush.com>
* Copyright (C) 2012 Robby <robby@chatbelgie.be>
* Copyright (C) 2012 ChrisTX <xpipe@hotmail.de>
* Copyright (C) 2010 Craig Edwards <brain@inspircd.org>
* Copyright (C) 2009-2010 Daniel De Graaf <danieldg@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 "exitcodes.h"
#include <iostream>
bool ModuleManager::Load(const std::string& modname, bool defer)
{
/* Don't allow people to specify paths for modules, it doesn't work as expected */
if (modname.find('/') != std::string::npos)
{
LastModuleError = "You can't load modules with a path: " + modname;
return false;
}
const std::string filename = ExpandModName(modname);
const std::string moduleFile = ServerInstance->Config->Paths.PrependModule(filename);
if (!FileSystem::FileExists(moduleFile))
{
LastModuleError = "Module file could not be found: " + filename;
ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
return false;
}
if (Modules.find(filename) != Modules.end())
{
LastModuleError = "Module " + filename + " is already loaded, cannot load a module twice!";
ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
return false;
}
Module* newmod = NULL;
DLLManager* newhandle = new DLLManager(moduleFile.c_str());
ServiceList newservices;
if (!defer)
this->NewServices = &newservices;
try
{
newmod = newhandle->CallInit();
this->NewServices = NULL;
if (newmod)
{
newmod->ModuleSourceFile = filename;
newmod->ModuleDLLManager = newhandle;
Modules[filename] = newmod;
const char* version = newhandle->GetVersion();
if (!version)
version = "unknown";
if (defer)
{
ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "New module introduced: %s (Module version %s)",
filename.c_str(), version);
}
else
{
ConfigStatus confstatus;
AttachAll(newmod);
AddServices(newservices);
newmod->init();
newmod->ReadConfig(confstatus);
Version v = newmod->GetVersion();
ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "New module introduced: %s (Module version %s)%s",
filename.c_str(), version, (!(v.Flags & VF_VENDOR) ? " [3rd Party]" : " [Vendor]"));
}
}
else
{
LastModuleError = "Unable to load " + filename + ": " + newhandle->LastError();
ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
delete newhandle;
return false;
}
}
catch (CoreException& modexcept)
{
this->NewServices = NULL;
// failure in module constructor
if (newmod)
{
DoSafeUnload(newmod);
ServerInstance->GlobalCulls.AddItem(newhandle);
}
else
delete newhandle;
LastModuleError = "Unable to load " + filename + ": " + modexcept.GetReason();
ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, LastModuleError);
return false;
}
if (defer)
return true;
FOREACH_MOD(OnLoadModule, (newmod));
PrioritizeHooks();
ServerInstance->ISupport.Build();
return true;
}
/* We must load the modules AFTER initializing the socket engine, now */
void ModuleManager::LoadCoreModules(std::map<std::string, ServiceList>& servicemap)
{
std::cout << "Loading core modules " << std::flush;
std::vector<std::string> files;
if (!FileSystem::GetFileList(ServerInstance->Config->Paths.Module, files, "core_*.so"))
{
std::cout << "failed!" << std::endl;
ServerInstance->Exit(EXIT_STATUS_MODULE);
}
for (std::vector<std::string>::const_iterator iter = files.begin(); iter != files.end(); ++iter)
{
std::cout << "." << std::flush;
const std::string& name = *iter;
this->NewServices = &servicemap[name];
if (!Load(name, true))
{
ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, this->LastError());
std::cout << std::endl << "[" << con_red << "*" << con_reset << "] " << this->LastError() << std::endl << std::endl;
ServerInstance->Exit(EXIT_STATUS_MODULE);
}
}
std::cout << std::endl;
}