Improved configuration file

Added: register_page.py into cfg and pyproj

Added: Alternative configuration file

Added: Logger into configuration file
This commit is contained in:
Sepalani 2015-09-05 22:56:19 +02:00
parent 35c253dc09
commit 2102a73b05
18 changed files with 342 additions and 160 deletions

View File

@ -34,12 +34,8 @@ import other.utils as utils
import gamespy.gs_utility as gs_utils
import dwc_config
logger_output_to_console = True
logger_output_to_file = True
logger_name = "AdminPage"
logger_filename = "admin_page.log"
logger = utils.create_logger(logger_name, logger_filename, -1,
logger_output_to_console, logger_output_to_file)
logger = dwc_config.get_logger('AdminPage')
_, port = dwc_config.get_ip_port('AdminPage')
# Example of adminpageconf.json
@ -480,8 +476,6 @@ class AdminPage(resource.Resource):
else:
return self.get_header() + self.get_footer()
_, port = dwc_config.get_ip_port('AdminPage')
class AdminPageServer(object):
def start(self):
@ -496,5 +490,6 @@ class AdminPageServer(object):
except ReactorAlreadyRunning:
pass
if __name__ == "__main__":
AdminPageServer().start()

View File

@ -1,51 +1,123 @@
# ALTWFC - Configuration File
[Config]
AlternativeConfig = OFF
AlternativeConfigFile = altwfc_nas.cfg
[StorageServer]
IP = 127.0.0.1
Port = 8000
LoggerName = StorageServer
LoggerFilename = storage_server.log
LoggerLevel = -1
LoggerOutputConsole = ON
LoggerOutputFile = ON
[NasServer]
IP = 127.0.0.1
Port = 9000
# Port = 80
LoggerName = NasServer
LoggerFilename = nas_server.log
LoggerLevel = -1
LoggerOutputConsole = ON
LoggerOutputFile = ON
[InternalStatsServer]
IP = 127.0.0.1
Port = 9001
LoggerName = InternalStatsServer
LoggerFilename = internal_stats_server.log
LoggerLevel = -1
LoggerOutputConsole = ON
LoggerOutputFile = ON
[GameStatsServerHttp]
IP = 127.0.0.1
Port = 9002
LoggerName = GameStatsServerHttp
LoggerFilename = gamestats_server_http.log
LoggerLevel = -1
LoggerOutputConsole = ON
LoggerOutputFile = ON
[AdminPage]
IP = 127.0.0.1
Port = 9009
LoggerName = AdminPage
LoggerFilename = admin_page.log
LoggerLevel = -1
LoggerOutputConsole = ON
LoggerOutputFile = ON
[RegisterPage]
IP = 127.0.0.1
Port = 9998
LoggerName = RegisterPage
LoggerFilename = register_page.log
LoggerLevel = -1
LoggerOutputConsole = ON
LoggerOutputFile = ON
[GameSpyManager]
# GamespyBackendServer
IP = 127.0.0.1
Port = 27500
LoggerName = GamespyBackendServer
LoggerFilename = gamespy_backend_server.log
LoggerLevel = -1
LoggerOutputConsole = ON
LoggerOutputFile = ON
[GameSpyQRServer]
IP = 0.0.0.0
Port = 27900
LoggerName = GameSpyQRServer
LoggerFilename = gamespy_qr_server.log
LoggerLevel = -1
LoggerOutputConsole = ON
LoggerOutputFile = ON
[GameSpyNatNegServer]
IP = 0.0.0.0
Port = 27901
LoggerName = GameSpyNatNegServer
LoggerFilename = gamespy_natneg_server.log
LoggerLevel = -1
LoggerOutputConsole = ON
LoggerOutputFile = ON
[GameSpyServerBrowserServer]
IP = 0.0.0.0
Port = 28910
LoggerName = GameSpyServerBrowserServer
LoggerFilename = gamespy_server_browser_server.log
LoggerLevel = -1
LoggerOutputConsole = ON
LoggerOutputFile = ON
[GameSpyProfileServer]
IP = 0.0.0.0
Port = 29900
LoggerName = GameSpyProfileServer
LoggerFilename = gamespy_profile_server.log
LoggerLevel = -1
LoggerOutputConsole = ON
LoggerOutputFile = ON
[GameSpyPlayerSearchServer]
IP = 0.0.0.0
Port = 29901
LoggerName = GameSpyPlayerSearchServer
LoggerFilename = gamespy_player_search_server.log
LoggerLevel = -1
LoggerOutputConsole = ON
LoggerOutputFile = ON
[GameSpyGamestatsServer]
IP = 0.0.0.0
Port = 29920
LoggerName = GameSpyGamestatsServer
LoggerFilename = gamespy_gamestats_server.log
LoggerLevel = -1
LoggerOutputConsole = ON
LoggerOutputFile = ON

119
altwfc_nas.cfg Normal file
View File

@ -0,0 +1,119 @@
# ALTWFC - Alternative Configuration File
[StorageServer]
IP = 127.0.0.1
Port = 8000
LoggerName = StorageServer
LoggerFilename = storage_server.log
LoggerLevel = -1
LoggerOutputConsole = ON
LoggerOutputFile = ON
[NasServer]
IP = 127.0.0.1
Port = 80
LoggerName = NasServer
LoggerFilename = nas_server.log
LoggerLevel = -1
LoggerOutputConsole = ON
LoggerOutputFile = ON
[InternalStatsServer]
IP = 127.0.0.1
Port = 9001
LoggerName = InternalStatsServer
LoggerFilename = internal_stats_server.log
LoggerLevel = -1
LoggerOutputConsole = ON
LoggerOutputFile = ON
[GameStatsServerHttp]
IP = 127.0.0.1
Port = 9002
LoggerName = GameStatsServerHttp
LoggerFilename = gamestats_server_http.log
LoggerLevel = -1
LoggerOutputConsole = ON
LoggerOutputFile = ON
[AdminPage]
IP = 127.0.0.1
Port = 9009
LoggerName = AdminPage
LoggerFilename = admin_page.log
LoggerLevel = -1
LoggerOutputConsole = ON
LoggerOutputFile = ON
[RegisterPage]
IP = 127.0.0.1
Port = 9998
LoggerName = RegisterPage
LoggerFilename = register_page.log
LoggerLevel = -1
LoggerOutputConsole = ON
LoggerOutputFile = ON
[GameSpyManager]
# GamespyBackendServer
IP = 127.0.0.1
Port = 27500
LoggerName = GamespyBackendServer
LoggerFilename = gamespy_backend_server.log
LoggerLevel = -1
LoggerOutputConsole = ON
LoggerOutputFile = ON
[GameSpyQRServer]
IP = 0.0.0.0
Port = 27900
LoggerName = GameSpyQRServer
LoggerFilename = gamespy_qr_server.log
LoggerLevel = -1
LoggerOutputConsole = ON
LoggerOutputFile = ON
[GameSpyNatNegServer]
IP = 0.0.0.0
Port = 27901
LoggerName = GameSpyNatNegServer
LoggerFilename = gamespy_natneg_server.log
LoggerLevel = -1
LoggerOutputConsole = ON
LoggerOutputFile = ON
[GameSpyServerBrowserServer]
IP = 0.0.0.0
Port = 28910
LoggerName = GameSpyServerBrowserServer
LoggerFilename = gamespy_server_browser_server.log
LoggerLevel = -1
LoggerOutputConsole = ON
LoggerOutputFile = ON
[GameSpyProfileServer]
IP = 0.0.0.0
Port = 29900
LoggerName = GameSpyProfileServer
LoggerFilename = gamespy_profile_server.log
LoggerLevel = -1
LoggerOutputConsole = ON
LoggerOutputFile = ON
[GameSpyPlayerSearchServer]
IP = 0.0.0.0
Port = 29901
LoggerName = GameSpyPlayerSearchServer
LoggerFilename = gamespy_player_search_server.log
LoggerLevel = -1
LoggerOutputConsole = ON
LoggerOutputFile = ON
[GameSpyGamestatsServer]
IP = 0.0.0.0
Port = 29920
LoggerName = GameSpyGamestatsServer
LoggerFilename = gamespy_gamestats_server.log
LoggerLevel = -1
LoggerOutputConsole = ON
LoggerOutputFile = ON

View File

@ -1,4 +1,4 @@
"""DWC Network Server Emulator
"""DWC Network Server Emulator
Copyright (C) 2014 SMTDDR
Copyright (C) 2014 kyle95wm
@ -28,23 +28,50 @@ except ImportError:
# Python 3
import configparser as ConfigParser
import other.utils as utils
def get_config_filename(filename='altwfc.cfg'):
"""Return the config filename that will be used."""
try:
config = ConfigParser.RawConfigParser(allow_no_value=True)
config.read(filename)
if config.getboolean('Config', 'AlternativeConfig'):
return config.get('Config', 'AlternativeConfigFile')
except Exception as e:
pass
return filename
def get_ip_port(section, filename='altwfc.cfg'):
"""Return a tuple (IP, Port) of the corresponding section."""
config = ConfigParser.RawConfigParser(allow_no_value=True)
config.read(filename)
config.read(get_config_filename(filename))
return (config.get(section, 'IP'), config.getint(section, 'Port'))
def get_ip(section, filename='altwfc.cfg'):
"""Return the IP of the corresponding section."""
config = ConfigParser.RawConfigParser(allow_no_value=True)
config.read(filename)
config.read(get_config_filename(filename))
return config.get(section, 'IP')
def get_port(section, filename='altwfc.cfg'):
"""Return the port of the corresponding section."""
config = ConfigParser.RawConfigParser(allow_no_value=True)
config.read(filename)
config.read(get_config_filename(filename))
return config.getint(section, 'Port')
def get_logger(section, filename='altwfc.cfg'):
"""Return the logger of the corresponding section."""
config = ConfigParser.RawConfigParser(allow_no_value=True)
config.read(get_config_filename(filename))
return utils.create_logger(
config.get(section, 'LoggerName'),
config.get(section, 'LoggerFilename'),
config.getint(section, 'LoggerLevel'),
config.getboolean(section, 'LoggerOutputConsole'),
config.getboolean(section, 'LoggerOutputFile')
)

View File

@ -42,6 +42,7 @@
<Compile Include="internal_stats_server.py" />
<Compile Include="master_server.py" />
<Compile Include="nas_server.py" />
<Compile Include="register_page.py" />
<Compile Include="storage_server.py" />
<Compile Include="gamespy\gs_database.py" />
<Compile Include="gamespy\gs_query.py" />

View File

@ -56,6 +56,8 @@ from multiprocessing import freeze_support
import other.utils as utils
import dwc_config
logger = dwc_config.get_logger('GameSpyManager')
class TokenType:
UNKNOWN = 0
@ -64,14 +66,6 @@ class TokenType:
NUMBER = 3
TOKEN = 4
# Logger settings
logger_output_to_console = True
logger_output_to_file = True
logger_name = "GamespyBackendServer"
logger_filename = "gamespy_backend_server.log"
logger = utils.create_logger(logger_name, logger_filename, -1,
logger_output_to_console, logger_output_to_file)
class GameSpyServerDatabase(BaseManager):
pass

View File

@ -37,14 +37,7 @@ import gamespy.gs_utility as gs_utils
import other.utils as utils
import dwc_config
# Logger settings
logger_output_to_console = True
logger_output_to_file = True
logger_name = "GameSpyGamestatsServer"
logger_filename = "gamespy_gamestats_server.log"
logger = utils.create_logger(logger_name, logger_filename, -1,
logger_output_to_console, logger_output_to_file)
logger = dwc_config.get_logger('GameSpyGamestatsServer')
address = dwc_config.get_ip_port('GameSpyGamestatsServer')

View File

@ -36,13 +36,7 @@ import traceback
from multiprocessing.managers import BaseManager
import dwc_config
# Logger settings
logger_output_to_console = True
logger_output_to_file = True
logger_name = "GameSpyNatNegServer"
logger_filename = "gamespy_natneg_server.log"
logger = utils.create_logger(logger_name, logger_filename, -1,
logger_output_to_console, logger_output_to_file)
logger = dwc_config.get_logger('GameSpyNatNegServer')
class GameSpyServerDatabase(BaseManager):

View File

@ -32,14 +32,7 @@ import gamespy.gs_query as gs_query
import other.utils as utils
import dwc_config
# Logger settings
logger_output_to_console = True
logger_output_to_file = True
logger_name = "GameSpyPlayerSearchServer"
logger_filename = "gamespy_player_search_server.log"
logger = utils.create_logger(logger_name, logger_filename, -1,
logger_output_to_console, logger_output_to_file)
logger = dwc_config.get_logger('GameSpyPlayerSearchServer')
address = dwc_config.get_ip_port('GameSpyPlayerSearchServer')

View File

@ -36,14 +36,7 @@ import gamespy.gs_utility as gs_utils
import other.utils as utils
import dwc_config
# Logger settings
logger_output_to_console = True
logger_output_to_file = True
logger_name = "GameSpyProfileServer"
logger_filename = "gamespy_profile_server.log"
logger = utils.create_logger(logger_name, logger_filename, -1,
logger_output_to_console, logger_output_to_file)
logger = dwc_config.get_logger('GameSpyProfileServer')
address = dwc_config.get_ip_port('GameSpyProfileServer')

View File

@ -41,13 +41,7 @@ import other.utils as utils
import dwc_config
from gamespy_server_browser_server import GameSpyServerBrowserServer
# Logger settings
logger_output_to_console = True
logger_output_to_file = True
logger_name = "GameSpyQRServer"
logger_filename = "gamespy_qr_server.log"
logger = utils.create_logger(logger_name, logger_filename, -1,
logger_output_to_console, logger_output_to_file)
logger = dwc_config.get_logger('GameSpyQRServer')
class GameSpyServerDatabase(BaseManager):

View File

@ -38,6 +38,8 @@ import dwc_config
from multiprocessing.managers import BaseManager
logger = dwc_config.get_logger('GameSpyServerBrowserServer')
class ServerListFlags:
UNSOLICITED_UDP_FLAG = 1
@ -49,14 +51,6 @@ class ServerListFlags:
HAS_KEYS_FLAG = 64
HAS_FULL_RULES_FLAG = 128
# Logger settings
logger_output_to_console = True
logger_output_to_file = True
logger_name = "GameSpyServerBrowserServer"
logger_filename = "gamespy_server_browser_server.log"
logger = utils.create_logger(logger_name, logger_filename, -1,
logger_output_to_console, logger_output_to_file)
class GameSpyServerDatabase(BaseManager):
pass

View File

@ -34,13 +34,7 @@ import gamespy.gs_utility as gs_utils
import other.utils as utils
import dwc_config
logger_output_to_console = True
logger_output_to_file = True
logger_name = "GameStatsServerHttp"
logger_filename = "gamestats_server_http.log"
logger = utils.create_logger(logger_name, logger_filename, -1,
logger_output_to_console, logger_output_to_file)
logger = dwc_config.get_logger('GameStatsServerHttp')
address = dwc_config.get_ip_port('GameStatsServerHttp')

View File

@ -30,12 +30,7 @@ import logging
import other.utils as utils
import dwc_config
logger_output_to_console = True
logger_output_to_file = True
logger_name = "InternalStatsServer"
logger_filename = "internal_stats_server.log"
logger = utils.create_logger(logger_name, logger_filename, -1,
logger_output_to_console, logger_output_to_file)
logger = dwc_config.get_logger('InternalStatsServer')
class GameSpyServerDatabase(BaseManager):

View File

@ -32,6 +32,7 @@ from internal_stats_server import InternalStatsServer
from admin_page_server import AdminPageServer
from storage_server import StorageServer
from gamestats_server_http import GameStatsServer
from register_page import RegPageServer
import gamespy.gs_database as gs_database
@ -60,6 +61,7 @@ if __name__ == "__main__":
NasServer,
InternalStatsServer,
AdminPageServer,
RegPageServer,
StorageServer,
GameStatsServer,
]

View File

@ -35,12 +35,7 @@ import gamespy.gs_database as gs_database
import other.utils as utils
import dwc_config
logger_output_to_console = True
logger_output_to_file = True
logger_name = "NasServer"
logger_filename = "nas_server.log"
logger = utils.create_logger(logger_name, logger_filename, -1,
logger_output_to_console, logger_output_to_file)
logger = dwc_config.get_logger('NasServer')
# If a game from this list requests a file listing, the server will return
# that only one exists and return a random one.
@ -178,23 +173,28 @@ class NasHTTPServerHandler(BaseHTTPServer.BaseHTTPRequestHandler):
"retry": "1",
"reason": "User banned."
}
#Un-comment these lines to enable console registration feature
#elif not self.server.db.pending(post):
#logger.log(logging.DEBUG, "Login denied - Unknown console"+str(post))
#ret = {
#"datetime": time.strftime("%Y%m%d%H%M%S"),
#"returncd": "3921",
#"locator": "gamespy.com",
#"retry": "1",
#}
#elif not self.server.db.registered(post):
#logger.log(logging.DEBUG, "Login denied - console pending"+str(post))
#ret = {
#"datetime": time.strftime("%Y%m%d%H%M%S"),
#"returncd": "3888",
#"locator": "gamespy.com",
#"retry": "1",
#}
# Un-comment these lines to enable console registration
# feature
# elif not self.server.db.pending(post):
# logger.log(logging.DEBUG,
# "Login denied - Unknown console %s",
# post)
# ret = {
# "datetime": time.strftime("%Y%m%d%H%M%S"),
# "returncd": "3921",
# "locator": "gamespy.com",
# "retry": "1",
# }
# elif not self.server.db.registered(post):
# logger.log(logging.DEBUG,
# "Login denied - console pending %s",
# post)
# ret = {
# "datetime": time.strftime("%Y%m%d%H%M%S"),
# "returncd": "3888",
# "locator": "gamespy.com",
# "retry": "1",
# }
else:
challenge = utils.generate_random_str(8)
post["challenge"] = challenge

View File

@ -1,20 +1,23 @@
# DWC Network Server Emulator
# Copyright (C) 2014 SMTDDR
# Copyright (C) 2014 kyle95wm
# Copyright (C) 2014 AdmiralCurtiss
#
# 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 <http://www.gnu.org/licenses/>.
"""DWC Network Server Emulator
Copyright (C) 2014 SMTDDR
Copyright (C) 2014 kyle95wm
Copyright (C) 2014 AdmiralCurtiss
Copyright (C) 2015 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 <http://www.gnu.org/licenses/>.
"""
from twisted.web import server, resource
from twisted.internet import reactor
@ -27,36 +30,40 @@ import collections
import json
import time
import datetime
import logging
import other.utils as utils
import gamespy
import gamespy.gs_utility as gs_utils
import dwc_config
logger = dwc_config.get_logger('RegisterPage')
_, port = dwc_config.get_ip_port('RegisterPage')
class RegPage(resource.Resource):
isLeaf = True
def __init__(self,regpage):
def __init__(self, regpage):
self.regpage = regpage
def get_header(self, title = None):
def get_header(self, title=None):
if not title:
title = 'Register a Console'
s = (
'<html>'
'<head>'
'<title>' + title + '</title>'
'</head>'
'<body>'
'<p>'
'<b>Register a console</b>'
'</p>'
)
s = """
<html>
<head>
<title>%s</title>
</head>
<body>
<p>
<b>Register a console</b>
</p>""" % title
return s
def get_footer(self):
s = (
'</body>'
'</html>'
)
s = """
</body>
</html>"""
return s
def update_maclist(self, request):
@ -65,13 +72,21 @@ class RegPage(resource.Resource):
macadr = request.args['macadr'][0].strip()
actiontype = request.args['action'][0]
macadr = macadr.lower()
if not re.match("[0-9a-f]{2}([-:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", macadr):
if not re.match("[0-9a-f]{2}([-:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$",
macadr):
request.setResponseCode(500)
return "The MAC you entered was invalid. Please click the back button and try again!"
macadr = macadr.replace(":","").replace("-","")
return "The MAC you entered was invalid." \
"Please click the back button and try again!"
macadr = macadr.replace(":", "").replace("-", "")
if actiontype == 'add':
dbconn.cursor().execute('insert into pending values(?)',(macadr,))
responsedata = "Added %s to pending list. Please close this window now. It's also not a bad idea to check back on the status of your activation by attempting to connect your console to the server." % (macadr)
dbconn.cursor().execute(
'INSERT INTO pending VALUES(?)',
(macadr,)
)
responsedata = "Added %s to pending list." % (macadr)
responsedata += "Please close this window now."
" It's also not a bad idea to check back on the status of your"
" activation by attempting to connect your console to the server."
dbconn.commit()
dbconn.close()
request.setHeader("Content-Type", "text/html; charset=utf-8")
@ -87,13 +102,14 @@ class RegPage(resource.Resource):
def render_maclist(self, request):
address = request.getClientIP()
dbconn = sqlite3.connect('gpcm.db')
responsedata = (""
"<form action='updatemaclist' method='POST'>"
"macadr (must be in the format of aa:bb:cc:dd:ee:ff or aa-bb-cc-dd-ee-ff):<input type='text' name='macadr'>\r\n"
"<input type='hidden' name='action' value='add'>\r\n"
"<input type='submit' value='Register console'></form>\r\n"
"<table border='1'>"
"")
responsedata = """
<form action='updatemaclist' method='POST'>
macadr (must be in the format of %s or %s):
<input type='text' name='macadr'>
<input type='hidden' name='action' value='add'>
<input type='submit' value='Register console'>
</form>
<table border='1'>""" % ('aa:bb:cc:dd:ee:ff', 'aa-bb-cc-dd-ee-ff')
dbconn.close()
request.setHeader("Content-Type", "text/html; charset=utf-8")
return responsedata
@ -113,16 +129,20 @@ class RegPage(resource.Resource):
else:
return self.get_header() + self.get_footer()
port = 9998
class RegPageServer(object):
def start(self):
site = server.Site(RegPage(self))
reactor.listenTCP(port, site)
logger.log(logging.INFO,
"Now listening for connections on port %d...",
port)
try:
if reactor.running == False:
if not reactor.running:
reactor.run(installSignalHandlers=0)
except ReactorAlreadyRunning:
pass
if __name__ == "__main__":
RegPageServer().start()

View File

@ -31,15 +31,11 @@ import other.utils as utils
import gamespy.gs_database as gs_database
import dwc_config
logger_output_to_console = True
logger_output_to_file = True
logger_name = "StorageServer"
logger_filename = "storage_server.log"
logger = utils.create_logger(logger_name, logger_filename, -1, logger_output_to_console, logger_output_to_file)
# Paths to ProxyPass: /SakeStorageServer, /SakeFileServer
logger = dwc_config.get_logger('StorageServer')
address = dwc_config.get_ip_port('StorageServer')
def escape_xml(s):
s = s.replace( "&", "&amp;" )
s = s.replace( '"', "&quot;" )
@ -48,12 +44,14 @@ def escape_xml(s):
s = s.replace( ">", "&gt;" )
return s
class StorageServer(object):
def start(self):
httpd = StorageHTTPServer((address[0], address[1]), StorageHTTPServerHandler)
logger.log(logging.INFO, "Now listening for connections on %s:%d...", address[0], address[1])
httpd.serve_forever()
class StorageHTTPServer(BaseHTTPServer.HTTPServer):
def __init__(self, server_address, RequestHandlerClass):
BaseHTTPServer.HTTPServer.__init__(self, server_address, RequestHandlerClass)
@ -210,11 +208,15 @@ class StorageHTTPServer(BaseHTTPServer.HTTPServer):
except TypeError:
return 'UNKNOWN'
class IllegalColumnAccessException(Exception):
pass
class FilterSyntaxException(Exception):
pass
class StorageHTTPServerHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def confirm_columns(self, columndata, table):
'''Check if the columns the user wants to access actually exist, which should prevent SQL Injection'''