plugins/admin-sql-threaded.sp
author David Anderson <dvander@alliedmods.net>
Sat Dec 22 08:57:08 2007 +0000 (2007-12-22)
changeset 1813 372939c88bc7
parent 1403 ecb9c66fd4bb
child 1874 6bd98dba0f2a
permissions -rw-r--r--
fixed amb1305 by making OnClientPostAdminCheck() always occur after OnClientPutInServer(). this is now GUARANTEED. it was undefined before, however the usual case was opposite. if plugins were relying on this, they were written poorly and shouldn't have worked in the first place, especially with the threaded SQL plugin.
dvander@1322
     1
/**
ds@1330
     2
 * vim: set ts=4 :
ds@1330
     3
 * =============================================================================
ds@1330
     4
 * SourceMod SQL Admins Plugin (Threaded)
ds@1330
     5
 * Fetches admins from an SQL database dynamically.
dvander@1322
     6
 *
ds@1330
     7
 * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
ds@1330
     8
 * =============================================================================
dvander@1322
     9
 *
ds@1330
    10
 * This program is free software; you can redistribute it and/or modify it under
ds@1330
    11
 * the terms of the GNU General Public License, version 3.0, as published by the
ds@1330
    12
 * Free Software Foundation.
ds@1330
    13
 * 
ds@1330
    14
 * This program is distributed in the hope that it will be useful, but WITHOUT
ds@1330
    15
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
ds@1330
    16
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
ds@1330
    17
 * details.
dvander@1322
    18
 *
ds@1330
    19
 * You should have received a copy of the GNU General Public License along with
ds@1330
    20
 * this program.  If not, see <http://www.gnu.org/licenses/>.
ds@1330
    21
 *
ds@1330
    22
 * As a special exception, AlliedModders LLC gives you permission to link the
ds@1330
    23
 * code of this program (as well as its derivative works) to "Half-Life 2," the
ds@1330
    24
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
ds@1330
    25
 * by the Valve Corporation.  You must obey the GNU General Public License in
ds@1330
    26
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
ds@1330
    27
 * this exception to all derivative works.  AlliedModders LLC defines further
ds@1330
    28
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
ds@1330
    29
 * or <http://www.sourcemod.net/license.php>.
dvander@1322
    30
 *
dvander@1322
    31
 * Version: $Id$
dvander@1322
    32
 */
dvander@1322
    33
dvander@1322
    34
/* We like semicolons */
dvander@1322
    35
#pragma semicolon 1
dvander@1322
    36
dvander@1322
    37
#include <sourcemod>
dvander@1322
    38
dvander@1322
    39
public Plugin:myinfo = 
dvander@1322
    40
{
dvander@1322
    41
	name = "SQL Admins (Threaded)",
dvander@1322
    42
	author = "AlliedModders LLC",
dvander@1322
    43
	description = "Reads admins from SQL dynamically",
dvander@1322
    44
	version = SOURCEMOD_VERSION,
dvander@1322
    45
	url = "http://www.sourcemod.net/"
dvander@1322
    46
};
dvander@1322
    47
dvander@1322
    48
/**
dvander@1322
    49
 * Notes:
dvander@1322
    50
 *
dvander@1322
    51
 * 1) All queries in here are high priority.  This is because the admin stuff 
dvander@1322
    52
 *    is very important.  Do not take this to mean that in your script, 
dvander@1322
    53
 *    everything should be high priority.  
dvander@1322
    54
 *
dvander@1322
    55
 * 2) All callbacks are locked with "sequence numbers."  This is to make sure 
dvander@1322
    56
 *    that multiple calls to sm_reloadadmins and the like do not make us 
dvander@1322
    57
 *    store the results from two or more callbacks accidentally.  Instead, we 
dvander@1322
    58
 *    check the sequence number in each callback with the current "allowed" 
dvander@1322
    59
 *    sequence number, and if it doesn't match, the callback is cancelled.
dvander@1322
    60
 *
dvander@1322
    61
 * 3) Sequence numbers for groups and overrides are not cleared unless there 
dvander@1322
    62
 *    was a 100% success in the fetch.  This is so we can potentially implement 
dvander@1322
    63
 *    connection retries in the future.
dvander@1322
    64
 *
dvander@1326
    65
 * 4) Sequence numbers for the user cache are ignored except for being 
dvander@1326
    66
 *    non-zero, which means players in-game should be re-checked for admin 
dvander@1322
    67
 *    powers.
dvander@1322
    68
 */
dvander@1322
    69
dvander@1322
    70
new Handle:hDatabase = INVALID_HANDLE;			/** Database connection */
dvander@1322
    71
new g_sequence = 0;								/** Global unique sequence number */
dvander@1322
    72
new ConnectLock = 0;							/** Connect sequence number */
dvander@1322
    73
new RebuildCachePart[3] = {0};					/** Cache part sequence numbers */
dvander@1322
    74
new PlayerSeq[MAXPLAYERS+1];					/** Player-specific sequence numbers */
dvander@1322
    75
new bool:PlayerAuth[MAXPLAYERS+1];				/** Whether a player has been "pre-authed" */
dvander@1322
    76
dvander@1322
    77
#define _DEBUG
dvander@1322
    78
dvander@1322
    79
public OnMapEnd()
dvander@1322
    80
{
dvander@1322
    81
	/**
dvander@1322
    82
	 * Clean up on map end just so we can start a fresh connection when we need it later.
dvander@1322
    83
	 */
dvander@1322
    84
	if (hDatabase != INVALID_HANDLE)
dvander@1322
    85
	{
dvander@1322
    86
		CloseHandle(hDatabase);
dvander@1322
    87
		hDatabase = INVALID_HANDLE;
dvander@1322
    88
	}
dvander@1322
    89
}
dvander@1322
    90
dvander@1322
    91
public bool:OnClientConnect(client, String:rejectmsg[], maxlen)
dvander@1322
    92
{
dvander@1322
    93
	PlayerSeq[client] = 0;
dvander@1322
    94
	PlayerAuth[client] = false;
dvander@1322
    95
	return true;
dvander@1322
    96
}
dvander@1322
    97
dvander@1322
    98
public OnClientDisconnect(client)
dvander@1322
    99
{
dvander@1322
   100
	PlayerSeq[client] = 0;
dvander@1322
   101
	PlayerAuth[client] = false;
dvander@1322
   102
}
dvander@1322
   103
dvander@1322
   104
public OnDatabaseConnect(Handle:owner, Handle:hndl, const String:error[], any:data)
dvander@1322
   105
{
dvander@1322
   106
#if defined _DEBUG
dvander@1322
   107
	PrintToServer("OnDatabaseConnect(%x,%x,%d) ConnectLock=%d", owner, hndl, data, ConnectLock);
dvander@1322
   108
#endif
dvander@1322
   109
dvander@1322
   110
	/**
dvander@1322
   111
	 * If this happens to be an old connection request, ignore it.
dvander@1322
   112
	 */
dvander@1322
   113
	if (data != ConnectLock || hDatabase != INVALID_HANDLE)
dvander@1322
   114
	{
dvander@1322
   115
		if (hndl != INVALID_HANDLE)
dvander@1322
   116
		{
dvander@1322
   117
			CloseHandle(hndl);
dvander@1322
   118
		}
dvander@1322
   119
		return;
dvander@1322
   120
	}
dvander@1322
   121
	
dvander@1322
   122
	ConnectLock = 0;
dvander@1322
   123
	hDatabase = hndl;
dvander@1322
   124
	
dvander@1322
   125
	/**
dvander@1322
   126
	 * See if the connection is valid.  If not, don't un-mark the caches
dvander@1322
   127
	 * as needing rebuilding, in case the next connection request works.
dvander@1322
   128
	 */
dvander@1322
   129
	if (hDatabase == INVALID_HANDLE)
dvander@1322
   130
	{
dvander@1322
   131
		LogError("Failed to connect to database: %s", error);
dvander@1322
   132
		return;
dvander@1322
   133
	}
dvander@1322
   134
	
dvander@1322
   135
	/**
dvander@1322
   136
	 * See if we need to get any of the cache stuff now.
dvander@1322
   137
	 */
dvander@1322
   138
	new sequence;
dvander@1322
   139
	if ((sequence = RebuildCachePart[_:AdminCache_Overrides]) != 0)
dvander@1322
   140
	{
dvander@1322
   141
		FetchOverrides(hDatabase, sequence);
dvander@1322
   142
	}
dvander@1322
   143
	if ((sequence = RebuildCachePart[_:AdminCache_Groups]) != 0)
dvander@1322
   144
	{
dvander@1322
   145
		FetchGroups(hDatabase, sequence);
dvander@1322
   146
	}
dvander@1322
   147
	if ((sequence = RebuildCachePart[_:AdminCache_Admins]) != 0)
dvander@1322
   148
	{
dvander@1322
   149
		FetchUsersWeCan(hDatabase);
dvander@1322
   150
	}
dvander@1322
   151
}
dvander@1322
   152
dvander@1322
   153
RequestDatabaseConnection()
dvander@1322
   154
{
dvander@1322
   155
	ConnectLock = ++g_sequence;
dvander@1324
   156
	if (SQL_CheckConfig("admins"))
dvander@1324
   157
	{
dvander@1324
   158
		SQL_TConnect(OnDatabaseConnect, "admins", ConnectLock);
dvander@1324
   159
	} else {
dvander@1324
   160
		SQL_TConnect(OnDatabaseConnect, "default", ConnectLock);
dvander@1324
   161
	}
dvander@1322
   162
}
dvander@1322
   163
dvander@1322
   164
public OnRebuildAdminCache(AdminCachePart:part)
dvander@1322
   165
{
dvander@1322
   166
	/**
dvander@1322
   167
	 * Mark this part of the cache as being rebuilt.  This is used by the 
dvander@1322
   168
	 * callback system to determine whether the results should still be 
dvander@1322
   169
	 * used.
dvander@1322
   170
	 */
dvander@1322
   171
	new sequence = ++g_sequence;
dvander@1322
   172
	RebuildCachePart[_:part] = sequence;
dvander@1322
   173
	
dvander@1322
   174
	/**
dvander@1322
   175
	 * If we don't have a database connection, we can't do any lookups just yet.
dvander@1322
   176
	 */
dvander@1322
   177
	if (!hDatabase)
dvander@1322
   178
	{
dvander@1322
   179
		/**
dvander@1322
   180
		 * Ask for a new connection if we need it.
dvander@1322
   181
		 */
dvander@1322
   182
		if (!ConnectLock)
dvander@1322
   183
		{
dvander@1322
   184
			RequestDatabaseConnection();
dvander@1322
   185
		}
dvander@1322
   186
		return;
dvander@1322
   187
	}
dvander@1322
   188
	
dvander@1322
   189
	if (part == AdminCache_Overrides)
dvander@1322
   190
	{
dvander@1322
   191
		FetchOverrides(hDatabase, sequence);
dvander@1322
   192
	} else if (part == AdminCache_Groups) {
dvander@1322
   193
		FetchGroups(hDatabase, sequence);
dvander@1322
   194
	} else if (part == AdminCache_Admins) {
dvander@1322
   195
		FetchUsersWeCan(hDatabase);
dvander@1322
   196
	}
dvander@1322
   197
}
dvander@1322
   198
dvander@1322
   199
public Action:OnClientPreAdminCheck(client)
dvander@1322
   200
{
dvander@1322
   201
	PlayerAuth[client] = true;
dvander@1322
   202
	
dvander@1322
   203
	/**
dvander@1322
   204
	 * Play nice with other plugins.  If there's no database, don't delay the 
dvander@1322
   205
	 * connection process.  Unfortunately, we can't attempt anything else and 
dvander@1322
   206
	 * we just have to hope either the database is waiting or someone will type 
dvander@1322
   207
	 * sm_reloadadmins.
dvander@1322
   208
	 */
dvander@1322
   209
	if (hDatabase == INVALID_HANDLE)
dvander@1322
   210
	{
dvander@1322
   211
		return Plugin_Continue;
dvander@1322
   212
	}
dvander@1322
   213
	
dvander@1322
   214
	/**
dvander@1322
   215
	 * Similarly, if the cache is in the process of being rebuilt, don't delay 
dvander@1322
   216
	 * the user's normal connection flow.  The database will soon auth the user 
dvander@1322
   217
	 * normally.
dvander@1322
   218
	 */
dvander@1322
   219
	if (RebuildCachePart[_:AdminCache_Admins] != 0)
dvander@1322
   220
	{
dvander@1322
   221
		return Plugin_Continue;
dvander@1322
   222
	}
dvander@1322
   223
	
dvander@1322
   224
	/**
dvander@1322
   225
	 * If someone has already assigned an admin ID (bad bad bad), don't 
dvander@1322
   226
	 * bother waiting.
dvander@1322
   227
	 */
dvander@1322
   228
	if (GetUserAdmin(client) != INVALID_ADMIN_ID)
dvander@1322
   229
	{
dvander@1322
   230
		return Plugin_Continue;
dvander@1322
   231
	}
dvander@1322
   232
	
dvander@1322
   233
	FetchUser(hDatabase, client);
dvander@1322
   234
	
dvander@1322
   235
	return Plugin_Handled;
dvander@1322
   236
}
dvander@1322
   237
dvander@1322
   238
public OnReceiveUserGroups(Handle:owner, Handle:hndl, const String:error[], any:data)
dvander@1322
   239
{
dvander@1322
   240
	new Handle:pk = Handle:data;
dvander@1322
   241
	ResetPack(pk);
dvander@1322
   242
	
dvander@1322
   243
	new client = ReadPackCell(pk);
dvander@1322
   244
	new sequence = ReadPackCell(pk);
dvander@1322
   245
	
dvander@1322
   246
	/**
dvander@1322
   247
	 * Make sure it's the same client.
dvander@1322
   248
	 */
dvander@1322
   249
	if (PlayerSeq[client] != sequence)
dvander@1322
   250
	{
dvander@1322
   251
		CloseHandle(pk);
dvander@1322
   252
		return;
dvander@1322
   253
	}
dvander@1322
   254
	
dvander@1322
   255
	new AdminId:adm = AdminId:ReadPackCell(pk);
dvander@1322
   256
	
dvander@1322
   257
	/**
dvander@1322
   258
	 * Someone could have sneakily changed the admin id while we waited.
dvander@1322
   259
	 */
dvander@1322
   260
	if (GetUserAdmin(client) != adm)
dvander@1322
   261
	{
dvander@1322
   262
		NotifyPostAdminCheck(client);
dvander@1322
   263
		CloseHandle(pk);
dvander@1322
   264
		return;
dvander@1322
   265
	}
dvander@1322
   266
	
dvander@1322
   267
	/**
dvander@1322
   268
	 * See if we got results.
dvander@1322
   269
	 */
dvander@1322
   270
	if (hndl == INVALID_HANDLE)
dvander@1322
   271
	{
dvander@1322
   272
		decl String:query[255];
dvander@1322
   273
		ReadPackString(pk, query, sizeof(query));
dvander@1322
   274
		LogError("SQL error receiving user: %s", error);
dvander@1322
   275
		LogError("Query dump: %s", query);
dvander@1322
   276
		NotifyPostAdminCheck(client);
dvander@1322
   277
		CloseHandle(pk);
dvander@1322
   278
		return;
dvander@1322
   279
	}
dvander@1322
   280
	
dvander@1322
   281
	decl String:name[80];
dvander@1322
   282
	new GroupId:gid;
dvander@1322
   283
	
dvander@1322
   284
	while (SQL_FetchRow(hndl))
dvander@1322
   285
	{
dvander@1322
   286
		SQL_FetchString(hndl, 0, name, sizeof(name));
dvander@1322
   287
		
dvander@1322
   288
		if ((gid = FindAdmGroup(name)) == INVALID_GROUP_ID)
dvander@1322
   289
		{
dvander@1322
   290
			continue;
dvander@1322
   291
		}
dvander@1322
   292
		
dvander@1322
   293
#if defined _DEBUG
dvander@1322
   294
		PrintToServer("Binding user group (%d, %d, %d, %s, %d)", client, sequence, adm, name, gid);
dvander@1322
   295
#endif
dvander@1322
   296
		
dvander@1322
   297
		AdminInheritGroup(adm, gid);
dvander@1322
   298
	}
dvander@1322
   299
	
dvander@1322
   300
	/**
dvander@1322
   301
	 * We're DONE! Omg.
dvander@1322
   302
	 */
dvander@1322
   303
	NotifyPostAdminCheck(client);
dvander@1322
   304
	CloseHandle(pk);
dvander@1322
   305
}
dvander@1322
   306
dvander@1322
   307
public OnReceiveUser(Handle:owner, Handle:hndl, const String:error[], any:data)
dvander@1322
   308
{
dvander@1322
   309
	new Handle:pk = Handle:data;
dvander@1322
   310
	ResetPack(pk);
dvander@1322
   311
	
dvander@1322
   312
	new client = ReadPackCell(pk);
dvander@1322
   313
	
dvander@1322
   314
	/**
dvander@1322
   315
	 * Check if this is the latest result request.
dvander@1322
   316
	 */
dvander@1322
   317
	new sequence = ReadPackCell(pk);
dvander@1322
   318
	if (PlayerSeq[client] != sequence)
dvander@1322
   319
	{
dvander@1322
   320
		/* Discard everything, since we're out of sequence. */
dvander@1322
   321
		CloseHandle(pk);
dvander@1322
   322
		return;
dvander@1322
   323
	}
dvander@1322
   324
	
dvander@1322
   325
	/**
dvander@1322
   326
	 * If we need to use the results, make sure they succeeded.
dvander@1322
   327
	 */
dvander@1322
   328
	if (hndl == INVALID_HANDLE)
dvander@1322
   329
	{
dvander@1322
   330
		decl String:query[255];
dvander@1322
   331
		ReadPackString(pk, query, sizeof(query));
dvander@1322
   332
		LogError("SQL error receiving user: %s", error);
dvander@1322
   333
		LogError("Query dump: %s", query);
dvander@1322
   334
		RunAdminCacheChecks(client);
dvander@1322
   335
		NotifyPostAdminCheck(client);
dvander@1322
   336
		CloseHandle(pk);
dvander@1322
   337
		return;
dvander@1322
   338
	}
dvander@1322
   339
	
dvander@1322
   340
	new num_accounts = SQL_GetRowCount(hndl);
dvander@1322
   341
	if (num_accounts == 0)
dvander@1322
   342
	{
dvander@1322
   343
		RunAdminCacheChecks(client);
dvander@1322
   344
		NotifyPostAdminCheck(client);
dvander@1322
   345
		CloseHandle(pk);
dvander@1322
   346
		return;
dvander@1322
   347
	}
dvander@1322
   348
	
dvander@1322
   349
	decl String:authtype[16];
dvander@1322
   350
	decl String:identity[80];
dvander@1322
   351
	decl String:password[80];
dvander@1322
   352
	decl String:flags[32];
dvander@1322
   353
	decl String:name[80];
dvander@1322
   354
	new AdminId:adm, id;
dvander@1403
   355
	new immunity;
dvander@1322
   356
	
dvander@1322
   357
	/**
dvander@1322
   358
	 * Cache user info -- [0] = db id, [1] = cache id, [2] = groups
dvander@1322
   359
	 */
dvander@1322
   360
	decl user_lookup[num_accounts][3];
dvander@1322
   361
	new total_users = 0;
dvander@1322
   362
	
dvander@1322
   363
	while (SQL_FetchRow(hndl))
dvander@1322
   364
	{
dvander@1322
   365
		id = SQL_FetchInt(hndl, 0);
dvander@1322
   366
		SQL_FetchString(hndl, 1, authtype, sizeof(authtype));
dvander@1322
   367
		SQL_FetchString(hndl, 2, identity, sizeof(identity));
dvander@1322
   368
		SQL_FetchString(hndl, 3, password, sizeof(password));
dvander@1322
   369
		SQL_FetchString(hndl, 4, flags, sizeof(flags));
dvander@1322
   370
		SQL_FetchString(hndl, 5, name, sizeof(name));
dvander@1403
   371
		immunity = SQL_FetchInt(hndl, 7);
dvander@1322
   372
		
dvander@1322
   373
		/* For dynamic admins we clear anything already in the cache. */
dvander@1322
   374
		if ((adm = FindAdminByIdentity(authtype, identity)) != INVALID_ADMIN_ID)
dvander@1322
   375
		{
dvander@1322
   376
			RemoveAdmin(adm);
dvander@1322
   377
		}
dvander@1322
   378
		
dvander@1322
   379
		adm = CreateAdmin(name);
dvander@1322
   380
		if (!BindAdminIdentity(adm, authtype, identity))
dvander@1322
   381
		{
dvander@1322
   382
			LogError("Could not bind prefetched SQL admin (authtype \"%s\") (identity \"%s\")", authtype, identity);
dvander@1322
   383
			continue;
dvander@1322
   384
		}
dvander@1322
   385
		
dvander@1322
   386
		user_lookup[total_users][0] = id;
dvander@1322
   387
		user_lookup[total_users][1] = _:adm;
dvander@1322
   388
		user_lookup[total_users][2] = SQL_FetchInt(hndl, 6);
dvander@1322
   389
		total_users++;
dvander@1322
   390
		
dvander@1322
   391
#if defined _DEBUG
dvander@1403
   392
		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]);
dvander@1322
   393
#endif
dvander@1322
   394
		
dvander@1322
   395
		/* See if this admin wants a password */
dvander@1322
   396
		if (password[0] != '\0')
dvander@1322
   397
		{
dvander@1322
   398
			SetAdminPassword(adm, password);
dvander@1322
   399
		}
dvander@1403
   400
dvander@1403
   401
		SetAdminImmunityLevel(adm, immunity);
dvander@1322
   402
		
dvander@1322
   403
		/* Apply each flag */
dvander@1322
   404
		new len = strlen(flags);
dvander@1322
   405
		new AdminFlag:flag;
dvander@1322
   406
		for (new i=0; i<len; i++)
dvander@1322
   407
		{
dvander@1322
   408
			if (!FindFlagByChar(flags[i], flag))
dvander@1322
   409
			{
dvander@1322
   410
				continue;
dvander@1322
   411
			}
dvander@1322
   412
			SetAdminFlag(adm, flag, true);
dvander@1322
   413
		}
dvander@1322
   414
	}
dvander@1322
   415
	
dvander@1322
   416
	/**
dvander@1322
   417
	 * Try binding the user.
dvander@1322
   418
	 */	
dvander@1322
   419
	new group_count = 0;
dvander@1322
   420
	RunAdminCacheChecks(client);
dvander@1322
   421
	adm = GetUserAdmin(client);
dvander@1322
   422
	id = 0;
dvander@1322
   423
	
dvander@1322
   424
	
dvander@1322
   425
	for (new i=0; i<total_users; i++)
dvander@1322
   426
	{
dvander@1322
   427
		if (user_lookup[i][1] == _:adm)
dvander@1322
   428
		{
dvander@1322
   429
			id = user_lookup[i][0];
dvander@1322
   430
			group_count = user_lookup[i][2];
dvander@1322
   431
			break;
dvander@1322
   432
		}
dvander@1322
   433
	}
dvander@1322
   434
	
dvander@1322
   435
#if defined _DEBUG
dvander@1322
   436
	PrintToServer("Binding client (%d, %d) resulted in: (%d, %d, %d)", client, sequence, id, adm, group_count);
dvander@1322
   437
#endif
dvander@1322
   438
	
dvander@1322
   439
	/**
dvander@1322
   440
	 * If we can't verify that we assigned a database admin, or the user has no 
dvander@1322
   441
	 * groups, don't bother doing anything.
dvander@1322
   442
	 */
dvander@1322
   443
	if (!id || !group_count)
dvander@1322
   444
	{
dvander@1322
   445
		NotifyPostAdminCheck(client);
dvander@1322
   446
		CloseHandle(pk);
dvander@1322
   447
		return;
dvander@1322
   448
	}
dvander@1322
   449
	
dvander@1322
   450
	/**
dvander@1322
   451
	 * The user has groups -- we need to fetch them!
dvander@1322
   452
	 */
dvander@1322
   453
	decl String:query[255];
dvander@1322
   454
	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);
dvander@1322
   455
	 
dvander@1322
   456
	ResetPack(pk);
dvander@1322
   457
	WritePackCell(pk, client);
dvander@1322
   458
	WritePackCell(pk, sequence);
dvander@1322
   459
	WritePackCell(pk, _:adm);
dvander@1322
   460
	WritePackString(pk, query);
dvander@1322
   461
	
dvander@1322
   462
	SQL_TQuery(owner, OnReceiveUserGroups, query, pk, DBPrio_High);
dvander@1322
   463
}
dvander@1322
   464
dvander@1322
   465
FetchUser(Handle:db, client)
dvander@1322
   466
{
dvander@1322
   467
	decl String:name[65];
dvander@1322
   468
	decl String:safe_name[140];
dvander@1322
   469
	decl String:steamid[32];
dvander@1322
   470
	decl String:ipaddr[24];
dvander@1322
   471
	
dvander@1322
   472
	/**
dvander@1322
   473
	 * Get authentication information from the client.
dvander@1322
   474
	 */
dvander@1322
   475
	GetClientName(client, name, sizeof(name));
dvander@1322
   476
	GetClientIP(client, ipaddr, sizeof(ipaddr));
dvander@1322
   477
	
dvander@1322
   478
	steamid[0] = '\0';
dvander@1322
   479
	if (GetClientAuthString(client, steamid, sizeof(steamid)))
dvander@1322
   480
	{
dvander@1322
   481
		if (StrEqual(steamid, "STEAM_ID_LAN"))
dvander@1322
   482
		{
dvander@1322
   483
			steamid[0] = '\0';
dvander@1322
   484
		}
dvander@1322
   485
	}
dvander@1322
   486
	
dvander@1322
   487
	SQL_QuoteString(db, name, safe_name, sizeof(safe_name));
dvander@1322
   488
	
dvander@1322
   489
	/**
dvander@1322
   490
	 * Construct the query using the information the user gave us.
dvander@1322
   491
	 */
dvander@1322
   492
	decl String:query[512];
dvander@1322
   493
	new len = 0;
dvander@1322
   494
	
dvander@1403
   495
	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");
dvander@1322
   496
	len += Format(query[len], sizeof(query)-len, " FROM sm_admins a LEFT JOIN sm_admins_groups ag ON a.id = ag.admin_id WHERE ");
dvander@1322
   497
	len += Format(query[len], sizeof(query)-len, " (a.authtype = 'ip' AND a.identity = '%s')", ipaddr);
dvander@1322
   498
	len += Format(query[len], sizeof(query)-len, " OR (a.authtype = 'name' AND a.identity = '%s')", safe_name);
dvander@1322
   499
	if (steamid[0] != '\0')
dvander@1322
   500
	{
dvander@1322
   501
		len += Format(query[len], sizeof(query)-len, " OR (a.authtype = 'steam' AND a.identity = '%s')", steamid);
dvander@1322
   502
	}
dvander@1322
   503
	len += Format(query[len], sizeof(query)-len, " GROUP BY a.id");
dvander@1322
   504
	
dvander@1322
   505
	/**
dvander@1322
   506
	 * Send the actual query.
dvander@1322
   507
	 */	
dvander@1322
   508
	PlayerSeq[client] = ++g_sequence;
dvander@1322
   509
	
dvander@1322
   510
	new Handle:pk;
dvander@1322
   511
	pk = CreateDataPack();
dvander@1322
   512
	WritePackCell(pk, client);
dvander@1322
   513
	WritePackCell(pk, PlayerSeq[client]);
dvander@1322
   514
	WritePackString(pk, query);
dvander@1322
   515
	
dvander@1322
   516
#if defined _DEBUG
dvander@1322
   517
	PrintToServer("Sending user query: %s", query);
dvander@1322
   518
#endif
dvander@1322
   519
	
dvander@1322
   520
	SQL_TQuery(db, OnReceiveUser, query, pk, DBPrio_High);
dvander@1322
   521
}
dvander@1322
   522
dvander@1322
   523
FetchUsersWeCan(Handle:db)
dvander@1322
   524
{
dvander@1322
   525
	new max_clients = GetMaxClients();
dvander@1322
   526
	
dvander@1322
   527
	for (new i=1; i<=max_clients; i++)
dvander@1322
   528
	{
dvander@1322
   529
		if (PlayerAuth[i] && GetUserAdmin(i) == INVALID_ADMIN_ID)
dvander@1322
   530
		{
dvander@1322
   531
			FetchUser(db, i);
dvander@1322
   532
		}
dvander@1322
   533
	}
dvander@1322
   534
	
dvander@1322
   535
	/**
dvander@1322
   536
	 * This round of updates is done.  Go in peace.
dvander@1322
   537
	 */
dvander@1322
   538
	RebuildCachePart[_:AdminCache_Admins] = 0;
dvander@1322
   539
}
dvander@1322
   540
dvander@1326
   541
dvander@1326
   542
public OnReceiveGroupImmunity(Handle:owner, Handle:hndl, const String:error[], any:data)
dvander@1326
   543
{
dvander@1326
   544
	new Handle:pk = Handle:data;
dvander@1326
   545
	ResetPack(pk);
dvander@1326
   546
	
dvander@1326
   547
	/**
dvander@1326
   548
	 * Check if this is the latest result request.
dvander@1326
   549
	 */
dvander@1326
   550
	new sequence = ReadPackCell(pk);
dvander@1326
   551
	if (RebuildCachePart[_:AdminCache_Groups] != sequence)
dvander@1326
   552
	{
dvander@1326
   553
		/* Discard everything, since we're out of sequence. */
dvander@1326
   554
		CloseHandle(pk);
dvander@1326
   555
		return;
dvander@1326
   556
	}
dvander@1326
   557
	
dvander@1326
   558
	/**
dvander@1326
   559
	 * If we need to use the results, make sure they succeeded.
dvander@1326
   560
	 */
dvander@1326
   561
	if (hndl == INVALID_HANDLE)
dvander@1326
   562
	{
dvander@1326
   563
		decl String:query[255];
dvander@1326
   564
		ReadPackString(pk, query, sizeof(query));		
dvander@1326
   565
		LogError("SQL error receiving group immunity: %s", error);
dvander@1326
   566
		LogError("Query dump: %s", query);
dvander@1326
   567
		CloseHandle(pk);	
dvander@1326
   568
		return;
dvander@1326
   569
	}
dvander@1326
   570
	
dvander@1326
   571
	/* We're done with the pack forever. */
dvander@1326
   572
	CloseHandle(pk);
dvander@1326
   573
	
dvander@1326
   574
	while (SQL_FetchRow(hndl))
dvander@1326
   575
	{
dvander@1326
   576
		decl String:group1[80];
dvander@1326
   577
		decl String:group2[80];
dvander@1326
   578
		new GroupId:gid1, GroupId:gid2;
dvander@1326
   579
		
dvander@1326
   580
		SQL_FetchString(hndl, 0, group1, sizeof(group1));
dvander@1326
   581
		SQL_FetchString(hndl, 1, group2, sizeof(group2));
dvander@1326
   582
		
dvander@1326
   583
		if (((gid1 = FindAdmGroup(group1)) == INVALID_GROUP_ID)
dvander@1326
   584
			|| (gid2 = FindAdmGroup(group2)) == INVALID_GROUP_ID)
dvander@1326
   585
		{
dvander@1326
   586
			continue;
dvander@1326
   587
		}
dvander@1326
   588
		
dvander@1326
   589
		SetAdmGroupImmuneFrom(gid1, gid2);
dvander@1326
   590
#if defined _DEBUG
dvander@1326
   591
		PrintToServer("SetAdmGroupImmuneFrom(%d, %d)", gid1, gid2);
dvander@1326
   592
#endif
dvander@1326
   593
	}
dvander@1326
   594
	
dvander@1326
   595
	/* Clear the sequence so another connect doesn't refetch */
dvander@1326
   596
	RebuildCachePart[_:AdminCache_Groups] = 0;
dvander@1326
   597
}
dvander@1326
   598
dvander@1322
   599
public OnReceiveGroupOverrides(Handle:owner, Handle:hndl, const String:error[], any:data)
dvander@1322
   600
{
dvander@1322
   601
	new Handle:pk = Handle:data;
dvander@1322
   602
	ResetPack(pk);
dvander@1322
   603
	
dvander@1322
   604
	/**
dvander@1322
   605
	 * Check if this is the latest result request.
dvander@1322
   606
	 */
dvander@1322
   607
	new sequence = ReadPackCell(pk);
dvander@1322
   608
	if (RebuildCachePart[_:AdminCache_Groups] != sequence)
dvander@1322
   609
	{
dvander@1322
   610
		/* Discard everything, since we're out of sequence. */
dvander@1322
   611
		CloseHandle(pk);
dvander@1322
   612
		return;
dvander@1322
   613
	}
dvander@1322
   614
	
dvander@1322
   615
	/**
dvander@1322
   616
	 * If we need to use the results, make sure they succeeded.
dvander@1322
   617
	 */
dvander@1322
   618
	if (hndl == INVALID_HANDLE)
dvander@1322
   619
	{
dvander@1322
   620
		decl String:query[255];
dvander@1322
   621
		ReadPackString(pk, query, sizeof(query));		
dvander@1322
   622
		LogError("SQL error receiving group overrides: %s", error);
dvander@1322
   623
		LogError("Query dump: %s", query);
dvander@1322
   624
		CloseHandle(pk);	
dvander@1322
   625
		return;
dvander@1322
   626
	}
dvander@1322
   627
	
dvander@1322
   628
	/**
dvander@1322
   629
	 * Fetch the overrides.
dvander@1322
   630
	 */
dvander@1322
   631
	decl String:name[80];
dvander@1322
   632
	decl String:type[16];
dvander@1322
   633
	decl String:command[64];
dvander@1322
   634
	decl String:access[16];
dvander@1322
   635
	new GroupId:gid;
dvander@1322
   636
	while (SQL_FetchRow(hndl))
dvander@1322
   637
	{
dvander@1322
   638
		SQL_FetchString(hndl, 0, name, sizeof(name));
dvander@1322
   639
		SQL_FetchString(hndl, 1, type, sizeof(type));
dvander@1322
   640
		SQL_FetchString(hndl, 2, command, sizeof(command));
dvander@1322
   641
		SQL_FetchString(hndl, 3, access, sizeof(access));
dvander@1322
   642
		
dvander@1322
   643
		/* Find the group.  This is actually faster than doing the ID lookup. */
dvander@1322
   644
		if ((gid = FindAdmGroup(name)) == INVALID_GROUP_ID)
dvander@1322
   645
		{
dvander@1322
   646
			/* Oh well, just ignore it. */
dvander@1322
   647
			continue;
dvander@1322
   648
		}
dvander@1322
   649
		
dvander@1322
   650
		new OverrideType:o_type = Override_Command;
dvander@1322
   651
		if (StrEqual(type, "group"))
dvander@1322
   652
		{
dvander@1322
   653
			o_type = Override_CommandGroup;
dvander@1322
   654
		}
dvander@1322
   655
				
dvander@1322
   656
		new OverrideRule:o_rule = Command_Deny;
dvander@1322
   657
		if (StrEqual(access, "allow"))
dvander@1322
   658
		{
dvander@1322
   659
			o_rule = Command_Allow;
dvander@1322
   660
		}
dvander@1322
   661
				
dvander@1322
   662
#if defined _DEBUG
dvander@1322
   663
		PrintToServer("AddAdmGroupCmdOverride(%d, %s, %d, %d)", gid, command, o_type, o_rule);
dvander@1322
   664
#endif
dvander@1322
   665
				
dvander@1322
   666
		AddAdmGroupCmdOverride(gid, command, o_type, o_rule);
dvander@1322
   667
	}
dvander@1322
   668
	
dvander@1326
   669
	/**
dvander@1326
   670
	 * It's time to get the group immunity list.
dvander@1326
   671
	 */
dvander@1326
   672
	new len = 0;
dvander@1326
   673
	decl String:query[256];
dvander@1326
   674
	len += Format(query[len], sizeof(query)-len, "SELECT g1.name, g2.name FROM sm_group_immunity gi");
dvander@1326
   675
	len += Format(query[len], sizeof(query)-len, " LEFT JOIN sm_groups g1 ON g1.id = gi.group_id ");
dvander@1326
   676
	len += Format(query[len], sizeof(query)-len, " LEFT JOIN sm_groups g2 ON g2.id = gi.other_id");
dvander@1326
   677
dvander@1326
   678
	ResetPack(pk);
dvander@1326
   679
	WritePackCell(pk, sequence);
dvander@1326
   680
	WritePackString(pk, query);
dvander@1326
   681
	
dvander@1326
   682
	SQL_TQuery(owner, OnReceiveGroupImmunity, query, pk, DBPrio_High);
dvander@1322
   683
}
dvander@1322
   684
dvander@1322
   685
public OnReceiveGroups(Handle:owner, Handle:hndl, const String:error[], any:data)
dvander@1322
   686
{
dvander@1322
   687
	new Handle:pk = Handle:data;
dvander@1322
   688
	ResetPack(pk);
dvander@1322
   689
	
dvander@1322
   690
	/**
dvander@1322
   691
	 * Check if this is the latest result request.
dvander@1322
   692
	 */
dvander@1322
   693
	new sequence = ReadPackCell(pk);
dvander@1322
   694
	if (RebuildCachePart[_:AdminCache_Groups] != sequence)
dvander@1322
   695
	{
dvander@1322
   696
		/* Discard everything, since we're out of sequence. */
dvander@1322
   697
		CloseHandle(pk);
dvander@1322
   698
		return;
dvander@1322
   699
	}
dvander@1322
   700
	
dvander@1322
   701
	/**
dvander@1322
   702
	 * If we need to use the results, make sure they succeeded.
dvander@1322
   703
	 */
dvander@1322
   704
	if (hndl == INVALID_HANDLE)
dvander@1322
   705
	{
dvander@1322
   706
		decl String:query[255];
dvander@1322
   707
		ReadPackString(pk, query, sizeof(query));
dvander@1322
   708
		LogError("SQL error receiving groups: %s", error);
dvander@1322
   709
		LogError("Query dump: %s", query);
dvander@1322
   710
		CloseHandle(pk);
dvander@1322
   711
		return;
dvander@1322
   712
	}
dvander@1322
   713
	
dvander@1322
   714
	/**
dvander@1322
   715
	 * Now start fetching groups.
dvander@1322
   716
	 */
dvander@1322
   717
	decl String:flags[32];
dvander@1322
   718
	decl String:name[128];
dvander@1403
   719
	new immunity;
dvander@1322
   720
	while (SQL_FetchRow(hndl))
dvander@1322
   721
	{
dvander@1403
   722
		SQL_FetchString(hndl, 0, flags, sizeof(flags));
dvander@1403
   723
		SQL_FetchString(hndl, 1, name, sizeof(name));
dvander@1403
   724
		immunity = SQL_FetchInt(hndl, 2);
dvander@1322
   725
		
dvander@1322
   726
#if defined _DEBUG
dvander@1403
   727
		PrintToServer("Adding group (%d, %s, %s)", immunity, flags, name);
dvander@1322
   728
#endif
dvander@1322
   729
		
dvander@1322
   730
		/* Find or create the group */
dvander@1322
   731
		new GroupId:gid;
dvander@1322
   732
		if ((gid = FindAdmGroup(name)) == INVALID_GROUP_ID)
dvander@1322
   733
		{
dvander@1322
   734
			gid = CreateAdmGroup(name);
dvander@1322
   735
		}
dvander@1403
   736
dvander@1322
   737
		/* Add flags from the database to the group */
dvander@1322
   738
		new num_flag_chars = strlen(flags);
dvander@1322
   739
		for (new i=0; i<num_flag_chars; i++)
dvander@1322
   740
		{
dvander@1322
   741
			decl AdminFlag:flag;
dvander@1322
   742
			if (!FindFlagByChar(flags[i], flag))
dvander@1322
   743
			{
dvander@1322
   744
				continue;
dvander@1322
   745
			}
dvander@1322
   746
			SetAdmGroupAddFlag(gid, flag, true);
dvander@1322
   747
		}
dvander@1322
   748
		
dvander@1403
   749
		SetAdmGroupImmunityLevel(gid, immunity);
dvander@1322
   750
	}
dvander@1322
   751
	
dvander@1322
   752
	/**
dvander@1326
   753
	 * It's time to get the group override list.
dvander@1322
   754
	 */
dvander@1322
   755
	decl String:query[255];
dvander@1322
   756
	Format(query, 
dvander@1322
   757
		sizeof(query), 
dvander@1322
   758
		"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");
dvander@1322
   759
dvander@1322
   760
	ResetPack(pk);
dvander@1322
   761
	WritePackCell(pk, sequence);
dvander@1322
   762
	WritePackString(pk, query);
dvander@1322
   763
	
dvander@1322
   764
	SQL_TQuery(owner, OnReceiveGroupOverrides, query, pk, DBPrio_High);
dvander@1322
   765
}
dvander@1322
   766
dvander@1322
   767
FetchGroups(Handle:db, sequence)
dvander@1322
   768
{
dvander@1322
   769
	decl String:query[255];
dvander@1322
   770
	new Handle:pk;
dvander@1322
   771
	
dvander@1403
   772
	Format(query, sizeof(query), "SELECT flags, name, immunity_level FROM sm_groups");
dvander@1322
   773
dvander@1322
   774
	pk = CreateDataPack();
dvander@1322
   775
	WritePackCell(pk, sequence);
dvander@1322
   776
	WritePackString(pk, query);
dvander@1322
   777
	
dvander@1322
   778
	SQL_TQuery(db, OnReceiveGroups, query, pk, DBPrio_High);
dvander@1322
   779
}
dvander@1322
   780
dvander@1322
   781
public OnReceiveOverrides(Handle:owner, Handle:hndl, const String:error[], any:data)
dvander@1322
   782
{
dvander@1322
   783
	new Handle:pk = Handle:data;
dvander@1322
   784
	ResetPack(pk);
dvander@1322
   785
	
dvander@1322
   786
	/**
dvander@1322
   787
	 * Check if this is the latest result request.
dvander@1322
   788
	 */
dvander@1322
   789
	new sequence = ReadPackCell(pk);
dvander@1322
   790
	if (RebuildCachePart[_:AdminCache_Overrides] != sequence)
dvander@1322
   791
	{
dvander@1322
   792
		/* Discard everything, since we're out of sequence. */
dvander@1322
   793
		CloseHandle(pk);
dvander@1322
   794
		return;
dvander@1322
   795
	}
dvander@1322
   796
	
dvander@1322
   797
	/**
dvander@1322
   798
	 * If we need to use the results, make sure they succeeded.
dvander@1322
   799
	 */
dvander@1322
   800
	if (hndl == INVALID_HANDLE)
dvander@1322
   801
	{
dvander@1322
   802
		decl String:query[255];
dvander@1322
   803
		ReadPackString(pk, query, sizeof(query));
dvander@1322
   804
		LogError("SQL error receiving overrides: %s", error);
dvander@1322
   805
		LogError("Query dump: %s", query);
dvander@1322
   806
		CloseHandle(pk);
dvander@1322
   807
		return;
dvander@1322
   808
	}
dvander@1322
   809
	
dvander@1322
   810
	/**
dvander@1322
   811
	 * We're done with you, now.
dvander@1322
   812
	 */
dvander@1322
   813
	CloseHandle(pk);
dvander@1322
   814
	
dvander@1322
   815
	decl String:type[64];
dvander@1322
   816
	decl String:name[64];
dvander@1322
   817
	decl String:flags[32];
dvander@1322
   818
	new flag_bits;
dvander@1322
   819
	while (SQL_FetchRow(hndl))
dvander@1322
   820
	{
dvander@1322
   821
		SQL_FetchString(hndl, 0, type, sizeof(type));
dvander@1322
   822
		SQL_FetchString(hndl, 1, name, sizeof(name));
dvander@1322
   823
		SQL_FetchString(hndl, 2, flags, sizeof(flags));
dvander@1322
   824
		
dvander@1322
   825
#if defined _DEBUG
dvander@1322
   826
		PrintToServer("Adding override (%s, %s, %s)", type, name, flags);
dvander@1322
   827
#endif
dvander@1322
   828
		
dvander@1322
   829
		flag_bits = ReadFlagString(flags);
dvander@1322
   830
		if (StrEqual(type, "command"))
dvander@1322
   831
		{
dvander@1322
   832
			AddCommandOverride(name, Override_Command, flag_bits);
dvander@1322
   833
		} else if (StrEqual(type, "group")) {
dvander@1322
   834
			AddCommandOverride(name, Override_CommandGroup, flag_bits);
dvander@1322
   835
		}
dvander@1322
   836
	}
dvander@1322
   837
	
dvander@1322
   838
	/* Clear the sequence so another connect doesn't refetch */
dvander@1322
   839
	RebuildCachePart[_:AdminCache_Overrides] = 0;
dvander@1322
   840
}
dvander@1322
   841
dvander@1322
   842
FetchOverrides(Handle:db, sequence)
dvander@1322
   843
{
dvander@1322
   844
	decl String:query[255];
dvander@1322
   845
	new Handle:pk;
dvander@1322
   846
	
dvander@1322
   847
	Format(query, sizeof(query), "SELECT type, name, flags FROM sm_overrides");
dvander@1322
   848
dvander@1322
   849
	pk = CreateDataPack();
dvander@1322
   850
	WritePackCell(pk, sequence);
dvander@1322
   851
	WritePackString(pk, query);
dvander@1322
   852
	
dvander@1322
   853
	SQL_TQuery(db, OnReceiveOverrides, query, pk, DBPrio_High);
dvander@1322
   854
}
dvander@1322
   855