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