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