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