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