gamestats_server_http.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. """DWC Network Server Emulator
  2. Copyright (C) 2014 polaris-
  3. Copyright (C) 2015 Sepalani
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU Affero General Public License as
  6. published by the Free Software Foundation, either version 3 of the
  7. License, or (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Affero General Public License for more details.
  12. You should have received a copy of the GNU Affero General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. TODO: Seperate gamestats.gs.nintendowifi.net
  15. and gamestats2.gs.nintendowifi.net
  16. TODO: Move gamestats list to database?
  17. """
  18. import logging
  19. import urllib.parse
  20. import http.server
  21. import traceback
  22. import os
  23. import hashlib
  24. import base64
  25. import gamespy.gs_database as gs_database
  26. import gamespy.gs_utility as gs_utils
  27. import other.utils as utils
  28. import dwc_config
  29. logger = dwc_config.get_logger('GameStatsServerHttp')
  30. address = dwc_config.get_ip_port('GameStatsServerHttp')
  31. class GameStatsBase(object):
  32. def do_GET(self, conn, key, append_hash, append_text=""):
  33. try:
  34. conn.send_response(200)
  35. conn.send_header("Content-type", "text/html")
  36. conn.send_header("Server", "Microsoft-IIS/6.0")
  37. conn.send_header("Server", "GSTPRDSTATSWEB2")
  38. conn.send_header("X-Powered-By", "ASP.NET")
  39. conn.end_headers()
  40. params = conn.str_to_dict(conn.path)
  41. ret = ""
  42. if "hash" not in params:
  43. # The token is used in combination with the game's secret key.
  44. # The format of the hash parameter sent from the client is
  45. # sha1(secret_key + token).
  46. token = utils.generate_random_str(32)
  47. ret = token
  48. else:
  49. # Handle data (generic response for now)
  50. ret += append_text
  51. if append_hash:
  52. h = hashlib.sha1()
  53. h.update(key + base64.urlsafe_b64encode(ret) + key)
  54. ret += h.hexdigest()
  55. conn.wfile.write(ret)
  56. except:
  57. logger.log(logging.ERROR,
  58. "Unknown exception: %s",
  59. traceback.format_exc())
  60. def do_POST(self, conn, key):
  61. try:
  62. conn.send_response(200)
  63. conn.send_header("Content-type", "text/html")
  64. conn.send_header("Server", "Microsoft-IIS/6.0")
  65. conn.send_header("Server", "GSTPRDSTATSWEB2")
  66. conn.send_header("X-Powered-By", "ASP.NET")
  67. conn.end_headers()
  68. conn.wfile.write("")
  69. except:
  70. logger.log(logging.ERROR,
  71. "Unknown exception: %s",
  72. traceback.format_exc())
  73. class GameStatsVersion1(GameStatsBase):
  74. def do_GET(self, conn, key):
  75. super(self.__class__, self).do_GET(conn, key, False, "")
  76. class GameStatsVersion2(GameStatsBase):
  77. def do_GET(self, conn, key):
  78. super(self.__class__, self).do_GET(conn, key, True, "")
  79. class GameStatsVersion3(GameStatsBase):
  80. def do_GET(self, conn, key):
  81. super(self.__class__, self).do_GET(conn, key, True, "done")
  82. class GameStatsServer(object):
  83. def start(self):
  84. httpd = GameStatsHTTPServer((address[0], address[1]),
  85. GameStatsHTTPServerHandler)
  86. logger.log(logging.INFO,
  87. "Now listening for connections on %s:%d...",
  88. address[0], address[1])
  89. httpd.serve_forever()
  90. class GameStatsHTTPServer(http.server.HTTPServer):
  91. gamestats_list = [
  92. GameStatsBase,
  93. GameStatsVersion1,
  94. GameStatsVersion2,
  95. GameStatsVersion3
  96. ]
  97. def __init__(self, server_address, RequestHandlerClass):
  98. # self.db = gs_database.GamespyDatabase()
  99. self.gamelist = self.parse_key_file()
  100. http.server.HTTPServer.__init__(self, server_address,
  101. RequestHandlerClass)
  102. def parse_key_file(self, filename="gamestats.cfg"):
  103. gamelist = {}
  104. with open(filename) as config_file:
  105. for line in config_file.readlines():
  106. line, sep, comment = line.partition("#")
  107. # Skip whitespaces (i.e. ' ', '\t', '\n')
  108. s = line.split(None)
  109. if len(s) != 3:
  110. continue
  111. gamestats = self.gamestats_list[0]
  112. if int(s[1]) < len(self.gamestats_list):
  113. gamestats = self.gamestats_list[int(s[1])]
  114. gamelist[s[0]] = {'key': s[2], 'class': gamestats}
  115. return gamelist
  116. class GameStatsHTTPServerHandler(http.server.BaseHTTPRequestHandler):
  117. def version_string(self):
  118. return "Nintendo Wii (http)"
  119. def do_GET(self):
  120. gameid = self.path.lstrip("/")
  121. if '/' in gameid:
  122. gameid = gameid[:gameid.index('/')]
  123. # logger.log(logging.DEBUG, "Request for '%s': %s", gameid, self.path)
  124. if gameid in self.server.gamelist:
  125. game = self.server.gamelist[gameid]['class']()
  126. game.do_GET(self, self.server.gamelist[gameid]['key'])
  127. else:
  128. logger.log(logging.DEBUG,
  129. "WARNING: Could not find '%s' in gamestats list",
  130. gameid)
  131. default = GameStatsBase()
  132. default.do_GET(self, "", False, "")
  133. def do_POST(self):
  134. pass
  135. def str_to_dict(self, str):
  136. ret = urllib.parse.parse_qs(urllib.parse.urlparse(str).query)
  137. for k, v in ret.items():
  138. ret[k] = v[0]
  139. return ret
  140. if __name__ == "__main__":
  141. gamestats = GameStatsServer()
  142. gamestats.start()