2023-03-05 04:46:26 +01:00
|
|
|
from typing import Dict, Any, Optional, List
|
2023-02-17 07:37:59 +01:00
|
|
|
import logging, coloredlogs
|
|
|
|
from logging.handlers import TimedRotatingFileHandler
|
|
|
|
from twisted.web import resource
|
|
|
|
from twisted.web.http import Request
|
|
|
|
from datetime import datetime
|
|
|
|
import pytz
|
|
|
|
|
2023-03-12 07:00:51 +01:00
|
|
|
from core import CoreConfig
|
2023-03-05 04:46:26 +01:00
|
|
|
from core.utils import Utils
|
2023-02-17 07:37:59 +01:00
|
|
|
|
2023-03-09 17:38:58 +01:00
|
|
|
|
2023-02-17 07:37:59 +01:00
|
|
|
class MuchaServlet:
|
2023-03-09 17:38:58 +01:00
|
|
|
def __init__(self, cfg: CoreConfig, cfg_dir: str) -> None:
|
2023-02-17 07:37:59 +01:00
|
|
|
self.config = cfg
|
2023-03-05 04:46:26 +01:00
|
|
|
self.config_dir = cfg_dir
|
|
|
|
self.mucha_registry: List[str] = []
|
2023-02-17 07:37:59 +01:00
|
|
|
|
2023-03-09 17:38:58 +01:00
|
|
|
self.logger = logging.getLogger("mucha")
|
2023-02-17 07:37:59 +01:00
|
|
|
log_fmt_str = "[%(asctime)s] Mucha | %(levelname)s | %(message)s"
|
|
|
|
log_fmt = logging.Formatter(log_fmt_str)
|
2023-03-09 17:38:58 +01:00
|
|
|
|
|
|
|
fileHandler = TimedRotatingFileHandler(
|
|
|
|
"{0}/{1}.log".format(self.config.server.log_dir, "mucha"),
|
|
|
|
when="d",
|
|
|
|
backupCount=10,
|
|
|
|
)
|
2023-02-17 07:37:59 +01:00
|
|
|
fileHandler.setFormatter(log_fmt)
|
2023-03-09 17:38:58 +01:00
|
|
|
|
2023-02-17 07:37:59 +01:00
|
|
|
consoleHandler = logging.StreamHandler()
|
|
|
|
consoleHandler.setFormatter(log_fmt)
|
|
|
|
|
|
|
|
self.logger.addHandler(fileHandler)
|
|
|
|
self.logger.addHandler(consoleHandler)
|
2023-03-09 17:38:58 +01:00
|
|
|
|
2023-02-17 07:37:59 +01:00
|
|
|
self.logger.setLevel(logging.INFO)
|
|
|
|
coloredlogs.install(level=logging.INFO, logger=self.logger, fmt=log_fmt_str)
|
|
|
|
|
2023-03-05 04:46:26 +01:00
|
|
|
all_titles = Utils.get_all_titles()
|
|
|
|
|
|
|
|
for _, mod in all_titles.items():
|
|
|
|
if hasattr(mod, "index") and hasattr(mod.index, "get_mucha_info"):
|
2023-03-09 17:38:58 +01:00
|
|
|
enabled, game_cd = mod.index.get_mucha_info(
|
|
|
|
self.config, self.config_dir
|
|
|
|
)
|
2023-03-05 04:46:26 +01:00
|
|
|
if enabled:
|
|
|
|
self.mucha_registry.append(game_cd)
|
2023-03-09 17:38:58 +01:00
|
|
|
|
2023-04-24 03:04:52 +02:00
|
|
|
self.logger.info(f"Serving {len(self.mucha_registry)} games")
|
2023-03-05 04:46:26 +01:00
|
|
|
|
2023-02-19 05:12:40 +01:00
|
|
|
def handle_boardauth(self, request: Request, _: Dict) -> bytes:
|
2023-02-17 07:37:59 +01:00
|
|
|
req_dict = self.mucha_preprocess(request.content.getvalue())
|
2023-03-12 07:00:51 +01:00
|
|
|
client_ip = Utils.get_ip_addr(request)
|
|
|
|
|
2023-02-17 07:37:59 +01:00
|
|
|
if req_dict is None:
|
2023-03-09 17:38:58 +01:00
|
|
|
self.logger.error(
|
|
|
|
f"Error processing mucha request {request.content.getvalue()}"
|
|
|
|
)
|
2023-02-17 07:37:59 +01:00
|
|
|
return b""
|
|
|
|
|
|
|
|
req = MuchaAuthRequest(req_dict)
|
2023-02-21 22:46:43 +01:00
|
|
|
self.logger.debug(f"Mucha request {vars(req)}")
|
2023-04-24 03:04:52 +02:00
|
|
|
self.logger.info(f"Boardauth request from {client_ip} for {req.gameVer}")
|
2023-03-05 04:46:26 +01:00
|
|
|
|
|
|
|
if req.gameCd not in self.mucha_registry:
|
|
|
|
self.logger.warn(f"Unknown gameCd {req.gameCd}")
|
|
|
|
return b""
|
|
|
|
|
2023-03-05 17:42:03 +01:00
|
|
|
# TODO: Decrypt S/N
|
2023-03-09 17:38:58 +01:00
|
|
|
|
|
|
|
resp = MuchaAuthResponse(
|
2023-03-12 07:59:12 +01:00
|
|
|
f"{self.config.mucha.hostname}{':' + str(self.config.allnet.port) if self.config.server.is_develop else ''}"
|
2023-03-09 17:38:58 +01:00
|
|
|
)
|
2023-02-21 22:46:43 +01:00
|
|
|
|
|
|
|
self.logger.debug(f"Mucha response {vars(resp)}")
|
2023-02-17 07:37:59 +01:00
|
|
|
|
|
|
|
return self.mucha_postprocess(vars(resp))
|
2023-03-09 17:38:58 +01:00
|
|
|
|
2023-02-19 05:12:40 +01:00
|
|
|
def handle_updatecheck(self, request: Request, _: Dict) -> bytes:
|
2023-02-17 07:37:59 +01:00
|
|
|
req_dict = self.mucha_preprocess(request.content.getvalue())
|
2023-03-12 07:00:51 +01:00
|
|
|
client_ip = Utils.get_ip_addr(request)
|
|
|
|
|
2023-02-17 07:37:59 +01:00
|
|
|
if req_dict is None:
|
2023-03-09 17:38:58 +01:00
|
|
|
self.logger.error(
|
|
|
|
f"Error processing mucha request {request.content.getvalue()}"
|
|
|
|
)
|
2023-02-17 07:37:59 +01:00
|
|
|
return b""
|
|
|
|
|
|
|
|
req = MuchaUpdateRequest(req_dict)
|
2023-02-21 22:46:43 +01:00
|
|
|
self.logger.debug(f"Mucha request {vars(req)}")
|
2023-04-24 03:04:52 +02:00
|
|
|
self.logger.info(f"Updatecheck request from {client_ip} for {req.gameVer}")
|
2023-03-05 04:46:26 +01:00
|
|
|
|
|
|
|
if req.gameCd not in self.mucha_registry:
|
|
|
|
self.logger.warn(f"Unknown gameCd {req.gameCd}")
|
|
|
|
return b""
|
2023-02-21 22:46:43 +01:00
|
|
|
|
2023-03-05 15:43:05 +01:00
|
|
|
resp = MuchaUpdateResponseStub(req.gameVer)
|
2023-02-21 22:46:43 +01:00
|
|
|
|
|
|
|
self.logger.debug(f"Mucha response {vars(resp)}")
|
2023-02-17 07:37:59 +01:00
|
|
|
|
|
|
|
return self.mucha_postprocess(vars(resp))
|
|
|
|
|
|
|
|
def mucha_preprocess(self, data: bytes) -> Optional[Dict]:
|
|
|
|
try:
|
|
|
|
ret: Dict[str, Any] = {}
|
2023-03-09 17:38:58 +01:00
|
|
|
|
|
|
|
for x in data.decode().split("&"):
|
|
|
|
kvp = x.split("=")
|
2023-02-17 07:37:59 +01:00
|
|
|
if len(kvp) == 2:
|
|
|
|
ret[kvp[0]] = kvp[1]
|
|
|
|
|
|
|
|
return ret
|
2023-03-09 17:38:58 +01:00
|
|
|
|
2023-02-17 07:37:59 +01:00
|
|
|
except:
|
|
|
|
self.logger.error(f"Error processing mucha request {data}")
|
|
|
|
return None
|
|
|
|
|
|
|
|
def mucha_postprocess(self, data: dict) -> Optional[bytes]:
|
|
|
|
try:
|
|
|
|
urlencode = ""
|
2023-03-09 17:38:58 +01:00
|
|
|
for k, v in data.items():
|
2023-02-17 07:37:59 +01:00
|
|
|
urlencode += f"{k}={v}&"
|
|
|
|
|
|
|
|
return urlencode.encode()
|
|
|
|
|
|
|
|
except:
|
|
|
|
self.logger.error("Error processing mucha response")
|
|
|
|
return None
|
|
|
|
|
2023-03-09 17:38:58 +01:00
|
|
|
|
|
|
|
class MuchaAuthRequest:
|
2023-02-17 07:37:59 +01:00
|
|
|
def __init__(self, request: Dict) -> None:
|
2023-03-09 18:17:10 +01:00
|
|
|
# gameCd + boardType + countryCd + version
|
|
|
|
self.gameVer = request.get("gameVer", "")
|
|
|
|
self.sendDate = request.get("sendDate", "") # %Y%m%d
|
|
|
|
self.serialNum = request.get("serialNum", "")
|
|
|
|
self.gameCd = request.get("gameCd", "")
|
|
|
|
self.boardType = request.get("boardType", "")
|
|
|
|
self.boardId = request.get("boardId", "")
|
|
|
|
self.mac = request.get("mac", "")
|
|
|
|
self.placeId = request.get("placeId", "")
|
|
|
|
self.storeRouterIp = request.get("storeRouterIp", "")
|
|
|
|
self.countryCd = request.get("countryCd", "")
|
|
|
|
self.useToken = request.get("useToken", "")
|
|
|
|
self.allToken = request.get("allToken", "")
|
2023-02-17 07:37:59 +01:00
|
|
|
|
2023-03-09 17:38:58 +01:00
|
|
|
|
|
|
|
class MuchaAuthResponse:
|
2023-03-05 04:48:12 +01:00
|
|
|
def __init__(self, mucha_url: str) -> None:
|
2023-03-09 17:38:58 +01:00
|
|
|
self.RESULTS = "001"
|
2023-02-17 07:37:59 +01:00
|
|
|
self.AUTH_INTERVAL = "86400"
|
|
|
|
self.SERVER_TIME = datetime.strftime(datetime.now(), "%Y%m%d%H%M")
|
|
|
|
self.UTC_SERVER_TIME = datetime.strftime(datetime.now(pytz.UTC), "%Y%m%d%H%M")
|
|
|
|
|
2023-03-09 17:38:58 +01:00
|
|
|
self.CHARGE_URL = f"https://{mucha_url}/charge/"
|
2023-02-17 07:37:59 +01:00
|
|
|
self.FILE_URL = f"https://{mucha_url}/file/"
|
|
|
|
self.URL_1 = f"https://{mucha_url}/url1/"
|
|
|
|
self.URL_2 = f"https://{mucha_url}/url2/"
|
|
|
|
self.URL_3 = f"https://{mucha_url}/url3/"
|
2023-03-09 17:38:58 +01:00
|
|
|
|
|
|
|
self.PLACE_ID = "JPN123"
|
|
|
|
self.COUNTRY_CD = "JPN"
|
2023-02-17 07:37:59 +01:00
|
|
|
self.SHOP_NAME = "TestShop!"
|
|
|
|
self.SHOP_NICKNAME = "TestShop"
|
|
|
|
self.AREA_0 = "008"
|
|
|
|
self.AREA_1 = "009"
|
|
|
|
self.AREA_2 = "010"
|
|
|
|
self.AREA_3 = "011"
|
|
|
|
self.AREA_FULL_0 = ""
|
|
|
|
self.AREA_FULL_1 = ""
|
|
|
|
self.AREA_FULL_2 = ""
|
|
|
|
self.AREA_FULL_3 = ""
|
2023-03-09 17:38:58 +01:00
|
|
|
|
2023-02-17 07:37:59 +01:00
|
|
|
self.SHOP_NAME_EN = "TestShop!"
|
|
|
|
self.SHOP_NICKNAME_EN = "TestShop"
|
|
|
|
self.AREA_0_EN = "008"
|
|
|
|
self.AREA_1_EN = "009"
|
|
|
|
self.AREA_2_EN = "010"
|
|
|
|
self.AREA_3_EN = "011"
|
|
|
|
self.AREA_FULL_0_EN = ""
|
|
|
|
self.AREA_FULL_1_EN = ""
|
|
|
|
self.AREA_FULL_2_EN = ""
|
|
|
|
self.AREA_FULL_3_EN = ""
|
|
|
|
|
2023-03-09 17:38:58 +01:00
|
|
|
self.PREFECTURE_ID = "1"
|
2023-02-17 07:37:59 +01:00
|
|
|
self.EXPIRATION_DATE = "null"
|
|
|
|
self.USE_TOKEN = "0"
|
|
|
|
self.CONSUME_TOKEN = "0"
|
|
|
|
self.DONGLE_FLG = "1"
|
|
|
|
self.FORCE_BOOT = "0"
|
|
|
|
|
2023-03-09 17:38:58 +01:00
|
|
|
|
|
|
|
class MuchaUpdateRequest:
|
2023-02-17 07:37:59 +01:00
|
|
|
def __init__(self, request: Dict) -> None:
|
2023-03-09 18:17:10 +01:00
|
|
|
self.gameVer = request.get("gameVer", "")
|
|
|
|
self.gameCd = request.get("gameCd", "")
|
|
|
|
self.serialNum = request.get("serialNum", "")
|
|
|
|
self.countryCd = request.get("countryCd", "")
|
|
|
|
self.placeId = request.get("placeId", "")
|
|
|
|
self.storeRouterIp = request.get("storeRouterIp", "")
|
2023-02-17 07:37:59 +01:00
|
|
|
|
2023-03-09 17:38:58 +01:00
|
|
|
|
|
|
|
class MuchaUpdateResponse:
|
2023-03-05 04:48:12 +01:00
|
|
|
def __init__(self, game_ver: str, mucha_url: str) -> None:
|
2023-02-17 07:37:59 +01:00
|
|
|
self.RESULTS = "001"
|
|
|
|
self.UPDATE_VER_1 = game_ver
|
|
|
|
self.UPDATE_URL_1 = f"https://{mucha_url}/updUrl1/"
|
|
|
|
self.UPDATE_SIZE_1 = "0"
|
|
|
|
self.UPDATE_CRC_1 = "0000000000000000"
|
|
|
|
self.CHECK_URL_1 = f"https://{mucha_url}/checkUrl/"
|
|
|
|
self.EXE_VER_1 = game_ver
|
|
|
|
self.INFO_SIZE_1 = "0"
|
|
|
|
self.COM_SIZE_1 = "0"
|
|
|
|
self.COM_TIME_1 = "0"
|
|
|
|
self.LAN_INFO_SIZE_1 = "0"
|
2023-03-05 15:45:34 +01:00
|
|
|
self.USER_ID = ""
|
|
|
|
self.PASSWORD = ""
|
2023-03-05 15:43:05 +01:00
|
|
|
|
2023-03-09 17:38:58 +01:00
|
|
|
|
|
|
|
class MuchaUpdateResponseStub:
|
2023-03-05 15:43:05 +01:00
|
|
|
def __init__(self, game_ver: str) -> None:
|
|
|
|
self.RESULTS = "001"
|
|
|
|
self.UPDATE_VER_1 = game_ver
|