aboutsummaryrefslogtreecommitdiff
/*
 * InspIRCd -- Internet Relay Chat Daemon
 *
 *   Copyright (C) 2026 Sadie Powell <sadie@witchery.services>
 *
 * 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"

static insp::intrusive_list<dynamic_reference_base>* dynrefs = nullptr;

dynamic_reference_base::dynamic_reference_base(const WeakModulePtr& mod, const std::string& stype, const std::string& sname, bool strict)
	: service_name(sname)
	, service_type(stype)
	, strict_ref(strict)
	, creator(mod)
{
	if (!dynrefs)
		dynrefs = new insp::intrusive_list<dynamic_reference_base>;
	dynrefs->push_front(this);

	// Resolve unless there is no ModuleManager (part of class InspIRCd)
	if (ServerInstance)
		resolve();
}

dynamic_reference_base::dynamic_reference_base(const dynamic_reference_base& other)
	: dynamic_reference_base(other.creator, other.service_type, other.service_name, other.strict_ref)
{
	// Intentionally left blank.
}

dynamic_reference_base::~dynamic_reference_base()
{
	dynrefs->erase(this);
	if (dynrefs->empty())
		insp::delete_zero(dynrefs);
}

void dynamic_reference_base::reset_all(const std::string& stype)
{
	if (!dynrefs)
		return;

	for (auto* dynref : *dynrefs)
	{
		if (stype.empty() || dynref->service_type == stype)
			dynref->resolve();
	}
}

void dynamic_reference_base::resolve()
{
	auto* oldvalue = this->value;
	this->value = nullptr;

	// Because find() may return any element with a matching key in case count(key) > 1 use lower_bound()
	// to ensure a dynref with the same name as another one resolves to the same object
	auto i = ServerInstance->Modules.Services.lower_bound(std::make_pair(this->service_type, this->service_name));
	if (i == ServerInstance->Modules.Services.end())
		return; // No service found.

	const auto& [stype, sname] = i->first;
	if (!insp::casemapped_equals(this->service_type, stype) || !insp::casemapped_equals(this->service_name, sname))
		return; // No service found.

	if (this->strict_ref && sname.empty())
		return; // Can't use generic services for strict references.

	this->value = i->second;
	if (oldvalue != value && hook)
		hook->OnCapture();
}

void dynamic_reference_base::ClearProviderName()
{
	this->service_name.clear();
	value = nullptr;
}

void dynamic_reference_base::SetProvider(const std::string& stype, const std::string& sname)
{
	this->service_type = stype;
	this->service_name = sname;
	resolve();
}

void dynamic_reference_base::SetProviderName(const std::string& sname)
{
	SetProvider(GetProviderType(), sname);
}

Service::Provider::Provider(const WeakModulePtr& mod, const std::string& stype, const std::string& sname)
	: service_creator(mod)
	, service_name(sname)
	, service_type(stype)
{
	if ((ServerInstance) && (ServerInstance->Modules.NewServices))
		ServerInstance->Modules.NewServices->push_back(this);
}

void Service::Provider::DisableAutoRegister()
{
	if ((ServerInstance) && (ServerInstance->Modules.NewServices))
		std::erase(*ServerInstance->Modules.NewServices, this);
}

std::string Service::Provider::GetSource() const
{
	if (insp::empty_ptr(this->service_creator))
		return "the core";

	const auto mod = this->service_creator.lock();
	if (!mod)
		return "an unknown module";

	return FMT::format("the {} module", ModuleManager::ShrinkModName(mod->ModuleFile));
}

void Service::Provider::RegisterService()
{
	// Intentionally left blank.
}

void Service::Provider::UnregisterService()
{
	// Intentionally left blank.
}

Service::SimpleProvider::SimpleProvider(const WeakModulePtr& mod, const std::string& stype, const std::string& sname)
	: Service::Provider(mod, stype, sname)
{
}

void Service::SimpleProvider::RegisterService()
{
	if (!this->service_name.empty())
		ServerInstance->Modules.AddReferent(this->service_type, this->service_name, this);
	ServerInstance->Modules.AddReferent(this->service_type, "", this);
}

void Service::SimpleProvider::UnregisterService()
{
	ServerInstance->Modules.DelReferent(this);
}