core/AdminCache.cpp
author Kyle Sanderson <kyle.leet@gmail.com>
Wed Jul 31 22:03:59 2013 -0400 (2013-07-31)
changeset 3963 39fc1ce67130
parent 3872 4c93f74cae51
child 4077 01e607ae37f0
permissions -rw-r--r--
Fixed crash in SDKHooks when throwing bad ent type error on logical ent (r=psychonic).
     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 "ForwardSys.h"
    37 #include "PlayerManager.h"
    38 #include "ConCmdManager.h"
    39 #include "Logger.h"
    40 #include "sourcemod.h"
    41 #include "sm_stringutil.h"
    42 #include "sourcemm_api.h"
    43 #include "sm_srvcmds.h"
    44 #include "logic_bridge.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 	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 	if (ident[0] == '\0')
  1174 	{
  1175 		return false;
  1176 	}
  1177 
  1178 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1179 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1180 	{
  1181 		return false;
  1182 	}
  1183 
  1184 	Trie *pTable;
  1185 	if (!sm_trie_retrieve(m_pAuthTables, auth, (void **)&pTable))
  1186 	{
  1187 		return false;
  1188 	}
  1189 
  1190 	/* If the id was a steam id strip off the STEAM_*: part */
  1191 	if (strcmp(auth, "steam") == 0 && strncmp(ident, "STEAM_", 6) == 0)
  1192 	{
  1193 		ident += 8;
  1194 	}
  1195 
  1196 	if (sm_trie_retrieve(pTable, ident, NULL))
  1197 	{
  1198 		return false;
  1199 	}
  1200 
  1201 	int i_ident = m_pStrings->AddString(ident);
  1202 
  1203 	pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1204 	pUser->auth.identidx = i_ident;
  1205 	GetMethodIndex(auth, &pUser->auth.index);
  1206 
  1207 	return sm_trie_insert(pTable, ident, (void **)id);
  1208 }
  1209 
  1210 AdminId AdminCache::FindAdminByIdentity(const char *auth, const char *identity)
  1211 {
  1212 	Trie *pTable;
  1213 	if (!sm_trie_retrieve(m_pAuthTables, auth, (void **)&pTable))
  1214 	{
  1215 		return INVALID_ADMIN_ID;
  1216 	}
  1217 
  1218 	/* If the id was a steam id strip off the STEAM_*: part */
  1219 	if (strcmp(auth, "steam") == 0 && strncmp(identity, "STEAM_", 6) == 0)
  1220 	{
  1221 		identity += 8;
  1222 	}
  1223 
  1224 	void *object;
  1225 	if (!sm_trie_retrieve(pTable, identity, &object))
  1226 	{
  1227 		return INVALID_ADMIN_ID;
  1228 	}
  1229 
  1230 	return (AdminId)object;
  1231 }
  1232 
  1233 void AdminCache::SetAdminFlag(AdminId id, AdminFlag flag, bool enabled)
  1234 {
  1235 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1236 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1237 	{
  1238 		return;
  1239 	}
  1240 
  1241 	if (flag < Admin_Reservation
  1242 		|| flag >= AdminFlags_TOTAL)
  1243 	{
  1244 		return;
  1245 	}
  1246 	
  1247 	FlagBits bits = (1<<(FlagBits)flag);
  1248 
  1249 	if (enabled)
  1250 	{
  1251 		pUser->flags |= bits;
  1252 		pUser->eflags |= bits;
  1253 	} else {
  1254 		pUser->flags &= ~bits;
  1255 		pUser->eflags &= ~bits;
  1256 	}
  1257 
  1258 	pUser->serialchange++;
  1259 }
  1260 
  1261 bool AdminCache::GetAdminFlag(AdminId id, AdminFlag flag, AccessMode mode)
  1262 {
  1263 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1264 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1265 	{
  1266 		return false;
  1267 	}
  1268 
  1269 	if (flag < Admin_Reservation
  1270 		|| flag >= AdminFlags_TOTAL)
  1271 	{
  1272 		return false;
  1273 	}
  1274 
  1275 	FlagBits bit = (1<<(FlagBits)flag);
  1276 
  1277 	if (mode == Access_Real)
  1278 	{
  1279 		return ((pUser->flags & bit) == bit);
  1280 	} else if (mode == Access_Effective) {
  1281 		bool has_bit = ((pUser->eflags & bit) == bit);
  1282 		if (!has_bit && flag != Admin_Root && ((pUser->eflags & ADMFLAG_ROOT) == ADMFLAG_ROOT))
  1283 		{
  1284 			has_bit = true;
  1285 		}
  1286 		return has_bit;
  1287 	}
  1288 
  1289 	return false;
  1290 }
  1291 
  1292 FlagBits AdminCache::GetAdminFlags(AdminId id, AccessMode mode)
  1293 {
  1294 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1295 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1296 	{
  1297 		return 0;
  1298 	}
  1299 
  1300 	if (mode == Access_Real)
  1301 	{
  1302 		return pUser->flags;
  1303 	} else if (mode == Access_Effective) {
  1304 		return pUser->eflags;
  1305 	}
  1306 
  1307 	return 0;
  1308 }
  1309 
  1310 void AdminCache::SetAdminFlags(AdminId id, AccessMode mode, FlagBits bits)
  1311 {
  1312 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1313 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1314 	{
  1315 		return;
  1316 	}
  1317 
  1318 	if (mode == Access_Real)
  1319 	{
  1320 		pUser->flags = bits;
  1321 		pUser->eflags = bits;
  1322 	} else if (mode == Access_Effective) {
  1323 		pUser->eflags = bits;
  1324 	}
  1325 
  1326 	pUser->serialchange++;
  1327 }
  1328 
  1329 bool AdminCache::AdminInheritGroup(AdminId id, GroupId gid)
  1330 {
  1331 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1332 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1333 	{
  1334 		return false;
  1335 	}
  1336 
  1337 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(gid);
  1338 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
  1339 	{
  1340 		return false;
  1341 	}
  1342 
  1343 	/* First check for duplicates */
  1344 	if (pUser->grp_count != 0)
  1345 	{
  1346 		int *temp_table = (int *)m_pMemory->GetAddress(pUser->grp_table);
  1347 		for (unsigned int i=0; i<pUser->grp_count; i++)
  1348 		{
  1349 			if (temp_table[i] == gid)
  1350 			{
  1351 				return false;
  1352 			}
  1353 		}
  1354 	}
  1355 
  1356 	int *table;
  1357 	if (pUser->grp_count + 1 > pUser->grp_size)
  1358 	{
  1359 		int new_size = 0;
  1360 		int tblidx;
  1361 		
  1362 		if (pUser->grp_size == 0)
  1363 		{
  1364 			new_size = 2;
  1365 		} else {
  1366 			new_size = pUser->grp_size * 2;
  1367 		}
  1368 		
  1369 		/* Create and refresh pointers */
  1370 		tblidx = m_pMemory->CreateMem(new_size * sizeof(int), (void **)&table);
  1371 		pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1372 		pGroup = (AdminGroup *)m_pMemory->GetAddress(gid);
  1373 
  1374 		/* Copy old data if necessary */
  1375 		if (pUser->grp_table != -1)
  1376 		{
  1377 			int *old_table = (int *)m_pMemory->GetAddress(pUser->grp_table);
  1378 			memcpy(table, old_table, sizeof(int) * pUser->grp_count);
  1379 		}
  1380 		pUser->grp_table = tblidx;
  1381 		pUser->grp_size = new_size;
  1382 	} else {
  1383 		table = (int *)m_pMemory->GetAddress(pUser->grp_table);
  1384 	}
  1385 
  1386 	table[pUser->grp_count] = gid;
  1387 	pUser->grp_count++;
  1388 
  1389 	/* Compute new effective permissions */
  1390 	pUser->eflags |= pGroup->addflags;
  1391 
  1392 	if (pGroup->immunity_level > pUser->immunity_level)
  1393 	{
  1394 		pUser->immunity_level = pGroup->immunity_level;
  1395 	}
  1396 
  1397 	pUser->serialchange++;
  1398 
  1399 	return true;
  1400 }
  1401 
  1402 bool AdminCache::IsValidAdmin(AdminId id)
  1403 {
  1404 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1405 	return (pUser != NULL && pUser->magic == USR_MAGIC_SET);
  1406 }
  1407 
  1408 unsigned int AdminCache::GetAdminGroupCount(AdminId id)
  1409 {
  1410 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1411 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1412 	{
  1413 		return 0;
  1414 	}
  1415 
  1416 	return pUser->grp_count;
  1417 }
  1418 
  1419 GroupId AdminCache::GetAdminGroup(AdminId id, unsigned int index, const char **name)
  1420 {
  1421 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1422 	if (!pUser || pUser->magic != USR_MAGIC_SET || index >= pUser->grp_count)
  1423 	{
  1424 		return INVALID_GROUP_ID;
  1425 	}
  1426 
  1427 	int *table = (int *)m_pMemory->GetAddress(pUser->grp_table);
  1428 
  1429 	GroupId gid = table[index];
  1430 
  1431 	if (name)
  1432 	{
  1433 		*name = GetGroupName(gid);
  1434 	}
  1435 
  1436 	return gid;
  1437 }
  1438 
  1439 const char *AdminCache::GetAdminPassword(AdminId id)
  1440 {
  1441 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1442 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1443 	{
  1444 		return NULL;
  1445 	}
  1446 
  1447 	return m_pStrings->GetString(pUser->password);
  1448 }
  1449 
  1450 void AdminCache::SetAdminPassword(AdminId id, const char *password)
  1451 {	
  1452 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1453 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1454 	{
  1455 		return;
  1456 	}
  1457 
  1458 	if (password[0] == '\0')
  1459 	{
  1460 		pUser->password = -1;
  1461 		return;
  1462 	}
  1463 
  1464 	int i_password = m_pStrings->AddString(password);
  1465 	pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1466 	pUser->password = i_password;
  1467 }
  1468 
  1469 unsigned int AdminCache::FlagBitsToBitArray(FlagBits bits, bool array[], unsigned int maxSize)
  1470 {
  1471 	unsigned int i;
  1472 	for (i=0; i<maxSize && i<AdminFlags_TOTAL; i++)
  1473 	{
  1474 		array[i] = ((bits & (1<<i)) == (unsigned)(1<<i));
  1475 	}
  1476 
  1477 	return i;
  1478 }
  1479 
  1480 FlagBits AdminCache::FlagBitArrayToBits(const bool array[], unsigned int maxSize)
  1481 {
  1482 	FlagBits bits = 0;
  1483 	for (unsigned int i=0; i<maxSize && i<AdminFlags_TOTAL; i++)
  1484 	{
  1485 		if (array[i])
  1486 		{
  1487 			bits |= (1<<i);
  1488 		}
  1489 	}
  1490 
  1491 	return bits;
  1492 }
  1493 
  1494 FlagBits AdminCache::FlagArrayToBits(const AdminFlag array[], unsigned int numFlags)
  1495 {
  1496 	FlagBits bits = 0;
  1497 	for (unsigned int i=0; i<numFlags && i<AdminFlags_TOTAL; i++)
  1498 	{
  1499 		bits |= (1 << (FlagBits)array[i]);
  1500 	}
  1501 
  1502 	return bits;
  1503 }
  1504 
  1505 unsigned int AdminCache::FlagBitsToArray(FlagBits bits, AdminFlag array[], unsigned int maxSize)
  1506 {
  1507 	unsigned int i, num=0;
  1508 	for (i=0; num<maxSize && i<AdminFlags_TOTAL; i++)
  1509 	{
  1510 		if ((bits & (1<<i)) == (unsigned)(1<<i))
  1511 		{
  1512 			array[num++] = (AdminFlag)i;
  1513 		}
  1514 	}
  1515 
  1516 	return num;
  1517 }
  1518 
  1519 bool AdminCache::CheckAdminFlags(AdminId id, FlagBits bits)
  1520 {
  1521 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1522 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1523 	{
  1524 		return false;
  1525 	}
  1526 
  1527 	return ((pUser->eflags & bits) == bits);
  1528 }
  1529 
  1530 bool AdminCache::CanAdminTarget(AdminId id, AdminId target)
  1531 {
  1532 	/** 
  1533 	 * Zeroth, if the targeting AdminId is INVALID_ADMIN_ID, targeting fails.
  1534 	 * First, if the targeted AdminId is INVALID_ADMIN_ID, targeting succeeds.
  1535 	 */
  1536 
  1537 	if (id == INVALID_ADMIN_ID)
  1538 	{
  1539 		return false;
  1540 	}
  1541 
  1542 	if (target == INVALID_ADMIN_ID)
  1543 	{
  1544 		return true;
  1545 	}
  1546 
  1547 	if (id == target)
  1548 	{
  1549 		return true;
  1550 	}
  1551 
  1552 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1553 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1554 	{
  1555 		return false;
  1556 	}
  1557 
  1558 	AdminUser *pTarget = (AdminUser *)m_pMemory->GetAddress(target);
  1559 	if (!pTarget || pTarget->magic != USR_MAGIC_SET)
  1560 	{
  1561 		return false;
  1562 	}
  1563 
  1564 	/** 
  1565 	 * Second, if the targeting admin is root, targeting succeeds.
  1566 	 */
  1567 	if (pUser->eflags & ADMFLAG_ROOT)
  1568 	{
  1569 		return true;
  1570 	}
  1571 
  1572 	/** Fourth, if the targeted admin is immune from targeting admin. */
  1573 	int mode = sm_immunity_mode.GetInt();
  1574 	switch (mode)
  1575 	{
  1576 		case 1:
  1577 		{
  1578 			if (pTarget->immunity_level > pUser->immunity_level)
  1579 			{
  1580 				return false;
  1581 			}
  1582 			break;
  1583 		}
  1584 		case 3:
  1585 		{
  1586 			/* If neither has any immunity, let this pass. */
  1587 			if (!pUser->immunity_level && !pTarget->immunity_level)
  1588 			{
  1589 				return true;
  1590 			}
  1591 			/* Don't break, go to the next case. */
  1592 		}
  1593 		case 2:
  1594 		{
  1595 			if (pTarget->immunity_level >= pUser->immunity_level)
  1596 			{
  1597 				return false;
  1598 			}
  1599 			break;
  1600 		}
  1601 	}
  1602 
  1603 	/** 
  1604 	 * Fifth, if the targeted admin has specific immunity from the
  1605 	 *  targeting admin via group immunities, targeting fails.
  1606 	 */
  1607 	//:TODO: speed this up... maybe with trie hacks.
  1608 	//idea is to insert %d.%d in the trie after computing this and use it as a cache lookup.
  1609 	//problem is the trie cannot delete prefixes, so we'd have a problem with invalidations.
  1610 	if (pTarget->grp_count > 0 && pUser->grp_count > 0)
  1611 	{
  1612 		int *grp_table = (int *)m_pMemory->GetAddress(pTarget->grp_table);
  1613 		int *src_table = (int *)m_pMemory->GetAddress(pUser->grp_table);
  1614 		GroupId id, other;
  1615 		unsigned int num;
  1616 		for (unsigned int i=0; i<pTarget->grp_count; i++)
  1617 		{
  1618 			id = grp_table[i];
  1619 			num = GetGroupImmunityCount(id);
  1620 			for (unsigned int j=0; j<num; i++)
  1621 			{
  1622 				other = GetGroupImmunity(id, j);
  1623 				for (unsigned int k=0; k<pUser->grp_count; k++)
  1624 				{
  1625 					if (other == src_table[k])
  1626 					{
  1627 						return false;
  1628 					}
  1629 				}
  1630 			}
  1631 		}
  1632 	}
  1633 	
  1634 	return true;
  1635 }
  1636 
  1637 bool AdminCache::FindFlag(char c, AdminFlag *pAdmFlag)
  1638 {
  1639 	if (c < 'a' 
  1640 		|| c > 'z'
  1641 		|| !g_FlagSet[(unsigned)c - (unsigned)'a'])
  1642 	{
  1643 		return false;
  1644 	}
  1645 
  1646 	if (pAdmFlag)
  1647 	{
  1648 		*pAdmFlag = g_FlagLetters[(unsigned)c - (unsigned)'a'];
  1649 	}
  1650 
  1651 	return true;
  1652 }
  1653 
  1654 bool AdminCache::FindFlagChar(AdminFlag flag, char *c)
  1655 {
  1656 	if (!g_FlagSet[flag])
  1657 	{
  1658 		return false;
  1659 	}
  1660 
  1661 	if (c)
  1662 	{
  1663 		*c = g_ReverseFlags[flag];
  1664 	}
  1665 
  1666 	return true;
  1667 }
  1668 
  1669 FlagBits AdminCache::ReadFlagString(const char *flags, const char **end)
  1670 {
  1671 	FlagBits bits = 0;
  1672 
  1673 	while (flags && (*flags != '\0'))
  1674 	{
  1675 		AdminFlag flag;
  1676 		if (!FindFlag(*flags, &flag))
  1677 		{
  1678 			break;
  1679 		}
  1680 		bits |= FlagArrayToBits(&flag, 1);
  1681 		flags++;
  1682 	}
  1683 
  1684 	if (end)
  1685 	{
  1686 		*end = flags;
  1687 	}
  1688 
  1689 	return bits;
  1690 }
  1691 
  1692 unsigned int AdminCache::GetAdminSerialChange(AdminId id)
  1693 {
  1694 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1695 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1696 	{
  1697 		return 0;
  1698 	}
  1699 
  1700 	return pUser->serialchange;
  1701 }
  1702 
  1703 bool AdminCache::CanAdminUseCommand(int client, const char *cmd)
  1704 {
  1705 	FlagBits bits;
  1706 	OverrideType otype = Override_Command;
  1707 
  1708 	if (cmd[0] == '@')
  1709 	{
  1710 		otype = Override_CommandGroup;
  1711 		cmd++;
  1712 	}
  1713 
  1714 	if (!g_ConCmds.LookForCommandAdminFlags(cmd, &bits))
  1715 	{
  1716 		if (!GetCommandOverride(cmd, otype, &bits))
  1717 		{
  1718 			bits = 0;
  1719 		}
  1720 	}
  1721 
  1722 	return g_ConCmds.CheckClientCommandAccess(client, cmd, bits);
  1723 }
  1724 
  1725 unsigned int AdminCache::SetGroupImmunityLevel(GroupId gid, unsigned int level)
  1726 {
  1727 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(gid);
  1728 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
  1729 	{
  1730 		return 0;
  1731 	}
  1732 
  1733 	unsigned int old_level = pGroup->immunity_level;
  1734 
  1735 	pGroup->immunity_level = level;
  1736 
  1737 	return old_level;
  1738 }
  1739 
  1740 unsigned int AdminCache::GetGroupImmunityLevel(GroupId gid)
  1741 {
  1742 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(gid);
  1743 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
  1744 	{
  1745 		return 0;
  1746 	}
  1747 
  1748 	return pGroup->immunity_level;
  1749 }
  1750 
  1751 unsigned int AdminCache::SetAdminImmunityLevel(AdminId id, unsigned int level)
  1752 {
  1753 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1754 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1755 	{
  1756 		return 0;
  1757 	}
  1758 
  1759 	unsigned int old_level = pUser->immunity_level;
  1760 
  1761 	pUser->immunity_level = level;
  1762 
  1763 	return old_level;
  1764 }
  1765 
  1766 unsigned int AdminCache::GetAdminImmunityLevel(AdminId id)
  1767 {
  1768 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1769 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1770 	{
  1771 		return 0;
  1772 	}
  1773 
  1774 	return pUser->immunity_level;
  1775 }
  1776 
  1777 bool AdminCache::CheckAccess(int client, const char *cmd, FlagBits flags, bool override_only)
  1778 {
  1779 	if (client == 0)
  1780 	{
  1781 		return true;
  1782 	}
  1783 
  1784 	/* Auto-detect a command if we can */
  1785 	FlagBits bits = flags;
  1786 	bool found_command = false;
  1787 	if (!override_only)
  1788 	{
  1789 		found_command = g_ConCmds.LookForCommandAdminFlags(cmd, &bits);
  1790 	}
  1791 
  1792 	if (!found_command)
  1793 	{
  1794 		GetCommandOverride(cmd, Override_Command, &bits);
  1795 	}
  1796 
  1797 	return g_ConCmds.CheckClientCommandAccess(client, cmd, bits) ? 1 : 0;
  1798 }
  1799 
  1800 void iterator_glob_basic_override(Trie *pTrie, const char *key, void **value, void *data)
  1801 {
  1802 	FILE *fp;
  1803 	int flags;
  1804 	char flagstr[64];
  1805 
  1806 	fp = (FILE *)data;
  1807 	flags = (int)*value;
  1808 	g_Admins.FillFlagString(flags, flagstr, sizeof(flagstr));
  1809 
  1810 	fprintf(fp, "\t\"%s\"\t\t\"%s\"\n", key, flagstr);
  1811 }
  1812 
  1813 void iterator_glob_grp_override(Trie *pTrie, const char *key, void **value, void *data)
  1814 {
  1815 	FILE *fp;
  1816 	int flags;
  1817 	char flagstr[64];
  1818 
  1819 	fp = (FILE *)data;
  1820 	flags = (int)*value;
  1821 	g_Admins.FillFlagString(flags, flagstr, sizeof(flagstr));
  1822 
  1823 	fprintf(fp, "\t\"@%s\"\t\t\"%s\"\n", key, flagstr);
  1824 }
  1825 
  1826 void iterator_group_basic_override(Trie *pTrie, const char *key, void **value, void *data)
  1827 {
  1828 	FILE *fp;
  1829 	int flags;
  1830 	char flagstr[64];
  1831 
  1832 	fp = (FILE *)data;
  1833 	flags = (int)*value;
  1834 	g_Admins.FillFlagString(flags, flagstr, sizeof(flagstr));
  1835 
  1836 	fprintf(fp, "\t\t\t\"%s\"\t\t\"%s\"\n", key, flagstr);
  1837 }
  1838 
  1839 void iterator_group_grp_override(Trie *pTrie, const char *key, void **value, void *data)
  1840 {
  1841 	FILE *fp;
  1842 	int flags;
  1843 	char flagstr[64];
  1844 
  1845 	fp = (FILE *)data;
  1846 	flags = (int)*value;
  1847 	g_Admins.FillFlagString(flags, flagstr, sizeof(flagstr));
  1848 
  1849 	fprintf(fp, "\t\t\t\"@%s\"\t\t\"%s\"\n", key, flagstr);
  1850 }
  1851 
  1852 void AdminCache::DumpCache(FILE *fp)
  1853 {
  1854 	int *itable;
  1855 	AdminId aid;
  1856 	GroupId gid;
  1857 	char flagstr[64];
  1858 	unsigned int num;
  1859 	AdminUser *pAdmin;
  1860 	AdminGroup *pGroup;
  1861 	char name_buffer[512];
  1862 
  1863 	fprintf(fp, "\"Groups\"\n{\n");
  1864 	
  1865 	num = 0;
  1866 	gid = m_FirstGroup;
  1867 	while (gid != INVALID_GROUP_ID
  1868 			&& (pGroup = GetGroup(gid)) != NULL)
  1869 	{
  1870 		num++;
  1871 		FillFlagString(pGroup->addflags, flagstr, sizeof(flagstr));
  1872 
  1873 		fprintf(fp, "\t/* num = %d, gid = 0x%X */\n", num, gid);
  1874 		fprintf(fp, "\t\"%s\"\n\t{\n", GetString(pGroup->nameidx));
  1875 		fprintf(fp, "\t\t\"flags\"\t\t\t\"%s\"\n", flagstr);
  1876 		fprintf(fp, "\t\t\"immunity\"\t\t\"%d\"\n", pGroup->immunity_level);
  1877 		
  1878 		if (pGroup->immune_table != -1
  1879 			&& (itable = (int *)m_pMemory->GetAddress(pGroup->immune_table)) != NULL)
  1880 		{
  1881 			AdminGroup *pAltGroup;
  1882 			const char *gname, *mod;
  1883 
  1884 			for (int i = 1; i <= itable[0]; i++)
  1885 			{
  1886 				if ((pAltGroup = GetGroup(itable[i])) == NULL)
  1887 				{
  1888 					/* Assume the rest of the table is corrupt */
  1889 					break;
  1890 				}
  1891 				gname = GetString(pAltGroup->nameidx);
  1892 				if (atoi(gname) != 0)
  1893 				{
  1894 					mod = "@";
  1895 				}
  1896 				else
  1897 				{
  1898 					mod = "";
  1899 				}
  1900 				fprintf(fp, "\t\t\"immunity\"\t\t\"%s%s\"\n", mod, gname);
  1901 			}
  1902 		}
  1903 
  1904 		fprintf(fp, "\n\t\t\"Overrides\"\n\t\t{\n");
  1905 		if (pGroup->pCmdGrpTable != NULL)
  1906 		{
  1907 			sm_trie_bad_iterator(pGroup->pCmdGrpTable, 
  1908 				name_buffer,
  1909 				sizeof(name_buffer),
  1910 				iterator_group_grp_override,
  1911 				fp);
  1912 		}
  1913 		if (pGroup->pCmdTable != NULL)
  1914 		{
  1915 			sm_trie_bad_iterator(pGroup->pCmdTable, 
  1916 				name_buffer,
  1917 				sizeof(name_buffer),
  1918 				iterator_group_basic_override,
  1919 				fp);
  1920 		}
  1921 		fprintf(fp, "\t\t}\n");
  1922 
  1923 		fprintf(fp, "\t}\n");
  1924 
  1925 		if ((gid = pGroup->next_grp) != INVALID_GROUP_ID)
  1926 		{
  1927 			fprintf(fp, "\n");
  1928 		}
  1929 	}
  1930 
  1931 	fprintf(fp, "}\n\n");
  1932 	fprintf(fp, "\"Admins\"\n{\n");
  1933 
  1934 	num = 0;
  1935 	aid = m_FirstUser;
  1936 	while (aid != INVALID_ADMIN_ID
  1937 			&& (pAdmin = GetUser(aid)) != NULL)
  1938 	{
  1939 		num++;
  1940 		FillFlagString(pAdmin->flags, flagstr, sizeof(flagstr));
  1941 
  1942 		fprintf(fp, "\t/* num = %d, aid = 0x%X, serialno = 0x%X*/\n", num, aid, pAdmin->serialchange);
  1943 
  1944 		if (pAdmin->nameidx != -1)
  1945 		{
  1946 			fprintf(fp, "\t\"%s\"\n\t{\n", GetString(pAdmin->nameidx));
  1947 		}
  1948 		else
  1949 		{
  1950 			fprintf(fp, "\t\"\"\n\t{\n");
  1951 		}
  1952 
  1953 		if (pAdmin->auth.identidx != -1)
  1954 		{
  1955 			fprintf(fp, "\t\t\"auth\"\t\t\t\"%s\"\n", GetMethodName(pAdmin->auth.index));
  1956 			fprintf(fp, "\t\t\"identity\"\t\t\"%s\"\n", GetString(pAdmin->auth.identidx));
  1957 		}
  1958 		if (pAdmin->password != -1)
  1959 		{
  1960 			fprintf(fp, "\t\t\"password\"\t\t\"%s\"\n", GetString(pAdmin->password));
  1961 		}
  1962 		fprintf(fp, "\t\t\"flags\"\t\t\t\"%s\"\n", flagstr);
  1963 		fprintf(fp, "\t\t\"immunity\"\t\t\"%d\"\n", pAdmin->immunity_level);
  1964 
  1965 		if (pAdmin->grp_count != 0 
  1966 			&& pAdmin->grp_table != -1 
  1967 			&& (itable = (int *)m_pMemory->GetAddress(pAdmin->grp_table)) != NULL)
  1968 		{
  1969 			unsigned int i;
  1970 
  1971 			for (i = 0; i < pAdmin->grp_count; i++)
  1972 			{
  1973 				if ((pGroup = GetGroup(itable[i])) == NULL)
  1974 				{
  1975 					/* Assume the rest of the table is corrupt */
  1976 					break;
  1977 				}
  1978 				fprintf(fp, "\t\t\"group\"\t\t\t\"%s\"\n", GetString(pGroup->nameidx));
  1979 			}
  1980 		}
  1981 
  1982 		fprintf(fp, "\t}\n");
  1983 
  1984 		if ((aid = pAdmin->next_user) != INVALID_ADMIN_ID)
  1985 		{
  1986 			fprintf(fp, "\n");
  1987 		}
  1988 	}
  1989 
  1990 	fprintf(fp, "}\n\n");
  1991 
  1992 	fprintf(fp, "\"Overrides\"\n{\n");
  1993 	if (m_pCmdGrpOverrides != NULL)
  1994 	{
  1995 		sm_trie_bad_iterator(m_pCmdGrpOverrides,
  1996 			name_buffer,
  1997 			sizeof(name_buffer),
  1998 			iterator_glob_grp_override,
  1999 			fp);
  2000 	}
  2001 	if (m_pCmdOverrides != NULL)
  2002 	{
  2003 		sm_trie_bad_iterator(m_pCmdOverrides,
  2004 			name_buffer, 
  2005 			sizeof(name_buffer), 
  2006 			iterator_glob_basic_override, 
  2007 			fp);
  2008 	}
  2009 	fprintf(fp, "}\n");
  2010 }
  2011 
  2012 AdminGroup *AdminCache::GetGroup(GroupId gid)
  2013 {
  2014 	AdminGroup *pGroup;
  2015 	
  2016 	pGroup = (AdminGroup *)m_pMemory->GetAddress(gid);
  2017 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
  2018 	{
  2019 		return NULL;
  2020 	}
  2021 
  2022 	return pGroup;
  2023 }
  2024 
  2025 AdminUser *AdminCache::GetUser(AdminId aid)
  2026 {
  2027 	AdminUser *pAdmin;
  2028 
  2029 	pAdmin = (AdminUser *)m_pMemory->GetAddress(aid);
  2030 	if (!pAdmin || pAdmin->magic != USR_MAGIC_SET)
  2031 	{
  2032 		return NULL;
  2033 	}
  2034 
  2035 	return pAdmin;
  2036 }
  2037 
  2038 const char *AdminCache::GetMethodName(unsigned int index)
  2039 {
  2040 	List<AuthMethod>::iterator iter;
  2041 	for (iter=m_AuthMethods.begin();
  2042 		iter!=m_AuthMethods.end();
  2043 		iter++)
  2044 	{
  2045 		if (index-- == 0)
  2046 		{
  2047 			return (*iter).name.c_str();
  2048 		}
  2049 	}
  2050 
  2051 	return NULL;
  2052 }
  2053 
  2054 const char *AdminCache::GetString(int idx)
  2055 {
  2056 	return m_pStrings->GetString(idx);
  2057 }
  2058 
  2059 size_t AdminCache::FillFlagString(FlagBits bits, char *buffer, size_t maxlen)
  2060 {
  2061 	size_t pos;
  2062 	unsigned int i, num_flags;
  2063 	AdminFlag flags[AdminFlags_TOTAL];
  2064 
  2065 	num_flags = FlagBitsToArray(bits, flags, AdminFlags_TOTAL);
  2066 
  2067 	pos = 0;
  2068 	for (i = 0; pos < maxlen && i < num_flags; i++)
  2069 	{
  2070 		if (FindFlagChar(flags[i], &buffer[pos]))
  2071 		{
  2072 			pos++;
  2073 		}
  2074 	}
  2075 	buffer[pos] = '\0';
  2076 
  2077 	return pos;
  2078 }
  2079 
  2080 CON_COMMAND(sm_dump_admcache, "Dumps the admin cache for debugging")
  2081 {
  2082 	FILE *fp;
  2083 	char buffer[PLATFORM_MAX_PATH];
  2084 
  2085 	g_SourceMod.BuildPath(Path_SM, buffer, sizeof(buffer), "data/admin_cache_dump.txt");
  2086 
  2087 	if ((fp = fopen(buffer, "wt")) == NULL)
  2088 	{
  2089 		g_RootMenu.ConsolePrint("Could not open file for writing: %s", buffer);
  2090 		return;
  2091 	}
  2092 
  2093 	g_Admins.DumpCache(fp);
  2094 
  2095 	g_RootMenu.ConsolePrint("Admin cache dumped to: %s", buffer);
  2096 
  2097 	fclose(fp);
  2098 }