diff --git a/gamestats.cfg b/gamestats.cfg new file mode 100644 index 0000000..1f4df39 --- /dev/null +++ b/gamestats.cfg @@ -0,0 +1,14 @@ +# Format: Gamename Gamestats Version Secret key +# Use tabs in between values. +# Hash (#) can be used for comments. +# Valid gamestats versions are 1 (no response footer, no encrypted requests), 2 (response footer), and 3 (sends "done" at the end of the message before the response footer). +# DS games will default to 1. +# Wii games will default to 3. + +tetrisds 1 Wo3vqrDoL56sAdveYeC1 # Tetris DS +bombls2ds 1 lUJtkWkDXhZtZvMWphlm # Bomberman Land Touch! 2 +acrossingds 1 gOAkBaBav5XHlGyUKOOD # Animal Crossing: Wild World +DSwars2ds 1 SqjRjgtBldqjVDNShTRS # Advance Wars: Dark Conflict +syachi2ds 2 HZEdGCzcGGLvguqUEKQN # Pokemon Black/White/Black 2/White 2 +#gwgalaxieswii 3 INSERTKEYHERE # Geometry Wars: Galaxies +#megaman9wii 3 INSERTKEYHERE # Mega Man 9 \ No newline at end of file diff --git a/gamestats_server_http.py b/gamestats_server_http.py new file mode 100644 index 0000000..85adb80 --- /dev/null +++ b/gamestats_server_http.py @@ -0,0 +1,147 @@ +# TODO: Add a way to redirect certain subdirectories (redirect Pokemon stuff to mm201's server, maybe others later) +# TODO: Seperate gamestats.gs.nintendowifi.net and gamestats2.gs.nintendowifi.net +# TODO: Move gamestats list to database? + +import logging +import urlparse +import BaseHTTPServer +import traceback +import os +import hashlib +import base64 + +import gamespy.gs_database as gs_database +import gamespy.gs_utility as gs_utils +import other.utils as utils + +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) + +#address = ("0.0.0.0", 80) +address = ("127.0.0.1", 9002) + +class GameStatsBase(object): + def do_GET(self, conn, key, append_hash, append_text = ""): + try: + conn.send_response(200) + conn.send_header("Content-type", "text/html") + conn.send_header("Server", "Microsoft-IIS/6.0") + conn.send_header("Server", "GSTPRDSTATSWEB2") + conn.send_header("X-Powered-By", "ASP.NET") + conn.end_headers() + + params = conn.str_to_dict(conn.path) + ret = "" + if "hash" not in params: + # The token is used in combination with the game's secret key. + # The format of the hash parameter sent from the client is sha1(secret_key + token). + token = utils.generate_random_str(32) + ret = token + else: + # Handle data (generic response for now) + ret += append_text + + if append_hash == True: + h = hashlib.sha1() + h.update(key + base64.urlsafe_b64encode(ret) + key) + ret += h.hexdigest() + + conn.wfile.write(ret) + except: + logger.log(logging.ERROR, "Unknown exception: %s" % traceback.format_exc()) + + def do_POST(self, conn, key): + try: + conn.send_response(200) + conn.send_header("Content-type", "text/html") + conn.send_header("Server", "Microsoft-IIS/6.0") + conn.send_header("Server", "GSTPRDSTATSWEB2") + conn.send_header("X-Powered-By", "ASP.NET") + conn.end_headers() + conn.wfile.write("") + + except: + logger.log(logging.ERROR, "Unknown exception: %s" % traceback.format_exc()) + +class GameStatsVersion1(GameStatsBase): + def do_GET(self, conn, key): + super(self.__class__, self).do_GET(conn, key, False, "") + +class GameStatsVersion2(GameStatsBase): + def do_GET(self, conn, key): + super(self.__class__, self).do_GET(conn, key, True, "") + +class GameStatsVersion3(GameStatsBase): + def do_GET(self, conn, key): + super(self.__class__, self).do_GET(conn, key, True, "done") + +class GameStatsServer(object): + def start(self): + httpd = GameStatsHTTPServer((address[0], address[1]), GameStatsHTTPServerHandler) + logger.log(logging.INFO, "Now listening for connections on %s:%d...", address[0], address[1]) + httpd.serve_forever() + +class GameStatsHTTPServer(BaseHTTPServer.HTTPServer): + def __init__(self, server_address, RequestHandlerClass): + self.db = gs_database.GamespyDatabase() + self.gamelist = self.parse_key_file() + + BaseHTTPServer.HTTPServer.__init__(self, server_address, RequestHandlerClass) + + def parse_key_file(self, filename = "gamestats.cfg"): + gamestats_list = [GameStatsBase, GameStatsVersion1, GameStatsVersion2, GameStatsVersion3] + gamelist = {} + + with open(filename) as config_file: + for line in config_file.readlines(): + if "#" in line: + line = line[:line.index('#')] + + s = line.split('\t') + + if len(s) != 3: + continue + + gamestats = gamestats_list[0] + if int(s[1]) < len(gamestats_list): + gamestats = gamestats_list[int(s[1])] + + gamelist[s[0]] = {'key': s[2], 'class': gamestats} + + return gamelist + +class GameStatsHTTPServerHandler(BaseHTTPServer.BaseHTTPRequestHandler): + def version_string(self): + return "Nintendo Wii (http)" + + def do_GET(self): + gameid = self.path.lstrip("/") + if '/' in gameid: + gameid = gameid[:gameid.index('/')] + + logger.log(logging.DEBUG, "Request for '%s': %s", gameid, self.path) + if gameid in self.server.gamelist: + game = self.server.gamelist[gameid]['class']() + game.do_GET(self, self.server.gamelist[gameid]['key']) + else: + logger.log(logging.DEBUG, "WARNING: Could not find '%s' in gamestats list", gameid) + default = GameStatsBase() + default.do_GET(self, "", False, "") + + def do_POST(self): + pass + + def str_to_dict(self, str): + ret = urlparse.parse_qs(urlparse.urlparse(str).query) + + for k, v in ret.iteritems(): + ret[k] = v[0] + + return ret + +if __name__ == "__main__": + gamestats = GameStatsServer() + gamestats.start()