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