package ins.frontend; import ins.frontend.utils.RegistrationService; import ins.frontend.utils.WebApp; import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket; import org.openautonomousconnection.protocol.side.web.ProtocolWebServer; import org.openautonomousconnection.protocol.side.web.managers.SessionManager; import org.openautonomousconnection.protocol.versions.v1_0_0.beta.WebRequestMethod; import org.openautonomousconnection.webserver.api.Route; import org.openautonomousconnection.webserver.api.SessionContext; import org.openautonomousconnection.webserver.api.WebPage; import org.openautonomousconnection.webserver.api.WebPageContext; import org.openautonomousconnection.webserver.utils.HeaderMaps; import org.openautonomousconnection.webserver.utils.Html; import java.nio.charset.StandardCharsets; import java.util.*; /** * Register page with existing-session short-circuit. */ @Route(path = "/register.html") public final class register implements WebPage { private static WebResponsePacket ok(String html) { return new WebResponsePacket(200, "text/html; charset=utf-8", HeaderMaps.mutable(), Html.utf8(html)); } private static WebResponsePacket text(int code, String msg) { return new WebResponsePacket(code, "text/plain; charset=utf-8", HeaderMaps.mutable(), msg.getBytes(StandardCharsets.UTF_8)); } private static WebResponsePacket redirect302(String location, String session) { Map headers = HeaderMaps.mutable(); headers.put("location", location); if (session != null && !session.isBlank()) { headers.put("Location", "dashboard.html"); headers.put("Set-Cookie", "session=" + session + "; Path=/; HttpOnly; SameSite=Lax"); headers.put("session", session); headers.put("cookie", session); } return new WebResponsePacket(302, "text/plain; charset=utf-8", headers, new byte[0]); } private static String renderForm(String errOrOk) { String line = ""; if (errOrOk != null && !errOrOk.isBlank()) { boolean ok = errOrOk.startsWith("OK:"); line = "

" + errOrOk + "

"; } String body = """

Register

%s
""".formatted(line); return Html.page("Register", body); } private static String resolveIp(WebPageContext ctx) { if (ctx.client == null || ctx.client.getConnection() == null) return ""; if (ctx.client.getConnection().getTcpSocket() == null) return ""; if (ctx.client.getConnection().getTcpSocket().getInetAddress() == null) return ""; return ctx.client.getConnection().getTcpSocket().getInetAddress().getHostAddress(); } private static String headerIgnoreCase(Map headers, String key) { if (headers == null || headers.isEmpty() || key == null) return null; for (Map.Entry e : headers.entrySet()) { if (e.getKey() != null && e.getKey().equalsIgnoreCase(key)) return e.getValue(); } return null; } private static Map> parseFormUrlEncoded(byte[] body) { if (body == null || body.length == 0) return Map.of(); String raw = new String(body, StandardCharsets.UTF_8); Map> out = new LinkedHashMap<>(); int start = 0; while (start <= raw.length()) { int amp = raw.indexOf('&', start); if (amp < 0) amp = raw.length(); String pair = raw.substring(start, amp); if (!pair.isEmpty()) { int eq = pair.indexOf('='); String k = (eq < 0) ? pair : pair.substring(0, eq); String v = (eq < 0) ? "" : pair.substring(eq + 1); String key = decodeFormToken(k); String val = decodeFormToken(v); if (!key.isEmpty()) out.computeIfAbsent(key, __ -> new ArrayList<>(1)).add(val); } start = amp + 1; if (amp == raw.length()) break; } Map> frozen = new LinkedHashMap<>(); for (Map.Entry> e : out.entrySet()) frozen.put(e.getKey(), List.copyOf(e.getValue())); return Map.copyOf(frozen); } private static String first(Map> params, String key) { if (params == null || key == null) return null; List v = params.get(key); if (v == null || v.isEmpty()) return null; String t = v.getFirst(); if (t == null) return null; String s = t.trim(); return s.isEmpty() ? null : s; } private static String decodeFormToken(String s) { if (s == null || s.isEmpty()) return ""; byte[] tmp = new byte[s.length()]; int n = 0; for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c == '+') { tmp[n++] = (byte) ' '; continue; } if (c == '%' && i + 2 < s.length()) { int hi = hex(s.charAt(i + 1)); int lo = hex(s.charAt(i + 2)); if (hi >= 0 && lo >= 0) { tmp[n++] = (byte) ((hi << 4) | lo); i += 2; continue; } } byte[] b = String.valueOf(c).getBytes(StandardCharsets.UTF_8); for (byte bb : b) tmp[n++] = bb; } return new String(tmp, 0, n, StandardCharsets.UTF_8); } private static int hex(char c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return 10 + (c - 'a'); if (c >= 'A' && c <= 'F') return 10 + (c - 'A'); return -1; } @Override public WebResponsePacket handle(WebPageContext ctx) throws Exception { WebApp.init(); // 1) If a valid session already exists -> go dashboard (keep session) SessionContext existing = SessionContext.from( ctx.client, (ProtocolWebServer) ctx.client.getServer(), ctx.request.getHeaders() ); if (existing.isValid() && existing.getUser() != null) { return redirect302("dashboard.html", existing.getSessionId()); } WebRequestMethod method = ctx.request.getMethod(); if (method == null) method = WebRequestMethod.GET; if (method == WebRequestMethod.GET) { return ok(renderForm(null)); } if (method != WebRequestMethod.POST) { return text(405, "Method Not Allowed"); } String contentType = headerIgnoreCase(ctx.request.getHeaders(), "content-type"); String ctLower = (contentType == null) ? "" : contentType.toLowerCase(Locale.ROOT); if (!ctLower.startsWith("application/x-www-form-urlencoded")) { return ok(renderForm("Unsupported content-type: " + Html.esc(contentType))); } Map> form = parseFormUrlEncoded(ctx.request.getBody()); String username = first(form, "username"); String password = first(form, "password"); RegistrationService service = new RegistrationService(WebApp.get().users(), WebApp.get().passwordHasher()); RegistrationService.Result r = service.register(username, password); if (!r.ok()) { return ok(renderForm(r.error())); } // 2) Create new session (user just registered) String ip = resolveIp(ctx); String ua = headerIgnoreCase(ctx.request.getHeaders(), "user-agent"); if (ua == null) ua = ""; String session = SessionManager.create( String.valueOf(r.userId()), ip, ua, (ProtocolWebServer) ctx.client.getServer() ); return redirect302("dashboard.html", session); } }