import { SQLHandler } from "./sql.ts"; class LoginParser { private login_line = "^" + "(?[0-9]+)\t" + "(?[^\t]+)\t" + "(?(?:!(?.{5})\\$(?[a-f0-9]{24}))|(?:!1a2b3c4d\\+))\t" + // KILL IT WITH FIRE! "(?[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{3})\t" + // YYYY-mm-dd HH:MM:SS.sss "(?M|F|S|N)\t" + "(?[0-9]+)\t" + "(?[0-9]+)\t" + "(?[^@]+@[^\t]+)\t" + "-\t" + // unused error_message "0\t" + // unused "(?[^\t]+)\t" + "(?[-!])\t" + // ! means salted hash, - means ??? "(?[0-9]+)\t" + "$"; private login_regex: RegExp; private encoder: TextEncoder; constructor () { this.login_regex = new RegExp(this.login_line); this.encoder = new TextEncoder(); } private inet_aton(ip: string) { const a = ip.split('.'); const dv = new DataView(new ArrayBuffer(4)); dv.setUint8(0, +a[0]); dv.setUint8(1, +a[1]); dv.setUint8(2, +a[2]); dv.setUint8(3, +a[3]); return dv.getUint32(0); } private parseLine (line: string) { const match = this.login_regex.exec(line); if (!(match instanceof Object) || !Reflect.has(match, "groups")) { console.error("\nline does not match the account regex:", line); throw new SyntaxError(); } const groups = (match as any).groups; if (+groups.logincount === 0) { groups.lastlogin = null; } else { groups.lastlogin = new Date(groups.lastlogin).toISOString().slice(0, 19).replace("T", " "); } if (groups.memo !== "!") { console.warn(`\nunsupported password mode (${groups.memo}) for account ${groups.account_id}`); return null; } if (groups.email === "a@a.com") { groups.email = null; } groups.ip = this.inet_aton(groups.ip); if (+groups.ban_until_time >= 0xFFFFFFFF) { groups.ban_until_time = 0; groups.state = 5; // perma ban instead } Deno.write(Deno.stdout.rid, this.encoder.encode(`\r⌛ processing login data of account ${groups.account_id}...`)); return groups; } public async * readDB () { const decoder = new TextDecoder("utf-8"); console.info("\r \nwalking through account.txt..."); const file = await Deno.open("login/save/account.txt"); const buf = new Uint8Array(1024); let accumulator = ""; while (true) { const nread = await Deno.read(file.rid, buf); if (nread === null) { break; } const str = decoder.decode(buf); if (nread < 1024) { for (let c of str) { if (c === "\n") { if (accumulator.slice(0, 2) !== "//") { yield this.parseLine(accumulator); } break; } else { accumulator += c; } } break; } for (let c of str) { if (c === "\n") { if (accumulator.slice(0, 2) !== "//") { yield this.parseLine(accumulator); } accumulator = ""; } else { accumulator += c; } } } } } class LoginSQL { private sql: SQLHandler; constructor (sql: SQLHandler) { this.sql = sql; } async write (acc: any) { if (acc === null) { return Promise.resolve(false); } await this.sql.do("INSERT INTO `login` ?? values?", [ ["account_id", "userid", "user_pass", "lastlogin", "logincount", "state", "email", "last_ip", "unban_time"], [+acc.account_id, acc.userid, acc.pass, acc.lastlogin, +acc.logincount, +acc.state, acc.email, acc.ip, acc.ban_until_time] ]); } } export { LoginParser, LoginSQL, }