diff -r c7b4237fff50 -r 03a67e41afbc plugins/admin-sql-threaded.sp --- a/plugins/admin-sql-threaded.sp Fri Mar 28 06:21:34 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,855 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * SourceMod SQL Admins Plugin (Threaded) - * Fetches admins from an SQL database dynamically. - * - * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -/* We like semicolons */ -#pragma semicolon 1 - -#include - -public Plugin:myinfo = -{ - name = "SQL Admins (Threaded)", - author = "AlliedModders LLC", - description = "Reads admins from SQL dynamically", - version = SOURCEMOD_VERSION, - url = "http://www.sourcemod.net/" -}; - -/** - * Notes: - * - * 1) All queries in here are high priority. This is because the admin stuff - * is very important. Do not take this to mean that in your script, - * everything should be high priority. - * - * 2) All callbacks are locked with "sequence numbers." This is to make sure - * that multiple calls to sm_reloadadmins and the like do not make us - * store the results from two or more callbacks accidentally. Instead, we - * check the sequence number in each callback with the current "allowed" - * sequence number, and if it doesn't match, the callback is cancelled. - * - * 3) Sequence numbers for groups and overrides are not cleared unless there - * was a 100% success in the fetch. This is so we can potentially implement - * connection retries in the future. - * - * 4) Sequence numbers for the user cache are ignored except for being - * non-zero, which means players in-game should be re-checked for admin - * powers. - */ - -new Handle:hDatabase = INVALID_HANDLE; /** Database connection */ -new g_sequence = 0; /** Global unique sequence number */ -new ConnectLock = 0; /** Connect sequence number */ -new RebuildCachePart[3] = {0}; /** Cache part sequence numbers */ -new PlayerSeq[MAXPLAYERS+1]; /** Player-specific sequence numbers */ -new bool:PlayerAuth[MAXPLAYERS+1]; /** Whether a player has been "pre-authed" */ - -#define _DEBUG - -public OnMapEnd() -{ - /** - * Clean up on map end just so we can start a fresh connection when we need it later. - */ - if (hDatabase != INVALID_HANDLE) - { - CloseHandle(hDatabase); - hDatabase = INVALID_HANDLE; - } -} - -public bool:OnClientConnect(client, String:rejectmsg[], maxlen) -{ - PlayerSeq[client] = 0; - PlayerAuth[client] = false; - return true; -} - -public OnClientDisconnect(client) -{ - PlayerSeq[client] = 0; - PlayerAuth[client] = false; -} - -public OnDatabaseConnect(Handle:owner, Handle:hndl, const String:error[], any:data) -{ -#if defined _DEBUG - PrintToServer("OnDatabaseConnect(%x,%x,%d) ConnectLock=%d", owner, hndl, data, ConnectLock); -#endif - - /** - * If this happens to be an old connection request, ignore it. - */ - if (data != ConnectLock || hDatabase != INVALID_HANDLE) - { - if (hndl != INVALID_HANDLE) - { - CloseHandle(hndl); - } - return; - } - - ConnectLock = 0; - hDatabase = hndl; - - /** - * See if the connection is valid. If not, don't un-mark the caches - * as needing rebuilding, in case the next connection request works. - */ - if (hDatabase == INVALID_HANDLE) - { - LogError("Failed to connect to database: %s", error); - return; - } - - /** - * See if we need to get any of the cache stuff now. - */ - new sequence; - if ((sequence = RebuildCachePart[_:AdminCache_Overrides]) != 0) - { - FetchOverrides(hDatabase, sequence); - } - if ((sequence = RebuildCachePart[_:AdminCache_Groups]) != 0) - { - FetchGroups(hDatabase, sequence); - } - if ((sequence = RebuildCachePart[_:AdminCache_Admins]) != 0) - { - FetchUsersWeCan(hDatabase); - } -} - -RequestDatabaseConnection() -{ - ConnectLock = ++g_sequence; - if (SQL_CheckConfig("admins")) - { - SQL_TConnect(OnDatabaseConnect, "admins", ConnectLock); - } else { - SQL_TConnect(OnDatabaseConnect, "default", ConnectLock); - } -} - -public OnRebuildAdminCache(AdminCachePart:part) -{ - /** - * Mark this part of the cache as being rebuilt. This is used by the - * callback system to determine whether the results should still be - * used. - */ - new sequence = ++g_sequence; - RebuildCachePart[_:part] = sequence; - - /** - * If we don't have a database connection, we can't do any lookups just yet. - */ - if (!hDatabase) - { - /** - * Ask for a new connection if we need it. - */ - if (!ConnectLock) - { - RequestDatabaseConnection(); - } - return; - } - - if (part == AdminCache_Overrides) - { - FetchOverrides(hDatabase, sequence); - } else if (part == AdminCache_Groups) { - FetchGroups(hDatabase, sequence); - } else if (part == AdminCache_Admins) { - FetchUsersWeCan(hDatabase); - } -} - -public Action:OnClientPreAdminCheck(client) -{ - PlayerAuth[client] = true; - - /** - * Play nice with other plugins. If there's no database, don't delay the - * connection process. Unfortunately, we can't attempt anything else and - * we just have to hope either the database is waiting or someone will type - * sm_reloadadmins. - */ - if (hDatabase == INVALID_HANDLE) - { - return Plugin_Continue; - } - - /** - * Similarly, if the cache is in the process of being rebuilt, don't delay - * the user's normal connection flow. The database will soon auth the user - * normally. - */ - if (RebuildCachePart[_:AdminCache_Admins] != 0) - { - return Plugin_Continue; - } - - /** - * If someone has already assigned an admin ID (bad bad bad), don't - * bother waiting. - */ - if (GetUserAdmin(client) != INVALID_ADMIN_ID) - { - return Plugin_Continue; - } - - FetchUser(hDatabase, client); - - return Plugin_Handled; -} - -public OnReceiveUserGroups(Handle:owner, Handle:hndl, const String:error[], any:data) -{ - new Handle:pk = Handle:data; - ResetPack(pk); - - new client = ReadPackCell(pk); - new sequence = ReadPackCell(pk); - - /** - * Make sure it's the same client. - */ - if (PlayerSeq[client] != sequence) - { - CloseHandle(pk); - return; - } - - new AdminId:adm = AdminId:ReadPackCell(pk); - - /** - * Someone could have sneakily changed the admin id while we waited. - */ - if (GetUserAdmin(client) != adm) - { - NotifyPostAdminCheck(client); - CloseHandle(pk); - return; - } - - /** - * See if we got results. - */ - if (hndl == INVALID_HANDLE) - { - decl String:query[255]; - ReadPackString(pk, query, sizeof(query)); - LogError("SQL error receiving user: %s", error); - LogError("Query dump: %s", query); - NotifyPostAdminCheck(client); - CloseHandle(pk); - return; - } - - decl String:name[80]; - new GroupId:gid; - - while (SQL_FetchRow(hndl)) - { - SQL_FetchString(hndl, 0, name, sizeof(name)); - - if ((gid = FindAdmGroup(name)) == INVALID_GROUP_ID) - { - continue; - } - -#if defined _DEBUG - PrintToServer("Binding user group (%d, %d, %d, %s, %d)", client, sequence, adm, name, gid); -#endif - - AdminInheritGroup(adm, gid); - } - - /** - * We're DONE! Omg. - */ - NotifyPostAdminCheck(client); - CloseHandle(pk); -} - -public OnReceiveUser(Handle:owner, Handle:hndl, const String:error[], any:data) -{ - new Handle:pk = Handle:data; - ResetPack(pk); - - new client = ReadPackCell(pk); - - /** - * Check if this is the latest result request. - */ - new sequence = ReadPackCell(pk); - if (PlayerSeq[client] != sequence) - { - /* Discard everything, since we're out of sequence. */ - CloseHandle(pk); - return; - } - - /** - * If we need to use the results, make sure they succeeded. - */ - if (hndl == INVALID_HANDLE) - { - decl String:query[255]; - ReadPackString(pk, query, sizeof(query)); - LogError("SQL error receiving user: %s", error); - LogError("Query dump: %s", query); - RunAdminCacheChecks(client); - NotifyPostAdminCheck(client); - CloseHandle(pk); - return; - } - - new num_accounts = SQL_GetRowCount(hndl); - if (num_accounts == 0) - { - RunAdminCacheChecks(client); - NotifyPostAdminCheck(client); - CloseHandle(pk); - return; - } - - decl String:authtype[16]; - decl String:identity[80]; - decl String:password[80]; - decl String:flags[32]; - decl String:name[80]; - new AdminId:adm, id; - new immunity; - - /** - * Cache user info -- [0] = db id, [1] = cache id, [2] = groups - */ - decl user_lookup[num_accounts][3]; - new total_users = 0; - - while (SQL_FetchRow(hndl)) - { - id = SQL_FetchInt(hndl, 0); - SQL_FetchString(hndl, 1, authtype, sizeof(authtype)); - SQL_FetchString(hndl, 2, identity, sizeof(identity)); - SQL_FetchString(hndl, 3, password, sizeof(password)); - SQL_FetchString(hndl, 4, flags, sizeof(flags)); - SQL_FetchString(hndl, 5, name, sizeof(name)); - immunity = SQL_FetchInt(hndl, 7); - - /* For dynamic admins we clear anything already in the cache. */ - if ((adm = FindAdminByIdentity(authtype, identity)) != INVALID_ADMIN_ID) - { - RemoveAdmin(adm); - } - - adm = CreateAdmin(name); - if (!BindAdminIdentity(adm, authtype, identity)) - { - LogError("Could not bind prefetched SQL admin (authtype \"%s\") (identity \"%s\")", authtype, identity); - continue; - } - - user_lookup[total_users][0] = id; - user_lookup[total_users][1] = _:adm; - user_lookup[total_users][2] = SQL_FetchInt(hndl, 6); - total_users++; - -#if defined _DEBUG - 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]); -#endif - - /* See if this admin wants a password */ - if (password[0] != '\0') - { - SetAdminPassword(adm, password); - } - - SetAdminImmunityLevel(adm, immunity); - - /* Apply each flag */ - new len = strlen(flags); - new AdminFlag:flag; - for (new i=0; i