#!/usr/bin/env python3 """FUT roster-update HTTPS server for FIFA 17 (OpenFUT). The FUT loading flow (checkFUTRostersFlow / state CheckFUTRosterUpdateXML) downloads a roster-update XML from ROSTERUPDATE_URL (which blaze_responder_v3b.py now serves as https://127.0.0.1:8081/fifa17/fut/rosterupdate.xml). On download SUCCESS the flow raises `advance` -> CheckFUTSquadBinFile -> (no squad) -> EnterFUT -> CardsDLL loads. On FAIL it raises `back` -> abort. So this must return something FIFA ACCEPTS. We don't have (or need) EA's real roster: the base player DB is baked into CardsDLL; the roster-update is an optional delta. Start by serving a minimal "no update" body, LOG every request (path/headers) so we learn exactly what FIFA fetches, and iterate. HTTPS because EA's value is https and the DirtySDK download mgr may reject http; FIFA's ProtoSSL cert-verify is patched (autopatch), so our self-signed cert is accepted. """ import http.server, ssl, os, sys, datetime HERE = os.path.dirname(os.path.abspath(__file__)) CERT = os.path.join(HERE, "redir_cert.pem") KEY = os.path.join(HERE, "redir_key.pem") LOG = "/tmp/roster_server.log" ADDR = ("127.0.0.1", 8081) # Minimal "no update available" roster body. Unknown-format -> iterate from the log. ROSTER_XML = b'\n\n' def log(m): line = "[%s] %s" % (datetime.datetime.now().strftime("%H:%M:%S"), m) print(line, flush=True) with open(LOG, "a") as f: f.write(line + "\n") class H(http.server.BaseHTTPRequestHandler): def _handle(self, method): log("%s %s from %s" % (method, self.path, self.client_address)) for k, v in self.headers.items(): log(" %s: %s" % (k, v)) body = ROSTER_XML self.send_response(200) self.send_header("Content-Type", "application/xml") self.send_header("Content-Length", str(len(body))) self.send_header("Connection", "close") self.end_headers() if method == "GET": self.wfile.write(body) log(" -> 200 %dB (%r)" % (len(body), body[:60])) def do_GET(self): self._handle("GET") def do_HEAD(self): self._handle("HEAD") def do_POST(self): n = int(self.headers.get("Content-Length", 0) or 0) if n: log(" POST body: %r" % self.rfile.read(n)[:200]) self._handle("POST") def log_message(self, *a): # silence default stderr logging pass def main(): open(LOG, "a").close() ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) ctx.load_cert_chain(CERT, KEY) ctx.minimum_version = ssl.TLSVersion.TLSv1 try: ctx.set_ciphers("ALL:@SECLEVEL=0") except Exception: pass httpd = http.server.HTTPServer(ADDR, H) httpd.socket = ctx.wrap_socket(httpd.socket, server_side=True) log("=== roster_server https://%s:%d (FUT roster-update) ===" % ADDR) httpd.serve_forever() if __name__ == "__main__": main()