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