plugins/admin-sql-threaded.sp
branchrefac-jit
changeset 2284 026da5437111
parent 2192 7602d7fb60d9
child 2288 15c77b21589a
     1.1 --- a/plugins/admin-sql-threaded.sp	Fri Jul 11 06:51:21 2008 +0000
     1.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.3 @@ -1,855 +0,0 @@
     1.4 -/**
     1.5 - * vim: set ts=4 :
     1.6 - * =============================================================================
     1.7 - * SourceMod SQL Admins Plugin (Threaded)
     1.8 - * Fetches admins from an SQL database dynamically.
     1.9 - *
    1.10 - * SourceMod (C)2004-2008 AlliedModders LLC.  All rights reserved.
    1.11 - * =============================================================================
    1.12 - *
    1.13 - * This program is free software; you can redistribute it and/or modify it under
    1.14 - * the terms of the GNU General Public License, version 3.0, as published by the
    1.15 - * Free Software Foundation.
    1.16 - * 
    1.17 - * This program is distributed in the hope that it will be useful, but WITHOUT
    1.18 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    1.19 - * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    1.20 - * details.
    1.21 - *
    1.22 - * You should have received a copy of the GNU General Public License along with
    1.23 - * this program.  If not, see <http://www.gnu.org/licenses/>.
    1.24 - *
    1.25 - * As a special exception, AlliedModders LLC gives you permission to link the
    1.26 - * code of this program (as well as its derivative works) to "Half-Life 2," the
    1.27 - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    1.28 - * by the Valve Corporation.  You must obey the GNU General Public License in
    1.29 - * all respects for all other code used.  Additionally, AlliedModders LLC grants
    1.30 - * this exception to all derivative works.  AlliedModders LLC defines further
    1.31 - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    1.32 - * or <http://www.sourcemod.net/license.php>.
    1.33 - *
    1.34 - * Version: $Id$
    1.35 - */
    1.36 -
    1.37 -/* We like semicolons */
    1.38 -#pragma semicolon 1
    1.39 -
    1.40 -#include <sourcemod>
    1.41 -
    1.42 -public Plugin:myinfo = 
    1.43 -{
    1.44 -	name = "SQL Admins (Threaded)",
    1.45 -	author = "AlliedModders LLC",
    1.46 -	description = "Reads admins from SQL dynamically",
    1.47 -	version = SOURCEMOD_VERSION,
    1.48 -	url = "http://www.sourcemod.net/"
    1.49 -};
    1.50 -
    1.51 -/**
    1.52 - * Notes:
    1.53 - *
    1.54 - * 1) All queries in here are high priority.  This is because the admin stuff 
    1.55 - *    is very important.  Do not take this to mean that in your script, 
    1.56 - *    everything should be high priority.  
    1.57 - *
    1.58 - * 2) All callbacks are locked with "sequence numbers."  This is to make sure 
    1.59 - *    that multiple calls to sm_reloadadmins and the like do not make us 
    1.60 - *    store the results from two or more callbacks accidentally.  Instead, we 
    1.61 - *    check the sequence number in each callback with the current "allowed" 
    1.62 - *    sequence number, and if it doesn't match, the callback is cancelled.
    1.63 - *
    1.64 - * 3) Sequence numbers for groups and overrides are not cleared unless there 
    1.65 - *    was a 100% success in the fetch.  This is so we can potentially implement 
    1.66 - *    connection retries in the future.
    1.67 - *
    1.68 - * 4) Sequence numbers for the user cache are ignored except for being 
    1.69 - *    non-zero, which means players in-game should be re-checked for admin 
    1.70 - *    powers.
    1.71 - */
    1.72 -
    1.73 -new Handle:hDatabase = INVALID_HANDLE;			/** Database connection */
    1.74 -new g_sequence = 0;								/** Global unique sequence number */
    1.75 -new ConnectLock = 0;							/** Connect sequence number */
    1.76 -new RebuildCachePart[3] = {0};					/** Cache part sequence numbers */
    1.77 -new PlayerSeq[MAXPLAYERS+1];					/** Player-specific sequence numbers */
    1.78 -new bool:PlayerAuth[MAXPLAYERS+1];				/** Whether a player has been "pre-authed" */
    1.79 -
    1.80 -//#define _DEBUG
    1.81 -
    1.82 -public OnMapEnd()
    1.83 -{
    1.84 -	/**
    1.85 -	 * Clean up on map end just so we can start a fresh connection when we need it later.
    1.86 -	 */
    1.87 -	if (hDatabase != INVALID_HANDLE)
    1.88 -	{
    1.89 -		CloseHandle(hDatabase);
    1.90 -		hDatabase = INVALID_HANDLE;
    1.91 -	}
    1.92 -}
    1.93 -
    1.94 -public bool:OnClientConnect(client, String:rejectmsg[], maxlen)
    1.95 -{
    1.96 -	PlayerSeq[client] = 0;
    1.97 -	PlayerAuth[client] = false;
    1.98 -	return true;
    1.99 -}
   1.100 -
   1.101 -public OnClientDisconnect(client)
   1.102 -{
   1.103 -	PlayerSeq[client] = 0;
   1.104 -	PlayerAuth[client] = false;
   1.105 -}
   1.106 -
   1.107 -public OnDatabaseConnect(Handle:owner, Handle:hndl, const String:error[], any:data)
   1.108 -{
   1.109 -#if defined _DEBUG
   1.110 -	PrintToServer("OnDatabaseConnect(%x,%x,%d) ConnectLock=%d", owner, hndl, data, ConnectLock);
   1.111 -#endif
   1.112 -
   1.113 -	/**
   1.114 -	 * If this happens to be an old connection request, ignore it.
   1.115 -	 */
   1.116 -	if (data != ConnectLock || hDatabase != INVALID_HANDLE)
   1.117 -	{
   1.118 -		if (hndl != INVALID_HANDLE)
   1.119 -		{
   1.120 -			CloseHandle(hndl);
   1.121 -		}
   1.122 -		return;
   1.123 -	}
   1.124 -	
   1.125 -	ConnectLock = 0;
   1.126 -	hDatabase = hndl;
   1.127 -	
   1.128 -	/**
   1.129 -	 * See if the connection is valid.  If not, don't un-mark the caches
   1.130 -	 * as needing rebuilding, in case the next connection request works.
   1.131 -	 */
   1.132 -	if (hDatabase == INVALID_HANDLE)
   1.133 -	{
   1.134 -		LogError("Failed to connect to database: %s", error);
   1.135 -		return;
   1.136 -	}
   1.137 -	
   1.138 -	/**
   1.139 -	 * See if we need to get any of the cache stuff now.
   1.140 -	 */
   1.141 -	new sequence;
   1.142 -	if ((sequence = RebuildCachePart[_:AdminCache_Overrides]) != 0)
   1.143 -	{
   1.144 -		FetchOverrides(hDatabase, sequence);
   1.145 -	}
   1.146 -	if ((sequence = RebuildCachePart[_:AdminCache_Groups]) != 0)
   1.147 -	{
   1.148 -		FetchGroups(hDatabase, sequence);
   1.149 -	}
   1.150 -	if ((sequence = RebuildCachePart[_:AdminCache_Admins]) != 0)
   1.151 -	{
   1.152 -		FetchUsersWeCan(hDatabase);
   1.153 -	}
   1.154 -}
   1.155 -
   1.156 -RequestDatabaseConnection()
   1.157 -{
   1.158 -	ConnectLock = ++g_sequence;
   1.159 -	if (SQL_CheckConfig("admins"))
   1.160 -	{
   1.161 -		SQL_TConnect(OnDatabaseConnect, "admins", ConnectLock);
   1.162 -	} else {
   1.163 -		SQL_TConnect(OnDatabaseConnect, "default", ConnectLock);
   1.164 -	}
   1.165 -}
   1.166 -
   1.167 -public OnRebuildAdminCache(AdminCachePart:part)
   1.168 -{
   1.169 -	/**
   1.170 -	 * Mark this part of the cache as being rebuilt.  This is used by the 
   1.171 -	 * callback system to determine whether the results should still be 
   1.172 -	 * used.
   1.173 -	 */
   1.174 -	new sequence = ++g_sequence;
   1.175 -	RebuildCachePart[_:part] = sequence;
   1.176 -	
   1.177 -	/**
   1.178 -	 * If we don't have a database connection, we can't do any lookups just yet.
   1.179 -	 */
   1.180 -	if (!hDatabase)
   1.181 -	{
   1.182 -		/**
   1.183 -		 * Ask for a new connection if we need it.
   1.184 -		 */
   1.185 -		if (!ConnectLock)
   1.186 -		{
   1.187 -			RequestDatabaseConnection();
   1.188 -		}
   1.189 -		return;
   1.190 -	}
   1.191 -	
   1.192 -	if (part == AdminCache_Overrides)
   1.193 -	{
   1.194 -		FetchOverrides(hDatabase, sequence);
   1.195 -	} else if (part == AdminCache_Groups) {
   1.196 -		FetchGroups(hDatabase, sequence);
   1.197 -	} else if (part == AdminCache_Admins) {
   1.198 -		FetchUsersWeCan(hDatabase);
   1.199 -	}
   1.200 -}
   1.201 -
   1.202 -public Action:OnClientPreAdminCheck(client)
   1.203 -{
   1.204 -	PlayerAuth[client] = true;
   1.205 -	
   1.206 -	/**
   1.207 -	 * Play nice with other plugins.  If there's no database, don't delay the 
   1.208 -	 * connection process.  Unfortunately, we can't attempt anything else and 
   1.209 -	 * we just have to hope either the database is waiting or someone will type 
   1.210 -	 * sm_reloadadmins.
   1.211 -	 */
   1.212 -	if (hDatabase == INVALID_HANDLE)
   1.213 -	{
   1.214 -		return Plugin_Continue;
   1.215 -	}
   1.216 -	
   1.217 -	/**
   1.218 -	 * Similarly, if the cache is in the process of being rebuilt, don't delay 
   1.219 -	 * the user's normal connection flow.  The database will soon auth the user 
   1.220 -	 * normally.
   1.221 -	 */
   1.222 -	if (RebuildCachePart[_:AdminCache_Admins] != 0)
   1.223 -	{
   1.224 -		return Plugin_Continue;
   1.225 -	}
   1.226 -	
   1.227 -	/**
   1.228 -	 * If someone has already assigned an admin ID (bad bad bad), don't 
   1.229 -	 * bother waiting.
   1.230 -	 */
   1.231 -	if (GetUserAdmin(client) != INVALID_ADMIN_ID)
   1.232 -	{
   1.233 -		return Plugin_Continue;
   1.234 -	}
   1.235 -	
   1.236 -	FetchUser(hDatabase, client);
   1.237 -	
   1.238 -	return Plugin_Handled;
   1.239 -}
   1.240 -
   1.241 -public OnReceiveUserGroups(Handle:owner, Handle:hndl, const String:error[], any:data)
   1.242 -{
   1.243 -	new Handle:pk = Handle:data;
   1.244 -	ResetPack(pk);
   1.245 -	
   1.246 -	new client = ReadPackCell(pk);
   1.247 -	new sequence = ReadPackCell(pk);
   1.248 -	
   1.249 -	/**
   1.250 -	 * Make sure it's the same client.
   1.251 -	 */
   1.252 -	if (PlayerSeq[client] != sequence)
   1.253 -	{
   1.254 -		CloseHandle(pk);
   1.255 -		return;
   1.256 -	}
   1.257 -	
   1.258 -	new AdminId:adm = AdminId:ReadPackCell(pk);
   1.259 -	
   1.260 -	/**
   1.261 -	 * Someone could have sneakily changed the admin id while we waited.
   1.262 -	 */
   1.263 -	if (GetUserAdmin(client) != adm)
   1.264 -	{
   1.265 -		NotifyPostAdminCheck(client);
   1.266 -		CloseHandle(pk);
   1.267 -		return;
   1.268 -	}
   1.269 -	
   1.270 -	/**
   1.271 -	 * See if we got results.
   1.272 -	 */
   1.273 -	if (hndl == INVALID_HANDLE)
   1.274 -	{
   1.275 -		decl String:query[255];
   1.276 -		ReadPackString(pk, query, sizeof(query));
   1.277 -		LogError("SQL error receiving user: %s", error);
   1.278 -		LogError("Query dump: %s", query);
   1.279 -		NotifyPostAdminCheck(client);
   1.280 -		CloseHandle(pk);
   1.281 -		return;
   1.282 -	}
   1.283 -	
   1.284 -	decl String:name[80];
   1.285 -	new GroupId:gid;
   1.286 -	
   1.287 -	while (SQL_FetchRow(hndl))
   1.288 -	{
   1.289 -		SQL_FetchString(hndl, 0, name, sizeof(name));
   1.290 -		
   1.291 -		if ((gid = FindAdmGroup(name)) == INVALID_GROUP_ID)
   1.292 -		{
   1.293 -			continue;
   1.294 -		}
   1.295 -		
   1.296 -#if defined _DEBUG
   1.297 -		PrintToServer("Binding user group (%d, %d, %d, %s, %d)", client, sequence, adm, name, gid);
   1.298 -#endif
   1.299 -		
   1.300 -		AdminInheritGroup(adm, gid);
   1.301 -	}
   1.302 -	
   1.303 -	/**
   1.304 -	 * We're DONE! Omg.
   1.305 -	 */
   1.306 -	NotifyPostAdminCheck(client);
   1.307 -	CloseHandle(pk);
   1.308 -}
   1.309 -
   1.310 -public OnReceiveUser(Handle:owner, Handle:hndl, const String:error[], any:data)
   1.311 -{
   1.312 -	new Handle:pk = Handle:data;
   1.313 -	ResetPack(pk);
   1.314 -	
   1.315 -	new client = ReadPackCell(pk);
   1.316 -	
   1.317 -	/**
   1.318 -	 * Check if this is the latest result request.
   1.319 -	 */
   1.320 -	new sequence = ReadPackCell(pk);
   1.321 -	if (PlayerSeq[client] != sequence)
   1.322 -	{
   1.323 -		/* Discard everything, since we're out of sequence. */
   1.324 -		CloseHandle(pk);
   1.325 -		return;
   1.326 -	}
   1.327 -	
   1.328 -	/**
   1.329 -	 * If we need to use the results, make sure they succeeded.
   1.330 -	 */
   1.331 -	if (hndl == INVALID_HANDLE)
   1.332 -	{
   1.333 -		decl String:query[255];
   1.334 -		ReadPackString(pk, query, sizeof(query));
   1.335 -		LogError("SQL error receiving user: %s", error);
   1.336 -		LogError("Query dump: %s", query);
   1.337 -		RunAdminCacheChecks(client);
   1.338 -		NotifyPostAdminCheck(client);
   1.339 -		CloseHandle(pk);
   1.340 -		return;
   1.341 -	}
   1.342 -	
   1.343 -	new num_accounts = SQL_GetRowCount(hndl);
   1.344 -	if (num_accounts == 0)
   1.345 -	{
   1.346 -		RunAdminCacheChecks(client);
   1.347 -		NotifyPostAdminCheck(client);
   1.348 -		CloseHandle(pk);
   1.349 -		return;
   1.350 -	}
   1.351 -	
   1.352 -	decl String:authtype[16];
   1.353 -	decl String:identity[80];
   1.354 -	decl String:password[80];
   1.355 -	decl String:flags[32];
   1.356 -	decl String:name[80];
   1.357 -	new AdminId:adm, id;
   1.358 -	new immunity;
   1.359 -	
   1.360 -	/**
   1.361 -	 * Cache user info -- [0] = db id, [1] = cache id, [2] = groups
   1.362 -	 */
   1.363 -	decl user_lookup[num_accounts][3];
   1.364 -	new total_users = 0;
   1.365 -	
   1.366 -	while (SQL_FetchRow(hndl))
   1.367 -	{
   1.368 -		id = SQL_FetchInt(hndl, 0);
   1.369 -		SQL_FetchString(hndl, 1, authtype, sizeof(authtype));
   1.370 -		SQL_FetchString(hndl, 2, identity, sizeof(identity));
   1.371 -		SQL_FetchString(hndl, 3, password, sizeof(password));
   1.372 -		SQL_FetchString(hndl, 4, flags, sizeof(flags));
   1.373 -		SQL_FetchString(hndl, 5, name, sizeof(name));
   1.374 -		immunity = SQL_FetchInt(hndl, 7);
   1.375 -		
   1.376 -		/* For dynamic admins we clear anything already in the cache. */
   1.377 -		if ((adm = FindAdminByIdentity(authtype, identity)) != INVALID_ADMIN_ID)
   1.378 -		{
   1.379 -			RemoveAdmin(adm);
   1.380 -		}
   1.381 -		
   1.382 -		adm = CreateAdmin(name);
   1.383 -		if (!BindAdminIdentity(adm, authtype, identity))
   1.384 -		{
   1.385 -			LogError("Could not bind prefetched SQL admin (authtype \"%s\") (identity \"%s\")", authtype, identity);
   1.386 -			continue;
   1.387 -		}
   1.388 -		
   1.389 -		user_lookup[total_users][0] = id;
   1.390 -		user_lookup[total_users][1] = _:adm;
   1.391 -		user_lookup[total_users][2] = SQL_FetchInt(hndl, 6);
   1.392 -		total_users++;
   1.393 -		
   1.394 -#if defined _DEBUG
   1.395 -		PrintToServer("Found SQL admin (%d,%s,%s,%s,%s,%s,%d):%d:%d", id, authtype, identity, password, flags, name, immunity, adm, user_lookup[total_users-1][2]);
   1.396 -#endif
   1.397 -		
   1.398 -		/* See if this admin wants a password */
   1.399 -		if (password[0] != '\0')
   1.400 -		{
   1.401 -			SetAdminPassword(adm, password);
   1.402 -		}
   1.403 -
   1.404 -		SetAdminImmunityLevel(adm, immunity);
   1.405 -		
   1.406 -		/* Apply each flag */
   1.407 -		new len = strlen(flags);
   1.408 -		new AdminFlag:flag;
   1.409 -		for (new i=0; i<len; i++)
   1.410 -		{
   1.411 -			if (!FindFlagByChar(flags[i], flag))
   1.412 -			{
   1.413 -				continue;
   1.414 -			}
   1.415 -			SetAdminFlag(adm, flag, true);
   1.416 -		}
   1.417 -	}
   1.418 -	
   1.419 -	/**
   1.420 -	 * Try binding the user.
   1.421 -	 */	
   1.422 -	new group_count = 0;
   1.423 -	RunAdminCacheChecks(client);
   1.424 -	adm = GetUserAdmin(client);
   1.425 -	id = 0;
   1.426 -	
   1.427 -	
   1.428 -	for (new i=0; i<total_users; i++)
   1.429 -	{
   1.430 -		if (user_lookup[i][1] == _:adm)
   1.431 -		{
   1.432 -			id = user_lookup[i][0];
   1.433 -			group_count = user_lookup[i][2];
   1.434 -			break;
   1.435 -		}
   1.436 -	}
   1.437 -	
   1.438 -#if defined _DEBUG
   1.439 -	PrintToServer("Binding client (%d, %d) resulted in: (%d, %d, %d)", client, sequence, id, adm, group_count);
   1.440 -#endif
   1.441 -	
   1.442 -	/**
   1.443 -	 * If we can't verify that we assigned a database admin, or the user has no 
   1.444 -	 * groups, don't bother doing anything.
   1.445 -	 */
   1.446 -	if (!id || !group_count)
   1.447 -	{
   1.448 -		NotifyPostAdminCheck(client);
   1.449 -		CloseHandle(pk);
   1.450 -		return;
   1.451 -	}
   1.452 -	
   1.453 -	/**
   1.454 -	 * The user has groups -- we need to fetch them!
   1.455 -	 */
   1.456 -	decl String:query[255];
   1.457 -	Format(query, sizeof(query), "SELECT g.name FROM sm_admins_groups ag JOIN sm_groups g ON ag.group_id = g.id WHERE ag.admin_id = %d", id);
   1.458 -	 
   1.459 -	ResetPack(pk);
   1.460 -	WritePackCell(pk, client);
   1.461 -	WritePackCell(pk, sequence);
   1.462 -	WritePackCell(pk, _:adm);
   1.463 -	WritePackString(pk, query);
   1.464 -	
   1.465 -	SQL_TQuery(owner, OnReceiveUserGroups, query, pk, DBPrio_High);
   1.466 -}
   1.467 -
   1.468 -FetchUser(Handle:db, client)
   1.469 -{
   1.470 -	decl String:name[65];
   1.471 -	decl String:safe_name[140];
   1.472 -	decl String:steamid[32];
   1.473 -	decl String:ipaddr[24];
   1.474 -	
   1.475 -	/**
   1.476 -	 * Get authentication information from the client.
   1.477 -	 */
   1.478 -	GetClientName(client, name, sizeof(name));
   1.479 -	GetClientIP(client, ipaddr, sizeof(ipaddr));
   1.480 -	
   1.481 -	steamid[0] = '\0';
   1.482 -	if (GetClientAuthString(client, steamid, sizeof(steamid)))
   1.483 -	{
   1.484 -		if (StrEqual(steamid, "STEAM_ID_LAN"))
   1.485 -		{
   1.486 -			steamid[0] = '\0';
   1.487 -		}
   1.488 -	}
   1.489 -	
   1.490 -	SQL_EscapeString(db, name, safe_name, sizeof(safe_name));
   1.491 -	
   1.492 -	/**
   1.493 -	 * Construct the query using the information the user gave us.
   1.494 -	 */
   1.495 -	decl String:query[512];
   1.496 -	new len = 0;
   1.497 -	
   1.498 -	len += Format(query[len], sizeof(query)-len, "SELECT a.id, a.authtype, a.identity, a.password, a.flags, a.name, COUNT(ag.group_id), immunity");
   1.499 -	len += Format(query[len], sizeof(query)-len, " FROM sm_admins a LEFT JOIN sm_admins_groups ag ON a.id = ag.admin_id WHERE ");
   1.500 -	len += Format(query[len], sizeof(query)-len, " (a.authtype = 'ip' AND a.identity = '%s')", ipaddr);
   1.501 -	len += Format(query[len], sizeof(query)-len, " OR (a.authtype = 'name' AND a.identity = '%s')", safe_name);
   1.502 -	if (steamid[0] != '\0')
   1.503 -	{
   1.504 -		len += Format(query[len], sizeof(query)-len, " OR (a.authtype = 'steam' AND a.identity = '%s')", steamid);
   1.505 -	}
   1.506 -	len += Format(query[len], sizeof(query)-len, " GROUP BY a.id");
   1.507 -	
   1.508 -	/**
   1.509 -	 * Send the actual query.
   1.510 -	 */	
   1.511 -	PlayerSeq[client] = ++g_sequence;
   1.512 -	
   1.513 -	new Handle:pk;
   1.514 -	pk = CreateDataPack();
   1.515 -	WritePackCell(pk, client);
   1.516 -	WritePackCell(pk, PlayerSeq[client]);
   1.517 -	WritePackString(pk, query);
   1.518 -	
   1.519 -#if defined _DEBUG
   1.520 -	PrintToServer("Sending user query: %s", query);
   1.521 -#endif
   1.522 -	
   1.523 -	SQL_TQuery(db, OnReceiveUser, query, pk, DBPrio_High);
   1.524 -}
   1.525 -
   1.526 -FetchUsersWeCan(Handle:db)
   1.527 -{
   1.528 -	new max_clients = GetMaxClients();
   1.529 -	
   1.530 -	for (new i=1; i<=max_clients; i++)
   1.531 -	{
   1.532 -		if (PlayerAuth[i] && GetUserAdmin(i) == INVALID_ADMIN_ID)
   1.533 -		{
   1.534 -			FetchUser(db, i);
   1.535 -		}
   1.536 -	}
   1.537 -	
   1.538 -	/**
   1.539 -	 * This round of updates is done.  Go in peace.
   1.540 -	 */
   1.541 -	RebuildCachePart[_:AdminCache_Admins] = 0;
   1.542 -}
   1.543 -
   1.544 -
   1.545 -public OnReceiveGroupImmunity(Handle:owner, Handle:hndl, const String:error[], any:data)
   1.546 -{
   1.547 -	new Handle:pk = Handle:data;
   1.548 -	ResetPack(pk);
   1.549 -	
   1.550 -	/**
   1.551 -	 * Check if this is the latest result request.
   1.552 -	 */
   1.553 -	new sequence = ReadPackCell(pk);
   1.554 -	if (RebuildCachePart[_:AdminCache_Groups] != sequence)
   1.555 -	{
   1.556 -		/* Discard everything, since we're out of sequence. */
   1.557 -		CloseHandle(pk);
   1.558 -		return;
   1.559 -	}
   1.560 -	
   1.561 -	/**
   1.562 -	 * If we need to use the results, make sure they succeeded.
   1.563 -	 */
   1.564 -	if (hndl == INVALID_HANDLE)
   1.565 -	{
   1.566 -		decl String:query[255];
   1.567 -		ReadPackString(pk, query, sizeof(query));		
   1.568 -		LogError("SQL error receiving group immunity: %s", error);
   1.569 -		LogError("Query dump: %s", query);
   1.570 -		CloseHandle(pk);	
   1.571 -		return;
   1.572 -	}
   1.573 -	
   1.574 -	/* We're done with the pack forever. */
   1.575 -	CloseHandle(pk);
   1.576 -	
   1.577 -	while (SQL_FetchRow(hndl))
   1.578 -	{
   1.579 -		decl String:group1[80];
   1.580 -		decl String:group2[80];
   1.581 -		new GroupId:gid1, GroupId:gid2;
   1.582 -		
   1.583 -		SQL_FetchString(hndl, 0, group1, sizeof(group1));
   1.584 -		SQL_FetchString(hndl, 1, group2, sizeof(group2));
   1.585 -		
   1.586 -		if (((gid1 = FindAdmGroup(group1)) == INVALID_GROUP_ID)
   1.587 -			|| (gid2 = FindAdmGroup(group2)) == INVALID_GROUP_ID)
   1.588 -		{
   1.589 -			continue;
   1.590 -		}
   1.591 -		
   1.592 -		SetAdmGroupImmuneFrom(gid1, gid2);
   1.593 -#if defined _DEBUG
   1.594 -		PrintToServer("SetAdmGroupImmuneFrom(%d, %d)", gid1, gid2);
   1.595 -#endif
   1.596 -	}
   1.597 -	
   1.598 -	/* Clear the sequence so another connect doesn't refetch */
   1.599 -	RebuildCachePart[_:AdminCache_Groups] = 0;
   1.600 -}
   1.601 -
   1.602 -public OnReceiveGroupOverrides(Handle:owner, Handle:hndl, const String:error[], any:data)
   1.603 -{
   1.604 -	new Handle:pk = Handle:data;
   1.605 -	ResetPack(pk);
   1.606 -	
   1.607 -	/**
   1.608 -	 * Check if this is the latest result request.
   1.609 -	 */
   1.610 -	new sequence = ReadPackCell(pk);
   1.611 -	if (RebuildCachePart[_:AdminCache_Groups] != sequence)
   1.612 -	{
   1.613 -		/* Discard everything, since we're out of sequence. */
   1.614 -		CloseHandle(pk);
   1.615 -		return;
   1.616 -	}
   1.617 -	
   1.618 -	/**
   1.619 -	 * If we need to use the results, make sure they succeeded.
   1.620 -	 */
   1.621 -	if (hndl == INVALID_HANDLE)
   1.622 -	{
   1.623 -		decl String:query[255];
   1.624 -		ReadPackString(pk, query, sizeof(query));		
   1.625 -		LogError("SQL error receiving group overrides: %s", error);
   1.626 -		LogError("Query dump: %s", query);
   1.627 -		CloseHandle(pk);	
   1.628 -		return;
   1.629 -	}
   1.630 -	
   1.631 -	/**
   1.632 -	 * Fetch the overrides.
   1.633 -	 */
   1.634 -	decl String:name[80];
   1.635 -	decl String:type[16];
   1.636 -	decl String:command[64];
   1.637 -	decl String:access[16];
   1.638 -	new GroupId:gid;
   1.639 -	while (SQL_FetchRow(hndl))
   1.640 -	{
   1.641 -		SQL_FetchString(hndl, 0, name, sizeof(name));
   1.642 -		SQL_FetchString(hndl, 1, type, sizeof(type));
   1.643 -		SQL_FetchString(hndl, 2, command, sizeof(command));
   1.644 -		SQL_FetchString(hndl, 3, access, sizeof(access));
   1.645 -		
   1.646 -		/* Find the group.  This is actually faster than doing the ID lookup. */
   1.647 -		if ((gid = FindAdmGroup(name)) == INVALID_GROUP_ID)
   1.648 -		{
   1.649 -			/* Oh well, just ignore it. */
   1.650 -			continue;
   1.651 -		}
   1.652 -		
   1.653 -		new OverrideType:o_type = Override_Command;
   1.654 -		if (StrEqual(type, "group"))
   1.655 -		{
   1.656 -			o_type = Override_CommandGroup;
   1.657 -		}
   1.658 -				
   1.659 -		new OverrideRule:o_rule = Command_Deny;
   1.660 -		if (StrEqual(access, "allow"))
   1.661 -		{
   1.662 -			o_rule = Command_Allow;
   1.663 -		}
   1.664 -				
   1.665 -#if defined _DEBUG
   1.666 -		PrintToServer("AddAdmGroupCmdOverride(%d, %s, %d, %d)", gid, command, o_type, o_rule);
   1.667 -#endif
   1.668 -				
   1.669 -		AddAdmGroupCmdOverride(gid, command, o_type, o_rule);
   1.670 -	}
   1.671 -	
   1.672 -	/**
   1.673 -	 * It's time to get the group immunity list.
   1.674 -	 */
   1.675 -	new len = 0;
   1.676 -	decl String:query[256];
   1.677 -	len += Format(query[len], sizeof(query)-len, "SELECT g1.name, g2.name FROM sm_group_immunity gi");
   1.678 -	len += Format(query[len], sizeof(query)-len, " LEFT JOIN sm_groups g1 ON g1.id = gi.group_id ");
   1.679 -	len += Format(query[len], sizeof(query)-len, " LEFT JOIN sm_groups g2 ON g2.id = gi.other_id");
   1.680 -
   1.681 -	ResetPack(pk);
   1.682 -	WritePackCell(pk, sequence);
   1.683 -	WritePackString(pk, query);
   1.684 -	
   1.685 -	SQL_TQuery(owner, OnReceiveGroupImmunity, query, pk, DBPrio_High);
   1.686 -}
   1.687 -
   1.688 -public OnReceiveGroups(Handle:owner, Handle:hndl, const String:error[], any:data)
   1.689 -{
   1.690 -	new Handle:pk = Handle:data;
   1.691 -	ResetPack(pk);
   1.692 -	
   1.693 -	/**
   1.694 -	 * Check if this is the latest result request.
   1.695 -	 */
   1.696 -	new sequence = ReadPackCell(pk);
   1.697 -	if (RebuildCachePart[_:AdminCache_Groups] != sequence)
   1.698 -	{
   1.699 -		/* Discard everything, since we're out of sequence. */
   1.700 -		CloseHandle(pk);
   1.701 -		return;
   1.702 -	}
   1.703 -	
   1.704 -	/**
   1.705 -	 * If we need to use the results, make sure they succeeded.
   1.706 -	 */
   1.707 -	if (hndl == INVALID_HANDLE)
   1.708 -	{
   1.709 -		decl String:query[255];
   1.710 -		ReadPackString(pk, query, sizeof(query));
   1.711 -		LogError("SQL error receiving groups: %s", error);
   1.712 -		LogError("Query dump: %s", query);
   1.713 -		CloseHandle(pk);
   1.714 -		return;
   1.715 -	}
   1.716 -	
   1.717 -	/**
   1.718 -	 * Now start fetching groups.
   1.719 -	 */
   1.720 -	decl String:flags[32];
   1.721 -	decl String:name[128];
   1.722 -	new immunity;
   1.723 -	while (SQL_FetchRow(hndl))
   1.724 -	{
   1.725 -		SQL_FetchString(hndl, 0, flags, sizeof(flags));
   1.726 -		SQL_FetchString(hndl, 1, name, sizeof(name));
   1.727 -		immunity = SQL_FetchInt(hndl, 2);
   1.728 -		
   1.729 -#if defined _DEBUG
   1.730 -		PrintToServer("Adding group (%d, %s, %s)", immunity, flags, name);
   1.731 -#endif
   1.732 -		
   1.733 -		/* Find or create the group */
   1.734 -		new GroupId:gid;
   1.735 -		if ((gid = FindAdmGroup(name)) == INVALID_GROUP_ID)
   1.736 -		{
   1.737 -			gid = CreateAdmGroup(name);
   1.738 -		}
   1.739 -
   1.740 -		/* Add flags from the database to the group */
   1.741 -		new num_flag_chars = strlen(flags);
   1.742 -		for (new i=0; i<num_flag_chars; i++)
   1.743 -		{
   1.744 -			decl AdminFlag:flag;
   1.745 -			if (!FindFlagByChar(flags[i], flag))
   1.746 -			{
   1.747 -				continue;
   1.748 -			}
   1.749 -			SetAdmGroupAddFlag(gid, flag, true);
   1.750 -		}
   1.751 -		
   1.752 -		SetAdmGroupImmunityLevel(gid, immunity);
   1.753 -	}
   1.754 -	
   1.755 -	/**
   1.756 -	 * It's time to get the group override list.
   1.757 -	 */
   1.758 -	decl String:query[255];
   1.759 -	Format(query, 
   1.760 -		sizeof(query), 
   1.761 -		"SELECT g.name, og.type, og.name, og.access FROM sm_group_overrides og JOIN sm_groups g ON og.group_id = g.id ORDER BY g.id DESC");
   1.762 -
   1.763 -	ResetPack(pk);
   1.764 -	WritePackCell(pk, sequence);
   1.765 -	WritePackString(pk, query);
   1.766 -	
   1.767 -	SQL_TQuery(owner, OnReceiveGroupOverrides, query, pk, DBPrio_High);
   1.768 -}
   1.769 -
   1.770 -FetchGroups(Handle:db, sequence)
   1.771 -{
   1.772 -	decl String:query[255];
   1.773 -	new Handle:pk;
   1.774 -	
   1.775 -	Format(query, sizeof(query), "SELECT flags, name, immunity_level FROM sm_groups");
   1.776 -
   1.777 -	pk = CreateDataPack();
   1.778 -	WritePackCell(pk, sequence);
   1.779 -	WritePackString(pk, query);
   1.780 -	
   1.781 -	SQL_TQuery(db, OnReceiveGroups, query, pk, DBPrio_High);
   1.782 -}
   1.783 -
   1.784 -public OnReceiveOverrides(Handle:owner, Handle:hndl, const String:error[], any:data)
   1.785 -{
   1.786 -	new Handle:pk = Handle:data;
   1.787 -	ResetPack(pk);
   1.788 -	
   1.789 -	/**
   1.790 -	 * Check if this is the latest result request.
   1.791 -	 */
   1.792 -	new sequence = ReadPackCell(pk);
   1.793 -	if (RebuildCachePart[_:AdminCache_Overrides] != sequence)
   1.794 -	{
   1.795 -		/* Discard everything, since we're out of sequence. */
   1.796 -		CloseHandle(pk);
   1.797 -		return;
   1.798 -	}
   1.799 -	
   1.800 -	/**
   1.801 -	 * If we need to use the results, make sure they succeeded.
   1.802 -	 */
   1.803 -	if (hndl == INVALID_HANDLE)
   1.804 -	{
   1.805 -		decl String:query[255];
   1.806 -		ReadPackString(pk, query, sizeof(query));
   1.807 -		LogError("SQL error receiving overrides: %s", error);
   1.808 -		LogError("Query dump: %s", query);
   1.809 -		CloseHandle(pk);
   1.810 -		return;
   1.811 -	}
   1.812 -	
   1.813 -	/**
   1.814 -	 * We're done with you, now.
   1.815 -	 */
   1.816 -	CloseHandle(pk);
   1.817 -	
   1.818 -	decl String:type[64];
   1.819 -	decl String:name[64];
   1.820 -	decl String:flags[32];
   1.821 -	new flag_bits;
   1.822 -	while (SQL_FetchRow(hndl))
   1.823 -	{
   1.824 -		SQL_FetchString(hndl, 0, type, sizeof(type));
   1.825 -		SQL_FetchString(hndl, 1, name, sizeof(name));
   1.826 -		SQL_FetchString(hndl, 2, flags, sizeof(flags));
   1.827 -		
   1.828 -#if defined _DEBUG
   1.829 -		PrintToServer("Adding override (%s, %s, %s)", type, name, flags);
   1.830 -#endif
   1.831 -		
   1.832 -		flag_bits = ReadFlagString(flags);
   1.833 -		if (StrEqual(type, "command"))
   1.834 -		{
   1.835 -			AddCommandOverride(name, Override_Command, flag_bits);
   1.836 -		} else if (StrEqual(type, "group")) {
   1.837 -			AddCommandOverride(name, Override_CommandGroup, flag_bits);
   1.838 -		}
   1.839 -	}
   1.840 -	
   1.841 -	/* Clear the sequence so another connect doesn't refetch */
   1.842 -	RebuildCachePart[_:AdminCache_Overrides] = 0;
   1.843 -}
   1.844 -
   1.845 -FetchOverrides(Handle:db, sequence)
   1.846 -{
   1.847 -	decl String:query[255];
   1.848 -	new Handle:pk;
   1.849 -	
   1.850 -	Format(query, sizeof(query), "SELECT type, name, flags FROM sm_overrides");
   1.851 -
   1.852 -	pk = CreateDataPack();
   1.853 -	WritePackCell(pk, sequence);
   1.854 -	WritePackString(pk, query);
   1.855 -	
   1.856 -	SQL_TQuery(db, OnReceiveOverrides, query, pk, DBPrio_High);
   1.857 -}
   1.858 -