core/AdminCache.cpp
author Borja Ferrer <faluco@alliedmods.net>
Fri Nov 16 21:34:29 2007 +0000 (2007-11-16)
changeset 1686 185cac1820fe
parent 1656 b0b86d57530c
child 1839 1f3aaf8fbd12
permissions -rw-r--r--
fixed amb1164, SM was crashing due to interface changes from valve (this change is just to make the build update using the latest sdk)
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include <string.h>
    33 #include <assert.h>
    34 #include <ITextParsers.h>
    35 #include "AdminCache.h"
    36 #include "ShareSys.h"
    37 #include "ForwardSys.h"
    38 #include "PlayerManager.h"
    39 #include "ConCmdManager.h"
    40 #include "Logger.h"
    41 #include "sourcemod.h"
    42 #include "sm_stringutil.h"
    43 #include "sourcemm_api.h"
    44 
    45 #define LEVEL_STATE_NONE		0
    46 #define LEVEL_STATE_LEVELS		1
    47 #define LEVEL_STATE_FLAGS		2
    48 
    49 AdminCache g_Admins;
    50 AdminFlag g_FlagLetters[26];
    51 bool g_FlagSet[26];
    52 
    53 ConVar sm_immunity_mode("sm_immunity_mode", "1", FCVAR_SPONLY|FCVAR_PROTECTED, "Mode for deciding immunity protection");
    54 
    55 /* Default flags */
    56 AdminFlag g_DefaultFlags[26] = 
    57 {
    58 	Admin_Reservation, Admin_Generic, Admin_Kick, Admin_Ban, Admin_Unban,
    59 	Admin_Slay, Admin_Changemap, Admin_Convars, Admin_Config, Admin_Chat,
    60 	Admin_Vote, Admin_Password, Admin_RCON, Admin_Cheats, Admin_Custom1, 
    61 	Admin_Custom2, Admin_Custom3, Admin_Custom4, Admin_Custom5, Admin_Custom6,
    62 	Admin_Generic, Admin_Generic, Admin_Generic, Admin_Generic, Admin_Generic,
    63 	Admin_Root
    64 };
    65 
    66 
    67 class FlagReader : public ITextListener_SMC
    68 {
    69 public:
    70 	void LoadLevels()
    71 	{
    72 		if (!Parse())
    73 		{
    74 			memcpy(g_FlagLetters, g_DefaultFlags, sizeof(AdminFlag) * 26);
    75 			for (unsigned int i=0; i<20; i++)
    76 			{
    77 				g_FlagSet[i] = true;
    78 			}
    79 			g_FlagSet[25] = true;
    80 		}
    81 	}
    82 private:
    83 	bool Parse()
    84 	{
    85 		SMCStates states;
    86 		SMCError error;
    87 
    88 		m_bFileNameLogged = false;
    89 		g_SourceMod.BuildPath(Path_SM, m_File, sizeof(m_File), "configs/admin_levels.cfg");
    90 
    91 		if ((error = textparsers->ParseFile_SMC(m_File, this, &states))
    92 			!= SMCError_Okay)
    93 		{
    94 			const char *err_string = textparsers->GetSMCErrorString(error);
    95 			if (!err_string)
    96 			{
    97 				err_string = "Unknown error";
    98 			}
    99 			ParseError(NULL, "Error %d (%s)", error, err_string);
   100 			return false;
   101 		}
   102 
   103 		return true;
   104 	}
   105 	void ReadSMC_ParseStart()
   106 	{
   107 		m_LevelState = LEVEL_STATE_NONE;
   108 		m_IgnoreLevel = 0;
   109 		memset(g_FlagSet, 0, sizeof(g_FlagSet));
   110 	}
   111 	SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name)
   112 	{
   113 		if (m_IgnoreLevel)
   114 		{
   115 			m_IgnoreLevel++;
   116 			return SMCResult_Continue;
   117 		}
   118 
   119 		if (m_LevelState == LEVEL_STATE_NONE)
   120 		{
   121 			if (strcmp(name, "Levels") == 0)
   122 			{
   123 				m_LevelState = LEVEL_STATE_LEVELS;
   124 			} 
   125 			else 
   126 			{
   127 				m_IgnoreLevel++;
   128 			}
   129 		} else if (m_LevelState == LEVEL_STATE_LEVELS) {
   130 			if (strcmp(name, "Flags") == 0)
   131 			{
   132 				m_LevelState = LEVEL_STATE_FLAGS;
   133 			} 
   134 			else 
   135 			{
   136 				m_IgnoreLevel++;
   137 			}
   138 		} 
   139 		else 
   140 		{
   141 			m_IgnoreLevel++;
   142 		}
   143 
   144 		return SMCResult_Continue;
   145 	}
   146 	SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
   147 	{
   148 		if (m_LevelState != LEVEL_STATE_FLAGS || m_IgnoreLevel)
   149 		{
   150 			return SMCResult_Continue;
   151 		}
   152 
   153 		unsigned char c = (unsigned)value[0];
   154 
   155 		if (c < (unsigned)'a' || c > (unsigned)'z')
   156 		{
   157 			ParseError(states, "Flag \"%c\" is not a lower-case ASCII letter", c);
   158 			return SMCResult_Continue;
   159 		}
   160 
   161 		c -= (unsigned)'a';
   162 
   163 		assert(c >= 0 && c < 26);
   164 
   165 		if (!g_Admins.FindFlag(key, &g_FlagLetters[c]))
   166 		{
   167 			ParseError(states, "Unrecognized admin level \"%s\"", key);
   168 			return SMCResult_Continue;
   169 		}
   170 
   171 		g_FlagSet[c] = true;
   172 
   173 		return SMCResult_Continue;
   174 	}
   175 	SMCResult ReadSMC_LeavingSection(const SMCStates *states)
   176 	{
   177 		if (m_IgnoreLevel)
   178 		{
   179 			m_IgnoreLevel--;
   180 			return SMCResult_Continue;
   181 		}
   182 
   183 		if (m_LevelState == LEVEL_STATE_FLAGS)
   184 		{
   185 			m_LevelState = LEVEL_STATE_LEVELS;
   186 			return SMCResult_Halt;
   187 		} 
   188 		else if (m_LevelState == LEVEL_STATE_LEVELS) 
   189 		{
   190 			m_LevelState = LEVEL_STATE_NONE;
   191 		}
   192 
   193 		return SMCResult_Continue;
   194 	}
   195 	void ParseError(const SMCStates *states, const char *message, ...)
   196 	{
   197 		va_list ap;
   198 		char buffer[256];
   199 
   200 		va_start(ap, message);
   201 		UTIL_FormatArgs(buffer, sizeof(buffer), message, ap);
   202 		va_end(ap);
   203 
   204 		if (!m_bFileNameLogged)
   205 		{
   206 			g_Logger.LogError("[SM] Parse error(s) detected in file \"%s\":", m_File);
   207 			m_bFileNameLogged = true;
   208 		}
   209 
   210 		g_Logger.LogError("[SM] (Line %d): %s", states ? states->line : 0, buffer);
   211 	}
   212 private:
   213 	bool m_bFileNameLogged;
   214 	char m_File[PLATFORM_MAX_PATH];
   215 	int m_LevelState;
   216 	int m_IgnoreLevel;
   217 } s_FlagReader;
   218 
   219 AdminCache::AdminCache()
   220 {
   221 	m_pCmdOverrides = sm_trie_create();
   222 	m_pCmdGrpOverrides = sm_trie_create();
   223 	m_pStrings = new BaseStringTable(1024);
   224 	m_pMemory = m_pStrings->GetMemTable();
   225 	m_FreeGroupList = m_FirstGroup = m_LastGroup = INVALID_GROUP_ID;
   226 	m_FreeUserList = m_FirstUser = m_LastUser = INVALID_ADMIN_ID;
   227 	m_pGroups = sm_trie_create();
   228 	m_pCacheFwd = NULL;
   229 	m_FirstGroup = -1;
   230 	m_pAuthTables = sm_trie_create();
   231 	m_InvalidatingAdmins = false;
   232 	m_destroying = false;
   233 	m_pLevelNames = sm_trie_create();
   234 }
   235 
   236 AdminCache::~AdminCache()
   237 {
   238 	m_destroying = true;
   239 	DumpAdminCache(AdminCache_Overrides, false);
   240 	DumpAdminCache(AdminCache_Groups, false);
   241 
   242 	sm_trie_destroy(m_pCmdGrpOverrides);
   243 	sm_trie_destroy(m_pCmdOverrides);
   244 
   245 	if (m_pGroups)
   246 	{
   247 		sm_trie_destroy(m_pGroups);
   248 	}
   249 
   250 	List<AuthMethod>::iterator iter;
   251 	for (iter=m_AuthMethods.begin();
   252 		 iter!=m_AuthMethods.end();
   253 		 iter++)
   254 	{
   255 		sm_trie_destroy((*iter).table);
   256 	}
   257 
   258 	sm_trie_destroy(m_pAuthTables);
   259 
   260 	delete m_pStrings;
   261 
   262 	sm_trie_destroy(m_pLevelNames);
   263 }
   264 
   265 void AdminCache::OnSourceModStartup(bool late)
   266 {
   267 	RegisterAuthIdentType("steam");
   268 	RegisterAuthIdentType("name");
   269 	RegisterAuthIdentType("ip");
   270 
   271 	NameFlag("reservation", Admin_Reservation);
   272 	NameFlag("kick", Admin_Kick);
   273 	NameFlag("generic", Admin_Generic);
   274 	NameFlag("ban", Admin_Ban);
   275 	NameFlag("unban", Admin_Unban);
   276 	NameFlag("slay", Admin_Slay);
   277 	NameFlag("changemap", Admin_Changemap);
   278 	NameFlag("cvars", Admin_Convars);
   279 	NameFlag("config", Admin_Config);
   280 	NameFlag("chat", Admin_Chat);
   281 	NameFlag("vote", Admin_Vote);
   282 	NameFlag("password", Admin_Password);
   283 	NameFlag("rcon", Admin_RCON);
   284 	NameFlag("cheats", Admin_Cheats);
   285 	NameFlag("root", Admin_Root);
   286 	NameFlag("custom1", Admin_Custom1);
   287 	NameFlag("custom2", Admin_Custom2);
   288 	NameFlag("custom3", Admin_Custom3);
   289 	NameFlag("custom4", Admin_Custom4);
   290 	NameFlag("custom5", Admin_Custom5);
   291 	NameFlag("custom6", Admin_Custom6);
   292 }
   293 
   294 void AdminCache::OnSourceModAllInitialized()
   295 {
   296 	m_pCacheFwd = g_Forwards.CreateForward("OnRebuildAdminCache", ET_Ignore, 1, NULL, Param_Cell);
   297 	g_ShareSys.AddInterface(NULL, this);
   298 }
   299 
   300 void AdminCache::OnSourceModLevelChange(const char *mapName)
   301 {
   302 	/* For now, we only read these once per level. */
   303 	s_FlagReader.LoadLevels();
   304 }
   305 
   306 void AdminCache::OnSourceModShutdown()
   307 {
   308 	g_Forwards.ReleaseForward(m_pCacheFwd);
   309 	m_pCacheFwd = NULL;
   310 }
   311 
   312 void AdminCache::OnSourceModPluginsLoaded()
   313 {
   314 	DumpAdminCache(AdminCache_Overrides, true);
   315 	DumpAdminCache(AdminCache_Groups, true);
   316 }
   317 
   318 void AdminCache::NameFlag(const char *str, AdminFlag flag)
   319 {
   320 	sm_trie_insert(m_pLevelNames, str, (void *)flag);
   321 }
   322 
   323 bool AdminCache::FindFlag(const char *str, AdminFlag *pFlag)
   324 {
   325 	void *obj;
   326 	if (!sm_trie_retrieve(m_pLevelNames, str, &obj))
   327 	{
   328 		return false;
   329 	}
   330 
   331 	if (pFlag)
   332 	{
   333 		*pFlag = (AdminFlag)(int)obj;
   334 	}
   335 
   336 	return true;
   337 }
   338 
   339 void AdminCache::AddCommandOverride(const char *cmd, OverrideType type, FlagBits flags)
   340 {
   341 	Trie *pTrie = NULL;
   342 	if (type == Override_Command)
   343 	{
   344 		pTrie = m_pCmdOverrides;
   345 	} else if (type == Override_CommandGroup) {
   346 		pTrie = m_pCmdGrpOverrides;
   347 	} else {
   348 		return;
   349 	}
   350 
   351 	sm_trie_insert(pTrie, cmd, (void *)(unsigned int)flags);
   352 
   353 	g_ConCmds.UpdateAdminCmdFlags(cmd, type, flags, false);
   354 }
   355 
   356 bool AdminCache::GetCommandOverride(const char *cmd, OverrideType type, FlagBits *pFlags)
   357 {
   358 	Trie *pTrie = NULL;
   359 
   360 	if (type == Override_Command)
   361 	{
   362 		pTrie = m_pCmdOverrides;
   363 	} else if (type == Override_CommandGroup) {
   364 		pTrie = m_pCmdGrpOverrides;
   365 	} else {
   366 		return false;
   367 	}
   368 
   369 	void *object;
   370 	if (sm_trie_retrieve(pTrie, cmd, &object))
   371 	{
   372 		if (pFlags)
   373 		{
   374 			*pFlags = (FlagBits)object;
   375 		}
   376 		return true;
   377 	}
   378 
   379 	return false;
   380 }
   381 
   382 void AdminCache::UnsetCommandOverride(const char *cmd, OverrideType type)
   383 {
   384 	if (type == Override_Command)
   385 	{
   386 		return _UnsetCommandOverride(cmd);
   387 	} else if (type == Override_CommandGroup) {
   388 		return _UnsetCommandGroupOverride(cmd);
   389 	}
   390 }
   391 
   392 void AdminCache::_UnsetCommandGroupOverride(const char *group)
   393 {
   394 	if (!m_pCmdGrpOverrides)
   395 	{
   396 		return;
   397 	}
   398 
   399 	sm_trie_delete(m_pCmdGrpOverrides, group);
   400 
   401 	g_ConCmds.UpdateAdminCmdFlags(group, Override_CommandGroup, 0, true);
   402 }
   403 
   404 void AdminCache::_UnsetCommandOverride(const char *cmd)
   405 {
   406 	if (!m_pCmdOverrides)
   407 	{
   408 		return;
   409 	}
   410 
   411 	sm_trie_delete(m_pCmdOverrides, cmd);
   412 
   413 	g_ConCmds.UpdateAdminCmdFlags(cmd, Override_Command, 0, true);
   414 }
   415 
   416 void AdminCache::DumpCommandOverrideCache(OverrideType type)
   417 {
   418 	if (type == Override_Command && m_pCmdOverrides)
   419 	{
   420 		sm_trie_clear(m_pCmdOverrides);
   421 	} else if (type == Override_CommandGroup && m_pCmdGrpOverrides) {
   422 		sm_trie_clear(m_pCmdGrpOverrides);
   423 	}
   424 }
   425 
   426 AdminId AdminCache::CreateAdmin(const char *name)
   427 {
   428 	AdminId id;
   429 	AdminUser *pUser;
   430 
   431 	if (m_FreeUserList != INVALID_ADMIN_ID)
   432 	{
   433 		pUser = (AdminUser *)m_pMemory->GetAddress(m_FreeUserList);
   434 		assert(pUser->magic == USR_MAGIC_UNSET);
   435 		id = m_FreeUserList;
   436 		m_FreeUserList = pUser->next_user;
   437 	} else {
   438 		id = m_pMemory->CreateMem(sizeof(AdminUser), (void **)&pUser);
   439 		pUser->grp_size = 0;
   440 		pUser->grp_table = -1;
   441 	}
   442 
   443 	pUser->flags = 0;
   444 	pUser->eflags = 0;
   445 	pUser->grp_count = 0;
   446 	pUser->password = -1;
   447 	pUser->magic = USR_MAGIC_SET;
   448 	pUser->auth.identidx = -1;
   449 	pUser->auth.index = 0;
   450 	pUser->immunity_level = 0;
   451 	pUser->serialchange = 1;
   452 
   453 	if (m_FirstUser == INVALID_ADMIN_ID)
   454 	{
   455 		m_FirstUser = id;
   456 		m_LastUser = id;
   457 	} else {
   458 		AdminUser *pPrev = (AdminUser *)m_pMemory->GetAddress(m_LastUser);
   459 		pPrev->next_user = id;
   460 		pUser->prev_user = m_LastUser;
   461 		m_LastUser = id;
   462 	}
   463 
   464 	if (name && name[0] != '\0')
   465 	{
   466 		int nameidx = m_pStrings->AddString(name);
   467 		pUser = (AdminUser *)m_pMemory->GetAddress(id);
   468 		pUser->nameidx = nameidx;
   469 	}
   470 
   471 	return id;
   472 }
   473 
   474 GroupId AdminCache::AddGroup(const char *group_name)
   475 {
   476 	if (sm_trie_retrieve(m_pGroups, group_name, NULL))
   477 	{
   478 		return INVALID_GROUP_ID;
   479 	}
   480 
   481 	GroupId id;
   482 	AdminGroup *pGroup;
   483 	if (m_FreeGroupList != INVALID_GROUP_ID)
   484 	{
   485 		pGroup = (AdminGroup *)m_pMemory->GetAddress(m_FreeGroupList);
   486 		assert(pGroup->magic == GRP_MAGIC_UNSET);
   487 		id = m_FreeGroupList;
   488 		m_FreeGroupList = pGroup->next_grp;
   489 	} else {
   490 		id = m_pMemory->CreateMem(sizeof(AdminGroup), (void **)&pGroup);
   491 	}
   492 
   493 	pGroup->immunity_level = 0;
   494 	pGroup->immune_table = -1;
   495 	pGroup->magic = GRP_MAGIC_SET;
   496 	pGroup->next_grp = INVALID_GROUP_ID;
   497 	pGroup->pCmdGrpTable = NULL;
   498 	pGroup->pCmdTable = NULL;
   499 	pGroup->addflags = 0;
   500 
   501 	if (m_FirstGroup == INVALID_GROUP_ID)
   502 	{
   503 		m_FirstGroup = id;
   504 		m_LastGroup = id;
   505 		pGroup->prev_grp = INVALID_GROUP_ID;
   506 	} else {
   507 		AdminGroup *pPrev = (AdminGroup *)m_pMemory->GetAddress(m_LastGroup);
   508 		assert(pPrev->magic == GRP_MAGIC_SET);
   509 		pPrev->next_grp = id;
   510 		pGroup->prev_grp = m_LastGroup;
   511 		m_LastGroup = id;
   512 	}
   513 
   514 	int nameidx = m_pStrings->AddString(group_name);
   515 	pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   516 	pGroup->nameidx = nameidx;
   517 
   518 	sm_trie_insert(m_pGroups, group_name, (void *)id);
   519 
   520 	return id;
   521 }
   522 
   523 GroupId AdminCache::FindGroupByName(const char *group_name)
   524 {
   525 	void *object;
   526 
   527 	if (!sm_trie_retrieve(m_pGroups, group_name, &object))
   528 	{
   529 		return INVALID_GROUP_ID;
   530 	}
   531 
   532 	GroupId id = (GroupId)object;
   533 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   534 
   535 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   536 	{
   537 		return INVALID_GROUP_ID;
   538 	}
   539 
   540 	return id;
   541 }
   542 
   543 void AdminCache::SetGroupAddFlag(GroupId id, AdminFlag flag, bool enabled)
   544 {
   545 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   546 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   547 	{
   548 		return;
   549 	}
   550 
   551 	if (flag < Admin_Reservation || flag >= AdminFlags_TOTAL)
   552 	{
   553 		return;
   554 	}
   555 
   556 	FlagBits bits = (1<<(FlagBits)flag);
   557 
   558 	if (enabled)
   559 	{
   560 		pGroup->addflags |= bits;
   561 	} else {
   562 		pGroup->addflags &= ~bits;
   563 	}
   564 }
   565 
   566 bool AdminCache::GetGroupAddFlag(GroupId id, AdminFlag flag)
   567 {
   568 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   569 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   570 	{
   571 		return false;
   572 	}
   573 
   574 	if (flag < Admin_Reservation || flag >= AdminFlags_TOTAL)
   575 	{
   576 		return false;
   577 	}
   578 
   579 	FlagBits bit = 1<<(FlagBits)flag;
   580 	return ((pGroup->addflags & bit) == bit);
   581 }
   582 
   583 FlagBits AdminCache::GetGroupAddFlags(GroupId id)
   584 {
   585 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   586 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   587 	{
   588 		return 0;
   589 	}
   590 
   591 	return pGroup->addflags;
   592 }
   593 
   594 const char *AdminCache::GetGroupName(GroupId gid)
   595 {
   596 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(gid);
   597 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   598 	{
   599 		return NULL;
   600 	}
   601 
   602 	return m_pStrings->GetString(pGroup->nameidx);
   603 }
   604 
   605 void AdminCache::SetGroupGenericImmunity(GroupId id, ImmunityType type, bool enabled)
   606 {
   607 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   608 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   609 	{
   610 		return;
   611 	}
   612 
   613 	unsigned int level = 0;
   614 
   615 	if (enabled)
   616 	{
   617 		if (type == Immunity_Default)
   618 		{
   619 			level = 1;
   620 		} else if (type == Immunity_Global) {
   621 			level = 2;
   622 		}
   623 		if (level > pGroup->immunity_level)
   624 		{
   625 			pGroup->immunity_level = level;
   626 		}
   627 	} else {
   628 		pGroup->immunity_level = 0;
   629 	}
   630 }
   631 
   632 bool AdminCache::GetGroupGenericImmunity(GroupId id, ImmunityType type)
   633 {
   634 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   635 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   636 	{
   637 		return false;
   638 	}
   639 
   640 	if (type == Immunity_Default && pGroup->immunity_level >= 1)
   641 	{
   642 		return true;
   643 	} else if (type == Immunity_Global && pGroup->immunity_level >= 2) {
   644 		return true;
   645 	}
   646 
   647 	return false;
   648 }
   649 
   650 void AdminCache::AddGroupImmunity(GroupId id, GroupId other_id)
   651 {
   652 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(other_id);
   653 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   654 	{
   655 		return;
   656 	}
   657 
   658 	pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   659 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   660 	{
   661 		return;
   662 	}
   663 
   664 	/* We always need to resize the immunity table */
   665 	int *table, tblidx;
   666 	if (pGroup->immune_table == -1)
   667 	{
   668 		tblidx = m_pMemory->CreateMem(sizeof(int) * 2, (void **)&table);
   669 		pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   670 		table[0] = 0;
   671 	} else {
   672 		int *old_table = (int *)m_pMemory->GetAddress(pGroup->immune_table);
   673 		/* Break out if this group is already in the list */
   674 		for (int i=0; i<old_table[0]; i++)
   675 		{
   676 			if (old_table[1+i] == other_id)
   677 			{
   678 				return;
   679 			}
   680 		}
   681 		tblidx = m_pMemory->CreateMem(sizeof(int) * (old_table[0] + 2), (void **)&table);
   682 		/* Get the old address again in case of resize */
   683 		pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   684 		old_table = (int *)m_pMemory->GetAddress(pGroup->immune_table);
   685 		table[0] = old_table[0];
   686 		for (unsigned int i=1; i<=(unsigned int)old_table[0]; i++)
   687 		{
   688 			table[i] = old_table[i];
   689 		}
   690 	}
   691 
   692 	/* Assign */
   693 	pGroup->immune_table = tblidx;
   694 
   695 	/* Add to the array */
   696 	table[0]++;
   697 	table[table[0]] = other_id;
   698 }
   699 
   700 unsigned int AdminCache::GetGroupImmunityCount(GroupId id)
   701 {
   702 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   703 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   704 	{
   705 		return 0;
   706 	}
   707 
   708 	if (pGroup->immune_table == -1)
   709 	{
   710 		return 0;
   711 	}
   712 
   713 	int *table = (int *)m_pMemory->GetAddress(pGroup->immune_table);
   714 
   715 	return table[0];
   716 }
   717 
   718 GroupId AdminCache::GetGroupImmunity(GroupId id, unsigned int number)
   719 {
   720 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   721 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   722 	{
   723 		return INVALID_GROUP_ID;
   724 	}
   725 
   726 	if (pGroup->immune_table == -1)
   727 	{
   728 		return INVALID_GROUP_ID;
   729 	}
   730 
   731 	int *table = (int *)m_pMemory->GetAddress(pGroup->immune_table);
   732 	if (number >= (unsigned int)table[0])
   733 	{
   734 		return INVALID_GROUP_ID;
   735 	}
   736 
   737 	return table[1 + number];
   738 }
   739 
   740 void AdminCache::AddGroupCommandOverride(GroupId id, const char *name, OverrideType type, OverrideRule rule)
   741 {
   742 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   743 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   744 	{
   745 		return;
   746 	}
   747 
   748 	Trie *pTrie = NULL;
   749 	if (type == Override_Command)
   750 	{
   751 		if (pGroup->pCmdTable == NULL)
   752 		{
   753 			pGroup->pCmdTable = sm_trie_create();
   754 		}
   755 		pTrie = pGroup->pCmdTable;
   756 	} else if (type == Override_CommandGroup) {
   757 		if (pGroup->pCmdGrpTable == NULL)
   758 		{
   759 			pGroup->pCmdGrpTable = sm_trie_create();
   760 		}
   761 		pTrie = pGroup->pCmdGrpTable;
   762 	} else {
   763 		return;
   764 	}
   765 
   766 	sm_trie_insert(pTrie, name, (void *)(int)rule);
   767 }
   768 
   769 bool AdminCache::GetGroupCommandOverride(GroupId id, const char *name, OverrideType type, OverrideRule *pRule)
   770 {
   771 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   772 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   773 	{
   774 		return false;
   775 	}
   776 
   777 	Trie *pTrie = NULL;
   778 	if (type == Override_Command)
   779 	{
   780 		if (pGroup->pCmdTable == NULL)
   781 		{
   782 			return false;
   783 		}
   784 		pTrie = pGroup->pCmdTable;
   785 	} else if (type == Override_CommandGroup) {
   786 		if (pGroup->pCmdGrpTable == NULL)
   787 		{
   788 			return false;
   789 		}
   790 		pTrie = pGroup->pCmdGrpTable;
   791 	} else {
   792 		return false;
   793 	}
   794 
   795 	void *object;
   796 	if (!sm_trie_retrieve(pTrie, name, &object))
   797 	{
   798 		return false;
   799 	}
   800 
   801 	if (pRule)
   802 	{
   803 		*pRule = (OverrideRule)(int)object;
   804 	}
   805 	
   806 	return true;
   807 }
   808 
   809 Trie *AdminCache::GetMethodByIndex(unsigned int index)
   810 {
   811 	List<AuthMethod>::iterator iter;
   812 	for (iter=m_AuthMethods.begin();
   813 		 iter!=m_AuthMethods.end();
   814 		 iter++)
   815 	{
   816 		if (index-- == 0)
   817 		{
   818 			return (*iter).table;
   819 		}
   820 	}
   821 
   822 	return NULL;
   823 }
   824 
   825 bool AdminCache::InvalidateAdmin(AdminId id)
   826 {
   827 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
   828 	AdminUser *pOther;
   829 	if (!pUser || pUser->magic != USR_MAGIC_SET)
   830 	{
   831 		return false;
   832 	}
   833 
   834 	if (!m_InvalidatingAdmins && !m_destroying)
   835 	{
   836 		g_Players.ClearAdminId(id);
   837 	}
   838 
   839 	/* Unlink from the dbl link list */
   840 	if (id == m_FirstUser && id == m_LastUser)
   841 	{
   842 		m_FirstUser = INVALID_ADMIN_ID;
   843 		m_LastUser = INVALID_ADMIN_ID;
   844 	} else if (id == m_FirstUser) {
   845 		m_FirstUser = pUser->next_user;
   846 		pOther = (AdminUser *)m_pMemory->GetAddress(m_FirstUser);
   847 		pOther->prev_user = INVALID_ADMIN_ID;
   848 	} else if (id == m_LastUser) {
   849 		m_LastUser = pUser->prev_user;
   850 		pOther = (AdminUser *)m_pMemory->GetAddress(m_LastUser);
   851 		pOther->next_user = INVALID_ADMIN_ID;
   852 	} else {
   853 		pOther = (AdminUser *)m_pMemory->GetAddress(pUser->prev_user);
   854 		pOther->next_user = pUser->next_user;
   855 		pOther = (AdminUser *)m_pMemory->GetAddress(pUser->next_user);
   856 		pOther->prev_user = pUser->prev_user;
   857 	}
   858 
   859 	/* Unlink from auth tables */
   860 	if (pUser->auth.identidx != -1)
   861 	{
   862 		Trie *pTrie = GetMethodByIndex(pUser->auth.index);
   863 		if (pTrie)
   864 		{
   865 			sm_trie_delete(pTrie, m_pStrings->GetString(pUser->auth.identidx));
   866 		}
   867 	}
   868 
   869 	/* Clear table counts */
   870 	pUser->grp_count = 0;
   871 	
   872 	/* Link into free list */
   873 	pUser->magic = USR_MAGIC_UNSET;
   874 	pUser->next_user = m_FreeUserList;
   875 	m_FreeUserList = id;
   876 
   877 	/* Unset serial change */
   878 	pUser->serialchange = 0;
   879 
   880 	return true;
   881 }
   882 
   883 
   884 void AdminCache::InvalidateGroup(GroupId id)
   885 {
   886 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   887 	AdminGroup *pOther;
   888 
   889 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   890 	{
   891 		return;
   892 	}
   893 
   894 	const char *str = m_pStrings->GetString(pGroup->nameidx);
   895 	sm_trie_delete(m_pGroups, str);
   896 
   897 	/* Unlink from the live dbllink list */
   898 	if (id == m_FirstGroup && id == m_LastGroup)
   899 	{
   900 		m_LastGroup = INVALID_GROUP_ID;
   901 		m_FirstGroup = INVALID_GROUP_ID;
   902 	} else if (id == m_FirstGroup) {
   903 		m_FirstGroup = pGroup->next_grp;
   904 		pOther = (AdminGroup *)m_pMemory->GetAddress(m_FirstGroup);
   905 		pOther->prev_grp = INVALID_GROUP_ID;
   906 	} else if (id == m_LastGroup) {
   907 		m_LastGroup = pGroup->prev_grp;
   908 		pOther = (AdminGroup *)m_pMemory->GetAddress(m_LastGroup);
   909 		pOther->next_grp = INVALID_GROUP_ID;
   910 	} else {
   911 		pOther = (AdminGroup *)m_pMemory->GetAddress(pGroup->prev_grp);
   912 		pOther->next_grp = pGroup->next_grp;
   913 		pOther = (AdminGroup *)m_pMemory->GetAddress(pGroup->next_grp);
   914 		pOther->prev_grp = pGroup->prev_grp;
   915 	}
   916 
   917 	/* Free any used memory to be safe */
   918 	if (pGroup->pCmdGrpTable)
   919 	{
   920 		sm_trie_destroy(pGroup->pCmdGrpTable);
   921 		pGroup->pCmdGrpTable = NULL;
   922 	}
   923 	if (pGroup->pCmdTable)
   924 	{
   925 		sm_trie_destroy(pGroup->pCmdTable);
   926 		pGroup->pCmdTable = NULL;
   927 	}
   928 
   929 	/* Link into the free list */
   930 	pGroup->magic = GRP_MAGIC_UNSET;
   931 	pGroup->next_grp = m_FreeGroupList;
   932 	m_FreeGroupList = id;
   933 
   934 	int idx = m_FirstUser;
   935 	AdminUser *pUser;
   936 	int *table;
   937 	while (idx != INVALID_ADMIN_ID)
   938 	{
   939 		pUser = (AdminUser *)m_pMemory->GetAddress(idx);
   940 		if (pUser->grp_count > 0)
   941 		{
   942 			table = (int *)m_pMemory->GetAddress(pUser->grp_table);
   943 			for (unsigned int i=0; i<pUser->grp_count; i++)
   944 			{
   945 				if (table[i] == id)
   946 				{
   947 					/* We have to remove this entry */
   948 					for (unsigned int j=i+1; j<pUser->grp_count; j++)
   949 					{
   950 						/* Move everything down by one */
   951 						table[j-1] = table[j];
   952 					}
   953 					/* Decrease count */
   954 					pUser->grp_count--;
   955 					/* Recalculate effective flags */
   956 					pUser->eflags = pUser->flags;
   957 					for (unsigned int j=0; j<pUser->grp_count; j++)
   958 					{
   959 						pOther = (AdminGroup *)m_pMemory->GetAddress(table[j]);
   960 						pUser->eflags |= pOther->addflags;
   961 					}
   962 					/* Mark as changed */
   963 					pUser->serialchange++;
   964 					/* Break now, duplicates aren't allowed */
   965 					break;
   966 				}
   967 			}
   968 		}
   969 		idx = pUser->next_user;
   970 	}
   971 }
   972 
   973 void AdminCache::InvalidateGroupCache()
   974 {
   975 	/* Nuke the free list */
   976 	m_FreeGroupList = -1;
   977 
   978 	/* Nuke reverse lookups */
   979 	sm_trie_clear(m_pGroups);
   980 
   981 	/* Free memory on groups */
   982 	GroupId cur = m_FirstGroup;
   983 	AdminGroup *pGroup;
   984 	while (cur != INVALID_GROUP_ID)
   985 	{
   986 		pGroup = (AdminGroup *)m_pMemory->GetAddress(cur);
   987 		assert(pGroup->magic == GRP_MAGIC_SET);
   988 		if (pGroup->pCmdGrpTable)
   989 		{
   990 			sm_trie_destroy(pGroup->pCmdGrpTable);
   991 		}
   992 		if (pGroup->pCmdTable)
   993 		{
   994 			sm_trie_destroy(pGroup->pCmdTable);
   995 		}
   996 		cur = pGroup->next_grp;
   997 	}
   998 
   999 	m_FirstGroup = INVALID_GROUP_ID;
  1000 	m_LastGroup = INVALID_GROUP_ID;
  1001 
  1002 	InvalidateAdminCache(false);
  1003 
  1004 	/* Reset the memory table */
  1005 	m_pMemory->Reset();
  1006 }
  1007 
  1008 void AdminCache::AddAdminListener(IAdminListener *pListener)
  1009 {
  1010 	m_hooks.push_back(pListener);
  1011 }
  1012 
  1013 void AdminCache::RemoveAdminListener(IAdminListener *pListener)
  1014 {
  1015 	m_hooks.remove(pListener);
  1016 }
  1017 
  1018 void AdminCache::RegisterAuthIdentType(const char *name)
  1019 {
  1020 	if (sm_trie_retrieve(m_pAuthTables, name, NULL))
  1021 	{
  1022 		return;
  1023 	}
  1024 
  1025 	Trie *pAuth = sm_trie_create();
  1026 
  1027 	AuthMethod method;
  1028 	method.name.assign(name);
  1029 	method.table = pAuth;
  1030 
  1031 	m_AuthMethods.push_back(method);
  1032 
  1033 	sm_trie_insert(m_pAuthTables, name, pAuth);
  1034 }
  1035 
  1036 void AdminCache::InvalidateAdminCache(bool unlink_admins)
  1037 {
  1038 	m_InvalidatingAdmins = true;
  1039 	if (!m_destroying)
  1040 	{
  1041 		g_Players.ClearAllAdmins();
  1042 	}
  1043 	/* Wipe the identity cache first */
  1044 	List<AuthMethod>::iterator iter;
  1045 	for (iter=m_AuthMethods.begin();
  1046 		 iter!=m_AuthMethods.end();
  1047 		 iter++)
  1048 	{
  1049 		sm_trie_clear((*iter).table);
  1050 	}
  1051 	
  1052 	if (unlink_admins)
  1053 	{
  1054 		while (m_FirstUser != INVALID_ADMIN_ID)
  1055 		{
  1056 			InvalidateAdmin(m_FirstUser);
  1057 		}
  1058 	} else {
  1059 		m_FirstUser = -1;
  1060 		m_LastUser = -1;
  1061 		m_FreeUserList = -1;
  1062 	}
  1063 	m_InvalidatingAdmins = false;
  1064 }
  1065 
  1066 void AdminCache::DumpAdminCache(AdminCachePart part, bool rebuild)
  1067 {
  1068 	List<IAdminListener *>::iterator iter;
  1069 	IAdminListener *pListener;
  1070 	cell_t result;
  1071 
  1072 	if (part == AdminCache_Overrides)
  1073 	{
  1074 		DumpCommandOverrideCache(Override_Command);
  1075 		DumpCommandOverrideCache(Override_CommandGroup);
  1076 		if (rebuild && !m_destroying)
  1077 		{
  1078 			for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++)
  1079 			{
  1080 				pListener = (*iter);
  1081 				pListener->OnRebuildOverrideCache();
  1082 			}
  1083 			m_pCacheFwd->PushCell(part);
  1084 			m_pCacheFwd->Execute(&result);
  1085 		}
  1086 	} else if (part == AdminCache_Groups || part == AdminCache_Admins) {
  1087 		if (part == AdminCache_Groups)
  1088 		{
  1089 			InvalidateGroupCache();
  1090 			if (rebuild && !m_destroying)
  1091 			{
  1092 				for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++)
  1093 				{
  1094 					pListener = (*iter);
  1095 					pListener->OnRebuildGroupCache();
  1096 				}
  1097 				m_pCacheFwd->PushCell(part);
  1098 				m_pCacheFwd->Execute(&result);
  1099 			}
  1100 		}
  1101 		InvalidateAdminCache(true);
  1102 		if (rebuild && !m_destroying)
  1103 		{
  1104 			for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++)
  1105 			{
  1106 				pListener = (*iter);
  1107 				pListener->OnRebuildAdminCache((part == AdminCache_Groups));
  1108 			}
  1109 			m_pCacheFwd->PushCell(AdminCache_Admins);
  1110 			m_pCacheFwd->Execute(&result);
  1111 			g_Players.RecheckAnyAdmins();
  1112 		}
  1113 	}
  1114 }
  1115 
  1116 const char *AdminCache::GetAdminName(AdminId id)
  1117 {
  1118 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1119 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1120 	{
  1121 		return NULL;
  1122 	}
  1123 
  1124 	return m_pStrings->GetString(pUser->nameidx);
  1125 }
  1126 
  1127 bool AdminCache::GetMethodIndex(const char *name, unsigned int *_index)
  1128 {
  1129 	List<AuthMethod>::iterator iter;
  1130 	unsigned int index = 0;
  1131 	for (iter=m_AuthMethods.begin();
  1132 		 iter!=m_AuthMethods.end();
  1133 		 iter++,index++)
  1134 	{
  1135 		if ((*iter).name.compare(name) == 0)
  1136 		{
  1137 			*_index = index;
  1138 			return true;
  1139 		}
  1140 	}
  1141 
  1142 	return false;
  1143 }
  1144 
  1145 bool AdminCache::BindAdminIdentity(AdminId id, const char *auth, const char *ident)
  1146 {
  1147 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1148 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1149 	{
  1150 		return false;
  1151 	}
  1152 
  1153 	Trie *pTable;
  1154 	if (!sm_trie_retrieve(m_pAuthTables, auth, (void **)&pTable))
  1155 	{
  1156 		return false;
  1157 	}
  1158 
  1159 	if (sm_trie_retrieve(pTable, ident, NULL))
  1160 	{
  1161 		return false;
  1162 	}
  1163 
  1164 	int i_ident = m_pStrings->AddString(ident);
  1165 
  1166 	pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1167 	pUser->auth.identidx = i_ident;
  1168 	GetMethodIndex(auth, &pUser->auth.index);
  1169 
  1170 	return sm_trie_insert(pTable, ident, (void **)id);
  1171 }
  1172 
  1173 AdminId AdminCache::FindAdminByIdentity(const char *auth, const char *identity)
  1174 {
  1175 	Trie *pTable;
  1176 	if (!sm_trie_retrieve(m_pAuthTables, auth, (void **)&pTable))
  1177 	{
  1178 		return INVALID_ADMIN_ID;
  1179 	}
  1180 
  1181 	void *object;
  1182 	if (!sm_trie_retrieve(pTable, identity, &object))
  1183 	{
  1184 		return INVALID_ADMIN_ID;
  1185 	}
  1186 
  1187 	return (AdminId)object;
  1188 }
  1189 
  1190 void AdminCache::SetAdminFlag(AdminId id, AdminFlag flag, bool enabled)
  1191 {
  1192 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1193 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1194 	{
  1195 		return;
  1196 	}
  1197 
  1198 	if (flag < Admin_Reservation
  1199 		|| flag >= AdminFlags_TOTAL)
  1200 	{
  1201 		return;
  1202 	}
  1203 	
  1204 	FlagBits bits = (1<<(FlagBits)flag);
  1205 
  1206 	if (enabled)
  1207 	{
  1208 		pUser->flags |= bits;
  1209 		pUser->eflags |= bits;
  1210 	} else {
  1211 		pUser->flags &= ~bits;
  1212 		pUser->eflags &= ~bits;
  1213 	}
  1214 
  1215 	pUser->serialchange++;
  1216 }
  1217 
  1218 bool AdminCache::GetAdminFlag(AdminId id, AdminFlag flag, AccessMode mode)
  1219 {
  1220 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1221 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1222 	{
  1223 		return false;
  1224 	}
  1225 
  1226 	if (flag < Admin_Reservation
  1227 		|| flag >= AdminFlags_TOTAL)
  1228 	{
  1229 		return false;
  1230 	}
  1231 
  1232 	FlagBits bit = (1<<(FlagBits)flag);
  1233 
  1234 	if (mode == Access_Real)
  1235 	{
  1236 		return ((pUser->flags & bit) == bit);
  1237 	} else if (mode == Access_Effective) {
  1238 		bool has_bit = ((pUser->eflags & bit) == bit);
  1239 		if (!has_bit && flag != Admin_Root && ((pUser->eflags & ADMFLAG_ROOT) == ADMFLAG_ROOT))
  1240 		{
  1241 			has_bit = true;
  1242 		}
  1243 		return has_bit;
  1244 	}
  1245 
  1246 	return false;
  1247 }
  1248 
  1249 FlagBits AdminCache::GetAdminFlags(AdminId id, AccessMode mode)
  1250 {
  1251 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1252 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1253 	{
  1254 		return 0;
  1255 	}
  1256 
  1257 	if (mode == Access_Real)
  1258 	{
  1259 		return pUser->flags;
  1260 	} else if (mode == Access_Effective) {
  1261 		return pUser->eflags;
  1262 	}
  1263 
  1264 	return 0;
  1265 }
  1266 
  1267 void AdminCache::SetAdminFlags(AdminId id, AccessMode mode, FlagBits bits)
  1268 {
  1269 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1270 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1271 	{
  1272 		return;
  1273 	}
  1274 
  1275 	if (mode == Access_Real)
  1276 	{
  1277 		pUser->flags = bits;
  1278 		pUser->eflags = bits;
  1279 	} else if (mode == Access_Effective) {
  1280 		pUser->eflags = bits;
  1281 	}
  1282 
  1283 	pUser->serialchange++;
  1284 }
  1285 
  1286 bool AdminCache::AdminInheritGroup(AdminId id, GroupId gid)
  1287 {
  1288 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1289 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1290 	{
  1291 		return false;
  1292 	}
  1293 
  1294 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(gid);
  1295 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
  1296 	{
  1297 		return false;
  1298 	}
  1299 
  1300 	/* First check for duplicates */
  1301 	if (pUser->grp_count != 0)
  1302 	{
  1303 		int *temp_table = (int *)m_pMemory->GetAddress(pUser->grp_table);
  1304 		for (unsigned int i=0; i<pUser->grp_count; i++)
  1305 		{
  1306 			if (temp_table[i] == gid)
  1307 			{
  1308 				return false;
  1309 			}
  1310 		}
  1311 	}
  1312 
  1313 	int *table;
  1314 	if (pUser->grp_count + 1 > pUser->grp_size)
  1315 	{
  1316 		int new_size = 0;
  1317 		int tblidx;
  1318 		
  1319 		if (pUser->grp_size == 0)
  1320 		{
  1321 			new_size = 2;
  1322 		} else {
  1323 			new_size = pUser->grp_size * 2;
  1324 		}
  1325 		
  1326 		/* Create and refresh pointers */
  1327 		tblidx = m_pMemory->CreateMem(new_size * sizeof(int), (void **)&table);
  1328 		pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1329 		pGroup = (AdminGroup *)m_pMemory->GetAddress(gid);
  1330 
  1331 		/* Copy old data if necessary */
  1332 		if (pUser->grp_table != -1)
  1333 		{
  1334 			int *old_table = (int *)m_pMemory->GetAddress(pUser->grp_table);
  1335 			memcpy(table, old_table, sizeof(int) * pUser->grp_count);
  1336 		}
  1337 		pUser->grp_table = tblidx;
  1338 		pUser->grp_size = new_size;
  1339 	} else {
  1340 		table = (int *)m_pMemory->GetAddress(pUser->grp_table);
  1341 	}
  1342 
  1343 	table[pUser->grp_count] = gid;
  1344 	pUser->grp_count++;
  1345 
  1346 	/* Compute new effective permissions */
  1347 	pUser->eflags |= pGroup->addflags;
  1348 
  1349 	if (pGroup->immunity_level > pUser->immunity_level)
  1350 	{
  1351 		pUser->immunity_level = pGroup->immunity_level;
  1352 	}
  1353 
  1354 	pUser->serialchange++;
  1355 
  1356 	return true;
  1357 }
  1358 
  1359 bool AdminCache::IsValidAdmin(AdminId id)
  1360 {
  1361 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1362 	return (pUser != NULL && pUser->magic == USR_MAGIC_SET);
  1363 }
  1364 
  1365 unsigned int AdminCache::GetAdminGroupCount(AdminId id)
  1366 {
  1367 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1368 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1369 	{
  1370 		return 0;
  1371 	}
  1372 
  1373 	return pUser->grp_count;
  1374 }
  1375 
  1376 GroupId AdminCache::GetAdminGroup(AdminId id, unsigned int index, const char **name)
  1377 {
  1378 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1379 	if (!pUser || pUser->magic != USR_MAGIC_SET || index >= pUser->grp_count)
  1380 	{
  1381 		return INVALID_GROUP_ID;
  1382 	}
  1383 
  1384 	int *table = (int *)m_pMemory->GetAddress(pUser->grp_table);
  1385 
  1386 	GroupId gid = table[index];
  1387 
  1388 	if (name)
  1389 	{
  1390 		*name = GetGroupName(gid);
  1391 	}
  1392 
  1393 	return gid;
  1394 }
  1395 
  1396 const char *AdminCache::GetAdminPassword(AdminId id)
  1397 {
  1398 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1399 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1400 	{
  1401 		return NULL;
  1402 	}
  1403 
  1404 	return m_pStrings->GetString(pUser->password);
  1405 }
  1406 
  1407 void AdminCache::SetAdminPassword(AdminId id, const char *password)
  1408 {	
  1409 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1410 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1411 	{
  1412 		return;
  1413 	}
  1414 
  1415 	int i_password = m_pStrings->AddString(password);
  1416 	pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1417 	pUser->password = i_password;
  1418 }
  1419 
  1420 unsigned int AdminCache::FlagBitsToBitArray(FlagBits bits, bool array[], unsigned int maxSize)
  1421 {
  1422 	unsigned int i;
  1423 	for (i=0; i<maxSize && i<AdminFlags_TOTAL; i++)
  1424 	{
  1425 		array[i] = ((bits & (1<<i)) == (unsigned)(1<<i));
  1426 	}
  1427 
  1428 	return i;
  1429 }
  1430 
  1431 FlagBits AdminCache::FlagBitArrayToBits(const bool array[], unsigned int maxSize)
  1432 {
  1433 	FlagBits bits = 0;
  1434 	for (unsigned int i=0; i<maxSize && i<AdminFlags_TOTAL; i++)
  1435 	{
  1436 		if (array[i])
  1437 		{
  1438 			bits |= (1<<i);
  1439 		}
  1440 	}
  1441 
  1442 	return bits;
  1443 }
  1444 
  1445 FlagBits AdminCache::FlagArrayToBits(const AdminFlag array[], unsigned int numFlags)
  1446 {
  1447 	FlagBits bits = 0;
  1448 	for (unsigned int i=0; i<numFlags && i<AdminFlags_TOTAL; i++)
  1449 	{
  1450 		bits |= (1 << (FlagBits)array[i]);
  1451 	}
  1452 
  1453 	return bits;
  1454 }
  1455 
  1456 unsigned int AdminCache::FlagBitsToArray(FlagBits bits, AdminFlag array[], unsigned int maxSize)
  1457 {
  1458 	unsigned int i, num=0;
  1459 	for (i=0; num<maxSize && i<AdminFlags_TOTAL; i++)
  1460 	{
  1461 		if ((bits & (1<<i)) == (unsigned)(1<<i))
  1462 		{
  1463 			array[num++] = (AdminFlag)i;
  1464 		}
  1465 	}
  1466 
  1467 	return num;
  1468 }
  1469 
  1470 bool AdminCache::CheckAdminFlags(AdminId id, FlagBits bits)
  1471 {
  1472 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1473 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1474 	{
  1475 		return false;
  1476 	}
  1477 
  1478 	return ((pUser->eflags & bits) == bits);
  1479 }
  1480 
  1481 bool AdminCache::CanAdminTarget(AdminId id, AdminId target)
  1482 {
  1483 	/** 
  1484 	 * Zeroth, if the targeting AdminId is INVALID_ADMIN_ID, targeting fails.
  1485 	 * First, if the targeted AdminId is INVALID_ADMIN_ID, targeting succeeds.
  1486 	 */
  1487 
  1488 	if (id == INVALID_ADMIN_ID)
  1489 	{
  1490 		return false;
  1491 	}
  1492 
  1493 	if (target == INVALID_ADMIN_ID)
  1494 	{
  1495 		return true;
  1496 	}
  1497 
  1498 	if (id == target)
  1499 	{
  1500 		return true;
  1501 	}
  1502 
  1503 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1504 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1505 	{
  1506 		return false;
  1507 	}
  1508 
  1509 	AdminUser *pTarget = (AdminUser *)m_pMemory->GetAddress(target);
  1510 	if (!pTarget || pTarget->magic != USR_MAGIC_SET)
  1511 	{
  1512 		return false;
  1513 	}
  1514 
  1515 	/** 
  1516 	 * Second, if the targeting admin is root, targeting succeeds.
  1517 	 */
  1518 	if (pUser->eflags & ADMFLAG_ROOT)
  1519 	{
  1520 		return true;
  1521 	}
  1522 
  1523 	/** Fourth, if the targeted admin is immune from targeting admin. */
  1524 	int mode = sm_immunity_mode.GetInt();
  1525 	switch (mode)
  1526 	{
  1527 		case 1:
  1528 		{
  1529 			if (pTarget->immunity_level > pUser->immunity_level)
  1530 			{
  1531 				return false;
  1532 			}
  1533 			break;
  1534 		}
  1535 		case 3:
  1536 		{
  1537 			/* If neither has any immunity, let this pass. */
  1538 			if (!pUser->immunity_level && !pTarget->immunity_level)
  1539 			{
  1540 				return true;
  1541 			}
  1542 			/* Don't break, go to the next case. */
  1543 		}
  1544 		case 2:
  1545 		{
  1546 			if (pTarget->immunity_level >= pUser->immunity_level)
  1547 			{
  1548 				return false;
  1549 			}
  1550 			break;
  1551 		}
  1552 	}
  1553 
  1554 	/** 
  1555 	 * Fifth, if the targeted admin has specific immunity from the
  1556 	 *  targeting admin via group immunities, targeting fails.
  1557 	 */
  1558 	//:TODO: speed this up... maybe with trie hacks.
  1559 	//idea is to insert %d.%d in the trie after computing this and use it as a cache lookup.
  1560 	//problem is the trie cannot delete prefixes, so we'd have a problem with invalidations.
  1561 	if (pTarget->grp_count > 0 && pUser->grp_count > 0)
  1562 	{
  1563 		int *grp_table = (int *)m_pMemory->GetAddress(pTarget->grp_table);
  1564 		int *src_table = (int *)m_pMemory->GetAddress(pUser->grp_table);
  1565 		GroupId id, other;
  1566 		unsigned int num;
  1567 		for (unsigned int i=0; i<pTarget->grp_count; i++)
  1568 		{
  1569 			id = grp_table[i];
  1570 			num = GetGroupImmunityCount(id);
  1571 			for (unsigned int j=0; j<num; i++)
  1572 			{
  1573 				other = GetGroupImmunity(id, j);
  1574 				for (unsigned int k=0; k<pUser->grp_count; k++)
  1575 				{
  1576 					if (other == src_table[k])
  1577 					{
  1578 						return false;
  1579 					}
  1580 				}
  1581 			}
  1582 		}
  1583 	}
  1584 	
  1585 	return true;
  1586 }
  1587 
  1588 bool AdminCache::FindFlag(char c, AdminFlag *pAdmFlag)
  1589 {
  1590 	if (c < 'a' || c > 'z')
  1591 	{
  1592 		return false;
  1593 	}
  1594 
  1595 	if (pAdmFlag)
  1596 	{
  1597 		*pAdmFlag = g_FlagLetters[(unsigned)c - (unsigned)'a'];
  1598 	}
  1599 
  1600 	return true;
  1601 }
  1602 
  1603 FlagBits AdminCache::ReadFlagString(const char *flags, const char **end)
  1604 {
  1605 	FlagBits bits = 0;
  1606 
  1607 	while (flags && (*flags != '\0'))
  1608 	{
  1609 		AdminFlag flag;
  1610 		if (!FindFlag(*flags, &flag))
  1611 		{
  1612 			break;
  1613 		}
  1614 		bits |= FlagArrayToBits(&flag, 1);
  1615 		flags++;
  1616 	}
  1617 
  1618 	if (end)
  1619 	{
  1620 		*end = flags;
  1621 	}
  1622 
  1623 	return bits;
  1624 }
  1625 
  1626 unsigned int AdminCache::GetAdminSerialChange(AdminId id)
  1627 {
  1628 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1629 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1630 	{
  1631 		return 0;
  1632 	}
  1633 
  1634 	return pUser->serialchange;
  1635 }
  1636 
  1637 bool AdminCache::CanAdminUseCommand(int client, const char *cmd)
  1638 {
  1639 	FlagBits bits;
  1640 	OverrideType otype = Override_Command;
  1641 
  1642 	if (cmd[0] == '@')
  1643 	{
  1644 		otype = Override_CommandGroup;
  1645 		cmd++;
  1646 	}
  1647 
  1648 	if (!g_ConCmds.LookForCommandAdminFlags(cmd, &bits))
  1649 	{
  1650 		if (!GetCommandOverride(cmd, otype, &bits))
  1651 		{
  1652 			bits = 0;
  1653 		}
  1654 	}
  1655 
  1656 	return g_ConCmds.CheckCommandAccess(client, cmd, bits);
  1657 }
  1658 
  1659 unsigned int AdminCache::SetGroupImmunityLevel(GroupId gid, unsigned int level)
  1660 {
  1661 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(gid);
  1662 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
  1663 	{
  1664 		return 0;
  1665 	}
  1666 
  1667 	unsigned int old_level = pGroup->immunity_level;
  1668 
  1669 	pGroup->immunity_level = level;
  1670 
  1671 	return old_level;
  1672 }
  1673 
  1674 unsigned int AdminCache::GetGroupImmunityLevel(GroupId gid)
  1675 {
  1676 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(gid);
  1677 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
  1678 	{
  1679 		return 0;
  1680 	}
  1681 
  1682 	return pGroup->immunity_level;
  1683 }
  1684 
  1685 unsigned int AdminCache::SetAdminImmunityLevel(AdminId id, unsigned int level)
  1686 {
  1687 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1688 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1689 	{
  1690 		return 0;
  1691 	}
  1692 
  1693 	unsigned int old_level = pUser->immunity_level;
  1694 
  1695 	pUser->immunity_level = level;
  1696 
  1697 	return old_level;
  1698 }
  1699 
  1700 unsigned int AdminCache::GetAdminImmunityLevel(AdminId id)
  1701 {
  1702 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1703 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1704 	{
  1705 		return 0;
  1706 	}
  1707 
  1708 	return pUser->immunity_level;
  1709 }
  1710 
  1711 bool AdminCache::CheckAccess(int client, const char *cmd, FlagBits flags, bool override_only)
  1712 {
  1713 	if (client == 0)
  1714 	{
  1715 		return true;
  1716 	}
  1717 
  1718 	/* Auto-detect a command if we can */
  1719 	FlagBits bits = flags;
  1720 	bool found_command = false;
  1721 	if (!override_only)
  1722 	{
  1723 		found_command = g_ConCmds.LookForCommandAdminFlags(cmd, &bits);
  1724 	}
  1725 
  1726 	if (!found_command)
  1727 	{
  1728 		GetCommandOverride(cmd, Override_Command, &bits);
  1729 	}
  1730 
  1731 	return g_ConCmds.CheckCommandAccess(client, cmd, bits) ? 1 : 0;
  1732 }