core/AdminCache.cpp
author David Anderson <dvander@alliedmods.net>
Sun Nov 16 16:06:18 2008 -0800 (2008-11-16)
changeset 2411 8dfaa3d730b5
parent 2289 44045ead61cd
child 2420 8b384e375877
permissions -rw-r--r--
Removed pointless "systems" folder from core.
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 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 #include "sm_srvcmds.h"
    45 
    46 #define LEVEL_STATE_NONE		0
    47 #define LEVEL_STATE_LEVELS		1
    48 #define LEVEL_STATE_FLAGS		2
    49 
    50 AdminCache g_Admins;
    51 char g_ReverseFlags[26];
    52 AdminFlag g_FlagLetters[26];
    53 bool g_FlagSet[26];
    54 
    55 ConVar sm_immunity_mode("sm_immunity_mode", "1", FCVAR_SPONLY, "Mode for deciding immunity protection");
    56 
    57 /* Default flags */
    58 AdminFlag g_DefaultFlags[26] = 
    59 {
    60 	Admin_Reservation, Admin_Generic, Admin_Kick, Admin_Ban, Admin_Unban,
    61 	Admin_Slay, Admin_Changemap, Admin_Convars, Admin_Config, Admin_Chat,
    62 	Admin_Vote, Admin_Password, Admin_RCON, Admin_Cheats, Admin_Custom1, 
    63 	Admin_Custom2, Admin_Custom3, Admin_Custom4, Admin_Custom5, Admin_Custom6,
    64 	Admin_Generic, Admin_Generic, Admin_Generic, Admin_Generic, Admin_Generic,
    65 	Admin_Root
    66 };
    67 
    68 
    69 class FlagReader : public ITextListener_SMC
    70 {
    71 public:
    72 	void LoadLevels()
    73 	{
    74 		if (!Parse())
    75 		{
    76 			memcpy(g_FlagLetters, g_DefaultFlags, sizeof(AdminFlag) * 26);
    77 			for (unsigned int i=0; i<20; i++)
    78 			{
    79 				g_FlagSet[i] = true;
    80 			}
    81 			g_FlagSet[25] = true;
    82 		}
    83 	}
    84 private:
    85 	bool Parse()
    86 	{
    87 		SMCStates states;
    88 		SMCError error;
    89 
    90 		m_bFileNameLogged = false;
    91 		g_SourceMod.BuildPath(Path_SM, m_File, sizeof(m_File), "configs/admin_levels.cfg");
    92 
    93 		if ((error = textparsers->ParseFile_SMC(m_File, this, &states))
    94 			!= SMCError_Okay)
    95 		{
    96 			const char *err_string = textparsers->GetSMCErrorString(error);
    97 			if (!err_string)
    98 			{
    99 				err_string = "Unknown error";
   100 			}
   101 			ParseError(NULL, "Error %d (%s)", error, err_string);
   102 			return false;
   103 		}
   104 
   105 		return true;
   106 	}
   107 	void ReadSMC_ParseStart()
   108 	{
   109 		m_LevelState = LEVEL_STATE_NONE;
   110 		m_IgnoreLevel = 0;
   111 		memset(g_FlagSet, 0, sizeof(g_FlagSet));
   112 	}
   113 	SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name)
   114 	{
   115 		if (m_IgnoreLevel)
   116 		{
   117 			m_IgnoreLevel++;
   118 			return SMCResult_Continue;
   119 		}
   120 
   121 		if (m_LevelState == LEVEL_STATE_NONE)
   122 		{
   123 			if (strcmp(name, "Levels") == 0)
   124 			{
   125 				m_LevelState = LEVEL_STATE_LEVELS;
   126 			} 
   127 			else 
   128 			{
   129 				m_IgnoreLevel++;
   130 			}
   131 		} else if (m_LevelState == LEVEL_STATE_LEVELS) {
   132 			if (strcmp(name, "Flags") == 0)
   133 			{
   134 				m_LevelState = LEVEL_STATE_FLAGS;
   135 			} 
   136 			else 
   137 			{
   138 				m_IgnoreLevel++;
   139 			}
   140 		} 
   141 		else 
   142 		{
   143 			m_IgnoreLevel++;
   144 		}
   145 
   146 		return SMCResult_Continue;
   147 	}
   148 	SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
   149 	{
   150 		if (m_LevelState != LEVEL_STATE_FLAGS || m_IgnoreLevel)
   151 		{
   152 			return SMCResult_Continue;
   153 		}
   154 
   155 		unsigned char c = (unsigned)value[0];
   156 
   157 		if (c < (unsigned)'a' || c > (unsigned)'z')
   158 		{
   159 			ParseError(states, "Flag \"%c\" is not a lower-case ASCII letter", c);
   160 			return SMCResult_Continue;
   161 		}
   162 
   163 		c -= (unsigned)'a';
   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 	int i;
   303 	AdminFlag flag;
   304 
   305 	/* For now, we only read these once per level. */
   306 	s_FlagReader.LoadLevels();
   307 
   308 	for (i = 0; i < 26; i++)
   309 	{
   310 		if (FindFlag('a' + i, &flag))
   311 		{
   312 			g_ReverseFlags[flag] = 'a' + i;
   313 		}
   314 		else
   315 		{
   316 			g_ReverseFlags[flag] = '?';
   317 		}
   318 	}
   319 }
   320 
   321 void AdminCache::OnSourceModShutdown()
   322 {
   323 	g_Forwards.ReleaseForward(m_pCacheFwd);
   324 	m_pCacheFwd = NULL;
   325 }
   326 
   327 void AdminCache::OnSourceModPluginsLoaded()
   328 {
   329 	DumpAdminCache(AdminCache_Overrides, true);
   330 	DumpAdminCache(AdminCache_Groups, true);
   331 }
   332 
   333 void AdminCache::NameFlag(const char *str, AdminFlag flag)
   334 {
   335 	sm_trie_insert(m_pLevelNames, str, (void *)flag);
   336 }
   337 
   338 bool AdminCache::FindFlag(const char *str, AdminFlag *pFlag)
   339 {
   340 	void *obj;
   341 	if (!sm_trie_retrieve(m_pLevelNames, str, &obj))
   342 	{
   343 		return false;
   344 	}
   345 
   346 	if (pFlag)
   347 	{
   348 		*pFlag = (AdminFlag)(int)obj;
   349 	}
   350 
   351 	return true;
   352 }
   353 
   354 void AdminCache::AddCommandOverride(const char *cmd, OverrideType type, FlagBits flags)
   355 {
   356 	Trie *pTrie = NULL;
   357 	if (type == Override_Command)
   358 	{
   359 		pTrie = m_pCmdOverrides;
   360 	} else if (type == Override_CommandGroup) {
   361 		pTrie = m_pCmdGrpOverrides;
   362 	} else {
   363 		return;
   364 	}
   365 
   366 	sm_trie_insert(pTrie, cmd, (void *)(unsigned int)flags);
   367 
   368 	g_ConCmds.UpdateAdminCmdFlags(cmd, type, flags, false);
   369 }
   370 
   371 bool AdminCache::GetCommandOverride(const char *cmd, OverrideType type, FlagBits *pFlags)
   372 {
   373 	Trie *pTrie = NULL;
   374 
   375 	if (type == Override_Command)
   376 	{
   377 		pTrie = m_pCmdOverrides;
   378 	} else if (type == Override_CommandGroup) {
   379 		pTrie = m_pCmdGrpOverrides;
   380 	} else {
   381 		return false;
   382 	}
   383 
   384 	void *object;
   385 	if (sm_trie_retrieve(pTrie, cmd, &object))
   386 	{
   387 		if (pFlags)
   388 		{
   389 			*pFlags = (FlagBits)object;
   390 		}
   391 		return true;
   392 	}
   393 
   394 	return false;
   395 }
   396 
   397 void AdminCache::UnsetCommandOverride(const char *cmd, OverrideType type)
   398 {
   399 	if (type == Override_Command)
   400 	{
   401 		return _UnsetCommandOverride(cmd);
   402 	} else if (type == Override_CommandGroup) {
   403 		return _UnsetCommandGroupOverride(cmd);
   404 	}
   405 }
   406 
   407 void AdminCache::_UnsetCommandGroupOverride(const char *group)
   408 {
   409 	if (!m_pCmdGrpOverrides)
   410 	{
   411 		return;
   412 	}
   413 
   414 	sm_trie_delete(m_pCmdGrpOverrides, group);
   415 
   416 	g_ConCmds.UpdateAdminCmdFlags(group, Override_CommandGroup, 0, true);
   417 }
   418 
   419 void AdminCache::_UnsetCommandOverride(const char *cmd)
   420 {
   421 	if (!m_pCmdOverrides)
   422 	{
   423 		return;
   424 	}
   425 
   426 	sm_trie_delete(m_pCmdOverrides, cmd);
   427 
   428 	g_ConCmds.UpdateAdminCmdFlags(cmd, Override_Command, 0, true);
   429 }
   430 
   431 void AdminCache::DumpCommandOverrideCache(OverrideType type)
   432 {
   433 	if (type == Override_Command && m_pCmdOverrides)
   434 	{
   435 		sm_trie_clear(m_pCmdOverrides);
   436 	} else if (type == Override_CommandGroup && m_pCmdGrpOverrides) {
   437 		sm_trie_clear(m_pCmdGrpOverrides);
   438 	}
   439 }
   440 
   441 AdminId AdminCache::CreateAdmin(const char *name)
   442 {
   443 	AdminId id;
   444 	AdminUser *pUser;
   445 
   446 	if (m_FreeUserList != INVALID_ADMIN_ID)
   447 	{
   448 		pUser = (AdminUser *)m_pMemory->GetAddress(m_FreeUserList);
   449 		assert(pUser->magic == USR_MAGIC_UNSET);
   450 		id = m_FreeUserList;
   451 		m_FreeUserList = pUser->next_user;
   452 	}
   453 	else
   454 	{
   455 		id = m_pMemory->CreateMem(sizeof(AdminUser), (void **)&pUser);
   456 		pUser->grp_size = 0;
   457 		pUser->grp_table = -1;
   458 	}
   459 
   460 	pUser->flags = 0;
   461 	pUser->eflags = 0;
   462 	pUser->grp_count = 0;
   463 	pUser->password = -1;
   464 	pUser->magic = USR_MAGIC_SET;
   465 	pUser->auth.identidx = -1;
   466 	pUser->auth.index = 0;
   467 	pUser->immunity_level = 0;
   468 	pUser->serialchange = 1;
   469 
   470 	if (m_FirstUser == INVALID_ADMIN_ID)
   471 	{
   472 		m_FirstUser = id;
   473 		m_LastUser = id;
   474 	}
   475 	else
   476 	{
   477 		AdminUser *pPrev = (AdminUser *)m_pMemory->GetAddress(m_LastUser);
   478 		pPrev->next_user = id;
   479 		pUser->prev_user = m_LastUser;
   480 		m_LastUser = id;
   481 	}
   482 
   483 	/* Since we always append to the tail, we should invalidate their next */
   484 	pUser->next_user = -1;
   485 
   486 	if (name && name[0] != '\0')
   487 	{
   488 		int nameidx = m_pStrings->AddString(name);
   489 		pUser = (AdminUser *)m_pMemory->GetAddress(id);
   490 		pUser->nameidx = nameidx;
   491 	}
   492 	else
   493 	{
   494 		pUser->nameidx = -1;
   495 	}
   496 
   497 	return id;
   498 }
   499 
   500 GroupId AdminCache::AddGroup(const char *group_name)
   501 {
   502 	if (sm_trie_retrieve(m_pGroups, group_name, NULL))
   503 	{
   504 		return INVALID_GROUP_ID;
   505 	}
   506 
   507 	GroupId id;
   508 	AdminGroup *pGroup;
   509 	if (m_FreeGroupList != INVALID_GROUP_ID)
   510 	{
   511 		pGroup = (AdminGroup *)m_pMemory->GetAddress(m_FreeGroupList);
   512 		assert(pGroup->magic == GRP_MAGIC_UNSET);
   513 		id = m_FreeGroupList;
   514 		m_FreeGroupList = pGroup->next_grp;
   515 	} else {
   516 		id = m_pMemory->CreateMem(sizeof(AdminGroup), (void **)&pGroup);
   517 	}
   518 
   519 	pGroup->immunity_level = 0;
   520 	pGroup->immune_table = -1;
   521 	pGroup->magic = GRP_MAGIC_SET;
   522 	pGroup->next_grp = INVALID_GROUP_ID;
   523 	pGroup->pCmdGrpTable = NULL;
   524 	pGroup->pCmdTable = NULL;
   525 	pGroup->addflags = 0;
   526 
   527 	if (m_FirstGroup == INVALID_GROUP_ID)
   528 	{
   529 		m_FirstGroup = id;
   530 		m_LastGroup = id;
   531 		pGroup->prev_grp = INVALID_GROUP_ID;
   532 	} else {
   533 		AdminGroup *pPrev = (AdminGroup *)m_pMemory->GetAddress(m_LastGroup);
   534 		assert(pPrev->magic == GRP_MAGIC_SET);
   535 		pPrev->next_grp = id;
   536 		pGroup->prev_grp = m_LastGroup;
   537 		m_LastGroup = id;
   538 	}
   539 
   540 	int nameidx = m_pStrings->AddString(group_name);
   541 	pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   542 	pGroup->nameidx = nameidx;
   543 
   544 	sm_trie_insert(m_pGroups, group_name, (void *)id);
   545 
   546 	return id;
   547 }
   548 
   549 GroupId AdminCache::FindGroupByName(const char *group_name)
   550 {
   551 	void *object;
   552 
   553 	if (!sm_trie_retrieve(m_pGroups, group_name, &object))
   554 	{
   555 		return INVALID_GROUP_ID;
   556 	}
   557 
   558 	GroupId id = (GroupId)object;
   559 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   560 
   561 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   562 	{
   563 		return INVALID_GROUP_ID;
   564 	}
   565 
   566 	return id;
   567 }
   568 
   569 void AdminCache::SetGroupAddFlag(GroupId id, AdminFlag flag, bool enabled)
   570 {
   571 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   572 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   573 	{
   574 		return;
   575 	}
   576 
   577 	if (flag < Admin_Reservation || flag >= AdminFlags_TOTAL)
   578 	{
   579 		return;
   580 	}
   581 
   582 	FlagBits bits = (1<<(FlagBits)flag);
   583 
   584 	if (enabled)
   585 	{
   586 		pGroup->addflags |= bits;
   587 	} else {
   588 		pGroup->addflags &= ~bits;
   589 	}
   590 }
   591 
   592 bool AdminCache::GetGroupAddFlag(GroupId id, AdminFlag flag)
   593 {
   594 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   595 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   596 	{
   597 		return false;
   598 	}
   599 
   600 	if (flag < Admin_Reservation || flag >= AdminFlags_TOTAL)
   601 	{
   602 		return false;
   603 	}
   604 
   605 	FlagBits bit = 1<<(FlagBits)flag;
   606 	return ((pGroup->addflags & bit) == bit);
   607 }
   608 
   609 FlagBits AdminCache::GetGroupAddFlags(GroupId id)
   610 {
   611 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   612 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   613 	{
   614 		return 0;
   615 	}
   616 
   617 	return pGroup->addflags;
   618 }
   619 
   620 const char *AdminCache::GetGroupName(GroupId gid)
   621 {
   622 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(gid);
   623 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   624 	{
   625 		return NULL;
   626 	}
   627 
   628 	return m_pStrings->GetString(pGroup->nameidx);
   629 }
   630 
   631 void AdminCache::SetGroupGenericImmunity(GroupId id, ImmunityType type, bool enabled)
   632 {
   633 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   634 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   635 	{
   636 		return;
   637 	}
   638 
   639 	unsigned int level = 0;
   640 
   641 	if (enabled)
   642 	{
   643 		if (type == Immunity_Default)
   644 		{
   645 			level = 1;
   646 		} else if (type == Immunity_Global) {
   647 			level = 2;
   648 		}
   649 		if (level > pGroup->immunity_level)
   650 		{
   651 			pGroup->immunity_level = level;
   652 		}
   653 	} else {
   654 		pGroup->immunity_level = 0;
   655 	}
   656 }
   657 
   658 bool AdminCache::GetGroupGenericImmunity(GroupId id, ImmunityType type)
   659 {
   660 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   661 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   662 	{
   663 		return false;
   664 	}
   665 
   666 	if (type == Immunity_Default && pGroup->immunity_level >= 1)
   667 	{
   668 		return true;
   669 	} else if (type == Immunity_Global && pGroup->immunity_level >= 2) {
   670 		return true;
   671 	}
   672 
   673 	return false;
   674 }
   675 
   676 void AdminCache::AddGroupImmunity(GroupId id, GroupId other_id)
   677 {
   678 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(other_id);
   679 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   680 	{
   681 		return;
   682 	}
   683 
   684 	pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   685 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   686 	{
   687 		return;
   688 	}
   689 
   690 	/* We always need to resize the immunity table */
   691 	int *table, tblidx;
   692 	if (pGroup->immune_table == -1)
   693 	{
   694 		tblidx = m_pMemory->CreateMem(sizeof(int) * 2, (void **)&table);
   695 		pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   696 		table[0] = 0;
   697 	} else {
   698 		int *old_table = (int *)m_pMemory->GetAddress(pGroup->immune_table);
   699 		/* Break out if this group is already in the list */
   700 		for (int i=0; i<old_table[0]; i++)
   701 		{
   702 			if (old_table[1+i] == other_id)
   703 			{
   704 				return;
   705 			}
   706 		}
   707 		tblidx = m_pMemory->CreateMem(sizeof(int) * (old_table[0] + 2), (void **)&table);
   708 		/* Get the old address again in case of resize */
   709 		pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   710 		old_table = (int *)m_pMemory->GetAddress(pGroup->immune_table);
   711 		table[0] = old_table[0];
   712 		for (unsigned int i=1; i<=(unsigned int)old_table[0]; i++)
   713 		{
   714 			table[i] = old_table[i];
   715 		}
   716 	}
   717 
   718 	/* Assign */
   719 	pGroup->immune_table = tblidx;
   720 
   721 	/* Add to the array */
   722 	table[0]++;
   723 	table[table[0]] = other_id;
   724 }
   725 
   726 unsigned int AdminCache::GetGroupImmunityCount(GroupId id)
   727 {
   728 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   729 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   730 	{
   731 		return 0;
   732 	}
   733 
   734 	if (pGroup->immune_table == -1)
   735 	{
   736 		return 0;
   737 	}
   738 
   739 	int *table = (int *)m_pMemory->GetAddress(pGroup->immune_table);
   740 
   741 	return table[0];
   742 }
   743 
   744 GroupId AdminCache::GetGroupImmunity(GroupId id, unsigned int number)
   745 {
   746 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   747 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   748 	{
   749 		return INVALID_GROUP_ID;
   750 	}
   751 
   752 	if (pGroup->immune_table == -1)
   753 	{
   754 		return INVALID_GROUP_ID;
   755 	}
   756 
   757 	int *table = (int *)m_pMemory->GetAddress(pGroup->immune_table);
   758 	if (number >= (unsigned int)table[0])
   759 	{
   760 		return INVALID_GROUP_ID;
   761 	}
   762 
   763 	return table[1 + number];
   764 }
   765 
   766 void AdminCache::AddGroupCommandOverride(GroupId id, const char *name, OverrideType type, OverrideRule rule)
   767 {
   768 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   769 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   770 	{
   771 		return;
   772 	}
   773 
   774 	Trie *pTrie = NULL;
   775 	if (type == Override_Command)
   776 	{
   777 		if (pGroup->pCmdTable == NULL)
   778 		{
   779 			pGroup->pCmdTable = sm_trie_create();
   780 		}
   781 		pTrie = pGroup->pCmdTable;
   782 	} else if (type == Override_CommandGroup) {
   783 		if (pGroup->pCmdGrpTable == NULL)
   784 		{
   785 			pGroup->pCmdGrpTable = sm_trie_create();
   786 		}
   787 		pTrie = pGroup->pCmdGrpTable;
   788 	} else {
   789 		return;
   790 	}
   791 
   792 	sm_trie_insert(pTrie, name, (void *)(int)rule);
   793 }
   794 
   795 bool AdminCache::GetGroupCommandOverride(GroupId id, const char *name, OverrideType type, OverrideRule *pRule)
   796 {
   797 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   798 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   799 	{
   800 		return false;
   801 	}
   802 
   803 	Trie *pTrie = NULL;
   804 	if (type == Override_Command)
   805 	{
   806 		if (pGroup->pCmdTable == NULL)
   807 		{
   808 			return false;
   809 		}
   810 		pTrie = pGroup->pCmdTable;
   811 	} else if (type == Override_CommandGroup) {
   812 		if (pGroup->pCmdGrpTable == NULL)
   813 		{
   814 			return false;
   815 		}
   816 		pTrie = pGroup->pCmdGrpTable;
   817 	} else {
   818 		return false;
   819 	}
   820 
   821 	void *object;
   822 	if (!sm_trie_retrieve(pTrie, name, &object))
   823 	{
   824 		return false;
   825 	}
   826 
   827 	if (pRule)
   828 	{
   829 		*pRule = (OverrideRule)(int)object;
   830 	}
   831 	
   832 	return true;
   833 }
   834 
   835 Trie *AdminCache::GetMethodByIndex(unsigned int index)
   836 {
   837 	List<AuthMethod>::iterator iter;
   838 	for (iter=m_AuthMethods.begin();
   839 		 iter!=m_AuthMethods.end();
   840 		 iter++)
   841 	{
   842 		if (index-- == 0)
   843 		{
   844 			return (*iter).table;
   845 		}
   846 	}
   847 
   848 	return NULL;
   849 }
   850 
   851 bool AdminCache::InvalidateAdmin(AdminId id)
   852 {
   853 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
   854 	AdminUser *pOther;
   855 	if (!pUser || pUser->magic != USR_MAGIC_SET)
   856 	{
   857 		return false;
   858 	}
   859 
   860 	if (!m_InvalidatingAdmins && !m_destroying)
   861 	{
   862 		g_Players.ClearAdminId(id);
   863 	}
   864 
   865 	/* Unlink from the dbl link list */
   866 	if (id == m_FirstUser && id == m_LastUser)
   867 	{
   868 		m_FirstUser = INVALID_ADMIN_ID;
   869 		m_LastUser = INVALID_ADMIN_ID;
   870 	} else if (id == m_FirstUser) {
   871 		m_FirstUser = pUser->next_user;
   872 		pOther = (AdminUser *)m_pMemory->GetAddress(m_FirstUser);
   873 		pOther->prev_user = INVALID_ADMIN_ID;
   874 	} else if (id == m_LastUser) {
   875 		m_LastUser = pUser->prev_user;
   876 		pOther = (AdminUser *)m_pMemory->GetAddress(m_LastUser);
   877 		pOther->next_user = INVALID_ADMIN_ID;
   878 	} else {
   879 		pOther = (AdminUser *)m_pMemory->GetAddress(pUser->prev_user);
   880 		pOther->next_user = pUser->next_user;
   881 		pOther = (AdminUser *)m_pMemory->GetAddress(pUser->next_user);
   882 		pOther->prev_user = pUser->prev_user;
   883 	}
   884 
   885 	/* Unlink from auth tables */
   886 	if (pUser->auth.identidx != -1)
   887 	{
   888 		Trie *pTrie = GetMethodByIndex(pUser->auth.index);
   889 		if (pTrie)
   890 		{
   891 			sm_trie_delete(pTrie, m_pStrings->GetString(pUser->auth.identidx));
   892 		}
   893 	}
   894 
   895 	/* Clear table counts */
   896 	pUser->grp_count = 0;
   897 	
   898 	/* Link into free list */
   899 	pUser->magic = USR_MAGIC_UNSET;
   900 	pUser->next_user = m_FreeUserList;
   901 	m_FreeUserList = id;
   902 
   903 	/* Unset serial change */
   904 	pUser->serialchange = 0;
   905 
   906 	return true;
   907 }
   908 
   909 
   910 void AdminCache::InvalidateGroup(GroupId id)
   911 {
   912 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   913 	AdminGroup *pOther;
   914 
   915 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   916 	{
   917 		return;
   918 	}
   919 
   920 	const char *str = m_pStrings->GetString(pGroup->nameidx);
   921 	sm_trie_delete(m_pGroups, str);
   922 
   923 	/* Unlink from the live dbllink list */
   924 	if (id == m_FirstGroup && id == m_LastGroup)
   925 	{
   926 		m_LastGroup = INVALID_GROUP_ID;
   927 		m_FirstGroup = INVALID_GROUP_ID;
   928 	} else if (id == m_FirstGroup) {
   929 		m_FirstGroup = pGroup->next_grp;
   930 		pOther = (AdminGroup *)m_pMemory->GetAddress(m_FirstGroup);
   931 		pOther->prev_grp = INVALID_GROUP_ID;
   932 	} else if (id == m_LastGroup) {
   933 		m_LastGroup = pGroup->prev_grp;
   934 		pOther = (AdminGroup *)m_pMemory->GetAddress(m_LastGroup);
   935 		pOther->next_grp = INVALID_GROUP_ID;
   936 	} else {
   937 		pOther = (AdminGroup *)m_pMemory->GetAddress(pGroup->prev_grp);
   938 		pOther->next_grp = pGroup->next_grp;
   939 		pOther = (AdminGroup *)m_pMemory->GetAddress(pGroup->next_grp);
   940 		pOther->prev_grp = pGroup->prev_grp;
   941 	}
   942 
   943 	/* Free any used memory to be safe */
   944 	if (pGroup->pCmdGrpTable)
   945 	{
   946 		sm_trie_destroy(pGroup->pCmdGrpTable);
   947 		pGroup->pCmdGrpTable = NULL;
   948 	}
   949 	if (pGroup->pCmdTable)
   950 	{
   951 		sm_trie_destroy(pGroup->pCmdTable);
   952 		pGroup->pCmdTable = NULL;
   953 	}
   954 
   955 	/* Link into the free list */
   956 	pGroup->magic = GRP_MAGIC_UNSET;
   957 	pGroup->next_grp = m_FreeGroupList;
   958 	m_FreeGroupList = id;
   959 
   960 	int idx = m_FirstUser;
   961 	AdminUser *pUser;
   962 	int *table;
   963 	while (idx != INVALID_ADMIN_ID)
   964 	{
   965 		pUser = (AdminUser *)m_pMemory->GetAddress(idx);
   966 		if (pUser->grp_count > 0)
   967 		{
   968 			table = (int *)m_pMemory->GetAddress(pUser->grp_table);
   969 			for (unsigned int i=0; i<pUser->grp_count; i++)
   970 			{
   971 				if (table[i] == id)
   972 				{
   973 					/* We have to remove this entry */
   974 					for (unsigned int j=i+1; j<pUser->grp_count; j++)
   975 					{
   976 						/* Move everything down by one */
   977 						table[j-1] = table[j];
   978 					}
   979 					/* Decrease count */
   980 					pUser->grp_count--;
   981 					/* Recalculate effective flags */
   982 					pUser->eflags = pUser->flags;
   983 					for (unsigned int j=0; j<pUser->grp_count; j++)
   984 					{
   985 						pOther = (AdminGroup *)m_pMemory->GetAddress(table[j]);
   986 						pUser->eflags |= pOther->addflags;
   987 					}
   988 					/* Mark as changed */
   989 					pUser->serialchange++;
   990 					/* Break now, duplicates aren't allowed */
   991 					break;
   992 				}
   993 			}
   994 		}
   995 		idx = pUser->next_user;
   996 	}
   997 }
   998 
   999 void AdminCache::InvalidateGroupCache()
  1000 {
  1001 	/* Nuke the free list */
  1002 	m_FreeGroupList = -1;
  1003 
  1004 	/* Nuke reverse lookups */
  1005 	sm_trie_clear(m_pGroups);
  1006 
  1007 	/* Free memory on groups */
  1008 	GroupId cur = m_FirstGroup;
  1009 	AdminGroup *pGroup;
  1010 	while (cur != INVALID_GROUP_ID)
  1011 	{
  1012 		pGroup = (AdminGroup *)m_pMemory->GetAddress(cur);
  1013 		assert(pGroup->magic == GRP_MAGIC_SET);
  1014 		if (pGroup->pCmdGrpTable)
  1015 		{
  1016 			sm_trie_destroy(pGroup->pCmdGrpTable);
  1017 		}
  1018 		if (pGroup->pCmdTable)
  1019 		{
  1020 			sm_trie_destroy(pGroup->pCmdTable);
  1021 		}
  1022 		cur = pGroup->next_grp;
  1023 	}
  1024 
  1025 	m_FirstGroup = INVALID_GROUP_ID;
  1026 	m_LastGroup = INVALID_GROUP_ID;
  1027 
  1028 	InvalidateAdminCache(false);
  1029 
  1030 	/* Reset the memory table */
  1031 	m_pMemory->Reset();
  1032 }
  1033 
  1034 void AdminCache::AddAdminListener(IAdminListener *pListener)
  1035 {
  1036 	m_hooks.push_back(pListener);
  1037 }
  1038 
  1039 void AdminCache::RemoveAdminListener(IAdminListener *pListener)
  1040 {
  1041 	m_hooks.remove(pListener);
  1042 }
  1043 
  1044 void AdminCache::RegisterAuthIdentType(const char *name)
  1045 {
  1046 	if (sm_trie_retrieve(m_pAuthTables, name, NULL))
  1047 	{
  1048 		return;
  1049 	}
  1050 
  1051 	Trie *pAuth = sm_trie_create();
  1052 
  1053 	AuthMethod method;
  1054 	method.name.assign(name);
  1055 	method.table = pAuth;
  1056 
  1057 	m_AuthMethods.push_back(method);
  1058 
  1059 	sm_trie_insert(m_pAuthTables, name, pAuth);
  1060 }
  1061 
  1062 void AdminCache::InvalidateAdminCache(bool unlink_admins)
  1063 {
  1064 	m_InvalidatingAdmins = true;
  1065 	if (!m_destroying)
  1066 	{
  1067 		g_Players.ClearAllAdmins();
  1068 	}
  1069 	/* Wipe the identity cache first */
  1070 	List<AuthMethod>::iterator iter;
  1071 	for (iter=m_AuthMethods.begin();
  1072 		 iter!=m_AuthMethods.end();
  1073 		 iter++)
  1074 	{
  1075 		sm_trie_clear((*iter).table);
  1076 	}
  1077 	
  1078 	if (unlink_admins)
  1079 	{
  1080 		while (m_FirstUser != INVALID_ADMIN_ID)
  1081 		{
  1082 			InvalidateAdmin(m_FirstUser);
  1083 		}
  1084 	} else {
  1085 		m_FirstUser = -1;
  1086 		m_LastUser = -1;
  1087 		m_FreeUserList = -1;
  1088 	}
  1089 	m_InvalidatingAdmins = false;
  1090 }
  1091 
  1092 void AdminCache::DumpAdminCache(AdminCachePart part, bool rebuild)
  1093 {
  1094 	List<IAdminListener *>::iterator iter;
  1095 	IAdminListener *pListener;
  1096 	cell_t result;
  1097 
  1098 	if (part == AdminCache_Overrides)
  1099 	{
  1100 		DumpCommandOverrideCache(Override_Command);
  1101 		DumpCommandOverrideCache(Override_CommandGroup);
  1102 		if (rebuild && !m_destroying)
  1103 		{
  1104 			for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++)
  1105 			{
  1106 				pListener = (*iter);
  1107 				pListener->OnRebuildOverrideCache();
  1108 			}
  1109 			m_pCacheFwd->PushCell(part);
  1110 			m_pCacheFwd->Execute(&result);
  1111 		}
  1112 	} else if (part == AdminCache_Groups || part == AdminCache_Admins) {
  1113 		if (part == AdminCache_Groups)
  1114 		{
  1115 			InvalidateGroupCache();
  1116 			if (rebuild && !m_destroying)
  1117 			{
  1118 				for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++)
  1119 				{
  1120 					pListener = (*iter);
  1121 					pListener->OnRebuildGroupCache();
  1122 				}
  1123 				m_pCacheFwd->PushCell(part);
  1124 				m_pCacheFwd->Execute(&result);
  1125 			}
  1126 		}
  1127 		InvalidateAdminCache(true);
  1128 		if (rebuild && !m_destroying)
  1129 		{
  1130 			for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++)
  1131 			{
  1132 				pListener = (*iter);
  1133 				pListener->OnRebuildAdminCache((part == AdminCache_Groups));
  1134 			}
  1135 			m_pCacheFwd->PushCell(AdminCache_Admins);
  1136 			m_pCacheFwd->Execute(&result);
  1137 			g_Players.RecheckAnyAdmins();
  1138 		}
  1139 	}
  1140 }
  1141 
  1142 const char *AdminCache::GetAdminName(AdminId id)
  1143 {
  1144 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1145 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1146 	{
  1147 		return NULL;
  1148 	}
  1149 
  1150 	return m_pStrings->GetString(pUser->nameidx);
  1151 }
  1152 
  1153 bool AdminCache::GetMethodIndex(const char *name, unsigned int *_index)
  1154 {
  1155 	List<AuthMethod>::iterator iter;
  1156 	unsigned int index = 0;
  1157 	for (iter=m_AuthMethods.begin();
  1158 		 iter!=m_AuthMethods.end();
  1159 		 iter++,index++)
  1160 	{
  1161 		if ((*iter).name.compare(name) == 0)
  1162 		{
  1163 			*_index = index;
  1164 			return true;
  1165 		}
  1166 	}
  1167 
  1168 	return false;
  1169 }
  1170 
  1171 bool AdminCache::BindAdminIdentity(AdminId id, const char *auth, const char *ident)
  1172 {
  1173 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1174 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1175 	{
  1176 		return false;
  1177 	}
  1178 
  1179 	Trie *pTable;
  1180 	if (!sm_trie_retrieve(m_pAuthTables, auth, (void **)&pTable))
  1181 	{
  1182 		return false;
  1183 	}
  1184 
  1185 	if (sm_trie_retrieve(pTable, ident, NULL))
  1186 	{
  1187 		return false;
  1188 	}
  1189 
  1190 	int i_ident = m_pStrings->AddString(ident);
  1191 
  1192 	pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1193 	pUser->auth.identidx = i_ident;
  1194 	GetMethodIndex(auth, &pUser->auth.index);
  1195 
  1196 	return sm_trie_insert(pTable, ident, (void **)id);
  1197 }
  1198 
  1199 AdminId AdminCache::FindAdminByIdentity(const char *auth, const char *identity)
  1200 {
  1201 	Trie *pTable;
  1202 	if (!sm_trie_retrieve(m_pAuthTables, auth, (void **)&pTable))
  1203 	{
  1204 		return INVALID_ADMIN_ID;
  1205 	}
  1206 
  1207 	void *object;
  1208 	if (!sm_trie_retrieve(pTable, identity, &object))
  1209 	{
  1210 		return INVALID_ADMIN_ID;
  1211 	}
  1212 
  1213 	return (AdminId)object;
  1214 }
  1215 
  1216 void AdminCache::SetAdminFlag(AdminId id, AdminFlag flag, bool enabled)
  1217 {
  1218 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1219 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1220 	{
  1221 		return;
  1222 	}
  1223 
  1224 	if (flag < Admin_Reservation
  1225 		|| flag >= AdminFlags_TOTAL)
  1226 	{
  1227 		return;
  1228 	}
  1229 	
  1230 	FlagBits bits = (1<<(FlagBits)flag);
  1231 
  1232 	if (enabled)
  1233 	{
  1234 		pUser->flags |= bits;
  1235 		pUser->eflags |= bits;
  1236 	} else {
  1237 		pUser->flags &= ~bits;
  1238 		pUser->eflags &= ~bits;
  1239 	}
  1240 
  1241 	pUser->serialchange++;
  1242 }
  1243 
  1244 bool AdminCache::GetAdminFlag(AdminId id, AdminFlag flag, AccessMode mode)
  1245 {
  1246 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1247 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1248 	{
  1249 		return false;
  1250 	}
  1251 
  1252 	if (flag < Admin_Reservation
  1253 		|| flag >= AdminFlags_TOTAL)
  1254 	{
  1255 		return false;
  1256 	}
  1257 
  1258 	FlagBits bit = (1<<(FlagBits)flag);
  1259 
  1260 	if (mode == Access_Real)
  1261 	{
  1262 		return ((pUser->flags & bit) == bit);
  1263 	} else if (mode == Access_Effective) {
  1264 		bool has_bit = ((pUser->eflags & bit) == bit);
  1265 		if (!has_bit && flag != Admin_Root && ((pUser->eflags & ADMFLAG_ROOT) == ADMFLAG_ROOT))
  1266 		{
  1267 			has_bit = true;
  1268 		}
  1269 		return has_bit;
  1270 	}
  1271 
  1272 	return false;
  1273 }
  1274 
  1275 FlagBits AdminCache::GetAdminFlags(AdminId id, AccessMode mode)
  1276 {
  1277 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1278 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1279 	{
  1280 		return 0;
  1281 	}
  1282 
  1283 	if (mode == Access_Real)
  1284 	{
  1285 		return pUser->flags;
  1286 	} else if (mode == Access_Effective) {
  1287 		return pUser->eflags;
  1288 	}
  1289 
  1290 	return 0;
  1291 }
  1292 
  1293 void AdminCache::SetAdminFlags(AdminId id, AccessMode mode, FlagBits bits)
  1294 {
  1295 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1296 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1297 	{
  1298 		return;
  1299 	}
  1300 
  1301 	if (mode == Access_Real)
  1302 	{
  1303 		pUser->flags = bits;
  1304 		pUser->eflags = bits;
  1305 	} else if (mode == Access_Effective) {
  1306 		pUser->eflags = bits;
  1307 	}
  1308 
  1309 	pUser->serialchange++;
  1310 }
  1311 
  1312 bool AdminCache::AdminInheritGroup(AdminId id, GroupId gid)
  1313 {
  1314 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1315 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1316 	{
  1317 		return false;
  1318 	}
  1319 
  1320 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(gid);
  1321 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
  1322 	{
  1323 		return false;
  1324 	}
  1325 
  1326 	/* First check for duplicates */
  1327 	if (pUser->grp_count != 0)
  1328 	{
  1329 		int *temp_table = (int *)m_pMemory->GetAddress(pUser->grp_table);
  1330 		for (unsigned int i=0; i<pUser->grp_count; i++)
  1331 		{
  1332 			if (temp_table[i] == gid)
  1333 			{
  1334 				return false;
  1335 			}
  1336 		}
  1337 	}
  1338 
  1339 	int *table;
  1340 	if (pUser->grp_count + 1 > pUser->grp_size)
  1341 	{
  1342 		int new_size = 0;
  1343 		int tblidx;
  1344 		
  1345 		if (pUser->grp_size == 0)
  1346 		{
  1347 			new_size = 2;
  1348 		} else {
  1349 			new_size = pUser->grp_size * 2;
  1350 		}
  1351 		
  1352 		/* Create and refresh pointers */
  1353 		tblidx = m_pMemory->CreateMem(new_size * sizeof(int), (void **)&table);
  1354 		pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1355 		pGroup = (AdminGroup *)m_pMemory->GetAddress(gid);
  1356 
  1357 		/* Copy old data if necessary */
  1358 		if (pUser->grp_table != -1)
  1359 		{
  1360 			int *old_table = (int *)m_pMemory->GetAddress(pUser->grp_table);
  1361 			memcpy(table, old_table, sizeof(int) * pUser->grp_count);
  1362 		}
  1363 		pUser->grp_table = tblidx;
  1364 		pUser->grp_size = new_size;
  1365 	} else {
  1366 		table = (int *)m_pMemory->GetAddress(pUser->grp_table);
  1367 	}
  1368 
  1369 	table[pUser->grp_count] = gid;
  1370 	pUser->grp_count++;
  1371 
  1372 	/* Compute new effective permissions */
  1373 	pUser->eflags |= pGroup->addflags;
  1374 
  1375 	if (pGroup->immunity_level > pUser->immunity_level)
  1376 	{
  1377 		pUser->immunity_level = pGroup->immunity_level;
  1378 	}
  1379 
  1380 	pUser->serialchange++;
  1381 
  1382 	return true;
  1383 }
  1384 
  1385 bool AdminCache::IsValidAdmin(AdminId id)
  1386 {
  1387 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1388 	return (pUser != NULL && pUser->magic == USR_MAGIC_SET);
  1389 }
  1390 
  1391 unsigned int AdminCache::GetAdminGroupCount(AdminId id)
  1392 {
  1393 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1394 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1395 	{
  1396 		return 0;
  1397 	}
  1398 
  1399 	return pUser->grp_count;
  1400 }
  1401 
  1402 GroupId AdminCache::GetAdminGroup(AdminId id, unsigned int index, const char **name)
  1403 {
  1404 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1405 	if (!pUser || pUser->magic != USR_MAGIC_SET || index >= pUser->grp_count)
  1406 	{
  1407 		return INVALID_GROUP_ID;
  1408 	}
  1409 
  1410 	int *table = (int *)m_pMemory->GetAddress(pUser->grp_table);
  1411 
  1412 	GroupId gid = table[index];
  1413 
  1414 	if (name)
  1415 	{
  1416 		*name = GetGroupName(gid);
  1417 	}
  1418 
  1419 	return gid;
  1420 }
  1421 
  1422 const char *AdminCache::GetAdminPassword(AdminId id)
  1423 {
  1424 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1425 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1426 	{
  1427 		return NULL;
  1428 	}
  1429 
  1430 	return m_pStrings->GetString(pUser->password);
  1431 }
  1432 
  1433 void AdminCache::SetAdminPassword(AdminId id, const char *password)
  1434 {	
  1435 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1436 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1437 	{
  1438 		return;
  1439 	}
  1440 
  1441 	if (password[0] == '\0')
  1442 	{
  1443 		pUser->password = -1;
  1444 		return;
  1445 	}
  1446 
  1447 	int i_password = m_pStrings->AddString(password);
  1448 	pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1449 	pUser->password = i_password;
  1450 }
  1451 
  1452 unsigned int AdminCache::FlagBitsToBitArray(FlagBits bits, bool array[], unsigned int maxSize)
  1453 {
  1454 	unsigned int i;
  1455 	for (i=0; i<maxSize && i<AdminFlags_TOTAL; i++)
  1456 	{
  1457 		array[i] = ((bits & (1<<i)) == (unsigned)(1<<i));
  1458 	}
  1459 
  1460 	return i;
  1461 }
  1462 
  1463 FlagBits AdminCache::FlagBitArrayToBits(const bool array[], unsigned int maxSize)
  1464 {
  1465 	FlagBits bits = 0;
  1466 	for (unsigned int i=0; i<maxSize && i<AdminFlags_TOTAL; i++)
  1467 	{
  1468 		if (array[i])
  1469 		{
  1470 			bits |= (1<<i);
  1471 		}
  1472 	}
  1473 
  1474 	return bits;
  1475 }
  1476 
  1477 FlagBits AdminCache::FlagArrayToBits(const AdminFlag array[], unsigned int numFlags)
  1478 {
  1479 	FlagBits bits = 0;
  1480 	for (unsigned int i=0; i<numFlags && i<AdminFlags_TOTAL; i++)
  1481 	{
  1482 		bits |= (1 << (FlagBits)array[i]);
  1483 	}
  1484 
  1485 	return bits;
  1486 }
  1487 
  1488 unsigned int AdminCache::FlagBitsToArray(FlagBits bits, AdminFlag array[], unsigned int maxSize)
  1489 {
  1490 	unsigned int i, num=0;
  1491 	for (i=0; num<maxSize && i<AdminFlags_TOTAL; i++)
  1492 	{
  1493 		if ((bits & (1<<i)) == (unsigned)(1<<i))
  1494 		{
  1495 			array[num++] = (AdminFlag)i;
  1496 		}
  1497 	}
  1498 
  1499 	return num;
  1500 }
  1501 
  1502 bool AdminCache::CheckAdminFlags(AdminId id, FlagBits bits)
  1503 {
  1504 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1505 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1506 	{
  1507 		return false;
  1508 	}
  1509 
  1510 	return ((pUser->eflags & bits) == bits);
  1511 }
  1512 
  1513 bool AdminCache::CanAdminTarget(AdminId id, AdminId target)
  1514 {
  1515 	/** 
  1516 	 * Zeroth, if the targeting AdminId is INVALID_ADMIN_ID, targeting fails.
  1517 	 * First, if the targeted AdminId is INVALID_ADMIN_ID, targeting succeeds.
  1518 	 */
  1519 
  1520 	if (id == INVALID_ADMIN_ID)
  1521 	{
  1522 		return false;
  1523 	}
  1524 
  1525 	if (target == INVALID_ADMIN_ID)
  1526 	{
  1527 		return true;
  1528 	}
  1529 
  1530 	if (id == target)
  1531 	{
  1532 		return true;
  1533 	}
  1534 
  1535 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1536 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1537 	{
  1538 		return false;
  1539 	}
  1540 
  1541 	AdminUser *pTarget = (AdminUser *)m_pMemory->GetAddress(target);
  1542 	if (!pTarget || pTarget->magic != USR_MAGIC_SET)
  1543 	{
  1544 		return false;
  1545 	}
  1546 
  1547 	/** 
  1548 	 * Second, if the targeting admin is root, targeting succeeds.
  1549 	 */
  1550 	if (pUser->eflags & ADMFLAG_ROOT)
  1551 	{
  1552 		return true;
  1553 	}
  1554 
  1555 	/** Fourth, if the targeted admin is immune from targeting admin. */
  1556 	int mode = sm_immunity_mode.GetInt();
  1557 	switch (mode)
  1558 	{
  1559 		case 1:
  1560 		{
  1561 			if (pTarget->immunity_level > pUser->immunity_level)
  1562 			{
  1563 				return false;
  1564 			}
  1565 			break;
  1566 		}
  1567 		case 3:
  1568 		{
  1569 			/* If neither has any immunity, let this pass. */
  1570 			if (!pUser->immunity_level && !pTarget->immunity_level)
  1571 			{
  1572 				return true;
  1573 			}
  1574 			/* Don't break, go to the next case. */
  1575 		}
  1576 		case 2:
  1577 		{
  1578 			if (pTarget->immunity_level >= pUser->immunity_level)
  1579 			{
  1580 				return false;
  1581 			}
  1582 			break;
  1583 		}
  1584 	}
  1585 
  1586 	/** 
  1587 	 * Fifth, if the targeted admin has specific immunity from the
  1588 	 *  targeting admin via group immunities, targeting fails.
  1589 	 */
  1590 	//:TODO: speed this up... maybe with trie hacks.
  1591 	//idea is to insert %d.%d in the trie after computing this and use it as a cache lookup.
  1592 	//problem is the trie cannot delete prefixes, so we'd have a problem with invalidations.
  1593 	if (pTarget->grp_count > 0 && pUser->grp_count > 0)
  1594 	{
  1595 		int *grp_table = (int *)m_pMemory->GetAddress(pTarget->grp_table);
  1596 		int *src_table = (int *)m_pMemory->GetAddress(pUser->grp_table);
  1597 		GroupId id, other;
  1598 		unsigned int num;
  1599 		for (unsigned int i=0; i<pTarget->grp_count; i++)
  1600 		{
  1601 			id = grp_table[i];
  1602 			num = GetGroupImmunityCount(id);
  1603 			for (unsigned int j=0; j<num; i++)
  1604 			{
  1605 				other = GetGroupImmunity(id, j);
  1606 				for (unsigned int k=0; k<pUser->grp_count; k++)
  1607 				{
  1608 					if (other == src_table[k])
  1609 					{
  1610 						return false;
  1611 					}
  1612 				}
  1613 			}
  1614 		}
  1615 	}
  1616 	
  1617 	return true;
  1618 }
  1619 
  1620 bool AdminCache::FindFlag(char c, AdminFlag *pAdmFlag)
  1621 {
  1622 	if (c < 'a' 
  1623 		|| c > 'z'
  1624 		|| !g_FlagSet[(unsigned)c - (unsigned)'a'])
  1625 	{
  1626 		return false;
  1627 	}
  1628 
  1629 	if (pAdmFlag)
  1630 	{
  1631 		*pAdmFlag = g_FlagLetters[(unsigned)c - (unsigned)'a'];
  1632 	}
  1633 
  1634 	return true;
  1635 }
  1636 
  1637 bool AdminCache::FindFlagChar(AdminFlag flag, char *c)
  1638 {
  1639 	if (!g_FlagSet[flag])
  1640 	{
  1641 		return false;
  1642 	}
  1643 
  1644 	if (c)
  1645 	{
  1646 		*c = g_ReverseFlags[flag];
  1647 	}
  1648 
  1649 	return true;
  1650 }
  1651 
  1652 FlagBits AdminCache::ReadFlagString(const char *flags, const char **end)
  1653 {
  1654 	FlagBits bits = 0;
  1655 
  1656 	while (flags && (*flags != '\0'))
  1657 	{
  1658 		AdminFlag flag;
  1659 		if (!FindFlag(*flags, &flag))
  1660 		{
  1661 			break;
  1662 		}
  1663 		bits |= FlagArrayToBits(&flag, 1);
  1664 		flags++;
  1665 	}
  1666 
  1667 	if (end)
  1668 	{
  1669 		*end = flags;
  1670 	}
  1671 
  1672 	return bits;
  1673 }
  1674 
  1675 unsigned int AdminCache::GetAdminSerialChange(AdminId id)
  1676 {
  1677 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1678 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1679 	{
  1680 		return 0;
  1681 	}
  1682 
  1683 	return pUser->serialchange;
  1684 }
  1685 
  1686 bool AdminCache::CanAdminUseCommand(int client, const char *cmd)
  1687 {
  1688 	FlagBits bits;
  1689 	OverrideType otype = Override_Command;
  1690 
  1691 	if (cmd[0] == '@')
  1692 	{
  1693 		otype = Override_CommandGroup;
  1694 		cmd++;
  1695 	}
  1696 
  1697 	if (!g_ConCmds.LookForCommandAdminFlags(cmd, &bits))
  1698 	{
  1699 		if (!GetCommandOverride(cmd, otype, &bits))
  1700 		{
  1701 			bits = 0;
  1702 		}
  1703 	}
  1704 
  1705 	return g_ConCmds.CheckCommandAccess(client, cmd, bits);
  1706 }
  1707 
  1708 unsigned int AdminCache::SetGroupImmunityLevel(GroupId gid, unsigned int level)
  1709 {
  1710 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(gid);
  1711 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
  1712 	{
  1713 		return 0;
  1714 	}
  1715 
  1716 	unsigned int old_level = pGroup->immunity_level;
  1717 
  1718 	pGroup->immunity_level = level;
  1719 
  1720 	return old_level;
  1721 }
  1722 
  1723 unsigned int AdminCache::GetGroupImmunityLevel(GroupId gid)
  1724 {
  1725 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(gid);
  1726 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
  1727 	{
  1728 		return 0;
  1729 	}
  1730 
  1731 	return pGroup->immunity_level;
  1732 }
  1733 
  1734 unsigned int AdminCache::SetAdminImmunityLevel(AdminId id, unsigned int level)
  1735 {
  1736 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1737 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1738 	{
  1739 		return 0;
  1740 	}
  1741 
  1742 	unsigned int old_level = pUser->immunity_level;
  1743 
  1744 	pUser->immunity_level = level;
  1745 
  1746 	return old_level;
  1747 }
  1748 
  1749 unsigned int AdminCache::GetAdminImmunityLevel(AdminId id)
  1750 {
  1751 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1752 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1753 	{
  1754 		return 0;
  1755 	}
  1756 
  1757 	return pUser->immunity_level;
  1758 }
  1759 
  1760 bool AdminCache::CheckAccess(int client, const char *cmd, FlagBits flags, bool override_only)
  1761 {
  1762 	if (client == 0)
  1763 	{
  1764 		return true;
  1765 	}
  1766 
  1767 	/* Auto-detect a command if we can */
  1768 	FlagBits bits = flags;
  1769 	bool found_command = false;
  1770 	if (!override_only)
  1771 	{
  1772 		found_command = g_ConCmds.LookForCommandAdminFlags(cmd, &bits);
  1773 	}
  1774 
  1775 	if (!found_command)
  1776 	{
  1777 		GetCommandOverride(cmd, Override_Command, &bits);
  1778 	}
  1779 
  1780 	return g_ConCmds.CheckCommandAccess(client, cmd, bits) ? 1 : 0;
  1781 }
  1782 
  1783 void iterator_glob_basic_override(Trie *pTrie, const char *key, void **value, void *data)
  1784 {
  1785 	FILE *fp;
  1786 	int flags;
  1787 	char flagstr[64];
  1788 
  1789 	fp = (FILE *)data;
  1790 	flags = (int)*value;
  1791 	g_Admins.FillFlagString(flags, flagstr, sizeof(flagstr));
  1792 
  1793 	fprintf(fp, "\t\"%s\"\t\t\"%s\"\n", key, flagstr);
  1794 }
  1795 
  1796 void iterator_glob_grp_override(Trie *pTrie, const char *key, void **value, void *data)
  1797 {
  1798 	FILE *fp;
  1799 	int flags;
  1800 	char flagstr[64];
  1801 
  1802 	fp = (FILE *)data;
  1803 	flags = (int)*value;
  1804 	g_Admins.FillFlagString(flags, flagstr, sizeof(flagstr));
  1805 
  1806 	fprintf(fp, "\t\"@%s\"\t\t\"%s\"\n", key, flagstr);
  1807 }
  1808 
  1809 void iterator_group_basic_override(Trie *pTrie, const char *key, void **value, void *data)
  1810 {
  1811 	FILE *fp;
  1812 	int flags;
  1813 	char flagstr[64];
  1814 
  1815 	fp = (FILE *)data;
  1816 	flags = (int)*value;
  1817 	g_Admins.FillFlagString(flags, flagstr, sizeof(flagstr));
  1818 
  1819 	fprintf(fp, "\t\t\t\"%s\"\t\t\"%s\"\n", key, flagstr);
  1820 }
  1821 
  1822 void iterator_group_grp_override(Trie *pTrie, const char *key, void **value, void *data)
  1823 {
  1824 	FILE *fp;
  1825 	int flags;
  1826 	char flagstr[64];
  1827 
  1828 	fp = (FILE *)data;
  1829 	flags = (int)*value;
  1830 	g_Admins.FillFlagString(flags, flagstr, sizeof(flagstr));
  1831 
  1832 	fprintf(fp, "\t\t\t\"@%s\"\t\t\"%s\"\n", key, flagstr);
  1833 }
  1834 
  1835 void AdminCache::DumpCache(FILE *fp)
  1836 {
  1837 	int *itable;
  1838 	AdminId aid;
  1839 	GroupId gid;
  1840 	char flagstr[64];
  1841 	unsigned int num;
  1842 	AdminUser *pAdmin;
  1843 	AdminGroup *pGroup;
  1844 	char name_buffer[512];
  1845 
  1846 	fprintf(fp, "\"Groups\"\n{\n");
  1847 	
  1848 	num = 0;
  1849 	gid = m_FirstGroup;
  1850 	while (gid != INVALID_GROUP_ID
  1851 			&& (pGroup = GetGroup(gid)) != NULL)
  1852 	{
  1853 		num++;
  1854 		FillFlagString(pGroup->addflags, flagstr, sizeof(flagstr));
  1855 
  1856 		fprintf(fp, "\t/* num = %d, gid = 0x%X */\n", num, gid);
  1857 		fprintf(fp, "\t\"%s\"\n\t{\n", GetString(pGroup->nameidx));
  1858 		fprintf(fp, "\t\t\"flags\"\t\t\t\"%s\"\n", flagstr);
  1859 		fprintf(fp, "\t\t\"immunity\"\t\t\"%d\"\n", pGroup->immunity_level);
  1860 		
  1861 		if (pGroup->immune_table != -1
  1862 			&& (itable = (int *)m_pMemory->GetAddress(pGroup->immune_table)) != NULL)
  1863 		{
  1864 			AdminGroup *pAltGroup;
  1865 			const char *gname, *mod;
  1866 
  1867 			for (int i = 1; i <= itable[0]; i++)
  1868 			{
  1869 				if ((pAltGroup = GetGroup(itable[i])) == NULL)
  1870 				{
  1871 					/* Assume the rest of the table is corrupt */
  1872 					break;
  1873 				}
  1874 				gname = GetString(pAltGroup->nameidx);
  1875 				if (atoi(gname) != 0)
  1876 				{
  1877 					mod = "@";
  1878 				}
  1879 				else
  1880 				{
  1881 					mod = "";
  1882 				}
  1883 				fprintf(fp, "\t\t\"immunity\"\t\t\"%s%s\"\n", mod, gname);
  1884 			}
  1885 		}
  1886 
  1887 		fprintf(fp, "\n\t\t\"Overrides\"\n\t\t{\n");
  1888 		if (pGroup->pCmdGrpTable != NULL)
  1889 		{
  1890 			sm_trie_bad_iterator(pGroup->pCmdGrpTable, 
  1891 				name_buffer,
  1892 				sizeof(name_buffer),
  1893 				iterator_group_grp_override,
  1894 				fp);
  1895 		}
  1896 		if (pGroup->pCmdTable != NULL)
  1897 		{
  1898 			sm_trie_bad_iterator(pGroup->pCmdTable, 
  1899 				name_buffer,
  1900 				sizeof(name_buffer),
  1901 				iterator_group_basic_override,
  1902 				fp);
  1903 		}
  1904 		fprintf(fp, "\t\t}\n");
  1905 
  1906 		fprintf(fp, "\t}\n");
  1907 
  1908 		if ((gid = pGroup->next_grp) != INVALID_GROUP_ID)
  1909 		{
  1910 			fprintf(fp, "\n");
  1911 		}
  1912 	}
  1913 
  1914 	fprintf(fp, "}\n\n");
  1915 	fprintf(fp, "\"Admins\"\n{\n");
  1916 
  1917 	num = 0;
  1918 	aid = m_FirstUser;
  1919 	while (aid != INVALID_ADMIN_ID
  1920 			&& (pAdmin = GetUser(aid)) != NULL)
  1921 	{
  1922 		num++;
  1923 		FillFlagString(pAdmin->flags, flagstr, sizeof(flagstr));
  1924 
  1925 		fprintf(fp, "\t/* num = %d, aid = 0x%X, serialno = 0x%X*/\n", num, aid, pAdmin->serialchange);
  1926 
  1927 		if (pAdmin->nameidx != -1)
  1928 		{
  1929 			fprintf(fp, "\t\"%s\"\n\t{\n", GetString(pAdmin->nameidx));
  1930 		}
  1931 		else
  1932 		{
  1933 			fprintf(fp, "\t\"\"\n\t{\n");
  1934 		}
  1935 
  1936 		if (pAdmin->auth.identidx != -1)
  1937 		{
  1938 			fprintf(fp, "\t\t\"auth\"\t\t\t\"%s\"\n", GetMethodName(pAdmin->auth.index));
  1939 			fprintf(fp, "\t\t\"identity\"\t\t\"%s\"\n", GetString(pAdmin->auth.identidx));
  1940 		}
  1941 		if (pAdmin->password != -1)
  1942 		{
  1943 			fprintf(fp, "\t\t\"password\"\t\t\"%s\"\n", GetString(pAdmin->password));
  1944 		}
  1945 		fprintf(fp, "\t\t\"flags\"\t\t\t\"%s\"\n", flagstr);
  1946 		fprintf(fp, "\t\t\"immunity\"\t\t\"%d\"\n", pAdmin->immunity_level);
  1947 
  1948 		if (pAdmin->grp_count != 0 
  1949 			&& pAdmin->grp_table != -1 
  1950 			&& (itable = (int *)m_pMemory->GetAddress(pAdmin->grp_table)) != NULL)
  1951 		{
  1952 			unsigned int i;
  1953 
  1954 			for (i = 0; i < pAdmin->grp_count; i++)
  1955 			{
  1956 				if ((pGroup = GetGroup(itable[i])) == NULL)
  1957 				{
  1958 					/* Assume the rest of the table is corrupt */
  1959 					break;
  1960 				}
  1961 				fprintf(fp, "\t\t\"group\"\t\t\t\"%s\"\n", GetString(pGroup->nameidx));
  1962 			}
  1963 		}
  1964 
  1965 		fprintf(fp, "\t}\n");
  1966 
  1967 		if ((aid = pAdmin->next_user) != INVALID_ADMIN_ID)
  1968 		{
  1969 			fprintf(fp, "\n");
  1970 		}
  1971 	}
  1972 
  1973 	fprintf(fp, "}\n\n");
  1974 
  1975 	fprintf(fp, "\"Overrides\"\n{\n");
  1976 	if (m_pCmdGrpOverrides != NULL)
  1977 	{
  1978 		sm_trie_bad_iterator(m_pCmdGrpOverrides,
  1979 			name_buffer,
  1980 			sizeof(name_buffer),
  1981 			iterator_glob_grp_override,
  1982 			fp);
  1983 	}
  1984 	if (m_pCmdOverrides != NULL)
  1985 	{
  1986 		sm_trie_bad_iterator(m_pCmdOverrides,
  1987 			name_buffer, 
  1988 			sizeof(name_buffer), 
  1989 			iterator_glob_basic_override, 
  1990 			fp);
  1991 	}
  1992 	fprintf(fp, "}\n");
  1993 }
  1994 
  1995 AdminGroup *AdminCache::GetGroup(GroupId gid)
  1996 {
  1997 	AdminGroup *pGroup;
  1998 	
  1999 	pGroup = (AdminGroup *)m_pMemory->GetAddress(gid);
  2000 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
  2001 	{
  2002 		return NULL;
  2003 	}
  2004 
  2005 	return pGroup;
  2006 }
  2007 
  2008 AdminUser *AdminCache::GetUser(AdminId aid)
  2009 {
  2010 	AdminUser *pAdmin;
  2011 
  2012 	pAdmin = (AdminUser *)m_pMemory->GetAddress(aid);
  2013 	if (!pAdmin || pAdmin->magic != USR_MAGIC_SET)
  2014 	{
  2015 		return NULL;
  2016 	}
  2017 
  2018 	return pAdmin;
  2019 }
  2020 
  2021 const char *AdminCache::GetMethodName(unsigned int index)
  2022 {
  2023 	List<AuthMethod>::iterator iter;
  2024 	for (iter=m_AuthMethods.begin();
  2025 		iter!=m_AuthMethods.end();
  2026 		iter++)
  2027 	{
  2028 		if (index-- == 0)
  2029 		{
  2030 			return (*iter).name.c_str();
  2031 		}
  2032 	}
  2033 
  2034 	return NULL;
  2035 }
  2036 
  2037 const char *AdminCache::GetString(int idx)
  2038 {
  2039 	return m_pStrings->GetString(idx);
  2040 }
  2041 
  2042 size_t AdminCache::FillFlagString(FlagBits bits, char *buffer, size_t maxlen)
  2043 {
  2044 	size_t pos;
  2045 	unsigned int i, num_flags;
  2046 	AdminFlag flags[AdminFlags_TOTAL];
  2047 
  2048 	num_flags = FlagBitsToArray(bits, flags, AdminFlags_TOTAL);
  2049 
  2050 	pos = 0;
  2051 	for (i = 0; pos < maxlen && i < num_flags; i++)
  2052 	{
  2053 		if (FindFlagChar(flags[i], &buffer[pos]))
  2054 		{
  2055 			pos++;
  2056 		}
  2057 	}
  2058 	buffer[pos] = '\0';
  2059 
  2060 	return pos;
  2061 }
  2062 
  2063 CON_COMMAND(sm_dump_admcache, "Dumps the admin cache for debugging")
  2064 {
  2065 	FILE *fp;
  2066 	char buffer[PLATFORM_MAX_PATH];
  2067 
  2068 	g_SourceMod.BuildPath(Path_SM, buffer, sizeof(buffer), "data/admin_cache_dump.txt");
  2069 
  2070 	if ((fp = fopen(buffer, "wt")) == NULL)
  2071 	{
  2072 		g_RootMenu.ConsolePrint("Could not open file for writing: %s", buffer);
  2073 		return;
  2074 	}
  2075 
  2076 	g_Admins.DumpCache(fp);
  2077 
  2078 	g_RootMenu.ConsolePrint("Admin cache dumped to: %s", buffer);
  2079 
  2080 	fclose(fp);
  2081 }