Storage: Super hacky Filter evaulation, needs to be improved later.

This commit is contained in:
Admiral H. Curtiss 2014-06-03 20:58:33 +02:00
parent cebf17efca
commit 7c522eab2d
3 changed files with 175 additions and 3 deletions

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{1a9e8a26-6cea-4e78-835e-b043356ce360}</ProjectGuid>
<ProjectHome>.</ProjectHome>
<StartupFile>master_server.py</StartupFile>
<SearchPath>
</SearchPath>
<WorkingDirectory>.</WorkingDirectory>
<OutputPath>.</OutputPath>
<InterpreterId>00000000-0000-0000-0000-000000000000</InterpreterId>
<InterpreterVersion>
</InterpreterVersion>
<Name>dwc_network_server_emulator</Name>
<RootNamespace>dwc_network_server_emulator</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugSymbols>true</DebugSymbols>
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DebugSymbols>true</DebugSymbols>
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
</PropertyGroup>
<ItemGroup>
<Compile Include="gamespy_backend_server.py" />
<Compile Include="gamespy_gamestats_server.py" />
<Compile Include="gamespy_natneg_server.py" />
<Compile Include="gamespy_player_search_server.py" />
<Compile Include="gamespy_profile_server.py" />
<Compile Include="gamespy_qr_server.py" />
<Compile Include="gamespy_server_browser_server.py" />
<Compile Include="internal_stats_server.py" />
<Compile Include="master_server.py" />
<Compile Include="nas_server.py" />
<Compile Include="storage_server.py" />
<Compile Include="gamespy\gs_database.py" />
<Compile Include="gamespy\gs_query.py" />
<Compile Include="gamespy\gs_utility.py" />
<Compile Include="gamespy\__init__.py" />
<Compile Include="other\utils.py" />
<Compile Include="other\__init__.py" />
<Compile Include="tools\import_wiimm_data.py" />
<Content Include="www\conntest.nintendowifi.net\public_html\index.html" />
<Content Include="www\gamestats.gs.nintendowifi.net\public_html\index.html" />
</ItemGroup>
<ItemGroup>
<Folder Include="gamespy" />
<Folder Include="other" />
<Folder Include="tools" />
<Folder Include="www\" />
<Folder Include="www\conntest.nintendowifi.net\" />
<Folder Include="www\conntest.nintendowifi.net\public_html" />
<Folder Include="www\gamestats.gs.nintendowifi.net\" />
<Folder Include="www\gamestats.gs.nintendowifi.net\public_html" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.Common.targets" />
</Project>

View File

@ -0,0 +1,20 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "dwc_network_server_emulator", "dwc_network_server_emulator.pyproj", "{1A9E8A26-6CEA-4E78-835E-B043356CE360}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1A9E8A26-6CEA-4E78-835E-B043356CE360}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1A9E8A26-6CEA-4E78-835E-B043356CE360}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1A9E8A26-6CEA-4E78-835E-B043356CE360}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1A9E8A26-6CEA-4E78-835E-B043356CE360}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@ -41,6 +41,7 @@ class StorageHTTPServer(BaseHTTPServer.HTTPServer):
self.db = sqlite3.connect('storage.db')
self.tables = {}
self.valid_sql_terms = ['LIKE', '=', 'AND', 'OR']
logger.log(logging.INFO, "Checking for and creating database tables...")
@ -190,6 +191,8 @@ class StorageHTTPServer(BaseHTTPServer.HTTPServer):
class IllegalColumnAccessException(Exception):
pass
class FilterSyntaxException(Exception):
pass
class StorageHTTPServerHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def confirm_columns(self, columndata, table):
@ -204,6 +207,66 @@ class StorageHTTPServerHandler(BaseHTTPServer.BaseHTTPRequestHandler):
return columns
def tokenize_filter(self, filter):
# TODO: Actual proper tokenization
return filter.split()
def parse_filter(self, table, filter):
# I think I need to read up on how to properly parse SQL-like data, but this should do for the stuff I've seen from games so far.
out = ''
if ';' in filter:
raise FilterSyntaxException("Semicolon in filter '%s'" & filter)
if '\\' in filter:
raise FilterSyntaxException("Backslash in filter '%s'" & filter)
brace_count = filter.count('(')
if brace_count != filter.count(')'):
raise FilterSyntaxException("Mismatching brace count in filter '%s'" & filter)
filter = self.tokenize_filter(filter)
for f in filter:
if f in self.server.tables[table]:
# is a table name
out += f + ' '
elif f.upper() in self.server.valid_sql_terms:
# is some SQL term such as LIKE, AND, OR, etc.
out += f + ' '
elif ( f.startswith("'") and f.endswith("'") ) or ( f.startswith('"') and f.endswith('"') ):
# is a string
out += f + ' '
else:
# is nothing valid, abort and return the statement so far
out = out.strip()
# try to make the output still valid by removing trailing connecting tokens
last_space = out.rfind(' ')
if last_space >= 0:
last_token = out[last_space + 1 : ]
if last_token in self.server.valid_sql_terms:
out = out[ : last_space ]
return out
return out
def append_filter(self, filter, table, statement, where_appended):
try:
filters = self.parse_filter(table, filter)
if filters:
if not where_appended:
statement += ' WHERE '
where_appended = True
else:
statement += ' AND '
statement += ' ( '
statement += filters
statement += ' ) '
except FilterSyntaxException as e:
logger.log(logging.WARNING, "FilterSyntaxException: %s by %s", e.message, self.client_address)
pass
return statement, where_appended
def do_POST(self):
# Alright, in case anyone is wondering: Yes, I am faking a SOAP service
# instead of using an actual one. That's because I've tried to do this
@ -249,26 +312,49 @@ class StorageHTTPServerHandler(BaseHTTPServer.BaseHTTPRequestHandler):
statement = 'SELECT '
statement += ",".join(columns)
statement += ' FROM ' + table
where_appended = False
if shortaction == 'SearchForRecords':
# this is ugly as hell but SearchForRecords can request specific ownerids like this
owneriddata = data.getElementsByTagName('ns1:ownerids')
if owneriddata and owneriddata[0] and owneriddata[0].firstChild:
oids = owneriddata[0].getElementsByTagName('ns1:int')
statement += ' WHERE '
if not where_appended:
statement += ' WHERE '
where_appended = True
else:
statement += ' AND '
statement += ' ( '
statement += ' OR '.join('ownerid = '+str(int(oid.firstChild.data)) for oid in oids)
statement += ' ) '
elif shortaction == 'GetMyRecords':
profileid = self.server.gamespydb.get_profileid_from_loginticket(loginticket)
statement += ' WHERE ownerid = ' + str(profileid)
if not where_appended:
statement += ' WHERE '
where_appended = True
else:
statement += ' AND '
statement += ' ( ownerid = ' + str(profileid) + ' ) '
elif shortaction == 'GetSpecificRecords':
recordids = data.getElementsByTagName('ns1:recordids')[0].getElementsByTagName('ns1:int')
# limit to requested records
statement += ' WHERE '
if not where_appended:
statement += ' WHERE '
where_appended = True
else:
statement += ' AND '
statement += ' ( '
statement += ' OR '.join('recordid = '+str(int(r.firstChild.data)) for r in recordids)
statement += ' ) '
# if there's a filter, evaluate it
filterdata = data.getElementsByTagName('ns1:filter')
if filterdata and filterdata[0] and filterdata[0].firstChild:
statement, where_appended = self.append_filter(filterdata[0].firstChild.data, table, statement, where_appended)
# if only a subset of the data is wanted
limit_offset_data = data.getElementsByTagName('ns1:offset')
limit_max_data = data.getElementsByTagName('ns1:max')
@ -311,7 +397,13 @@ class StorageHTTPServerHandler(BaseHTTPServer.BaseHTTPRequestHandler):
elif shortaction == 'GetRecordCount':
statement = 'SELECT COUNT(1) FROM ' + table
filterdata = data.getElementsByTagName('ns1:filter')
if filterdata and filterdata[0] and filterdata[0].firstChild:
statement, where_appended = self.append_filter(filterdata[0].firstChild.data, table, statement, False)
logger.log(logging.DEBUG, statement)
cursor = self.server.db.cursor()
cursor.execute(statement)
count = cursor.fetchone()[0]