#!/usr/bin/env python3 # # InspIRCd -- Internet Relay Chat Daemon # # Copyright (C) 2024 Val Lorentz # Copyright (C) 2024 Sadie Powell # # This file is part of InspIRCd. InspIRCd is free software: you can # redistribute it and/or modify it under the terms of the GNU General Public # License as published by the Free Software Foundation, version 2. # # 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 General Public License for more # details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import io import os import sys class UnrealDB: def __init__(self, path): self.error = None try: with open(sys.argv[1], mode="rb") as fh: data = fh.read() if data.startswith(b"UnrealIRCd-DB-v1"): self.data = io.BytesIO(data[40:]) elif data.startswith(b"UnrealIRCd-DB"): self.error = f"Unsupported database version: {data[0:32]}" else: self.data = io.BytesIO(data) except OSError as e: self.error = f"Read error: {e}" def read_char(self): return self.data.read(1).decode("utf-8") def read_i16(self): return int.from_bytes(self.data.read(2), byteorder="little") def read_i32(self): return int.from_bytes(self.data.read(4), byteorder="little") def read_i64(self): return int.from_bytes(self.data.read(8), byteorder="little") def read_str(self): len = self.read_i16() if len == 0 or len == 0xFFFF: return "" return self.data.read(len).decode("utf-8") def error(msg): print(msg, file=sys.stderr) sys.exit(1) if len(sys.argv) < 3: program = os.path.basename(__file__) error(f"Usage: {program} ") db = UnrealDB(sys.argv[1]) if db.error: error(db.error) magic = db.read_i32() if magic != 0x10101010: error(f"Unrecognised database magic: {magic}") version = db.read_i32() if version != 4999: error(f"Unrecognised TKL database version: {version}") count = db.read_i64() print(f"Converting {count} TKLs ...") try: with open(sys.argv[2], mode="w") as fh: print("VERSION 1", file=fh) for i in range(count): type = db.read_char() set_by = db.read_str() set_at = db.read_i64() expire_at = db.read_i64() duration = expire_at - set_at if type in "Ee": username = db.read_str() hostname = db.read_str() etype = db.read_str() # unused reason = db.read_str() print( f"LINE E {username}@{hostname} {set_by} {set_at} {duration} :{reason}", file=fh, ) elif type in "Ff": method = db.read_str() # unused pattern = db.read_str() # unused target = db.read_str() # unused action = db.read_char() # unused reason = db.read_str() # unused duration = db.read_i64() # unused elif type in "GksZz": username = db.read_str() hostname = db.read_str() reason = db.read_str() line = "SHUN" if type == "s" else type.upper() print( f"LINE {line} {username}@{hostname} {set_by} {set_at} {duration} :{reason}", file=fh, ) elif type in "Qq": etype = db.read_str() # unused name = db.read_str() reason = db.read_str() print(f"LINE Q {name} {set_by} {set_at} {duration} :{reason}", file=fh) else: error(f"Unsupported row type: {type}") except OSError as e: error(f"Write error: {e}")