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
+}