/* src/modules/svso.c - Grant IRCOp rights (for Services)
 * (C) Copyright 2022 Bram Matthys (Syzop) and the UnrealIRCd team
 * License: GPLv2 or later
 */
#include "unrealircd.h"

ModuleHeader MOD_HEADER
= {
	"svso",
	"6.0.0",
	"Grant oper privileges via SVSO services command",
	"UnrealIRCd Team",
	"unrealircd-6",
};

/* Forward declarations */
CMD_FUNC(cmd_svso);

MOD_INIT()
{
	MARK_AS_OFFICIAL_MODULE(modinfo);

	CommandAdd(modinfo->handle, "SVSO", cmd_svso, MAXPARA, CMD_USER|CMD_SERVER);
	return MOD_SUCCESS;
}

MOD_LOAD()
{
	return MOD_SUCCESS;
}

MOD_UNLOAD()
{
	return MOD_SUCCESS;
}

/* Syntax: SVSO <uid|nick> <oper account> <operclass> <class> <modes> <snomask> <vhost>
 * All these parameters need to be set, you cannot leave any of them out,
 * HOWEVER some can be set to "-" to skip setting them, this is true for:
 * <class>, <modes>, <snomask>, <vhost>
 *
 * In UnrealIRCd the <operclass> will be prefixed by "services:" if not already
 * present. It is up to you to include or omit it.
 */
CMD_FUNC(cmd_svso)
{
	Client *acptr;
	char oper_account[64];
	const char *operclass;
	const char *clientclass;
	ConfigItem_class *clientclass_c;
	const char *modes;
	long modes_i = 0;
	const char *snomask;
	const char *vhost;

	if (!IsSvsCmdOk(client))
		return;

	if ((parc < 8) || BadPtr(parv[7]))
	{
		sendnumeric(client, ERR_NEEDMOREPARAMS, "SVSO");
		return;
	}

	operclass = parv[3];
	clientclass = parv[4];
	modes = parv[5];
	snomask = parv[6];
	vhost = parv[7];

	acptr = find_user(parv[1], NULL);
	if (!acptr)
	{
		sendnumeric(client, ERR_NOSUCHNICK, parv[1]);
		return;
	}

	if (!MyUser(acptr))
	{
		/* Forward it to the correct server, and we are done... */
		sendto_one(acptr, recv_mtags, ":%s SVSO %s %s %s %s %s %s %s",
		           client->name, acptr->id, parv[2], parv[3], parv[4], parv[5], parv[6], parv[7]);
		return;
	}

	/* CAVEAT ALERT !
	 * Don't mix up 'client' and 'acptr' below...
	 * 'client' is the server or services pseudouser that requests the change
	 * 'acptr' is the person that will be made OPER
	 */

	/* If we get here, we validate the request and then make the user oper. */
	if (!find_operclass(operclass))
	{
		sendnumeric(client, ERR_CANNOTDOCOMMAND, "SVSO", "Operclass does not exist");
		return;
	}

	/* Set any items to NULL if they are skipped (on request) */
	if (!strcmp(clientclass, "-"))
		clientclass = NULL;
	if (!strcmp(modes, "-"))
		modes = NULL;
	if (!strcmp(snomask, "-"))
		snomask = NULL;
	if (!strcmp(vhost, "-"))
		vhost = NULL;

	/* First, maybe the user is oper already? Then de-oper them.. */
	if (IsOper(acptr))
	{
		int was_hidden_oper = IsHideOper(acptr) ? 1 : 0;

		list_del(&acptr->special_node);
		RunHook(HOOKTYPE_LOCAL_OPER, acptr, 0, NULL, NULL);
		remove_oper_privileges(acptr, 1);

		if (!was_hidden_oper)
			irccounts.operators--;
		VERIFY_OPERCOUNT(acptr, "svso");

	}

	if (vhost && !valid_vhost(vhost))
		sendnumeric(client, ERR_CANNOTDOCOMMAND, "SVSO", "Failed to make user oper: vhost contains illegal characters or is too long");

	/* Prefix the oper block name with "remote:" if it hasn't already */
	if (!strncmp(parv[2], "remote:", 7))
		strlcpy(oper_account, parv[2], sizeof(oper_account));
	else
		snprintf(oper_account, sizeof(oper_account), "remote:%s", parv[2]);

	/* These needs to be looked up... */
	clientclass_c = find_class(clientclass); /* NULL is fine! */
	if (modes)
		modes_i = set_usermode(modes);

	if (!make_oper(acptr, oper_account, operclass, clientclass_c, modes_i, snomask, vhost, NULL))
		sendnumeric(client, ERR_CANNOTDOCOMMAND, "SVSO", "Failed to make user oper");
}
