diff --git a/dwc_network_server_emulator.pyproj b/dwc_network_server_emulator.pyproj index d83d627..89b2580 100644 --- a/dwc_network_server_emulator.pyproj +++ b/dwc_network_server_emulator.pyproj @@ -49,6 +49,7 @@ + diff --git a/gamespy_backend_server.py b/gamespy_backend_server.py index 4adda33..c1f3aa5 100644 --- a/gamespy_backend_server.py +++ b/gamespy_backend_server.py @@ -53,6 +53,7 @@ import ast from multiprocessing.managers import BaseManager from multiprocessing import freeze_support +from other.sql import sql_commands, LIKE import other.utils as utils import dwc_config @@ -142,6 +143,12 @@ class GameSpyBackendServer(object): numplayers < 11 and dwc_mtype = 0 and dwc_hoststate = 2 and dwc_suspend = 0 and (rk = 'vs_123' and (ev > 4263 or ev <= 5763) and p = 0) + + Example with LIKE from Fortune Street: + dwc_mver = 90 and dwc_pid != 15 and maxplayers = 3 and + numplayers < 3 and dwc_mtype = 0 and dwc_suspend = 0 and + dwc_hoststate = 2 and ((zvar LIKE '102') AND (zmtp LIKE 'EU') AND + (zrule LIKE '1') AND (zpnum LIKE '2') AND (zaddc LIKE '0') AND rel='1') """ i = 0 start = i @@ -249,14 +256,19 @@ class GameSpyBackendServer(object): token = "==" elif token_type == TokenType.FIELD: - # Each server has its own variables so handle it later. - variables.append(len(output)) + if token.upper() in sql_commands: + # Convert "A SQL_COMMAND B" into "A |SQL_COMMAND| B" + output.extend(["|", token.upper(), "|"]) + continue + else: + # Each server has its own variables so handle it later. + variables.append(len(output)) output.append(token) return output, variables - def validate_ast(self, node, num_literal_only): + def validate_ast(self, node, num_literal_only, is_sql=False): # This function tries to verify that the expression is a valid # expression before it gets evaluated. # Anything besides the whitelisted things below are strictly @@ -278,7 +290,7 @@ class GameSpyBackendServer(object): valid_node = True elif isinstance(node, ast.Str): - if not num_literal_only: + if is_sql or not num_literal_only: valid_node = True elif isinstance(node, ast.BoolOp): @@ -289,10 +301,16 @@ class GameSpyBackendServer(object): break elif isinstance(node, ast.BinOp): - valid_node = self.validate_ast(node.left, True) + # Allow SQL_COMMAND infix operator with more types + is_sql |= \ + hasattr(node, "left") and \ + hasattr(node.left, "right") and \ + isinstance(node.left.right, ast.Name) and \ + node.left.right.id in sql_commands + valid_node = self.validate_ast(node.left, True, is_sql) if valid_node: - valid_node = self.validate_ast(node.right, True) + valid_node = self.validate_ast(node.right, True, is_sql) elif isinstance(node, ast.UnaryOp): valid_node = self.validate_ast(node.operand, num_literal_only) @@ -322,6 +340,9 @@ class GameSpyBackendServer(object): elif isinstance(node, ast.Call): valid_node = False + elif isinstance(node, ast.Name): + valid_node = node.id in sql_commands + return valid_node def find_servers(self, gameid, filters, fields, max_count): diff --git a/other/sql.py b/other/sql.py new file mode 100644 index 0000000..f349005 --- /dev/null +++ b/other/sql.py @@ -0,0 +1,54 @@ +"""DWC Network Server Emulator + + Copyright (C) 2016 Sepalani + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +""" + + +class Infix(object): + """Infix operator class. + + A |Infix| B + """ + def __init__(self, function): + self.function = function + + def __ror__(self, other): + return Infix(lambda x, self=self: self.function(other, x)) + + def __or__(self, other): + return self.function(other) + + +def sql_like(a, b): + """SQL LIKE command. + + TODO: + - Handle % character + - Handle _ character + - Handle escape character + - Handle [] + - Handle [^] + """ + a = str(a).lower() + b = str(b).lower() + return a == b + + +LIKE = Infix(sql_like) + +sql_commands = { + "LIKE": LIKE +}