core/AdminCache.cpp
author David Anderson <dvander@alliedmods.net>
Sat Feb 17 09:02:22 2007 +0000 (2007-02-17)
changeset 516 0807e52b4e02
parent 514 4c02b38c26ed
child 520 7676d61cde2d
permissions -rw-r--r--
clientconnect now blocks properly when needed
removed cruft
     1 /**
     2  * ===============================================================
     3  * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
     4  * ===============================================================
     5  *
     6  * This file is not open source and may not be copied without explicit
     7  * written permission of AlliedModders LLC.  This file may not be redistributed 
     8  * in whole or significant part.
     9  * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
    10  *
    11  * Version: $Id$
    12  */
    13 
    14 #include <string.h>
    15 #include <assert.h>
    16 #include "AdminCache.h"
    17 #include "ShareSys.h"
    18 #include "ForwardSys.h"
    19 #include "CPlayerManager.h"
    20 #include "CConCmdManager.h"
    21 
    22 AdminCache g_Admins;
    23 
    24 AdminCache::AdminCache()
    25 {
    26 	m_pCmdOverrides = sm_trie_create();
    27 	m_pCmdGrpOverrides = sm_trie_create();
    28 	m_pStrings = new BaseStringTable(1024);
    29 	m_pMemory = m_pStrings->GetMemTable();
    30 	m_FreeGroupList = m_FirstGroup = m_LastGroup = INVALID_GROUP_ID;
    31 	m_FreeUserList = m_FirstUser = m_LastUser = INVALID_ADMIN_ID;
    32 	m_pGroups = sm_trie_create();
    33 	m_pCacheFwd = NULL;
    34 	m_FirstGroup = -1;
    35 	m_pAuthTables = sm_trie_create();
    36 	m_InvalidatingAdmins = false;
    37 }
    38 
    39 AdminCache::~AdminCache()
    40 {
    41 	DumpAdminCache(AdminCache_Overrides, false);
    42 	DumpAdminCache(AdminCache_Groups, false);
    43 
    44 	sm_trie_destroy(m_pCmdGrpOverrides);
    45 	sm_trie_destroy(m_pCmdOverrides);
    46 
    47 	if (m_pGroups)
    48 	{
    49 		sm_trie_destroy(m_pGroups);
    50 	}
    51 
    52 	List<AuthMethod>::iterator iter;
    53 	for (iter=m_AuthMethods.begin();
    54 		 iter!=m_AuthMethods.end();
    55 		 iter++)
    56 	{
    57 		sm_trie_destroy((*iter).table);
    58 	}
    59 
    60 	sm_trie_destroy(m_pAuthTables);
    61 
    62 	delete m_pStrings;
    63 }
    64 
    65 void AdminCache::OnSourceModStartup(bool late)
    66 {
    67 	RegisterAuthIdentType("steam");
    68 	RegisterAuthIdentType("name");
    69 	RegisterAuthIdentType("ip");
    70 }
    71 
    72 void AdminCache::OnSourceModAllInitialized()
    73 {
    74 	m_pCacheFwd = g_Forwards.CreateForward("OnRebuildAdminCache", ET_Ignore, 1, NULL, Param_Cell);
    75 	g_ShareSys.AddInterface(NULL, this);
    76 }
    77 
    78 void AdminCache::OnSourceModShutdown()
    79 {
    80 	g_Forwards.ReleaseForward(m_pCacheFwd);
    81 	m_pCacheFwd = NULL;
    82 }
    83 
    84 void AdminCache::AddCommandOverride(const char *cmd, OverrideType type, FlagBits flags)
    85 {
    86 	Trie *pTrie = NULL;
    87 	if (type == Override_Command)
    88 	{
    89 		pTrie = m_pCmdOverrides;
    90 	} else if (type == Override_CommandGroup) {
    91 		pTrie = m_pCmdGrpOverrides;
    92 	} else {
    93 		return;
    94 	}
    95 
    96 	sm_trie_insert(pTrie, cmd, (void *)(unsigned int)flags);
    97 
    98 	g_ConCmds.UpdateAdminCmdFlags(cmd, type, flags);
    99 }
   100 
   101 bool AdminCache::GetCommandOverride(const char *cmd, OverrideType type, FlagBits *pFlags)
   102 {
   103 	Trie *pTrie = NULL;
   104 
   105 	if (type == Override_Command)
   106 	{
   107 		pTrie = m_pCmdOverrides;
   108 	} else if (type == Override_CommandGroup) {
   109 		pTrie = m_pCmdGrpOverrides;
   110 	} else {
   111 		return false;
   112 	}
   113 
   114 	void *object;
   115 	if (sm_trie_retrieve(pTrie, cmd, &object))
   116 	{
   117 		if (pFlags)
   118 		{
   119 			*pFlags = (FlagBits)object;
   120 		}
   121 		return true;
   122 	}
   123 
   124 	return false;
   125 }
   126 
   127 void AdminCache::UnsetCommandOverride(const char *cmd, OverrideType type)
   128 {
   129 	if (type == Override_Command)
   130 	{
   131 		return _UnsetCommandOverride(cmd);
   132 	} else if (type == Override_CommandGroup) {
   133 		return _UnsetCommandGroupOverride(cmd);
   134 	}
   135 }
   136 
   137 void AdminCache::_UnsetCommandGroupOverride(const char *group)
   138 {
   139 	if (!m_pCmdGrpOverrides)
   140 	{
   141 		return;
   142 	}
   143 
   144 	sm_trie_delete(m_pCmdGrpOverrides, group);
   145 
   146 	g_ConCmds.UpdateAdminCmdFlags(group, Override_CommandGroup, 0);
   147 }
   148 
   149 void AdminCache::_UnsetCommandOverride(const char *cmd)
   150 {
   151 	if (!m_pCmdOverrides)
   152 	{
   153 		return;
   154 	}
   155 
   156 	sm_trie_delete(m_pCmdOverrides, cmd);
   157 
   158 	g_ConCmds.UpdateAdminCmdFlags(cmd, Override_Command, 0);
   159 }
   160 
   161 void AdminCache::DumpCommandOverrideCache(OverrideType type)
   162 {
   163 	if (type == Override_Command && m_pCmdOverrides)
   164 	{
   165 		sm_trie_clear(m_pCmdOverrides);
   166 	} else if (type == Override_CommandGroup && m_pCmdGrpOverrides) {
   167 		sm_trie_clear(m_pCmdGrpOverrides);
   168 	}
   169 }
   170 
   171 AdminId AdminCache::CreateAdmin(const char *name)
   172 {
   173 	AdminId id;
   174 	AdminUser *pUser;
   175 
   176 	if (m_FreeUserList != INVALID_ADMIN_ID)
   177 	{
   178 		pUser = (AdminUser *)m_pMemory->GetAddress(m_FreeUserList);
   179 		assert(pUser->magic == USR_MAGIC_UNSET);
   180 		id = m_FreeUserList;
   181 		m_FreeUserList = pUser->next_user;
   182 	} else {
   183 		id = m_pMemory->CreateMem(sizeof(AdminUser), (void **)&pUser);
   184 		pUser->grp_size = 0;
   185 		pUser->grp_table = -1;
   186 	}
   187 
   188 	pUser->flags = 0;
   189 	pUser->eflags = 0;
   190 	pUser->grp_count = 0;
   191 	pUser->password = -1;
   192 	pUser->magic = USR_MAGIC_SET;
   193 	pUser->auth.identidx = -1;
   194 	pUser->auth.index = 0;
   195 	pUser->immune_default = false;
   196 	pUser->immune_global = false;
   197 
   198 	if (m_FirstUser == INVALID_ADMIN_ID)
   199 	{
   200 		m_FirstUser = id;
   201 		m_LastUser = id;
   202 	} else {
   203 		AdminUser *pPrev = (AdminUser *)m_pMemory->GetAddress(m_LastUser);
   204 		pPrev->next_user = id;
   205 		pUser->prev_user = m_LastUser;
   206 		m_LastUser = id;
   207 	}
   208 
   209 	if (name && name[0] != '\0')
   210 	{
   211 		pUser->nameidx = m_pStrings->AddString(name);
   212 	}
   213 
   214 	return id;
   215 }
   216 
   217 GroupId AdminCache::AddGroup(const char *group_name)
   218 {
   219 	if (sm_trie_retrieve(m_pGroups, group_name, NULL))
   220 	{
   221 		return INVALID_GROUP_ID;
   222 	}
   223 
   224 	GroupId id;
   225 	AdminGroup *pGroup;
   226 	if (m_FreeGroupList != INVALID_GROUP_ID)
   227 	{
   228 		pGroup = (AdminGroup *)m_pMemory->GetAddress(m_FreeGroupList);
   229 		assert(pGroup->magic == GRP_MAGIC_UNSET);
   230 		id = m_FreeGroupList;
   231 		m_FreeGroupList = pGroup->next_grp;
   232 	} else {
   233 		id = m_pMemory->CreateMem(sizeof(AdminGroup), (void **)&pGroup);
   234 	}
   235 
   236 	pGroup->immune_default = false;
   237 	pGroup->immune_global = false;
   238 	pGroup->immune_table = -1;
   239 	pGroup->magic = GRP_MAGIC_SET;
   240 	pGroup->next_grp = INVALID_GROUP_ID;
   241 	pGroup->pCmdGrpTable = NULL;
   242 	pGroup->pCmdTable = NULL;
   243 	pGroup->addflags = 0;
   244 
   245 	if (m_FirstGroup == INVALID_GROUP_ID)
   246 	{
   247 		m_FirstGroup = id;
   248 		m_LastGroup = id;
   249 		pGroup->prev_grp = INVALID_GROUP_ID;
   250 	} else {
   251 		AdminGroup *pPrev = (AdminGroup *)m_pMemory->GetAddress(m_LastGroup);
   252 		assert(pPrev->magic == GRP_MAGIC_SET);
   253 		pPrev->next_grp = id;
   254 		pGroup->prev_grp = m_LastGroup;
   255 		m_LastGroup = id;
   256 	}
   257 
   258 	pGroup->nameidx = m_pStrings->AddString(group_name);
   259 
   260 	sm_trie_insert(m_pGroups, group_name, (void *)id);
   261 
   262 	return id;
   263 }
   264 
   265 GroupId AdminCache::FindGroupByName(const char *group_name)
   266 {
   267 	void *object;
   268 
   269 	if (!sm_trie_retrieve(m_pGroups, group_name, &object))
   270 	{
   271 		return INVALID_GROUP_ID;
   272 	}
   273 
   274 	GroupId id = (GroupId)object;
   275 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   276 
   277 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   278 	{
   279 		return INVALID_GROUP_ID;
   280 	}
   281 
   282 	return id;
   283 }
   284 
   285 void AdminCache::SetGroupAddFlag(GroupId id, AdminFlag flag, bool enabled)
   286 {
   287 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   288 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   289 	{
   290 		return;
   291 	}
   292 
   293 	if (flag < Admin_Reservation || flag >= AdminFlags_TOTAL)
   294 	{
   295 		return;
   296 	}
   297 
   298 	FlagBits bits = (1<<(FlagBits)flag);
   299 
   300 	if (enabled)
   301 	{
   302 		pGroup->addflags |= bits;
   303 	} else {
   304 		pGroup->addflags &= ~bits;
   305 	}
   306 }
   307 
   308 bool AdminCache::GetGroupAddFlag(GroupId id, AdminFlag flag)
   309 {
   310 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   311 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   312 	{
   313 		return false;
   314 	}
   315 
   316 	if (flag < Admin_Reservation || flag >= AdminFlags_TOTAL)
   317 	{
   318 		return false;
   319 	}
   320 
   321 	FlagBits bit = 1<<(FlagBits)flag;
   322 	return ((pGroup->addflags & bit) == bit);
   323 }
   324 
   325 FlagBits AdminCache::GetGroupAddFlags(GroupId id)
   326 {
   327 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   328 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   329 	{
   330 		return 0;
   331 	}
   332 
   333 	return pGroup->addflags;
   334 }
   335 
   336 void AdminCache::SetGroupGenericImmunity(GroupId id, ImmunityType type, bool enabled)
   337 {
   338 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   339 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   340 	{
   341 		return;
   342 	}
   343 
   344 	if (type == Immunity_Default)
   345 	{
   346 		pGroup->immune_default = enabled;
   347 	} else if (type == Immunity_Global) {
   348 		pGroup->immune_global = enabled;
   349 	} 
   350 }
   351 
   352 bool AdminCache::GetGroupGenericImmunity(GroupId id, ImmunityType type)
   353 {
   354 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   355 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   356 	{
   357 		return false;
   358 	}
   359 
   360 	if (type == Immunity_Default)
   361 	{
   362 		return pGroup->immune_default;
   363 	} else if (type == Immunity_Global) {
   364 		return pGroup->immune_global;
   365 	}
   366 
   367 	return false;
   368 }
   369 
   370 void AdminCache::AddGroupImmunity(GroupId id, GroupId other_id)
   371 {
   372 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   373 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   374 	{
   375 		return;
   376 	}
   377 
   378 	AdminGroup *pOther = (AdminGroup *)m_pMemory->GetAddress(id);
   379 	if (!pOther || pOther->magic != GRP_MAGIC_SET)
   380 	{
   381 		return;
   382 	}
   383 
   384 	/* We always need to resize the immunity table */
   385 	int *table, tblidx;
   386 	if (pOther->immune_table == -1)
   387 	{
   388 		tblidx = m_pMemory->CreateMem(sizeof(int) * 2, (void **)&table);
   389 		table[0] = 0;
   390 	} else {
   391 		int *old_table = (int *)m_pMemory->GetAddress(pOther->immune_table);
   392 		/* Break out if this group is already in the list */
   393 		for (int i=0; i<old_table[0]; i++)
   394 		{
   395 			if (old_table[1+i] == other_id)
   396 			{
   397 				return;
   398 			}
   399 		}
   400 		tblidx = m_pMemory->CreateMem(sizeof(int) * (old_table[0] + 2), (void **)&table);
   401 		/* Get the old address again in case of resize */
   402 		old_table = (int *)m_pMemory->GetAddress(pOther->immune_table);
   403 		table[0] = old_table[0];
   404 		for (unsigned int i=1; i<=(unsigned int)old_table[0]; i++)
   405 		{
   406 			table[i] = old_table[i];
   407 		}
   408 	}
   409 
   410 	/* Assign */
   411 	pOther->immune_table = tblidx;
   412 
   413 	/* Add to the array */
   414 	table[0]++;
   415 	table[table[0]] = other_id;
   416 }
   417 
   418 unsigned int AdminCache::GetGroupImmunityCount(GroupId id)
   419 {
   420 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   421 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   422 	{
   423 		return 0;
   424 	}
   425 
   426 	if (pGroup->immune_table == -1)
   427 	{
   428 		return 0;
   429 	}
   430 
   431 	int *table = (int *)m_pMemory->GetAddress(pGroup->immune_table);
   432 
   433 	return table[0];
   434 }
   435 
   436 GroupId AdminCache::GetGroupImmunity(GroupId id, unsigned int number)
   437 {
   438 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   439 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   440 	{
   441 		return INVALID_GROUP_ID;
   442 	}
   443 
   444 	if (pGroup->immune_table == -1)
   445 	{
   446 		return INVALID_GROUP_ID;
   447 	}
   448 
   449 	int *table = (int *)m_pMemory->GetAddress(pGroup->immune_table);
   450 	if (number >= (unsigned int)table[0])
   451 	{
   452 		return INVALID_GROUP_ID;
   453 	}
   454 
   455 	return table[1 + number];
   456 }
   457 
   458 void AdminCache::AddGroupCommandOverride(GroupId id, const char *name, OverrideType type, OverrideRule rule)
   459 {
   460 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   461 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   462 	{
   463 		return;
   464 	}
   465 
   466 	Trie *pTrie = NULL;
   467 	if (type == Override_Command)
   468 	{
   469 		if (pGroup->pCmdTable == NULL)
   470 		{
   471 			pGroup->pCmdTable = sm_trie_create();
   472 		}
   473 		pTrie = pGroup->pCmdTable;
   474 	} else if (type == Override_CommandGroup) {
   475 		if (pGroup->pCmdGrpTable == NULL)
   476 		{
   477 			pGroup->pCmdGrpTable = sm_trie_create();
   478 		}
   479 		pTrie = pGroup->pCmdGrpTable;
   480 	} else {
   481 		return;
   482 	}
   483 
   484 	sm_trie_insert(pTrie, name, (void *)(int)rule);
   485 }
   486 
   487 bool AdminCache::GetGroupCommandOverride(GroupId id, const char *name, OverrideType type, OverrideRule *pRule)
   488 {
   489 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   490 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   491 	{
   492 		return false;
   493 	}
   494 
   495 	Trie *pTrie = NULL;
   496 	if (type == Override_Command)
   497 	{
   498 		if (pGroup->pCmdTable == NULL)
   499 		{
   500 			return false;
   501 		}
   502 		pTrie = pGroup->pCmdTable;
   503 	} else if (type == Override_CommandGroup) {
   504 		if (pGroup->pCmdGrpTable == NULL)
   505 		{
   506 			return false;
   507 		}
   508 		pTrie = pGroup->pCmdGrpTable;
   509 	} else {
   510 		return false;
   511 	}
   512 
   513 	void *object;
   514 	if (!sm_trie_retrieve(pTrie, name, &object))
   515 	{
   516 		return false;
   517 	}
   518 
   519 	if (pRule)
   520 	{
   521 		*pRule = (OverrideRule)(int)object;
   522 	}
   523 	
   524 	return true;
   525 }
   526 
   527 Trie *AdminCache::GetMethodByIndex(unsigned int index)
   528 {
   529 	List<AuthMethod>::iterator iter;
   530 	for (iter=m_AuthMethods.begin();
   531 		 iter!=m_AuthMethods.end();
   532 		 iter++)
   533 	{
   534 		if (index-- == 0)
   535 		{
   536 			return (*iter).table;
   537 		}
   538 	}
   539 
   540 	return NULL;
   541 }
   542 
   543 bool AdminCache::InvalidateAdmin(AdminId id)
   544 {
   545 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
   546 	AdminUser *pOther;
   547 	if (!pUser || pUser->magic != USR_MAGIC_SET)
   548 	{
   549 		return false;
   550 	}
   551 
   552 	if (!m_InvalidatingAdmins)
   553 	{
   554 		g_Players.ClearAdminId(id);
   555 	}
   556 
   557 	/* Unlink from the dbl link list */
   558 	if (id == m_FirstUser && id == m_LastUser)
   559 	{
   560 		m_FirstUser = INVALID_ADMIN_ID;
   561 		m_LastUser = INVALID_ADMIN_ID;
   562 	} else if (id == m_FirstUser) {
   563 		m_FirstUser = pUser->next_user;
   564 		pOther = (AdminUser *)m_pMemory->GetAddress(m_FirstUser);
   565 		pOther->prev_user = INVALID_ADMIN_ID;
   566 	} else if (id == m_LastUser) {
   567 		m_LastUser = pUser->prev_user;
   568 		pOther = (AdminUser *)m_pMemory->GetAddress(m_LastUser);
   569 		pOther->next_user = INVALID_ADMIN_ID;
   570 	} else {
   571 		pOther = (AdminUser *)m_pMemory->GetAddress(pUser->prev_user);
   572 		pOther->next_user = pUser->next_user;
   573 		pOther = (AdminUser *)m_pMemory->GetAddress(pUser->next_user);
   574 		pOther->prev_user = pUser->prev_user;
   575 	}
   576 
   577 	/* Unlink from auth tables */
   578 	if (pUser->auth.identidx != -1)
   579 	{
   580 		Trie *pTrie = GetMethodByIndex(pUser->auth.index);
   581 		if (pTrie)
   582 		{
   583 			sm_trie_delete(pTrie, m_pStrings->GetString(pUser->auth.identidx));
   584 		}
   585 	}
   586 
   587 	/* Clear table counts */
   588 	pUser->grp_count = 0;
   589 	
   590 	/* Link into free list */
   591 	pUser->magic = USR_MAGIC_UNSET;
   592 	pUser->next_user = m_FreeUserList;
   593 	m_FreeUserList = id;
   594 
   595 	return true;
   596 }
   597 
   598 
   599 void AdminCache::InvalidateGroup(GroupId id)
   600 {
   601 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(id);
   602 	AdminGroup *pOther;
   603 
   604 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   605 	{
   606 		return;
   607 	}
   608 
   609 	const char *str = m_pStrings->GetString(pGroup->nameidx);
   610 	sm_trie_delete(m_pGroups, str);
   611 
   612 	/* Unlink from the live dbllink list */
   613 	if (id == m_FirstGroup && id == m_LastGroup)
   614 	{
   615 		m_LastGroup = INVALID_GROUP_ID;
   616 		m_FirstGroup = INVALID_GROUP_ID;
   617 	} else if (id == m_FirstGroup) {
   618 		m_FirstGroup = pGroup->next_grp;
   619 		pOther = (AdminGroup *)m_pMemory->GetAddress(m_FirstGroup);
   620 		pOther->prev_grp = INVALID_GROUP_ID;
   621 	} else if (id == m_LastGroup) {
   622 		m_LastGroup = pGroup->prev_grp;
   623 		pOther = (AdminGroup *)m_pMemory->GetAddress(m_LastGroup);
   624 		pOther->next_grp = INVALID_GROUP_ID;
   625 	} else {
   626 		pOther = (AdminGroup *)m_pMemory->GetAddress(pGroup->prev_grp);
   627 		pOther->next_grp = pGroup->next_grp;
   628 		pOther = (AdminGroup *)m_pMemory->GetAddress(pGroup->next_grp);
   629 		pOther->prev_grp = pGroup->prev_grp;
   630 	}
   631 
   632 	/* Free any used memory to be safe */
   633 	if (pGroup->pCmdGrpTable)
   634 	{
   635 		sm_trie_destroy(pGroup->pCmdGrpTable);
   636 		pGroup->pCmdGrpTable = NULL;
   637 	}
   638 	if (pGroup->pCmdTable)
   639 	{
   640 		sm_trie_destroy(pGroup->pCmdTable);
   641 		pGroup->pCmdTable = NULL;
   642 	}
   643 
   644 	/* Link into the free list */
   645 	pGroup->magic = GRP_MAGIC_UNSET;
   646 	pGroup->next_grp = m_FreeGroupList;
   647 	m_FreeGroupList = id;
   648 
   649 	int idx = m_FirstUser;
   650 	AdminUser *pUser;
   651 	int *table;
   652 	while (idx != INVALID_ADMIN_ID)
   653 	{
   654 		pUser = (AdminUser *)m_pMemory->GetAddress(idx);
   655 		if (pUser->grp_count > 0)
   656 		{
   657 			table = (int *)m_pMemory->GetAddress(pUser->grp_table);
   658 			for (unsigned int i=0; i<pUser->grp_count; i++)
   659 			{
   660 				if (table[i] == id)
   661 				{
   662 					/* We have to remove this entry */
   663 					for (unsigned int j=i+1; j<pUser->grp_count; j++)
   664 					{
   665 						/* Move everything down by one */
   666 						table[j-1] = table[j];
   667 					}
   668 					/* Decrease count */
   669 					pUser->grp_count--;
   670 					/* Recalculate effective flags */
   671 					pUser->eflags = pUser->flags;
   672 					for (unsigned int j=0; j<pUser->grp_count; j++)
   673 					{
   674 						pOther = (AdminGroup *)m_pMemory->GetAddress(table[j]);
   675 						pUser->eflags |= pOther->addflags;
   676 					}
   677 					/* Break now, duplicates aren't allowed */
   678 					break;
   679 				}
   680 			}
   681 		}
   682 		idx = pUser->next_user;
   683 	}
   684 }
   685 
   686 void AdminCache::InvalidateGroupCache()
   687 {
   688 	/* Nuke the free list */
   689 	m_FreeGroupList = -1;
   690 
   691 	/* Nuke reverse lookups */
   692 	sm_trie_clear(m_pGroups);
   693 
   694 	/* Free memory on groups */
   695 	GroupId cur = m_FirstGroup;
   696 	AdminGroup *pGroup;
   697 	while (cur != INVALID_GROUP_ID)
   698 	{
   699 		pGroup = (AdminGroup *)m_pMemory->GetAddress(cur);
   700 		assert(pGroup->magic == GRP_MAGIC_SET);
   701 		if (pGroup->pCmdGrpTable)
   702 		{
   703 			sm_trie_destroy(pGroup->pCmdGrpTable);
   704 		}
   705 		if (pGroup->pCmdTable)
   706 		{
   707 			sm_trie_destroy(pGroup->pCmdTable);
   708 		}
   709 		cur = pGroup->next_grp;
   710 	}
   711 
   712 	m_FirstGroup = INVALID_GROUP_ID;
   713 	m_LastGroup = INVALID_GROUP_ID;
   714 
   715 	InvalidateAdminCache(false);
   716 
   717 	/* Reset the memory table */
   718 	m_pMemory->Reset();
   719 }
   720 
   721 void AdminCache::AddAdminListener(IAdminListener *pListener)
   722 {
   723 	m_hooks.push_back(pListener);
   724 }
   725 
   726 void AdminCache::RemoveAdminListener(IAdminListener *pListener)
   727 {
   728 	m_hooks.remove(pListener);
   729 }
   730 
   731 void AdminCache::RegisterAuthIdentType(const char *name)
   732 {
   733 	if (sm_trie_retrieve(m_pAuthTables, name, NULL))
   734 	{
   735 		return;
   736 	}
   737 
   738 	Trie *pAuth = sm_trie_create();
   739 
   740 	AuthMethod method;
   741 	method.name.assign(name);
   742 	method.table = pAuth;
   743 
   744 	m_AuthMethods.push_back(method);
   745 
   746 	sm_trie_insert(m_pAuthTables, name, pAuth);
   747 }
   748 
   749 void AdminCache::InvalidateAdminCache(bool unlink_admins)
   750 {
   751 	m_InvalidatingAdmins = true;
   752 	g_Players.ClearAllAdmins();
   753 	/* Wipe the identity cache first */
   754 	List<AuthMethod>::iterator iter;
   755 	for (iter=m_AuthMethods.begin();
   756 		 iter!=m_AuthMethods.end();
   757 		 iter++)
   758 	{
   759 		sm_trie_clear((*iter).table);
   760 	}
   761 	
   762 	if (unlink_admins)
   763 	{
   764 		while (m_FirstUser != INVALID_ADMIN_ID)
   765 		{
   766 			InvalidateAdmin(m_FirstUser);
   767 		}
   768 	} else {
   769 		m_FirstUser = -1;
   770 		m_LastUser = -1;
   771 		m_FreeUserList = -1;
   772 	}
   773 	m_InvalidatingAdmins = false;
   774 }
   775 
   776 void AdminCache::DumpAdminCache(AdminCachePart part, bool rebuild)
   777 {
   778 	List<IAdminListener *>::iterator iter;
   779 	IAdminListener *pListener;
   780 	cell_t result;
   781 
   782 	if (part == AdminCache_Overrides)
   783 	{
   784 		DumpCommandOverrideCache(Override_Command);
   785 		DumpCommandOverrideCache(Override_CommandGroup);
   786 		if (rebuild)
   787 		{
   788 			for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++)
   789 			{
   790 				pListener = (*iter);
   791 				pListener->OnRebuildOverrideCache();
   792 			}
   793 			m_pCacheFwd->PushCell(part);
   794 			m_pCacheFwd->Execute(&result);
   795 		}
   796 	} else if (part == AdminCache_Groups || part == AdminCache_Admins) {
   797 		if (part == AdminCache_Groups)
   798 		{
   799 			InvalidateGroupCache();
   800 			if (rebuild)
   801 			{
   802 				for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++)
   803 				{
   804 					pListener = (*iter);
   805 					pListener->OnRebuildGroupCache();
   806 				}
   807 				m_pCacheFwd->PushCell(part);
   808 				m_pCacheFwd->Execute(&result);
   809 			}
   810 		}
   811 		InvalidateAdminCache(true);
   812 		if (rebuild)
   813 		{
   814 			for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++)
   815 			{
   816 				pListener = (*iter);
   817 				pListener->OnRebuildAdminCache((part == AdminCache_Groups));
   818 			}
   819 			m_pCacheFwd->PushCell(AdminCache_Admins);
   820 			m_pCacheFwd->Execute(&result);
   821 		}
   822 	}
   823 }
   824 
   825 const char *AdminCache::GetAdminName(AdminId id)
   826 {
   827 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
   828 	if (!pUser || pUser->magic != USR_MAGIC_SET)
   829 	{
   830 		return NULL;
   831 	}
   832 
   833 	return m_pStrings->GetString(pUser->nameidx);
   834 }
   835 
   836 bool AdminCache::GetMethodIndex(const char *name, unsigned int *_index)
   837 {
   838 	List<AuthMethod>::iterator iter;
   839 	unsigned int index = 0;
   840 	for (iter=m_AuthMethods.begin();
   841 		 iter!=m_AuthMethods.end();
   842 		 iter++,index++)
   843 	{
   844 		if ((*iter).name.compare(name) == 0)
   845 		{
   846 			*_index = index;
   847 			return true;
   848 		}
   849 	}
   850 
   851 	return false;
   852 }
   853 
   854 bool AdminCache::BindAdminIdentity(AdminId id, const char *auth, const char *ident)
   855 {
   856 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
   857 	if (!pUser || pUser->magic != USR_MAGIC_SET)
   858 	{
   859 		return false;
   860 	}
   861 
   862 	Trie *pTable;
   863 	if (!sm_trie_retrieve(m_pAuthTables, auth, (void **)&pTable))
   864 	{
   865 		return false;
   866 	}
   867 
   868 	if (sm_trie_retrieve(pTable, ident, NULL))
   869 	{
   870 		return false;
   871 	}
   872 
   873 	pUser->auth.identidx = m_pStrings->AddString(ident);
   874 	GetMethodIndex(auth, &pUser->auth.index);
   875 
   876 	return sm_trie_insert(pTable, ident, (void **)id);
   877 }
   878 
   879 AdminId AdminCache::FindAdminByIdentity(const char *auth, const char *identity)
   880 {
   881 	Trie *pTable;
   882 	if (!sm_trie_retrieve(m_pAuthTables, auth, (void **)&pTable))
   883 	{
   884 		return INVALID_ADMIN_ID;
   885 	}
   886 
   887 	void *object;
   888 	if (!sm_trie_retrieve(pTable, identity, &object))
   889 	{
   890 		return INVALID_ADMIN_ID;
   891 	}
   892 
   893 	return (AdminId)object;
   894 }
   895 
   896 void AdminCache::SetAdminFlag(AdminId id, AdminFlag flag, bool enabled)
   897 {
   898 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
   899 	if (!pUser || pUser->magic != USR_MAGIC_SET)
   900 	{
   901 		return;
   902 	}
   903 
   904 	if (flag < Admin_Reservation
   905 		|| flag >= AdminFlags_TOTAL)
   906 	{
   907 		return;
   908 	}
   909 	
   910 	FlagBits bits = (1<<(FlagBits)flag);
   911 
   912 	if (enabled)
   913 	{
   914 		pUser->flags |= bits;
   915 		pUser->eflags |= bits;
   916 	} else {
   917 		pUser->flags &= ~bits;
   918 		pUser->eflags &= ~bits;
   919 	}
   920 }
   921 
   922 bool AdminCache::GetAdminFlag(AdminId id, AdminFlag flag, AccessMode mode)
   923 {
   924 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
   925 	if (!pUser || pUser->magic != USR_MAGIC_SET)
   926 	{
   927 		return false;
   928 	}
   929 
   930 	if (flag < Admin_Reservation
   931 		|| flag >= AdminFlags_TOTAL)
   932 	{
   933 		return false;
   934 	}
   935 
   936 	FlagBits bit = (1<<(FlagBits)flag);
   937 
   938 	if (mode == Access_Real)
   939 	{
   940 		return ((pUser->flags & bit) == bit);
   941 	} else if (mode == Access_Effective) {
   942 		return ((pUser->eflags & bit) == bit);
   943 	}
   944 
   945 	return false;
   946 }
   947 
   948 FlagBits AdminCache::GetAdminFlags(AdminId id, AccessMode mode)
   949 {
   950 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
   951 	if (!pUser || pUser->magic != USR_MAGIC_SET)
   952 	{
   953 		return 0;
   954 	}
   955 
   956 	if (mode == Access_Real)
   957 	{
   958 		return pUser->flags;
   959 	} else if (mode == Access_Effective) {
   960 		return pUser->eflags;
   961 	}
   962 
   963 	return 0;
   964 }
   965 
   966 void AdminCache::SetAdminFlags(AdminId id, AccessMode mode, FlagBits bits)
   967 {
   968 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
   969 	if (!pUser || pUser->magic != USR_MAGIC_SET)
   970 	{
   971 		return;
   972 	}
   973 
   974 	if (mode == Access_Real)
   975 	{
   976 		pUser->flags = bits;
   977 		pUser->eflags = bits;
   978 	} else if (mode == Access_Effective) {
   979 		pUser->eflags = bits;
   980 	}
   981 }
   982 
   983 bool AdminCache::AdminInheritGroup(AdminId id, GroupId gid)
   984 {
   985 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
   986 	if (!pUser || pUser->magic != USR_MAGIC_SET)
   987 	{
   988 		return false;
   989 	}
   990 
   991 	AdminGroup *pGroup = (AdminGroup *)m_pMemory->GetAddress(gid);
   992 	if (!pGroup || pGroup->magic != GRP_MAGIC_SET)
   993 	{
   994 		return false;
   995 	}
   996 
   997 	/* First check for duplicates */
   998 	if (pUser->grp_count != 0)
   999 	{
  1000 		int *temp_table = (int *)m_pMemory->GetAddress(pUser->grp_table);
  1001 		for (unsigned int i=0; i<pUser->grp_count; i++)
  1002 		{
  1003 			if (temp_table[i] == gid)
  1004 			{
  1005 				return false;
  1006 			}
  1007 		}
  1008 	}
  1009 
  1010 	int *table;
  1011 	if (pUser->grp_count + 1 > pUser->grp_size)
  1012 	{
  1013 		int new_size = 0;
  1014 		int tblidx;
  1015 		
  1016 		if (pUser->grp_size == 0)
  1017 		{
  1018 			new_size = 2;
  1019 		} else {
  1020 			new_size = pUser->grp_size * 2;
  1021 		}
  1022 		
  1023 		/* Create and refresh pointers */
  1024 		tblidx = m_pMemory->CreateMem(new_size * sizeof(int), (void **)&table);
  1025 		pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1026 		pGroup = (AdminGroup *)m_pMemory->GetAddress(gid);
  1027 
  1028 		/* Copy old data if necessary */
  1029 		if (pUser->grp_table != -1)
  1030 		{
  1031 			int *old_table = (int *)m_pMemory->GetAddress(pUser->grp_table);
  1032 			memcpy(table, old_table, sizeof(int) * pUser->grp_count);
  1033 		}
  1034 		pUser->grp_table = tblidx;
  1035 		pUser->grp_size = new_size;
  1036 	} else {
  1037 		table = (int *)m_pMemory->GetAddress(pUser->grp_table);
  1038 	}
  1039 
  1040 	table[pUser->grp_count] = gid;
  1041 	pUser->grp_count++;
  1042 
  1043 	/* Compute new effective permissions */
  1044 	pUser->eflags |= pGroup->addflags;
  1045 
  1046 	if (pGroup->immune_default)
  1047 	{
  1048 		pUser->immune_default = true;
  1049 	}
  1050 	if (pGroup->immune_global)
  1051 	{
  1052 		pUser->immune_global = true;
  1053 	}
  1054 
  1055 	return true;
  1056 }
  1057 
  1058 bool AdminCache::IsValidAdmin(AdminId id)
  1059 {
  1060 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1061 	return (pUser != NULL && pUser->magic == USR_MAGIC_SET);
  1062 }
  1063 
  1064 unsigned int AdminCache::GetAdminGroupCount(AdminId id)
  1065 {
  1066 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1067 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1068 	{
  1069 		return 0;
  1070 	}
  1071 
  1072 	return pUser->grp_count;
  1073 }
  1074 
  1075 GroupId AdminCache::GetAdminGroup(AdminId id, unsigned int index, const char **name)
  1076 {
  1077 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1078 	if (!pUser || pUser->magic != USR_MAGIC_SET || index >= pUser->grp_count)
  1079 	{
  1080 		return INVALID_GROUP_ID;
  1081 	}
  1082 
  1083 	int *table = (int *)m_pMemory->GetAddress(pUser->grp_table);
  1084 
  1085 	return table[index];
  1086 }
  1087 
  1088 const char *AdminCache::GetAdminPassword(AdminId id)
  1089 {
  1090 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1091 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1092 	{
  1093 		return NULL;
  1094 	}
  1095 
  1096 	return m_pStrings->GetString(pUser->password);
  1097 }
  1098 
  1099 void AdminCache::SetAdminPassword(AdminId id, const char *password)
  1100 {	
  1101 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1102 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1103 	{
  1104 		return;
  1105 	}
  1106 
  1107 	pUser->password = m_pStrings->AddString(password);
  1108 }
  1109 
  1110 unsigned int AdminCache::FlagBitsToBitArray(FlagBits bits, bool array[], unsigned int maxSize)
  1111 {
  1112 	unsigned int i;
  1113 	for (i=0; i<maxSize && i<AdminFlags_TOTAL; i++)
  1114 	{
  1115 		array[i] = ((bits & (1<<i)) == (unsigned)(1<<i));
  1116 	}
  1117 
  1118 	return i;
  1119 }
  1120 
  1121 FlagBits AdminCache::FlagBitArrayToBits(const bool array[], unsigned int maxSize)
  1122 {
  1123 	FlagBits bits = 0;
  1124 	for (unsigned int i=0; i<maxSize && i<AdminFlags_TOTAL; i++)
  1125 	{
  1126 		if (array[i])
  1127 		{
  1128 			bits |= (1<<i);
  1129 		}
  1130 	}
  1131 
  1132 	return bits;
  1133 }
  1134 
  1135 FlagBits AdminCache::FlagArrayToBits(const AdminFlag array[], unsigned int numFlags)
  1136 {
  1137 	FlagBits bits = 0;
  1138 	for (unsigned int i=0; i<numFlags && i<AdminFlags_TOTAL; i++)
  1139 	{
  1140 		bits |= (1 << (FlagBits)array[i]);
  1141 	}
  1142 
  1143 	return bits;
  1144 }
  1145 
  1146 unsigned int AdminCache::FlagBitsToArray(FlagBits bits, AdminFlag array[], unsigned int maxSize)
  1147 {
  1148 	unsigned int i, num=0;
  1149 	for (i=0; i<maxSize && i<AdminFlags_TOTAL; i++)
  1150 	{
  1151 		if ((bits & (1<<i)) == (unsigned)(1<<i))
  1152 		{
  1153 			array[num++] = (AdminFlag)i;
  1154 		}
  1155 	}
  1156 
  1157 	return num;
  1158 }
  1159 
  1160 bool AdminCache::CheckAdminFlags(AdminId id, FlagBits bits)
  1161 {
  1162 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1163 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1164 	{
  1165 		return false;
  1166 	}
  1167 
  1168 	return ((pUser->eflags & bits) == bits);
  1169 }
  1170 
  1171 bool AdminCache::CanAdminTarget(AdminId id, AdminId target)
  1172 {
  1173 	/** 
  1174 	 * Zeroth, if the targeting AdminId is INVALID_ADMIN_ID, targeting fails.
  1175 	 * First, if the targetted AdminId is INVALID_ADMIN_ID, targeting succeeds.
  1176 	 */
  1177 
  1178 	if (id == INVALID_ADMIN_ID)
  1179 	{
  1180 		return false;
  1181 	}
  1182 
  1183 	if (target == INVALID_ADMIN_ID)
  1184 	{
  1185 		return true;
  1186 	}
  1187 
  1188 	AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id);
  1189 	if (!pUser || pUser->magic != USR_MAGIC_SET)
  1190 	{
  1191 		return false;
  1192 	}
  1193 
  1194 	AdminUser *pTarget = (AdminUser *)m_pMemory->GetAddress(target);
  1195 	if (!pTarget || pTarget->magic != USR_MAGIC_SET)
  1196 	{
  1197 		return false;
  1198 	}
  1199 
  1200 	/** 
  1201 	 * Second, if the targeting admin is root, targeting suceeds.
  1202 	 */
  1203 	if (pUser->eflags & ADMFLAG_ROOT)
  1204 	{
  1205 		return true;
  1206 	}
  1207 
  1208 	/** Fourth, if the targetted admin has global immunity, targeting fails. */
  1209 	if (pTarget->immune_global)
  1210 	{
  1211 		return false;
  1212 	}
  1213 
  1214 	/** 
  1215 	 * Fifth, if the targetted admin has default immunity 
  1216 	 *  and the admin belongs to no groups, targeting fails.
  1217 	 */
  1218 	if (pTarget->immune_default && pUser->grp_count < 1)
  1219 	{
  1220 		return false;
  1221 	}
  1222 
  1223 	/** 
  1224 	 * Sixth, if the targetted admin has specific immunity from the
  1225 	 *  targeting admin via group immunities, targeting fails.
  1226 	 */
  1227 	//:TODO: speed this up... maybe with trie hacks.
  1228 	//idea is to insert %d.%d in the trie after computing this and use it as a cache lookup.
  1229 	//problem is the trie cannot delete prefixes, so we'd have a problem with invalidations.
  1230 	if (pTarget->grp_count > 0 && pUser->grp_count > 0)
  1231 	{
  1232 		int *grp_table = (int *)m_pMemory->GetAddress(pTarget->grp_table);
  1233 		int *src_table = (int *)m_pMemory->GetAddress(pUser->grp_table);
  1234 		GroupId id, other;
  1235 		unsigned int num;
  1236 		for (unsigned int i=0; i<pTarget->grp_count; i++)
  1237 		{
  1238 			id = grp_table[i];
  1239 			num = GetGroupImmunityCount(id);
  1240 			for (unsigned int j=0; j<num; i++)
  1241 			{
  1242 				other = GetGroupImmunity(id, j);
  1243 				for (unsigned int k=0; k<pUser->grp_count; k++)
  1244 				{
  1245 					if (other == src_table[k])
  1246 					{
  1247 						return false;
  1248 					}
  1249 				}
  1250 			}
  1251 		}
  1252 	}
  1253 	
  1254 	return true;
  1255 }