/*                  _________   __    ___   ___   ___               _________   _________   ___
                   /  ______/  /  |  /  /  /  /  /  /              /  ______/  /  ___   /  /  /
                  /  /        /   | /  /  /  /  /  /  _________   /  /        /  /__/  /  /  /
                 /  / ____   /  | |/  /  /  /  /  /  /        /  /  / ____   /  ______/  /  /
                /  / /_  /  /  /| |  /  /  /  /  /  /________/  /  / /_  /  /  /        /  /
               /  /___/ /  /  / |   /  /  /__/  /              /  /___/ /  /  /        /  /_____
              /________/  /__/  |__/  /________/              /________/  /__/        /________/
          _________   ___   ___    ______     _______     ________   _________     ______     __    ___
         /  ______/  /  /  /  /  _/     /_   /  ___  |   /  __   /  /___  ___/   _/     /_   /  |  /  /
        /  /        /  /  /  /  /  _/_   /  /  /  /  /  /  / /   /     / /      /  _/_   /  /   | /  /
       /  / ____   /  /  /  /  /  /__/  /  /  /__/ _/  /  /  /  /     / /      /  /__/  /  /  | |/  /
      /  / /_  /  /  /  /  /  /  ___   /  /  ___  /   /  / _/  /     / /      /  ___   /  /  /| |  /
     /  /___/ /  /  /__/  /  /  /  /  /  /  /  /  |  /  /_/  _/  ___/ /___   /  /  /  /  /  / |   /
    /________/  /________/  /__/  /__/  /__/  /__/  /_______/   /________/  /__/  /__/  /__/  |__/

	Guardian
	Copyright (C) 2004-2005 Paul Taylor

	Guardian 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; either version 2, or (at your option) any later
	version.

	The Guardian plugin and plugin source 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 be able to get a copy of the GNU General Public License at:
	*  http://www.gnu.org/copyleft/gpl.html
	Or, if these sites do not work, write to the Free Software Foundation,
	59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#ifndef __GUARDIAN_C__
#define __GUARDIAN_C__

#include "guardian.h"

  AnyType initializeServerPlugin(PluginID pluginID, int argc, char *argv[])
{
	setPluginUserInfoSize(plgnID = pluginID, sizeof(usrMgr));

	startTime = time(NULL);

	uint8 j;
	for (--argc, argv = &argv[1], j = 0; j < argc && argv[j]; j++)
	{
		FILE *fPtr;
		if (fPtr = fopen(argv[j], "rt"))
		{
			uint8 bfr[fChk], *Ptr;
			while (!feof(fPtr))
			{
				fgets(bfr, fChk, fPtr);
				if (strlen(bfr))
					if (Ptr = strtoken(bfr, " ,:\t\r\n"))
						if (!strncmp(Ptr, "#!/", 3))
							switch (*((uint32*)&Ptr[2]))
							{
								case CPP_ADM:
									if (!cfgLst[CFG_ADM])
										cfgLst[CFG_ADM] = argv[j];
									break;
								case CPP_MOD:
									if (!cfgLst[CFG_MOD])
										cfgLst[CFG_MOD] = argv[j];
									break;
								case CPP_GRP:
									if (!cfgLst[CFG_GRP])
										cfgLst[CFG_GRP] = argv[j];
									break;
							}
			}
			fclose(fPtr);
		} else {
			uint8 i;
			for (i = 0; i < nbrCfg; i++)
				if (!cfgLst[i])
				{
					cfgLst[i] = argv[j];
					break;
				}
		}
	}

	FormattedLogMessage(NULL, "Guardian", "v%d.%d Build %d (2004-2005) by Paul Taylor (Janus) <janus@palacecommunity.com>", vMajor, vMinor, vBuild);

	swtch = loadCfg(NULL, 1);

	handleTimerEvent(NULL, (AnyType)plgnID, 0);

	pluginRegisterCommand("guardian/", "<arguments>", 0, 99, &cmdMain, 0, "Backbone Command.\nArguments:\n\nUnique:\nabout - About guardian.\nreload - Reloads all Guardian files.\n\nAliases:\nlist = `blslst\nclean = `clnbls\ngroup = `grplst\nglist = `grdlst\nexit = `logout");
	pluginRegisterCommand("addgrp/", "<group>", 1, 1, &cmdAddGrp, 0, "Create <group>.");
	pluginRegisterCommand("addgprv/", "<group> <CMD/TLK/WHS_???|PKT_???|IPT_???|INT_???>", 2, 99, &cmdAddGPrv, 0, "Add privilege(s) to <group>.");
	pluginRegisterCommand("adduprv/", "<group> [-r -p -i] <CMD/TLK/WHS_???|PKT_???|IPT_???|INT_???>", 2, 99, &cmdAddUPrv, 0, "Add privilege(s) to targeted user.");
	pluginRegisterCommand("bless/", "<group> <password> [-r -p -i]", 2, 7, &cmdBless, 0, "Bless whispered user under <group>.\nArguments:\n\nr - (Pre-defined) Registration Code Code (if unspecified, this is used by default ONLY)\np - (Pre-defined) PUID\ni - (Pre-defined/Specify) IP Address");
	pluginRegisterCommand("blslst/", "[<group>]", 0, 1, &cmdBlsLst, 0, "List bless entries under <group>.");
	pluginRegisterCommand("chpass/", "<group> <password> [-r -p -i]", 2, 7, &cmdChngePass, 0, "Change <password> for whispered user under <group>.");
	pluginRegisterCommand("clnbls/", "<number>", 0, 1, &cmdClnBls, 0, "Clean bless list of entries unused in <number> of days (30 days by default).");
	pluginRegisterCommand("delgrp/", "<group>", 1, 1, &cmdDelGrp, 0, "Delete <group>.");
	pluginRegisterCommand("delgprv/", "<group> <CMD/TLK/WHS_???|PKT_???|IPT_???|INT_???>", 2, 99, &cmdDelGPrv, 0, "Delete privilege(s) from <group>.");
	pluginRegisterCommand("deluprv/", "<group> [-r -p -i] <CMD/TLK/WHS_???|PKT_???|IPT_???|INT_???>", 2, 99, &cmdDelUPrv, 0, "Delete privilege(s) from targeted user.");
	pluginRegisterCommand("grdlst/", "", 0, 0, &cmdGrdLst, 0, "List all users on by their \"Group\" \"User (#ID)\" \"Room (#ID)\".");
	pluginRegisterCommand("grplst/", "[<group>]", 0, 1, &cmdGrpLst, 0, "List groups if <group> not defined, list contents of group <group> if defined.");
	pluginRegisterCommand("logout/", "", 0, 1, &cmdLogOut, 0, "Log out of staff status (restores to member rank).");
	pluginRegisterCommand("unbless/", "<group> [-r -p -i]", 1, 8, &cmdUnBless, 0, "Unbless by whispering or specifying [-r -p -i] under <group>.\nArguments:\n\nr - Registration\np - PUID\ni - IP Address");

	pluginRegisterCommand("accept/", "", 0, 0, &cmdAccept, 0, "Accept avatar which has been offered to you.");
	pluginRegisterCommand("offer/", "", 0, 1, &cmdOffer, 0, "Offer your avatar to anyone.");
	pluginRegisterCommand("offerall/", "", 0, 0, &cmdOfferAll, 0, "Offer your avatar to everybody in the same room.");
	pluginRegisterCommand("sysop/", "[<SYSOP code>]", 0, 1, &cmdSysop, 0, "Get/Set server SYSOP code.");
	pluginRegisterCommand("uptime/", "", 0, 0, &cmdUpTime, 0, "Get server's uptime!");

	return (AnyType)plgnID;
}

  void shutdownServerPlugin(ServerState *state, AnyType pluginDat)
{
	ServerUserRec *usr;
	for (usr = state->userList; usr; usr = usr->nextUser)
		freeUsr(&((usrMgr *)getPluginUserInfo(plgnID, usr))->usr, 0, 1);
	saveBls();
	freeLst(0);
}

  void handleTimerEvent(ServerState *state, AnyType pluginDat, AnyType timerDat)
{
	if (admLst)
		free(admLst);
	nbrAdm = 0;
	admLst = NULL;
	FILE *fPtr = fopen(cfgLst[CFG_ADM], "rt");
	if (fPtr)
	{
		uint8 bfr[fChk];
		while (1)
		{
			fgets(bfr, fChk, fPtr);
			if (feof(fPtr))
				break;
			loadBls(NULL, bfr, 1);
		}
		fclose(fPtr);
	}
	scheduleTimerEvent(plgnID, 0, (nbrAdm ? 150 : 600));
}

  void noteUserCreation(ServerState *state, AnyType pluginDat, ServerUserRec *user)
{
	usrMgr *rUsr = (usrMgr *)getPluginUserInfo(plgnID, user);
	freeUsr(&rUsr->usr, 1, 0);
	memset(&rUsr->prp, 0, sizeof(prpRec));
	bldUsr(user, NULL, RNK_LIMBO, 0, 0);
}

  void noteUserDeletion(ServerState *state, AnyType pluginDat, ServerUserRec *user)
{
	freeUsr(&((usrMgr *)getPluginUserInfo(plgnID, user))->usr, 0, 1);
}

  ClientMsg *handleMessage(ServerState *state, AnyType pluginDat, ClientMsg *msg)
{
	usrLst = state->userList;
	nbrUsr = state->nbrUsers;
	ServerUserRec *usr = NULL, *user = state->currentUser;

	usrMgr *tUsr = NULL, *rUsr = (usrMgr *)getPluginUserInfo(plgnID, user);

	uint8 bfr[MaxTxt], *Ptr = (char *)&msg->msg, cmdPrv = PRV_NON;

	if (user->loggedOn)
	{
		switch (msg->eventType)
		{
			case MSG_XTALK:
				DecryptCString(&Ptr[2], bfr, (*((uint16 *)Ptr) - 3) % MaxTxt, FALSE);
			case MSG_TALK:
				rUsr->tID = 0;
				if (msg->eventType == MSG_TALK)
					sprintf(bfr, "%0.255s", Ptr);
				Ptr = bfr;
				cmdPrv |= PRV_TLK;
				break;
			case MSG_XWHISPER:
				DecryptCString(&Ptr[6], bfr, (*((uint16 *)&Ptr[4]) - 3) % MaxTxt, FALSE);
			case MSG_WHISPER:
				rUsr->tID = *((uint32 *)Ptr);
				if (msg->eventType == MSG_WHISPER)
					sprintf(bfr, "%0.255s", &Ptr[4]);
				Ptr = bfr;
				cmdPrv |= PRV_WHS;
				break;
			case MSG_SUPERUSER:
				DecryptCString(&Ptr[1], bfr, *Ptr % MaxStr, FALSE);
				Ptr = bfr;
				break;
			case MSG_KILLUSER:
				rUsr->tID = *((uint32 *)Ptr);
				break;
			default:
				rUsr->tID = 0;
				break;
		}

		if (rUsr->tID)
			usr = GetServerUser(rUsr->tID);

		if (!swtch || rUsr->usr.rnk == RNK_ROOT)
			switch (msg->eventType)
			{
				case MSG_SUPERUSER:
					if (!UserRank(user))
						return NULL;
					if (rUsr->usr.rnk > RNK_MEMBER)
					{
						bldUsr(user, NULL, rUsr->usr.rnk, rUsr->usr.grp, 0);
						return NULL;
					}
					break;
				case MSG_TALK:
				case MSG_XTALK:
					if (!strchr("`'", *Ptr))
						break;
					if (!usr)
						if (strtoken(&Ptr[1], " ,:\t\r\n"))
						{
							uint8 *cmp, *cmd = strtoken(NULL, NULL);
							while (cmd && !usr)
							{
								Str31 nme;
								strtolower(cmd);
								for (usr = usrLst; usr; usr = usr->nextUser)
								{
									sprintf(nme, "%0.31s", CvtToCString(usr->user.name));
									strtolower(nme);
									if (!strcmp(cmd, nme))
									{
										rUsr->tID = usr->user.userID;
										break;
									}
								}
								if (cmp == cmd)
									cmd = strtoken(NULL, " ,:\t\r\n");
								else
									cmp = cmd = strtoken(NULL, NULL);
							}
						} else
							return NULL;
				default:
					if (rUsr->usr.rnk == RNK_ROOT)
						return msg;
			}

		if (swtch && UserRank(user) < RANK_GOD && rUsr->usr.rnk < RNK_ROOT)
		{
			switch (msg->eventType)
			{
				case MSG_TALK:
				case MSG_XTALK:
				case MSG_WHISPER:
				case MSG_XWHISPER:
					if (!strchr("`'", *Ptr))
						break;
				case MSG_BLOWTHRU:
				case MSG_KILLUSER:
				case MSG_ROOMNEW:
				case MSG_ROOMSETDESC:
				case MSG_SUPERUSER:
					UserMessage(user, "Protocol usage is limited.");
				case MSG_LOGON:
				case MSG_PICTMOVE:
				case MSG_SPOTDEL:
				case MSG_SPOTMOVE:
				case MSG_SPOTNEW:
					return NULL;
			}
			return msg;
		}
	} else
		if (swtch && msg->eventType != MSG_LOGON)
			return NULL;

	if (usr)
		tUsr = (usrMgr *)getPluginUserInfo(plgnID, usr);

	uint16 j;

	if (rUsr->usr.msk.pktMsk)
	{
		for (j = 0; j < nbrPkt; j++)
			if (msg->eventType == pktLst[j])
			{
				if (!(rUsr->usr.msk.pktMsk[j] & PRV_ALL))
				{
					UserMessage(user, "Sorry, you are not privileged to use that %s.", "protocol");
					return NULL;
				}
				if (rstMsk.pktMsk && tUsr)
					if (rstMsk.pktMsk[j] && tUsr->usr.rnk > rUsr->usr.rnk)
					{
						UserMessage(user, "Sorry, you can't send '%0.4s' (protocol), to a person of higher rank.", (char *)&msg->eventType);
						return NULL;
					}
				break;
			}
		if (j == nbrPkt)
			return NULL;
	}

	if (!nbrBls && rUsr->usr.rnk < RNK_STAFF && UserRank(user) > RANK_WIZARD)
		bldUsr(user, NULL, RNK_OWNER, 0, 0);

	switch (msg->eventType)
	{
		case MSG_TALK:
		case MSG_XTALK:
		case MSG_WHISPER:
		case MSG_XWHISPER:
			if (!strchr("`'", *Ptr))
				return msg;
			if (rUsr->usr.msk.cmdMsk && chrChk(user, strtoken(++Ptr, " ,:\t\r\n")))
			{
				for (j = 0; j < nbrCmd; j++)
					if (!strcmp(Ptr, cmdLst[j]))
					{
						if (!(rUsr->usr.msk.cmdMsk[j] & cmdPrv))
						{
							UserMessage(user, "Sorry, you are not privileged to use that %s.", "command");
							return NULL;
						}
						if (rstMsk.cmdMsk && tUsr)
							if (rstMsk.cmdMsk[j] && tUsr->usr.rnk > rUsr->usr.rnk)
							{
								UserMessage(user, "Sorry, you can't send `%0.31s, to a person of higher rank.", Ptr);
								return NULL;
							}
						break;
					}
				if (j == nbrCmd)
				{
					UserMessage(user, "Invalid server command.");
					return NULL;
				}
			} else {
				UserMessage(user, "Invalid server command.");
				return NULL;
			}
			break;
		case MSG_LOGON:
			{
				if (user->loggedOn || rUsr->usr.rnk != RNK_LIMBO)
					return NULL;
				AuxRegistrationRec *aR = (AuxRegistrationRec *)Ptr;
				if (aR->crc == GuestCRC && aR->counter == GuestCtr)
					bldUsr(user, NULL, RNK_GUEST, 0, 0);
				else {
					uint32 reg = aR->crc ^ aR->counter ^ MAGICLNG;
					if (ComputeLicenseCRC(reg) == aR->crc)
					{
						if (nbrAdm && admLst)
						{
							uint32 ip = htonl(user->feIPAddr);
							uint32 puid = aR->puidCRC ^ aR->puidCtr;
							for (j = 0; j < nbrAdm; j++)
								if (cmpBls(&admLst[j].bls, reg, puid, ip))
								{
									memmove(&user->crc, &aR->crc, 8);
									memmove(&user->puidCtr, &aR->puidCtr, 8);
									FormattedLogMessage(user, "Guardian", "%0.31s \"%0.31s\"", (char *)rnkNme[RNK_ROOT], admLst[j].nme);
									WizGlobalMessage("Page from %s: Attention on deck, %0.31s has come aboard!", "Guardian", admLst[j].nme);
									bldUsr(user, NULL, RNK_ROOT, 0, 1);
									return msg;
								}
						}
						bldUsr(user, NULL, RNK_MEMBER, 0, 0);
					} else {
						LogoffUser(user->user.userID, K_InvalidSerialNumber);
						return NULL;
					}
				}
			}
			break;
		case MSG_USERNAME:
			Ptr = CvtToCString(Ptr);
			if (!(rUsr->usr.msk.intMsk & INT_BADGE) && strchr(Ptr, '*'))
				UserMessage(user, "Sorry, that name is illegal.");
			else
				chngNme(user, &rUsr->usr, Ptr, CvtToCString(user->user.name));
			return NULL;
		case MSG_SUPERUSER:
			if (rUsr->usr.rnk > RNK_GUEST)
			{
				if (nbrBls && blsLst)
				{
					uint32 reg = user->crc ^ user->counter ^ MAGICLNG;
					uint32 ip = htonl(user->feIPAddr);
					uint32 puid = user->puidCRC ^ user->puidCtr;
					for (j = 0; j < nbrBls; j++)
						if (cmpBls(&blsLst[j].bls, reg, puid, ip))
						{
							hPwd(blsLst[j].nme, Ptr, rUsr->usr.pwd);
							if (!strcmp(blsLst[j].usr.pwd, rUsr->usr.pwd))
							{
								user->authAttempts = 0;
								blsLst[j].lst = (uint32)time(NULL);
								Ptr = txtRnkGrp(&blsLst[j].usr);
								FormattedLogMessage(user, "Guardian", "%0.31s \"%0.31s\"", Ptr, CvtToCString(user->user.name));
								WizGlobalMessage("Page from %s: %0.31s made %0.31s.", "Guardian", CvtToCString(user->user.name), Ptr);
								bldUsr(user, &blsLst[j].usr.msk, blsLst[j].usr.rnk, blsLst[j].usr.grp, 1);
								return NULL;
							}
						}
					getServerConfiguration(&cfg);
					if (cfg.authAttempts)
					{
						if (++(user->authAttempts) < cfg.authAttempts)
							UserMessage(user, "%s If you continue to try invalid passwords you will be automatically kicked off by the system.", "That password is invalid.");
						else
							ScheduleUserKill(user, K_KilledBySysop, NULL, (cfg.deathPenaltyMinutes ? cfg.deathPenaltyMinutes : 1500));
					} else
						UserMessage(user, "That password is invalid.");
					return NULL;
				}
			} else
				UserMessage(user, "Please, register first.");
			break;
		case MSG_ROOMSETDESC:
			{
				uint8 i, *ipt = NULL;
				RoomRec *room = (RoomRec *)Ptr;
				Hotspot *spt = (Hotspot *)&room->varBuf[room->hotspotOfst];
				if (spt)
					for (j = 0; j < room->nbrHotspots; j++)
					{
						ipt = realloc(ipt, strlen(&room->varBuf[spt[j].scriptTextOfst]) + 1);
						sprintf(ipt, "%s", &room->varBuf[spt[j].scriptTextOfst]);
						Ptr = ipt;
						strtolower(Ptr);
						while (*Ptr)
						{
							for (i = 0; i < rUsr->usr.msk.nbrIpt; i++)
								if (!strncmp(Ptr, rUsr->usr.msk.iptLst[i], strlen(rUsr->usr.msk.iptLst[i])))
								{
									free(ipt);
									UserMessage(user, "Script contained '%0.31s', which is not allowed.", rUsr->usr.msk.iptLst[i]);
									sendMsgRoomSetDesc(toUser(user), GetRoom(user->user.roomID));
									return NULL;
								}
							Ptr++;
						}
					}
				if (ipt)
					free(ipt);
			}
			break;
	}
	return msg;
}

  static CommandResult cmdMain(ServerUserRec *user, int argc, char *argv[])
{
	if (!user)
		return;
	if (argc)
	{
		uint8 j, *Ptr = argv[0];
		for (j = 0; j < argc; j++)
			rmvChr(argv[j], "`\'");
		// --- Unique ---
		if (!strcmp(Ptr, "about"))
		{
			UserMessage(user, ":%s (v%d.%d Build %d), released under the GNU-GPL (http://www.gnu.org/copyleft/gpl.html)", "Guardian", vMajor, vMinor, vBuild);
			UserMessage(user, ":%s is a server plugin that gives complete command and protocol control over the server, picking up where hostkeeper left off.", "Guardian");
			UserMessage(user, ":Great appreciation goes out to Jon Kelley (Jon) from Mount Olympus (olympus-mount.com) for ideas, bug hunting, writing all documentation, and overall support.");
			UserMessage(user, ":Written by Paul Taylor (Janus), CEO/Programmer of palacecommunity.com.");
		} else if (!strcmp(Ptr, "reload")) {
			swtch = 1;
			swtch = loadCfg(user, 0);
			rldUsr(1);
			rptErr(user, 11, NULL);
		} else {
			// --- Aliases ---
			argc--;
			argv = &argv[1];
			if (!(strcmp(Ptr, "list") && strcmp(Ptr, "blslst")))
				cmdBlsLst(user, argc, argv);
			else if (!(strcmp(Ptr, "clean") && strcmp(Ptr, "clnbls")))
				cmdClnBls(user, argc, argv);
			else if (!(strcmp(Ptr, "group") && strcmp(Ptr, "grplst")))
				cmdGrpLst(user, argc, argv);
			else if (!(strcmp(Ptr, "glist") && strcmp(Ptr, "grdlst")))
				cmdGrdLst(user, argc, argv);
			else if (!(strcmp(Ptr, "exit") && strcmp(Ptr, "logout")))
				cmdLogOut(user, argc, argv);
		}
	}
	return;
}

  static CommandResult cmdAddGrp(ServerUserRec *user, int argc, char *argv[])
{
	if (chrChk(user, argv[0]))
	{
		usrRec usr;
		if (fndRnkGrp(&usr, argv[0]) > 1)
			UserMessage(user, "Group already exists.");
		else {
			grpLst = realloc(grpLst, ++nbrGrp * sizeof(grpMgr));
			freeMsk(&grpLst[nbrGrp - 1].msk, 1, 0);
			bldMsk(user, &grpLst[nbrGrp - 1].msk);
			sprintf(grpLst[nbrGrp - 1].nme, "%0.31s", argv[0]);
			saveMod();
			UserMessage(user, "'%0.31s' has been created.", argv[0]);
		}
	}
	return;
}

  static CommandResult cmdAddGPrv(ServerUserRec *user, int argc, char *argv[])
{
	if (chrChk(user, argv[0]))
	{
		usrRec usr;
		if (fndRnkGrp(&usr, argv[0]))
		{
			if (argc > 1)
			{
				uint8 j;
				grdMsk *msk;
				if (usr.rnk > RNK_STAFF)
					msk = &rnkLst[usr.rnk];
				else
					msk = &grpLst[usr.grp].msk;
				for (j = 1; j < argc; j++)
					cfgMsk(user, msk, argv[j], 1, 0);
			}
			cmdGrpLst(user, 1, argv);
		} else
			UserMessage(user, "No such group (%0.31s).", argv[0]);
	}
	return;
}

  static CommandResult cmdAddUPrv(ServerUserRec *user, int argc, char *argv[])
{
	fncCmds(2, user, argc, argv);
	return;
}

  static CommandResult cmdBless(ServerUserRec *user, int argc, char *argv[])
{
	blsMgr bls;
	if (fndRnkGrp(&bls.usr, argv[0]))
	{
		ServerUserRec *usr = GetServerUser(GetTarget(user));
		if (usr)
		{
			uint8 j;
			uint32 reg = usr->crc ^ usr->counter ^ MAGICLNG;
			uint32 puid = usr->puidCRC ^ usr->puidCtr;
			uint32 ip = htonl(usr->feIPAddr);
			for (j = 0; j < nbrBls; j++)
				if (cmpBls(&blsLst[j].bls, reg, puid, ip) && (blsLst[j].usr.rnk == bls.usr.rnk &&
				  (blsLst[j].usr.rnk > RNK_STAFF || blsLst[j].usr.grp == bls.usr.grp)))
				{
					UserMessage(user, "This user has already been blessed.");
					return;
				}
			if (strlen(argv[1]) > 3)
			{
				sprintf(bls.nme, "%0.31s", CvtToCString(usr->user.name));
				rmvChr(bls.nme, ";#/\\\"\'‘’`´“”‹™‰¼½¾÷");
				rplChr(bls.nme,
				  "123AAAAAAAABCCCCDEEEEEIIIILNOOOOOOOPRSSTTUUUUXYYZaaaaaaaacdeeeefiiiinooooooooppsuuuuuyyz|!?...,<><>+---~ ",
					"¹²³@ÀÁÂÃÄÅÆß©¢ÇŒÐ€ÈÉÊËÌÍÎÏ£ÑÒÓÔÕÖØ¤Þ®§Š‡†ÙÚÛÜ×Ý¥Žªàáâãäåæçðèéêëƒìíîïñ°ºòóôõöø¶þšùúûüµýÿž¦¡¿…•·¸›„«»±—¯¬¨ ");
				if (strlen(bls.nme) > 3)
				{
					uint8 bfr[34], *Ptr;
					hPwd(bls.nme, argv[1], bls.usr.pwd);
					memset(&bls.bls, 0, sizeof(blsRec));
					if (argc > 2)
						for (j = 2; j < argc; j++)
						{
							Ptr = argv[j];
							if (*Ptr == '-')
								while (*(++Ptr))
									switch (*Ptr)
									{
										case 'r':
											bls.bls.reg = reg;
											break;
										case 'p':
											bls.bls.puid = puid;
											break;
										case 'i':
											if (++j < argc)
											{
												rmvChr(argv[j], "\"\'");
												sprintf(bfr, "\"%0.31s\"", argv[j]);
												if (cvtIP2I(&bls.bls.ip, bfr))
													break;
											}
											if (!cmpBls(&bls.bls, 0, 0, ip))
											{
												bls.bls.ip.nIP.i.ip = ip;
												bls.bls.ip.xIP.i.ip = ip;
											}
											break;
									}
						}
					else
						bls.bls.reg = reg;
					if (cmpBls(&bls.bls, reg, puid, ip))
					{
						bls.lst = time(NULL);
						freeMsk(&bls.usr.msk, 1, 0);
						bldMsk(user, &bls.usr.msk);
						addBls(user, &bls, "blessed");
						saveBls();
						UserMessage(usr, "You are blessed my child, be fruitful and multiply. Go forth and sin no more. Your password is '%0.31s', your rank is '%0.31s'.", argv[1], argv[0]);
					} else
						UserMessage(user, "User information invalid, try again.", argv[1]);
				} else
					UserMessage(user, "'%0.31s', has invalid characters, or is too short.", CvtToCString(usr->user.name));
			} else
				UserMessage(user, "Password '%0.31s', was too short.", argv[1]);
		} else
			UserMessage(user, "Please select a target.");
	} else
			UserMessage(user, "No such group (%0.31s).", argv[0]);
	return;
}

  static CommandResult cmdBlsLst(ServerUserRec *user, int argc, char *argv[])
{
	uint8 i, j;
	if (nbrBls)
	{
		if (argc)
		{
			i = 0;
			usrRec usr;
			fndRnkGrp(&usr, argv[0]);
			if (usr.rnk)
				for (j = 0; j < nbrBls; j++)
					if (blsLst[j].usr.grp == usr.grp || usr.rnk > RNK_STAFF)
					{
						i++;
						UserMessage(user, ";");
						txtBls(user, &blsLst[j]);
					}
			if (!i)
				UserMessage(user, "Group '%s' is empty.", argv[0]);
		} else
			for (i = (RNK_ROOT - 1); i > RNK_MEMBER; i--)
				for (j = 0; j < nbrBls; j++)
					if (blsLst[j].usr.rnk == i)
					{
						UserMessage(user, ";");
						txtBls(user, &blsLst[j]);
					}
	} else
		UserMessage(user, "Bless list empty.");
	return;
}

  static CommandResult cmdChngePass(ServerUserRec *user, int argc, char *argv[])
{
	fncCmds(0, user, argc, argv);
	return;
}

  static CommandResult cmdClnBls(ServerUserRec *user, int argc, char *argv[])
{
	if (nbrBls && blsLst)
	{
		uint8 i, j = 30;
		if (argc)
			j = (uint8)atoi(argv[0]);
		uint32 c = 0;
		if (j)
		{
			for (i = 0; i < nbrBls; i++)
				if (blsLst[i].lst < (time(NULL) - (j * 86400)))
				{
					c++;
					usrRec *rUsr = &((usrMgr *)getPluginUserInfo(plgnID, user))->usr;
					if ((rUsr->msk.intMsk & INT_CLNBLS) || rUsr->rnk == RNK_ROOT)
						rptUsr(user, &blsLst[i], "unblessed");
					memmove(&blsLst[i], &blsLst[++i], (nbrBls - i) * sizeof(blsMgr));
					blsLst = realloc(blsLst, --nbrBls * sizeof(blsMgr));
					i -= 2;
				}
			saveBls();
			rldUsr(0);
		}
		if (c)
			FormattedLogMessage(user, "Guardian", "%u record(s) purged!", c);
		UserMessage(user, "%u record(s) purged!", c);
	}
	else
		UserMessage(user, "Bless list empty.");
	return;
}

  static CommandResult cmdDelGrp(ServerUserRec *user, int argc, char *argv[])
{
	if (chrChk(user, argv[0]))
	{
		usrRec usr;
		uint8 i;
		if (fndRnkGrp(&usr, argv[0]) > 1 && usr.grp < nbrGrp)
		{
			memmove(&grpLst[usr.grp], &grpLst[++usr.grp], (nbrGrp - usr.grp) * sizeof(grpMgr));
			grpLst = realloc(grpLst, --nbrGrp * sizeof(grpMgr));
			saveMod();
			if (nbrBls && blsLst)
				for (usr.grp--, i = 0; i < nbrBls; i++)
					if (blsLst[i].usr.grp == usr.grp)
					{
						memmove(&blsLst[i], &blsLst[++i], (nbrBls - i) * sizeof(blsMgr));
						blsLst = realloc(blsLst, --nbrBls * sizeof(blsMgr));
						i -= 2;
					} else if (blsLst[i].usr.grp > usr.grp)
						blsLst[i].usr.grp--;
			saveBls();
			rldUsr(0);
			UserMessage(user, "'%0.31s' has been deleted.", argv[0]);
		} else
			UserMessage(user, "No such group (%0.31s).", argv[0]);
	}
	return;
}

  static CommandResult cmdDelGPrv(ServerUserRec *user, int argc, char *argv[])
{
	if (chrChk(user, argv[0]))
	{
		usrRec usr;
		if (fndRnkGrp(&usr, argv[0]))
		{
			if (argc > 1)
			{
				uint8 j;
				grdMsk *msk;
				if (usr.rnk > RNK_STAFF)
					msk = &rnkLst[usr.rnk];
				else
					msk = &grpLst[usr.grp].msk;
				for (j = 1; j < argc; j++)
					cfgMsk(user, msk, argv[j], 0, 0);
			}
			cmdGrpLst(user, 1, argv);
		} else
			UserMessage(user, "No such group (%0.31s).", argv[0]);
	}
	return;
}

  static CommandResult cmdDelUPrv(ServerUserRec *user, int argc, char *argv[])
{
	fncCmds(1, user, argc, argv);
	return;
}

  static CommandResult cmdGrpLst(ServerUserRec *user, int argc, char *argv[])
{
	uint8 j;
	for (j = 0; j < RNK_ROOT; j++)
		if (!(argc && strcmp(argv[0], (char *)rnkNme[j])))
		{
				UserMessage(user, "; Rank: %0.8s", (char *)rnkNme[j]);
				if (argc)
				{
					txtMsk(user, &rnkLst[j]);
					return;
				}
		}
	if (nbrGrp)
	{
		for (j = 0; j < nbrGrp; j++)
			if (!(argc && strcmp(argv[0], grpLst[j].nme)))
			{
				UserMessage(user, "; Group: %0.31s", grpLst[j].nme);
				if (argc)
				{
					txtMsk(user, &rnkLst[j]);
					return;
				}
			}
		if (argc && j == nbrGrp)
			UserMessage(user, "No such group (%0.31s).", argv[0]);
	} else
		UserMessage(user, "No groups created.");
	return;
}

  static CommandResult cmdGrdLst(ServerUserRec *user, int argc, char *argv[])
{
	ServerUserRec *usr;
	for (usr = usrLst; usr; usr = usr->nextUser)
	{
		usrMgr *rUsr = (usrMgr *)getPluginUserInfo(plgnID, usr);
		if (rUsr->usr.rnk > RNK_MEMBER)
		{
			ServerRoomRec *room = GetRoom(usr->user.roomID);
			if (room)
				UserMessage(user, "; \"%0.31s\" \"%0.31s (%ld)\" \"%0.255s (%ld)\"",
				  txtRnkGrp(&rUsr->usr), CvtToCString(usr->user.name), usr->user.userID,
					CvtToCString(&room->varBuf[room->room.roomNameOfst]), room->room.roomID);
		}
	}
	return;
}

  static CommandResult cmdLogOut(ServerUserRec *user, int argc, char *argv[])
{
	ServerUserRec *usr = GetServerUser(GetTarget(user));
	if (!usr)
		fncLogOut(user);
	else
		if (fncLogOut(usr))
		{
			usrRec *rUsr = &((usrMgr *)getPluginUserInfo(plgnID, user))->usr;
			if (((usrMgr *)getPluginUserInfo(plgnID, usr))->usr.rnk < rUsr->rnk && (rUsr->msk.intMsk & INT_LOGOUT))
				GodGlobalMessage("Page from %s: '%0.31s' logged out '%0.31s'!", "Guardian", CvtToCString(user->user.name), CvtToCString(usr->user.name));
			UserMessage(user, "You have logged out '%0.31s'.", CvtToCString(usr->user.name));
		}
	return;
}

  static uint8 fncLogOut(ServerUserRec *user)
{
	usrRec *rUsr = &((usrMgr *)getPluginUserInfo(plgnID, user))->usr;
	if (rUsr->rnk < RNK_ROOT && rUsr->rnk > RNK_MEMBER)
	{
		UserMessage(user, "You have logged out of %0.31s.", txtRnkGrp(rUsr));
		bldUsr(user, NULL, RNK_MEMBER, 0, 0);
		FormattedLogMessage(user, "Guardian", "logout \"%0.31s\"", CvtToCString(user->user.name));
		return 1;
	}
	return 0;
}

  static CommandResult cmdUnBless(ServerUserRec *user, int argc, char *argv[])
{
	fncCmds(3, user, argc, argv);
	return;
}

  static CommandResult cmdAccept(ServerUserRec *user, int argc, char *argv[])
{
	prpRec *prp = &((usrMgr *)getPluginUserInfo(plgnID, user))->prp;
	if (prp->nbrPrp)
	{
		ChangeUserProp(user, prp->nbrPrp, prp->Asset);
		sendMsgUserProp(toUser(user), user, prp->nbrPrp, prp->Asset);
	} else
		UserMessage(user, "Can't accept a naked avatar.");
	return;
}

  static CommandResult cmdOffer(ServerUserRec *user, int argc, char *argv[])
{
	if (user->user.nbrProps)
	{
		ServerUserRec *usr = GetServerUser(GetTarget(user));
		if (usr)
		{
			if (user->user.roomID == usr->user.roomID)
			{
				if (usr->flags & U_RejectPrivate)
				{
					if (argc < 2)
						sendMsgTalk(toUser(user), user->user.userID, "I am rejecting whispers!");
					return;
				} else if (ActionExists(usr, user, UA_Mute)) {
					if (argc < 2)
						sendMsgTalk(toUser(user), user->user.userID, "I am muting you!");
					return;
				}
			} else {
				if (usr->flags & U_RejectESP)
				{
					UserPrivateMessage(user, "Message %s %0.31s: %s", "from", CvtToCString(usr->user.name), "I am rejecting ESPs!");
					return;
				} else if (ActionExists(usr, user, UA_Mute)) {
					UserPrivateMessage(user, "Message %s %0.31s: %s", "from", CvtToCString(usr->user.name), "I am muting you!");
					return;
				}
			}
			uint8 j;
			prpRec *prp = &((usrMgr *)getPluginUserInfo(plgnID, usr))->prp;
			memset(prp, 0, sizeof(prpRec));
			for (j = 0; j < MaxUserProps && j < user->user.nbrProps; j++)
				if (user->user.propSpec[j].id)
					prp->Asset[prp->nbrPrp++] = user->user.propSpec[j];
			if (user->user.roomID == usr->user.roomID)
			{
				if (argc < 2)
					sendMsgTalk(toUser(user), user->user.userID, "I have offered you my avatar. Type `accept to receive it.");
				sendMsgTalk(toUser(usr), user->user.userID, "I have offered you my avatar. Type `accept to receive it.");
			} else {
				UserPrivateMessage(user, "Message %s %0.31s: %s", "to", CvtToCString(usr->user.name), "I have offered you my avatar. Type `accept to receive it.");
				UserPrivateMessage(usr, "Message %s %0.31s: %s", "from", CvtToCString(user->user.name), "I have offered you my avatar. Type `accept to receive it.");
			}
		} else
			UserMessage(user, "Please select a target.");
	} else
		UserMessage(user, "Can't offer a naked avatar.");
	return;
}

  static CommandResult cmdOfferAll(ServerUserRec *user, int argc, char *argv[])
{
	if (user->user.nbrProps)
	{
		ServerUserRec *usr;
		usrMgr *rUsr = (usrMgr *)getPluginUserInfo(plgnID, user);
		for (usr = usrLst; usr; usr = usr->nextUser)
		{
			if (user->user.roomID == usr->user.roomID && user->user.userID != usr->user.userID)
			{
				rUsr->tID = usr->user.userID;
				cmdOffer(user, 2, NULL);
			}
		}
		UserMessage(user, "You have offered your avatar to the whole room.");
	} else
		UserMessage(user, "Can't offer a naked avatar.");
	return;
}

  static CommandResult cmdSysop(ServerUserRec *user, int argc, char *argv[])
{
	getServerConfiguration(&cfg);
	if (argc && strcmp(cfg.sysop, argv[0]))
	{
		FormattedLogMessage(user, "SYSOP", "{%0.64s} -> {%0.64s}", cfg.sysop, argv[0]);
		sprintf(cfg.sysop, "%0.63s", argv[0]);
		setServerConfiguration(&cfg);
	}
	UserMessage(user, "%s: {%0.63s}", "SYSOP", cfg.sysop);
	return;
}

  static CommandResult cmdUpTime(ServerUserRec *user, int argc, char *argv[])
{
	float lav[3];
	FILE *fPtr = fopen("/proc/loadavg", "rt");
	if (fPtr)
	{
		fscanf(fPtr, "%f", &lav[0]);
		fscanf(fPtr, "%f", &lav[1]);
		fscanf(fPtr, "%f", &lav[2]);
		fclose(fPtr);
	} else
		lav[0] = lav[1] = lav[2] = 0;
	uint32 t = time(NULL), s = t - startTime;
	UserMessage(user, "; %0.2u:%0.2u:%0.2u GMT  up %u day%s,  %0.2u:%0.2u,  %u user%s,  load average: %0.2f, %0.2f, %0.2f",
	  (t / 3600) % 24, (t / 60) % 60, t % 60, (s / 86400), ((s / 86400) == 1 ? "" : "s"),
		(s / 3600) % 24, (s / 60) % 60, nbrUsr, (nbrUsr == 1 ? "" : "s"), lav[0], lav[1], lav[2]);
	return;
}

  static uint8 loadCfg(ServerUserRec *user, uint8 ini)
{
	uint8 j;
	swtch = 1;
	freeLst(ini);
	rnkLst = malloc(nbrRnk * sizeof(grdMsk));
	for (j = 0; j < nbrRnk; j++)
		freeMsk(&rnkLst[j], 1, 0);
	for (j = ini; j < nbrCfg; j++)
	{
		reLoad:
		if (cfgLst[j])
		{
			FILE *fPtr = fopen(cfgLst[j], "rt");
			if (fPtr)
			{
				uint8 bfr[fChk];
				while (1)
				{
					fgets(bfr, fChk, fPtr);
					if (feof(fPtr))
						break;
					if (j == CFG_MOD)
						loadMod(user, bfr);
					else
						loadBls(user, bfr, !j);
				}
				fclose(fPtr);
			} else {
				rptErr(user, 3, cfgLst[j]);
				if (j == CFG_MOD)
					return 1;
			}
		} else {
			rptErr(user, j, NULL);
			if (j > CFG_MOD)
			{
				cfgLst[j] = "psdata/guardian.bless";
				goto reLoad;
			}
			if (j == CFG_MOD)
				return 1;
		}
	}
	return 0;
}

  static void saveMod()
{
	uint8 i;
	uint16 j;
	if (cfgLst[CFG_MOD])
	{
		FILE *fPtr = fopen(cfgLst[CFG_MOD], "wt");
		if (fPtr)
		{
			fprintf(fPtr, "#!/mod\n");
			if (nbrCmd && cmdLst)
			{
				fprintf(fPtr, "commands:");
				for (j = 0; j < nbrCmd; j++)
				{
					if (!(j % 10))
					{
						if (j)
							fprintf(fPtr, ",\n\t");
						else
							fprintf(fPtr, "\n\t");
					} else
						fprintf(fPtr, ", ");
					fprintf(fPtr, "%0.31s", cmdLst[j]);
				}
				fprintf(fPtr, "\n\nrestricted:");
				prntMsk(&rstMsk, fPtr, 1);
				if (rnkLst)
					for (i = 0; i < RNK_ROOT; i++)
					{
						fprintf(fPtr, "\nrank %0.8s:", rnkNme[i]);
						prntMsk(&rnkLst[i], fPtr, 1);
					}
				if (grpLst)
					for (i = 0; i < nbrGrp; i++)
					{
						fprintf(fPtr, "\ngroup %0.31s:", grpLst[i].nme);
						prntMsk(&grpLst[i].msk, fPtr, 1);
					}
			}
			fclose(fPtr);
		} else
			rptErr(NULL, 3, cfgLst[CFG_MOD]);
	} else
		rptErr(NULL, CFG_MOD, NULL);
}

  static uint8 loadMod(ServerUserRec *user, uint8 *Ptr)
{
	static uint8 m, c;
	static grdMsk *msk;
	uint8 l;
	uint16 j;
	Ptr = strtoken(Ptr, " ,:\t\r\n");
	while (Ptr)
	{
		reEval:
		l = strlen(Ptr);
		if (l && l < (sizeof(Str31) + (m == 4 ? 4 : 0)))
		{
			if (!strncmp(Ptr, "*/", 2))
			{
				if (c)
					c--;
				Ptr += 2;
				goto reEval;
			} else if (!strncmp(Ptr, "/*", 2)) {
				Ptr += 2;
				c++;
				goto reEval;
			} else if (*Ptr == ';' || *Ptr == '#' || !strncmp(Ptr, "//", 2))
				return;
			if (c)
				goto nextIpt;
			if (!strncmp(Ptr, "commands", 8))
			{
				if (nbrCmd)
					m = 0;
				else
					m = 1;
				msk = NULL;
				goto nextIpt;
			}
			if (!strncmp(Ptr, "restricted", 10))
			{
				m = 5;
				msk = &rstMsk;
				bldMsk(user, msk);
				goto nextIpt;
			}
			if (!strncmp(Ptr, "group", 5))
			{
				m = 2;
				msk = NULL;
				goto nextIpt;
			}
			if (!strncmp(Ptr, "rank", 4))
			{
				m = 3;
				msk = NULL;
				goto nextIpt;
			}
			switch (m)
			{
				case 1:
					if (nbrCmd < MaxCmd)
					{
						if (chrChk(user, Ptr))
						{
							for (j = 0; j < nbrCmd; j++)
								if (!strcmp(Ptr, cmdLst[j]))
									goto nextIpt;
							cmdLst = realloc(cmdLst, ++nbrCmd * sizeof(Str31));
							if (cmdLst)
								sprintf(cmdLst[nbrCmd - 1], "%0.31s", Ptr);
							else {
								m = 0;
								nbrCmd = 0;
								rptErr(user, 9, "C");
							}
						}
					}
					break;
				case 2:
					if (nbrGrp < MaxGrp && nbrCmd)
					{
						if (chrChk(user, Ptr))
						{
							grpLst = realloc(grpLst, ++nbrGrp * sizeof(grpMgr));
							if (grpLst)
							{
								msk = &grpLst[nbrGrp - 1].msk;
								sprintf(grpLst[nbrGrp - 1].nme, "%0.31s", Ptr);
								freeMsk(msk, 1, 0);
								bldMsk(user, msk);
								m = 4;
							} else {
								m = 0;
								nbrGrp = 0;
								rptErr(user, 9, "G");
							}
						} else
							m = 0;
					}
					break;
				case 3:
					if (nbrCmd)
					{
						if (chrChk(user, Ptr))
						{
							for (j = 0; j < nbrRnk; j++)
							{
								if (!strcmp(Ptr, rnkNme[j]))
								{
									msk = &rnkLst[j];
									freeMsk(msk, 0, 0);
									bldMsk(user, msk);
									m = 4;
									goto nextIpt;
								}
							}
							rptErr(user, 10, Ptr);
							m = 0;
						}
					}
					break;
				case 4:
				case 5:
					if (!msk)
						goto nextIpt;
					cfgMsk(user, msk, Ptr, 1, (m == 5));
					break;
			}
		} else if (l > sizeof(Str31) && !c)
			rptErr(user, 4, Ptr);
		nextIpt:
		Ptr = strtoken(NULL, " ,:\t\r\n");
	}
	return;
}

  static void saveBls()
{
	uint16 i;
	if (cfgLst[CFG_GRP])
	{
		FILE *fPtr = fopen(cfgLst[CFG_GRP], "wt");
		if (fPtr)
		{
			fprintf(fPtr, "#!/grp\n");
			if (nbrBls && blsLst)
			{
				uint8 bfr[34];
				for (i = 0; i < nbrBls; i++)
				{
					cvtS2H(blsLst[i].bls.reg, bfr, 0, 1);
					fprintf(fPtr, "%0.11s", bfr);
					cvtS2H(blsLst[i].bls.puid, bfr, 1, 1);
					fprintf(fPtr, " %0.12s", bfr);
					cvtI2IP(&blsLst[i].bls.ip, bfr, 1);
					fprintf(fPtr, " %0.33s", bfr);
					sprintf(bfr, "\"%0.31s\"", blsLst[i].nme);
					fprintf(fPtr, " %0.33s", bfr);
					sprintf(bfr, "\"%0.31s\"", blsLst[i].usr.pwd);
					fprintf(fPtr, " %0.33s", bfr);
					if (blsLst[i].usr.rnk > RNK_STAFF && blsLst[i].usr.rnk < RNK_ROOT)
						sprintf(bfr, "\"%0.8s\"", (char *)rnkNme[blsLst[i].usr.rnk]);
					else
						sprintf(bfr, "\"%0.31s\"", grpLst[blsLst[i].usr.grp].nme);
					fprintf(fPtr, " %0.33s", bfr);
					fprintf(fPtr, " %u", blsLst[i].lst);
					prntMsk(&blsLst[i].usr.msk, fPtr, 0);
				}
			}
			fclose(fPtr);
		} else
			rptErr(NULL, 3, cfgLst[CFG_GRP]);
	} else
		rptErr(NULL, CFG_GRP, NULL);
}

  static uint8 loadBls(ServerUserRec *user, uint8 *Ptr, uint8 Adm)
{
	static uint8 c;
	uint8 m = 0, j, l;
	blsMgr bls;
	memset(&bls.bls, 0, sizeof(blsRec));
	freeMsk(&bls.usr.msk, 1, 0);
	bldMsk(user, &bls.usr.msk);
	Ptr = strtoken(Ptr, " ,:\t\r\n");
	while (Ptr && (!Adm || m < 4))
	{
		reEval:
		l = strlen(Ptr);
		if (l && l < blsLyr[m])
		{
			--l;
			if (!strncmp(Ptr, "*/", 2))
			{
				if (c)
					c--;
				Ptr += 2;
				goto reEval;
			} else if (!strncmp(Ptr, "/*", 2)) {
				Ptr += 2;
				c++;
				goto reEval;
			} else if (*Ptr == ';' || *Ptr == '#' || !strncmp(Ptr, "//", 2))
				return;
			if (c)
				goto nextIpt;
			switch (m)
			{
				case 6:
					bls.lst = (uint32)atol(Ptr);
					break;
				case 7:
					while (Ptr)
					{
						if (strlen(Ptr) < blsLyr[m])
							cfgMsk(user, &bls.usr.msk, Ptr, 1, 0);
						Ptr = strtoken(NULL, " ,:\t\r\n");
					}
					break;
				case 0:
					if (*Ptr != '{' && Ptr[l] != '}')
						return;
					bls.bls.reg = cvtH2S(Ptr);
					break;
				case 1:
					if (*Ptr != '{' && Ptr[l] != '}')
						return;
					bls.bls.puid = cvtH2S(Ptr);
					break;
				case 2:
				case 3:
				case 4:
				case 5:
					if (*Ptr != '\"' && Ptr[l] != '\"')
						return;
					if (m > 2)
						rmvChr(Ptr, "\"");
					switch (m)
					{
						case 2:
							cvtIP2I(&bls.bls.ip, Ptr);
							break;
						case 3:
							sprintf(bls.nme, "%0.31s", Ptr);
							break;
					}
					if (!Adm)
						switch (m)
						{
							case 4:
								sprintf(bls.usr.pwd, "%0.31s", Ptr);
								break;
							case 5:
								if (!fndRnkGrp(&bls.usr, Ptr))
									return;
								break;
						}
			}
			m++;
		}
		nextIpt:
		Ptr = strtoken(NULL, " ,:\t\r\n");
	}
	if (!(Adm || strlen(bls.usr.pwd)))
		return;
	if (!(bls.bls.reg || bls.bls.puid || (bls.bls.ip.nIP.i.ip && bls.bls.ip.xIP.i.ip)))
		return;
	if (Adm)
	{
		if (nbrAdm < MaxAdm)
		{
			admLst = realloc(admLst, ++nbrAdm * sizeof(admMgr));
			if (admLst)
			{
				sprintf(admLst[nbrAdm - 1].nme, "%0.31s", bls.nme);
				admLst[nbrAdm - 1].bls = bls.bls;
			} else
				rptErr(user, 9, "A");
		}
	} else
		addBls(NULL, &bls, "bless record");
	return;
}

  static void addBls(ServerUserRec *user, blsMgr *bls, uint8 *Act)
{
	if (nbrBls < MaxBls)
	{
		blsLst = realloc(blsLst, ++nbrBls * sizeof(blsMgr));
		if (blsLst)
		{
			blsLst[nbrBls - 1] = *bls;
			rptUsr(user, bls, Act);
		} else
			rptErr(user, 9, "B");
	}
}

  static void txtBls(ServerUserRec *user, blsMgr *bls)
{
	uint8 bfr[34];
	UserMessage(user, "; %0.8s: %0.31s", txtRnkGrp(&bls->usr), bls->nme);
	if (bls->bls.reg)
	{
		cvtS2H(bls->bls.reg, bfr, 0, 1);
		UserMessage(user, "; RegCode: %0.12s", bfr);
	}
	if (bls->bls.puid)
	{
		cvtS2H(bls->bls.puid, bfr, 1, 1);
		UserMessage(user, "; PUID: %0.13s", bfr);
	}
	if (bls->bls.ip.nIP.i.ip && bls->bls.ip.xIP.i.ip)
	{
		cvtI2IP(&bls->bls.ip, bfr, 1);
		UserMessage(user, "; IP Address: %0.33s", bfr);
	}
	if (bls->lst)
		UserMessage(user, "; Last Signon: %0.24s", ctime(&bls->lst));
	txtMsk(user, &bls->usr.msk);
}

  static void freeLst(uint8 ini)
{
	if (!ini)
	{
		uint8 j;
		if (cmdLst)
			free(cmdLst);
		if (rnkLst)
		{
			for (j = 0; j < nbrRnk; j++)
				freeMsk(&rnkLst[j], 0, 0);
			free(rnkLst);
		}
		if (grpLst)
		{
			for (j = 0; j < nbrGrp; j++)
				freeMsk(&grpLst[j].msk, 0, 0);
			free(grpLst);
		}
		if (admLst)
			free(admLst);
		if (blsLst)
		{
			for (j = 0; j < nbrBls; j++)
				freeUsr(&blsLst[j].usr, 0, 0);
			free(blsLst);
		}
	}

	freeMsk(&rstMsk, ini, 0);

	nbrCmd = 0;
	cmdLst = NULL;
	rnkLst = NULL;
	nbrGrp = 0;
	grpLst = NULL;
	nbrAdm = 0;
	admLst = NULL;
	nbrBls = 0;
	blsLst = NULL;
}

  static void bldUsr(ServerUserRec *user, grdMsk *msk, uint8 rnk, uint8 grp, uint8 Page)
{
	if (rnk < nbrRnk && user)
	{
		sint16 i;
		usrMgr *rUsr = (usrMgr *)getPluginUserInfo(plgnID, user);
		freeUsr(&rUsr->usr, 0, 0);
		rUsr->usr.rnk = rnk;
		rUsr->usr.grp = grp;
		if (rnk < RNK_ROOT)
		{
			bldMsk(user, &rUsr->usr.msk);
			if (rnk > RNK_STAFF)
				for (i = nbrGrp - 1; i >= 0; i--)
					bldUsrMsk(&grpLst[i].msk, &rUsr->usr.msk, 0);
			else if (rnk == RNK_STAFF && grp < nbrGrp)
				bldUsrMsk(&grpLst[grp].msk, &rUsr->usr.msk, 1);
			for (i = rnk; i >= 0; i--)
				bldUsrMsk(&rnkLst[i], &rUsr->usr.msk, (i == rnk));
			if (msk)
				bldUsrMsk(msk, &rUsr->usr.msk, 1);
		}
		user->flags = ((user->flags / MaxTxt) * MaxTxt) | rnkEqv[rnk];
		sendMsgUserStatus(toUser(user), user, user->flags, "Guardian");
		if (rnk > RNK_LIMBO && user->loggedOn)
			chngNme(user, &rUsr->usr, CvtToCString(user->user.name), NULL);
		if (rnk > RNK_MEMBER && Page)
			UserMessage(user, "You now have %0.31s privileges.", txtRnkGrp(&rUsr->usr));
	}
}

  static void bldUsrMsk(grdMsk *grp, grdMsk *usr, uint8 doStk)
{
	uint16 j;
	if (doStk)
		usr->intMsk |= grp->intMsk;
	if (grp->cmdMsk && usr->cmdMsk)
		for (j = 0; j < nbrCmd; j++)
			usr->cmdMsk[j] |= grp->cmdMsk[j];
	if (grp->pktMsk && usr->pktMsk)
		for (j = 0; j < nbrPkt; j++)
			usr->pktMsk[j] |= grp->pktMsk[j];
	if (doStk)
		if (usr->nbrIpt < MaxIpt)
		{
			uint8 i;
			for (j = 0; j < grp->nbrIpt; j++)
			{
				for (i = 0; i < usr->nbrIpt; i++)
					if (usr->iptLst[i] == grp->iptLst[j])
						break;
				if (i == usr->nbrIpt)
				{
					usr->iptLst = realloc(usr->iptLst, ++(usr->nbrIpt) * sizeof(Str31));
					sprintf(usr->iptLst[usr->nbrIpt - 1], "%0.31s", grp->iptLst[j]);
				}
			}
		}
}

  static void freeUsr(usrRec *rUsr, uint8 ini, uint8 shtd)
{
	freeMsk(&rUsr->msk, ini, shtd);
	memset(rUsr, 0, sizeof(short));
}

  static void rldUsr(uint8 Adm)
{
	uint8 j;
	ServerUserRec *usr;
	uint32 reg, puid, ip;
	for (usr = usrLst; usr; usr = usr->nextUser)
	{
		usrMgr *rUsr = (usrMgr *)getPluginUserInfo(plgnID, usr);
		if (rUsr->usr.rnk < RNK_ROOT && rUsr->usr.rnk > RNK_MEMBER)
			if (nbrBls && blsLst)
			{
				reg = usr->crc ^ usr->counter ^ MAGICLNG;
				puid = usr->puidCRC ^ usr->puidCtr;
				ip = htonl(usr->feIPAddr);
				for (j = 0; j < nbrBls; j++)
					if (cmpBls(&blsLst[j].bls, reg, puid, ip))
						if (!strcmp(blsLst[j].usr.pwd, rUsr->usr.pwd))
						{
							bldUsr(usr, &blsLst[j].usr.msk, blsLst[j].usr.rnk, blsLst[j].usr.grp, 0);
							break;
						}
				if (j == nbrBls)
					fncLogOut(usr);
			} else
				fncLogOut(usr);
		else if (Adm)
			bldUsr(usr, NULL, rUsr->usr.rnk, 0, 0);
	}
}

  static void bldMsk(ServerUserRec *user, grdMsk *rMsk)
{
	if (nbrCmd)
	{
		rMsk->cmdMsk = realloc(rMsk->cmdMsk, nbrCmd);
		if (rMsk->cmdMsk)
			memset(rMsk->cmdMsk, 0, nbrCmd);
		else
			rptErr(user, 9, "CM");
	}
	rMsk->pktMsk = realloc(rMsk->pktMsk, nbrPkt);
	if (rMsk->pktMsk)
		memset(rMsk->pktMsk, 0, nbrPkt);
	else
		rptErr(user, 9, "PM");
}

  static uint8 cfgMsk(ServerUserRec *user, grdMsk *msk, uint8 *Ptr, uint8 onOff, uint8 rstMsk)
{
	uint8 *i;
	uint16 j;
	for (i = Ptr; i < &Ptr[4]; i++)
		if (isalpha(*i))
			*i = toupper(*i);
	switch (*((uint32 *)Ptr))
	{
		case MOD_INT:
			if (!rstMsk)
				if (chrChk(user, i))
				{
					if (!strcmp(i, "badge"))
					{
						cfgIntMsk(onOff, &msk->intMsk, INT_BADGE);
						return;
					}
					if (!strcmp(i, "clnbls"))
					{
						cfgIntMsk(onOff, &msk->intMsk, INT_CLNBLS);
						return;
					}
					if (!strcmp(i, "logout"))
					{
						cfgIntMsk(onOff, &msk->intMsk, INT_LOGOUT);
						return;
					}
					rptErr(user, 6, i);
				}
			break;
		case MOD_CMD:
		case MOD_TLK:
		case MOD_WHS:
			if (chrChk(user, i) && msk->cmdMsk)
			{
				for (j = 0; j < nbrCmd; j++)
					if (!strcmp(i, cmdLst[j]))
					{
						switch (*((uint32 *)Ptr))
						{
							case MOD_CMD:
								i = (char *)((uint32)PRV_ALL);
								break;
							case MOD_TLK:
								i = (char *)((uint32)PRV_TLK);
								break;
							case MOD_WHS:
								i = (char *)((uint32)PRV_WHS);
								break;
						}
						if (onOff)
							msk->cmdMsk[j] |= (uint32)i;
						else
							if (msk->cmdMsk[j] & (uint32)i)
								msk->cmdMsk[j] ^= (uint32)i;
						return;
					}
				rptErr(user, 6, i);
			}
			break;
		case MOD_PKT:
			if (msk->pktMsk)
			{
				for (j = 0; j < sizeof(uint32); j++)
					if (!i[j])
						i[j] = ' ';
				for (j = 0; j < nbrPkt; j++)
					if (*((uint32 *)i) == pktLst[j] ||
					  htonl(*((uint32 *)i)) == pktLst[j])
					{
						if (onOff)
							msk->pktMsk[j] |= PRV_ALL;
						else {
							if (msk->pktMsk[j] & PRV_ALL)
								msk->pktMsk[j] ^= PRV_ALL;
						}
						return;
					}
				rptErr(user, 7, i);
			}
			break;
		case MOD_IPT:
			if (!rstMsk)
				if (chrChk(user, i))
				{
					for (j = 0; j < msk->nbrIpt; j++)
						if (!strcmp(i, msk->iptLst[j]))
						{
							if (!onOff)
							{
								memmove(&msk->iptLst[j], &msk->iptLst[++j], (msk->nbrIpt - j) * sizeof(Str31));
								msk->iptLst = realloc(msk->iptLst, --msk->nbrIpt * sizeof(Str31));
							}
							return;
						}
					if (msk->nbrIpt < MaxIpt)
					{
						msk->iptLst = realloc(msk->iptLst, ++(msk->nbrIpt) * sizeof(Str31));
						if (msk->iptLst)
							sprintf(msk->iptLst[msk->nbrIpt - 1], "%0.31s", i);
						else {
							msk->nbrIpt = 0;
							rptErr(user, 9, "IM");
						}
					}
				}
			else
				rptErr(user, 6, i);
			break;
		default:
			rptErr(user, 8, Ptr);
			break;
	}
	return;
}

  static void cfgIntMsk(uint8 onOff, uint8 *msk, uint8 flag)
{
	if (onOff)
		*msk |= flag;
	else
		if (*msk & flag)
			*msk ^= flag;
}

  static void freeMsk(grdMsk *rMsk, uint8 ini, uint8 shtd)
{
	if (!ini)
	{
		if (rMsk->cmdMsk)
			free(rMsk->cmdMsk);
		if (rMsk->pktMsk)
			free(rMsk->pktMsk);
		if (rMsk->iptLst)
			free(rMsk->iptLst);
	}
	if (!shtd)
		memset(rMsk, 0, sizeof(grdMsk));
}

  static void txtMsk(ServerUserRec *user, grdMsk *msk)
{
	uint16 j;
	uint32 k, c = 0;
	UserMessage(user, "; Privilege(s):");
	if (msk->intMsk & INT_BADGE)
	{
		c++;
		UserMessage(user, "; %s", "INT_BADGE");
	}
	if (msk->intMsk & INT_CLNBLS)
	{
		c++;
		UserMessage(user, "; %s", "INT_CLNBLS");
	}
	if (msk->intMsk & INT_LOGOUT)
	{
		c++;
		UserMessage(user, "; %s", "INT_LOGOUT");
	}
	if (msk->cmdMsk)
		for (j = 0; j < nbrCmd; j++)
		{
			if (k = msk->cmdMsk[j])
			{
				c++;
				if (k & PRV_ALL)
					k = MOD_CMD;
				else if (k & PRV_TLK)
					k = MOD_TLK;
				else if (k & PRV_WHS)
					k = MOD_WHS;
				UserMessage(user, "; %0.4s%0.31s", &k, cmdLst[j]);
			}
		}
	if (msk->pktMsk)
		for (j = 0; j < nbrPkt; j++)
			if (msk->pktMsk[j] & PRV_ALL)
			{
				c++;
				k = htonl(pktLst[j]);
				UserMessage(user, "; PKT_%0.4s", &k);
			}
	for (j = 0; j < msk->nbrIpt; j++)
	{
		c++;
		UserMessage(user, "; IPT_%0.31s", msk->iptLst[j]);
	}
	UserMessage(user, "; %u privilege%s found...", c, (c == 1 ? "" : "s"));
}

  static void prntMsk(grdMsk *msk, FILE *fPtr, uint8 nL)
{
	uint16 i, j;
	if (msk->intMsk & INT_BADGE)
	{
		prntNL(fPtr, 1, nL, 0);
		fprintf(fPtr, "INT_BADGE");
	}
	if (msk->intMsk & INT_CLNBLS)
	{
		prntNL(fPtr, 1, nL, 0);
		fprintf(fPtr, "INT_CLNBLS");
	}
	if (msk->intMsk & INT_LOGOUT)
	{
		prntNL(fPtr, 1, nL, 0);
		fprintf(fPtr, "INT_LOGOUT");
	}
	if (msk->cmdMsk)
		for (i = j = 0; j < nbrCmd; j++)
		{
			uint32 k = msk->cmdMsk[j];
			if (k)
			{
				if (k & PRV_ALL)
					k = MOD_CMD;
				else if (k & PRV_TLK)
					k = MOD_TLK;
				else if (k & PRV_WHS)
					k = MOD_WHS;
				prntNL(fPtr, 0, nL, i++);
				fprintf(fPtr, "%0.4s%0.31s", &k, cmdLst[j]);
			}
		}
	if (msk->pktMsk)
		for (i = j = 0; j < nbrPkt; j++)
			if (msk->pktMsk[j] & PRV_ALL)
			{
				prntNL(fPtr, 0, nL, i++);
				fprintf(fPtr, "PKT_%0.4s", &pktLst[j]);
			}
	if (msk->iptLst)
		for (j = 0; j < msk->nbrIpt; j++)
		{
			prntNL(fPtr, 0, nL, j);
			fprintf(fPtr, "IPT_%0.31s", msk->iptLst[j]);
		}
	fprintf(fPtr, "\n");
}

  static void prntNL(FILE *fPtr, uint8 type, uint8 nL, uint8 n)
{
	if (type)
	{
		if (nL)
			fprintf(fPtr, "\n\t");
		else
			fprintf(fPtr, ", ");
	} else {
		if (!(n % 8) && nL)
		{
			if (n)
				fprintf(fPtr, ",\n\t");
			else
				fprintf(fPtr, "\n\t");
		} else if (n || !nL)
			fprintf(fPtr, ", ");
	}
}

  static uint8 fncCmds(uint8 cmdIdx, ServerUserRec *user, int argc, char *argv[])
{
	if (nbrBls)
	{
		uint8 j;
		usrRec rUsr;
		if (fndRnkGrp(&rUsr, argv[0]))
		{
			blsRec bls;
			memset(&bls, 0, sizeof(blsRec));
			ServerUserRec *usr = GetServerUser(GetTarget(user));
			if (usr)
			{
				bls.reg = usr->crc ^ usr->counter ^ MAGICLNG;
				bls.puid = usr->puidCRC ^ usr->puidCtr;
				bls.ip.nIP.i.ip = htonl(usr->feIPAddr);
			} else {
				j = (cmdIdx ? 1 : 2);
				if (argc > j)
				{
					uint8 bfr[34], *Ptr;
					for (; j < argc; j++)
					{
						Ptr = argv[j];
						if (*Ptr == '-')
							while (*(++Ptr))
								switch (*Ptr)
								{
									case 'r':
										if (++j < argc)
										{
											rmvChr(argv[j], "{}");
											sprintf(bfr, "%0.9s", argv[j]);
											bls.reg = cvtH2S(bfr);
										}
										break;
									case 'p':
										if (++j < argc)
										{
											rmvChr(argv[j], "{Z}");
											sprintf(bfr, "%0.9s", argv[j]);
											bls.puid = cvtH2S(bfr);
										}
										break;
									case 'i':
										if (++j < argc)
										{
											rmvChr(argv[j], "\"\'");
											sprintf(bfr, "\"%0.31s\"", argv[j]);
											cvtIP2I(&bls.ip, bfr);
										}
										break;
								}
					}
				} else {
					UserMessage(user, "Please select a target.");
					return;
				}
			}
			blsRec lst;
			for (j = 0; j < nbrBls; j++)
			{
				lst.reg = (bls.reg ? blsLst[j].bls.reg : 0);
				lst.puid = (bls.puid ? blsLst[j].bls.puid : 0);
				lst.ip.nIP.i.ip = (bls.ip.nIP.i.ip ? blsLst[j].bls.ip.nIP.i.ip : 0);
				lst.ip.xIP.i.ip = (bls.ip.xIP.i.ip ? blsLst[j].bls.ip.xIP.i.ip : 0);
				if (cmpBls(&lst, bls.reg, bls.puid, bls.ip.nIP.i.ip) &&
				  (blsLst[j].usr.rnk == rUsr.rnk && (rUsr.rnk > RNK_STAFF || blsLst[j].usr.grp == rUsr.grp)))
				{
					uint8 i;
					switch (cmdIdx)
					{
						case 0:
							if (usr)
							{
								sprintf(blsLst[j].nme, "%0.31s", CvtToCString(usr->user.name));
								rmvChr(blsLst[j].nme, ";#/\\\"\'‘’`´“”‹™‰¼½¾÷");
								rplChr(blsLst[j].nme,
								  "123AAAAAAAABCCCCDEEEEEIIIILNOOOOOOOPRSSTTUUUUXYYZaaaaaaaacdeeeefiiiinooooooooppsuuuuuyyz|!?...,<><>+---~ ",
									"¹²³@ÀÁÂÃÄÅÆß©¢ÇŒÐ€ÈÉÊËÌÍÎÏ£ÑÒÓÔÕÖØ¤Þ®§Š‡†ÙÚÛÜ×Ý¥Žªàáâãäåæçðèéêëƒìíîïñ°ºòóôõöø¶þšùúûüµýÿž¦¡¿…•·¸›„«»±—¯¬¨ ");
							}
							UserMessage(user, "'%0.31s' password, is now '%0.31s'.", blsLst[j].nme, argv[1]);
							hPwd(blsLst[j].nme, argv[1], blsLst[j].usr.pwd);
							break;
						case 1:
						case 2:
							for (i = 1; i < argc; i++)
								switch (*((uint32 *)argv[i]))
								{
									case MOD_INT:
									case MOD_CMD:
									case MOD_TLK:
									case MOD_WHS:
									case MOD_PKT:
									case MOD_IPT:
										cfgMsk(user, &blsLst[j].usr.msk, argv[i], cmdIdx - 1, 0);
										if (usr)
											if (cmdIdx > 1)
											UserMessage(user, "Privilege '%0.31s' %s '%0.31s'.", argv[i], "added to", CvtToCString(usr->user.name));
											else
												UserMessage(user, "Privilege '%0.31s' %s '%0.31s'.", argv[i], "removed from", CvtToCString(usr->user.name));
										else
											if (cmdIdx > 1)
												UserMessage(user, "Privilege '%0.31s' %s '%0.31s'.", argv[i], "added to", blsLst[j].nme);
											else
												UserMessage(user, "Privilege '%0.31s' %s '%0.31s'.", argv[i], "removed from", blsLst[j].nme);
										txtBls(user, &blsLst[j]);
										break;
								}
							break;
						case 3:
							if (usr)
							{
								if (((usrMgr *)getPluginUserInfo(plgnID, usr))->usr.rnk < RNK_ROOT)
									fncLogOut(usr);
								sprintf(blsLst[j].nme, "%0.31s", CvtToCString(usr->user.name));
							}
							rptUsr(user, &blsLst[j], "unblessed");
							memmove(&blsLst[j], &blsLst[++j], (nbrBls - j) * sizeof(blsMgr));
							blsLst = realloc(blsLst, --nbrBls * sizeof(blsMgr));
							break;
					}
					saveBls();
					return;
				}
			}
			if (j == nbrBls)
				UserMessage(user, "No such user.");
		} else
			UserMessage(user, "No such group (%0.31s).", argv[0]);
	} else
		UserMessage(user, "Bless list empty.");
}

  static uint8 cmpBls(blsRec *bls, uint32 reg, uint32 puid, uint32 ip)
{
	if ((bls->reg && bls->reg != reg) || (bls->puid && bls->puid != puid) ||
	  (bls->ip.nIP.i.ip && bls->ip.xIP.i.ip && (bls->ip.nIP.i.ip > ip || bls->ip.xIP.i.ip < ip)))
		return 0;
	if (bls->reg || bls->puid || (bls->ip.nIP.i.ip && bls->ip.xIP.i.ip))
		return 1;
	return 0;
}

  static void hPwd(uint8 *usr, uint8 *pwd, uint8 *out)
{
	uint8 a, c, u, p;
	u = strlen((char *)usr);
	p = strlen((char *)pwd);
	a = c = (u & ~p) ^ wMskLen;
	while (*pwd)
	{
		if (!*usr)
			usr -= u;
		a ^= *(usr++) % p;
		*out = (*(pwd++) ^ a) ^ c;
		a ^= (~*out % p) ^ (~*out / wMskLen);
		*out = wMsk[*out % wMskLen];
		a ^= *(out++) % u;
	}
	*out = 0;
}

  static uint8 chrChk(ServerUserRec *user, uint8 *Ptr)
{
	uint8 *Str = Ptr;
	while (*Str)
	{
		if (!isalnum(*Str))
		{
			rptErr(user, 5, Ptr);
			return 0;
		}
		if (isalpha(*Str))
			*Str = tolower(*Str);
		Str++;
	}
	return 1;
}

  static void chngNme(ServerUserRec *user, usrRec *rUsr, uint8 *Ptr, uint8 *uNme)
{
	uint8 bfr[MaxStr];
	sprintf(bfr, "%0.31s", Ptr);
	if (rUsr->rnk < RNK_STAFF || !(rUsr->msk.intMsk & INT_BADGE))
		rmvChr(bfr, "*");
	if (!strlen(bfr))
		sprintf(bfr, "%0.8s %u", (char *)rnkNme[rUsr->rnk], user->user.userID);
	if (strcmp(bfr, (uNme ? uNme : Ptr)))
	{
		CtoPstr(bfr);
		ChangeUserName(user, bfr);
	}
}

  static void strtolower(uint8 *Ptr)
{
	while (*Ptr)
	{
		if (isalpha(*Ptr))
			*Ptr = tolower(*Ptr);
		Ptr++;
	}
}

  static void rmvChr(uint8 *Ptr, uint8 *Chr)
{
	while (*Ptr)
	{
		while (*Ptr && strchr(Chr, *Ptr))
			memmove(--Ptr, ++Ptr, strlen(Ptr));
		Ptr++;
	}
}

  static void rplChr(uint8 *Ptr, uint8 *Chr, uint8 *Rpl)
{
	while (*Ptr)
	{
		if (strchr(Rpl, *Ptr))
			*Ptr = Chr[(uint32)strchr(Rpl, *Ptr) - (uint32)Rpl];
		Ptr++;
	}
}

  static uint8 fndRnkGrp(usrRec *rUsr, uint8 *Ptr)
{
	uint8 i;
	memset(rUsr, 0, sizeof(short));
	for (i = RNK_STAFF + 1; i < RNK_ROOT; i++)
		if (!strcmp(Ptr, (char *)rnkNme[i]))
		{
			rUsr->rnk = i;
			return 1;
		}
	if (!rUsr->rnk)
		for (i = 0; i < nbrGrp; i++)
			if (!strcmp(Ptr, grpLst[i].nme))
			{
				rUsr->rnk = RNK_STAFF;
				rUsr->grp = i;
				return 2;
			}
	return 0;
}

  static uint8 *txtRnkGrp(usrRec *rUsr)
{
	uint8 *Ptr = NULL;
	if (rUsr->rnk > RNK_STAFF && rUsr->rnk < nbrRnk)
		Ptr = (char *)rnkNme[rUsr->rnk];
	else if (rUsr->grp < nbrGrp)
		Ptr = grpLst[rUsr->grp].nme;
	return Ptr;
}

  static UserID GetTarget(ServerUserRec *user)
{
	usrMgr *rUsr = (usrMgr *)getPluginUserInfo(plgnID, user);
	UserID j = rUsr->tID;
	rUsr->tID = 0;
	return j;
}

  static uint8 *strtoken(uint8 *Ptr, uint8 *Dlm)
{
	static uint8 *Str;
	if (Dlm)
	{
		uint8 q;
		if (Ptr)
		{
			if (strchr(Dlm, *Ptr))
				while (*Ptr && strchr(Dlm, *Ptr))
					Str = ++Ptr;
			else
				Str = Ptr;
		} else
			Ptr = Str;
		while (*Str)
		{
			if (*Str == '\"' || *Str == '\'')
			{
				q = *Str;
				while (*(++Str) && *Str != q)
					if (*Str == '\\')
						if (strchr("\\\"\'", *(++Str)))
							Str++;
			}
			if (strchr(Dlm, *Str))
			{
				while (*Str && strchr(Dlm, *Str))
					*(Str++) = 0;
				break;
			}
			Str++;
		}
	} else
		Ptr = Str;
	if (!*Ptr)
		return NULL;
	else
		return Ptr;
}

  static void rptUsr(ServerUserRec *user, blsMgr *bls, uint8 *Act)
{
	uint8 hReg[12], hPUID[13], sIP[34], *RG = txtRnkGrp(&bls->usr);
	cvtS2H(bls->bls.reg, hReg, 0, 1);
	cvtS2H(bls->bls.puid, hPUID, 1, 1);
	cvtI2IP(&bls->bls.ip, sIP, 1);
	if (user)
		UserMessage(user, "You have %s '%0.31s' as '%0.31s'.", Act, bls->nme, RG);
	FormattedLogMessage(user, "Guardian", "%s %0.33s %0.12s %0.13s \"%0.31s\" \"%0.31s\"", Act, sIP, hReg, hPUID, bls->nme, RG);
}

  static void rptErr(ServerUserRec *user, uint8 i, uint8 *Ptr)
{
	FormattedLogMessage(user, "Guardian", (char *)errLst[i], Ptr);
	if (user)
		UserMessage(user, (char *)errLst[i], Ptr);
}

  static uint8 cvtIP2I(ipRec *ip, uint8 *Ptr)
{
	uint8 i = 3, *Str = &Ptr[1];
	memset(ip, 0, sizeof(long));
	while (*(++Ptr))
	{
		switch (*Ptr)
		{
			case '*':
				while (*Ptr != '.' && *Ptr != '\"')
					*(Ptr++) = 0;
				ip->nIP.b.ip[i] = 0;
				ip->xIP.b.ip[i] = 255;
				i--;
				Str = &Ptr[1];
				break;
			case '-':
				*Ptr = 0;
				ip->nIP.b.ip[i] = (uint8)atoi(Str);
				Str = &Ptr[1];
				break;
			case '.':
				*Ptr = 0;
			case '\"':
				if (*Ptr == '\"' && i)
					goto errReturn;
				ip->xIP.b.ip[i] = (uint8)atoi(Str);
				if (ip->nIP.b.ip[i] > ip->xIP.b.ip[i])
				{
					ip->nIP.b.ip[i] ^= ip->xIP.b.ip[i];
					ip->xIP.b.ip[i] ^= ip->nIP.b.ip[i];
					ip->nIP.b.ip[i] ^= ip->xIP.b.ip[i];
				} else if (!ip->nIP.b.ip[i] && ip->xIP.b.ip[i] < 255)
					ip->nIP.b.ip[i] = ip->xIP.b.ip[i];
				i--;
				Str = &Ptr[1];
				break;
			default:
				if (!isdigit(*Ptr))
				{
					errReturn:
					memset(ip, 0, sizeof(ipRec));
					return 0;
				}
				break;
		}
	}
	return 1;
}

  static void cvtI2IP(ipRec *ip, uint8 *Ptr, uint8 Qut)
{
	uint8 i = 4;
	memset(Ptr, 0, 34);
	if (Qut)
		*(Ptr++) = '\"';
	if (ip->nIP.i.ip || ip->xIP.i.ip)
		while (i--)
		{
			if (!ip->nIP.b.ip[i] && ip->xIP.b.ip[i] > 254)
				*(Ptr++) = '*';
			else
				if (ip->nIP.b.ip[i] == ip->xIP.b.ip[i])
					sprintf(Ptr, "%d", ip->nIP.b.ip[i]);
				else
					sprintf(Ptr, "%d-%d", ip->nIP.b.ip[i], ip->xIP.b.ip[i]);
			while (*Ptr)
				Ptr++;
			if (i)
				*(Ptr++) = '.';
		}
	if (Qut)
		*Ptr = '\"';
}

  static uint32 cvtH2S(uint8 *Hash)
{
	uint8 i, j = 0;
	uint32 Seed = 0;
	if (*Hash == '{')
		Hash++;
	if (*Hash == 'Z')
		Hash++;
	while (*Hash && strchr(hMsk, *Hash) && j < 9)
	{
		for (i = 0; i < hMskLen; i++)
			if (toupper(*Hash) == hMsk[i])
			{
				Seed += (i * pow(hMskLen, j++));
				break;
			}
		if (i == hMskLen)
			return 0;
		Hash++;
	}
	if (*Hash && !strchr(hMsk, *Hash) && *Hash != '}')
		return 0;
	return Seed;
}

  static void cvtS2H(uint32 Seed, uint8 *Ptr, uint8 isPUID, uint8 Brk)
{
	memset(Ptr, 0, 13);
	if (Brk)
		*(Ptr++) = '{';
	if (Seed && isPUID)
		*(Ptr++) = 'Z';
	while (Seed)
	{
		*(Ptr++) = hMsk[Seed % hMskLen];
		Seed /= hMskLen;
	}
	if (Brk)
		*Ptr = '}';
}

#endif /* __GUARDIAN_C__ */
