From 09dd207bb125dc2f335585bd7f4d62c227fd777f Mon Sep 17 00:00:00 2001 From: UnlegitDqrk Date: Sun, 22 Feb 2026 17:21:23 +0100 Subject: [PATCH] Updated frontend to new Protocol --- frontend/dashboard.java | 89 ++++++++++------- frontend/index.java | 57 +++++++---- frontend/info.java | 50 +++++++--- frontend/login.java | 207 +++++++++++++++++++++++----------------- frontend/register.java | 173 +++++++++++++++++++-------------- 5 files changed, 355 insertions(+), 221 deletions(-) diff --git a/frontend/dashboard.java b/frontend/dashboard.java index f4d8e4c..e50b24d 100644 --- a/frontend/dashboard.java +++ b/frontend/dashboard.java @@ -2,22 +2,24 @@ package ins.frontend; import ins.frontend.utils.RegistrarDao; 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.packets.v1_0_1.beta.web.impl.resource.WebResourceResponsePacket; +import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketFlags; +import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader; 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 org.openautonomousconnection.webserver.utils.MergedRequestParams; -import org.openautonomousconnection.webserver.utils.QuerySupport; +import org.openautonomousconnection.webserver.utils.WebUrlUtil; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Locale; import java.util.Map; /** - * INS registrar ins.frontend (TLN / InfoName / Records) with proper POST parameter parsing. + * INS registrar dashboard (TLN / InfoName / Records) for protocol v1.0.1 resource packets. * *

Supported actions (POST recommended for mutations):

* - * - *

Important: Listing/editing/deleting records requires DAO methods that are not part of the provided snippet. - * This page currently supports adding records only.

*/ -@Route(path = "dashboard.html") +@Route(path = "/dashboard.html") public final class dashboard implements WebPage { private static Integer normalizeNullableInt(String s) { @@ -47,30 +46,26 @@ public final class dashboard implements WebPage { } @Override - public WebResponsePacket handle(WebPageContext ctx) throws Exception { + public WebResourceResponsePacket handle(WebPageContext ctx) throws Exception { WebApp.init(); - SessionContext session = SessionContext.from( - ctx.client, - (ProtocolWebServer) ctx.client.getServer(), - ctx.request.getHeaders() - ); - - if (!session.isValid() || session.getUser() == null) { - return plain(401, "Authentication required (session)."); + if (ctx.session == null || !ctx.session.isValid() || ctx.session.getUser() == null) { + return plain(ctx, 401, "Authentication required (session)."); } int userId; try { - userId = Integer.parseInt(session.getUser()); + userId = Integer.parseInt(ctx.session.getUser()); } catch (Exception e) { - return plain(401, "Invalid session user."); + return plain(ctx, 401, "Invalid session user."); } RegistrarDao dao = WebApp.get().dao(); - // Raw target and merged params (GET + POST). - String rawTarget = org.openautonomousconnection.webserver.utils.QuerySupport.extractRawTarget(ctx.request); + // Build target for param merging: "/path?query" + String rawTarget = WebUrlUtil.extractPathAndQuery(ctx.request.getUrl()); + if (rawTarget == null) rawTarget = "/dashboard.html"; + Map headers = ctx.request.getHeaders(); byte[] body = ctx.request.getBody(); @@ -90,7 +85,7 @@ public final class dashboard implements WebPage { } } - return render(userId, msg, err, dao); + return render(ctx, userId, msg, err, dao); } private ActionResult executeAction(String action, MergedRequestParams p, int userId, RegistrarDao dao) throws Exception { @@ -171,7 +166,6 @@ public final class dashboard implements WebPage { if (!dao.isOwnerOfInfoName(infonameId, userId)) return ActionResult.err("Not owner of this infoname."); if (type.isBlank() || value == null || value.isBlank()) return ActionResult.err("Missing type/value."); - // Validate allow_subdomains against TLN of this infoname (owned list contains TLN metadata). RegistrarDao.InfoNameRow[] owned = dao.listOwnedInfoNames(userId); RegistrarDao.InfoNameRow row = null; for (RegistrarDao.InfoNameRow r : owned) { @@ -194,7 +188,7 @@ public final class dashboard implements WebPage { return ActionResult.err("Unknown action: " + action); } - private WebResponsePacket render(int userId, String msg, String err, RegistrarDao dao) throws Exception { + private WebResourceResponsePacket render(WebPageContext ctx, int userId, String msg, String err, RegistrarDao dao) throws Exception { RegistrarDao.TlnRow[] tlns = dao.listVisibleTlns(userId); RegistrarDao.InfoNameRow[] owned = dao.listOwnedInfoNames(userId); @@ -206,7 +200,7 @@ public final class dashboard implements WebPage {

INS Registrar

%s %s - +

Create TLN

@@ -216,10 +210,10 @@ public final class dashboard implements WebPage {
- +

TLNs (public + owned)

%s - +

Create InfoName

Allowed if TLN is public or owned by you.

@@ -228,7 +222,7 @@ public final class dashboard implements WebPage {
- +

Add Record

Subname requires allow_subdomains=1 unless you own the TLN. Root (no sub) always allowed.

@@ -243,10 +237,10 @@ public final class dashboard implements WebPage {
- +

Your InfoNames

%s - + @@ -259,7 +253,7 @@ public final class dashboard implements WebPage { ); String html = Html.page("INS Registrar", body); - return new WebResponsePacket(200, "text/html", new HashMap<>(), Html.utf8(html)); + return html(ctx, 200, html); } private String renderTlnSection(RegistrarDao.TlnRow[] tlns, int userId) { @@ -300,7 +294,7 @@ public final class dashboard implements WebPage { - +
@@ -348,8 +342,33 @@ public final class dashboard implements WebPage { return sb.toString(); } - private WebResponsePacket plain(int code, String text) { - return new WebResponsePacket(code, "text/plain", new HashMap<>(), Html.utf8(text)); + private WebResourceResponsePacket plain(WebPageContext ctx, int code, String text) { + byte[] body = (text == null ? "" : text).getBytes(StandardCharsets.UTF_8); + Map headers = HeaderMaps.mutable(); + headers.put("content-length", String.valueOf(body.length)); + return new WebResourceResponsePacket(outHeader(ctx), code, "text/plain; charset=utf-8", headers, body, null); + } + + private WebResourceResponsePacket html(WebPageContext ctx, int code, String html) { + byte[] body = Html.utf8(html); + Map headers = HeaderMaps.mutable(); + headers.put("content-length", String.valueOf(body.length)); + return new WebResourceResponsePacket(outHeader(ctx), code, "text/html; charset=utf-8", headers, body, null); + } + + private WebPacketHeader outHeader(WebPageContext ctx) { + WebPacketHeader in = (ctx != null && ctx.request != null) ? ctx.request.getHeader() : null; + if (in == null) { + return new WebPacketHeader(0, 0, 0, 0, WebPacketFlags.RESOURCE, System.currentTimeMillis()); + } + return new WebPacketHeader( + in.getRequestId(), + in.getTabId(), + in.getPageId(), + in.getFrameId(), + in.getFlags() | WebPacketFlags.RESOURCE, + System.currentTimeMillis() + ); } private String safeMsg(Exception e) { diff --git a/frontend/index.java b/frontend/index.java index 1b5b7b1..1d23369 100644 --- a/frontend/index.java +++ b/frontend/index.java @@ -1,36 +1,59 @@ package ins.frontend; import ins.frontend.utils.WebApp; -import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket; +import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.resource.WebResourceResponsePacket; +import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketFlags; +import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader; import org.openautonomousconnection.webserver.api.Route; 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.util.HashMap; +import java.util.Map; /** - * Landing page for the registrar ins.frontend. + * Landing page for INS registrar frontend (v1.0.1). */ -@Route(path = "index.html") +@Route(path = "/index.html") public final class index implements WebPage { @Override - public WebResponsePacket handle(WebPageContext ctx) { + public WebResourceResponsePacket handle(WebPageContext ctx) { WebApp.init(); + String html = Html.page("OAC INS Registrar", """ -
-

OAC INS Registrar

-

What you want to do?

-
- -
+
+

OAC INS Registrar

+

What you want to do?

+
+ +
"""); - return new WebResponsePacket(200, "text/html", new HashMap<>(), Html.utf8(html)); + byte[] body = Html.utf8(html); + Map headers = HeaderMaps.mutable(); + headers.put("content-length", String.valueOf(body.length)); + + return new WebResourceResponsePacket(outHeader(ctx), 200, "text/html; charset=utf-8", headers, body, null); } -} + + private WebPacketHeader outHeader(WebPageContext ctx) { + WebPacketHeader in = (ctx != null && ctx.request != null) ? ctx.request.getHeader() : null; + if (in == null) { + return new WebPacketHeader(0, 0, 0, 0, WebPacketFlags.RESOURCE, System.currentTimeMillis()); + } + return new WebPacketHeader( + in.getRequestId(), + in.getTabId(), + in.getPageId(), + in.getFrameId(), + in.getFlags() | WebPacketFlags.RESOURCE, + System.currentTimeMillis() + ); + } +} \ No newline at end of file diff --git a/frontend/info.java b/frontend/info.java index 7297c95..4495d36 100644 --- a/frontend/info.java +++ b/frontend/info.java @@ -1,49 +1,73 @@ package ins.frontend; import ins.frontend.utils.WebApp; -import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket; +import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.resource.WebResourceResponsePacket; +import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketFlags; +import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader; import org.openautonomousconnection.webserver.api.Route; 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.util.HashMap; +import java.util.Map; /** - * Landing page for the registrar ins.frontend. + * Info page for INS registrar frontend (v1.0.1). */ -@Route(path = "info.html") +@Route(path = "/info.html") public final class info implements WebPage { @Override - public WebResponsePacket handle(WebPageContext ctx) { + public WebResourceResponsePacket handle(WebPageContext ctx) { WebApp.init(); + String html = Html.page("INS Info", """ -
+

OAC Default INS Server

- +

The Default INS Server is the official, project-operated name service of the Open Autonomous Connection (OAC) network.

- +

It provides a trusted reference point for resolving InfoNames and enables initial client connections to the OAC ecosystem.

- +

This server is maintained by the OAC project and is intended as the recommended entry point for public services and new clients.

- +

Note: Alternative or private INS servers may exist, but the default INS server represents the official and stable reference instance.

-
"""); +
+ """); - return new WebResponsePacket(200, "text/html", new HashMap<>(), Html.utf8(html)); + byte[] body = Html.utf8(html); + Map headers = HeaderMaps.mutable(); + headers.put("content-length", String.valueOf(body.length)); + + return new WebResourceResponsePacket(outHeader(ctx), 200, "text/html; charset=utf-8", headers, body, null); } -} + + private WebPacketHeader outHeader(WebPageContext ctx) { + WebPacketHeader in = (ctx != null && ctx.request != null) ? ctx.request.getHeader() : null; + if (in == null) { + return new WebPacketHeader(0, 0, 0, 0, WebPacketFlags.RESOURCE, System.currentTimeMillis()); + } + return new WebPacketHeader( + in.getRequestId(), + in.getTabId(), + in.getPageId(), + in.getFrameId(), + in.getFlags() | WebPacketFlags.RESOURCE, + System.currentTimeMillis() + ); + } +} \ No newline at end of file diff --git a/frontend/login.java b/frontend/login.java index 631864e..5145186 100644 --- a/frontend/login.java +++ b/frontend/login.java @@ -2,46 +2,132 @@ package ins.frontend; import ins.frontend.utils.UserDao; import ins.frontend.utils.WebApp; -import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket; +import org.openautonomousconnection.protocol.packets.v1_0_1.beta.web.impl.resource.WebResourceResponsePacket; 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.protocol.versions.v1_0_1.beta.WebPacketFlags; +import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader; 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.lang.reflect.Method; import java.nio.charset.StandardCharsets; import java.util.*; /** - * Login page with existing-session short-circuit. - * - *

Username stored in DB as plain text.

+ * Login page (v1.0.1). */ @Route(path = "/login.html") public final class login implements WebPage { - private static WebResponsePacket ok(String html) { - return new WebResponsePacket(200, "text/html; charset=utf-8", HeaderMaps.mutable(), Html.utf8(html)); - } + private enum ReqMethod { GET, POST, OTHER } - private static WebResponsePacket text(int code, String msg) { - return new WebResponsePacket(code, "text/plain; charset=utf-8", HeaderMaps.mutable(), msg.getBytes(StandardCharsets.UTF_8)); - } + @Override + public WebResourceResponsePacket handle(WebPageContext ctx) throws Exception { + WebApp.init(); - 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); + // If a valid session already exists -> go dashboard (keep session) + if (ctx.session != null && ctx.session.isValid() && ctx.session.getUser() != null) { + return redirect302(ctx, "dashboard.html", ctx.session.getSessionId()); } - return new WebResponsePacket(302, "text/plain; charset=utf-8", headers, new byte[0]); + + ReqMethod method = detectMethod(ctx); + if (method == ReqMethod.GET) { + return ok(ctx, renderForm(null)); + } + + if (method != ReqMethod.POST) { + return text(ctx, 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(ctx, renderForm("Unsupported content-type: " + Html.esc(contentType))); + } + + Map> form = parseFormUrlEncoded(ctx.request.getBody()); + String username = first(form, "username"); + String password = first(form, "password"); + + if (username == null || password == null) { + return ok(ctx, renderForm("Missing username/password.")); + } + + String lookupUsername = username.trim(); + + UserDao.UserRow user = WebApp.get().users().findByUsername(lookupUsername).orElse(null); + if (user == null) { + return ok(ctx, renderForm("Invalid credentials.")); + } + + boolean okPw = WebApp.get().passwordHasher().verify(password, user.passwordEncoded()); + if (!okPw) { + return ok(ctx, renderForm("Invalid credentials.")); + } + + String ip = resolveIp(ctx); + String ua = headerIgnoreCase(ctx.request.getHeaders(), "user-agent"); + if (ua == null) ua = ""; + + String session = SessionManager.create( + String.valueOf(user.id()), + ip, + ua, + (ProtocolWebServer) ctx.client.getServer() + ); + + return redirect302(ctx, "dashboard.html", session); + } + + private ReqMethod detectMethod(WebPageContext ctx) { + // Preferred: request.getMethod() via reflection (avoids depending on specific enum package). + try { + Method m = ctx.request.getClass().getMethod("getMethod"); + Object v = m.invoke(ctx.request); + if (v != null) { + String s = String.valueOf(v).trim().toUpperCase(Locale.ROOT); + if ("GET".equals(s)) return ReqMethod.GET; + if ("POST".equals(s)) return ReqMethod.POST; + return ReqMethod.OTHER; + } + } catch (Exception ignored) { + // Fall back below. + } + + // Fallback: treat presence of body as POST-like. + byte[] body = ctx.request.getBody(); + if (body != null && body.length > 0) return ReqMethod.POST; + return ReqMethod.GET; + } + + private static WebResourceResponsePacket ok(WebPageContext ctx, String html) { + byte[] body = Html.utf8(html); + Map headers = HeaderMaps.mutable(); + headers.put("content-length", String.valueOf(body.length)); + return new WebResourceResponsePacket(outHeader(ctx), 200, "text/html; charset=utf-8", headers, body, null); + } + + private static WebResourceResponsePacket text(WebPageContext ctx, int code, String msg) { + byte[] body = (msg == null ? "" : msg).getBytes(StandardCharsets.UTF_8); + Map headers = HeaderMaps.mutable(); + headers.put("content-length", String.valueOf(body.length)); + return new WebResourceResponsePacket(outHeader(ctx), code, "text/plain; charset=utf-8", headers, body, null); + } + + private static WebResourceResponsePacket redirect302(WebPageContext ctx, String location, String session) { + Map headers = HeaderMaps.mutable(); + headers.put("Location", location); + if (session != null && !session.isBlank()) { + headers.put("Set-Cookie", "session=" + session + "; Path=/; HttpOnly; SameSite=Lax"); + // optional fallback for stacks that read a direct header: + headers.put("session", session); + } + return new WebResourceResponsePacket(outHeader(ctx), 302, "text/plain; charset=utf-8", headers, new byte[0], null); } private static String renderForm(String errOrOk) { @@ -140,7 +226,7 @@ public final class login implements WebPage { continue; } if (c == '%' && i + 2 < s.length()) { - int hi = hex(s.charAt(i + 1)); + int hi = hex(c = s.charAt(i + 1)); int lo = hex(s.charAt(i + 2)); if (hi >= 0 && lo >= 0) { tmp[n++] = (byte) ((hi << 4) | lo); @@ -149,7 +235,7 @@ public final class login implements WebPage { } } - byte[] b = String.valueOf(c).getBytes(StandardCharsets.UTF_8); + byte[] b = String.valueOf(s.charAt(i)).getBytes(StandardCharsets.UTF_8); for (byte bb : b) tmp[n++] = bb; } @@ -163,69 +249,18 @@ public final class login implements WebPage { 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() + private static WebPacketHeader outHeader(WebPageContext ctx) { + WebPacketHeader in = (ctx != null && ctx.request != null) ? ctx.request.getHeader() : null; + if (in == null) { + return new WebPacketHeader(0, 0, 0, 0, WebPacketFlags.RESOURCE, System.currentTimeMillis()); + } + return new WebPacketHeader( + in.getRequestId(), + in.getTabId(), + in.getPageId(), + in.getFrameId(), + in.getFlags() | WebPacketFlags.RESOURCE, + System.currentTimeMillis() ); - 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"); - - if (username == null || password == null) { - return ok(renderForm("Missing username/password.")); - } - - String lookupUsername = username.trim(); - - UserDao.UserRow user = WebApp.get().users().findByUsername(lookupUsername).orElse(null); - if (user == null) { - return ok(renderForm("Invalid credentials.")); - } - - boolean okPw = WebApp.get().passwordHasher().verify(password, user.passwordEncoded()); - if (!okPw) { - return ok(renderForm("Invalid credentials.")); - } - - String ip = resolveIp(ctx); - String ua = headerIgnoreCase(ctx.request.getHeaders(), "user-agent"); - if (ua == null) ua = ""; - - String session = SessionManager.create( - String.valueOf(user.id()), - ip, - ua, - (ProtocolWebServer) ctx.client.getServer() - ); - - return redirect302("dashboard.html", session); } } \ No newline at end of file diff --git a/frontend/register.java b/frontend/register.java index 7a2fe74..c47c9a4 100644 --- a/frontend/register.java +++ b/frontend/register.java @@ -2,44 +2,120 @@ 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.packets.v1_0_1.beta.web.impl.resource.WebResourceResponsePacket; 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.protocol.versions.v1_0_1.beta.WebPacketFlags; +import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader; 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.lang.reflect.Method; import java.nio.charset.StandardCharsets; import java.util.*; /** - * Register page with existing-session short-circuit. + * Register page (v1.0.1). */ @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 enum ReqMethod { GET, POST, OTHER } + + @Override + public WebResourceResponsePacket handle(WebPageContext ctx) throws Exception { + WebApp.init(); + + // If a valid session already exists -> go dashboard (keep session) + if (ctx.session != null && ctx.session.isValid() && ctx.session.getUser() != null) { + return redirect302(ctx, "dashboard.html", ctx.session.getSessionId()); + } + + ReqMethod method = detectMethod(ctx); + if (method == ReqMethod.GET) { + return ok(ctx, renderForm(null)); + } + + if (method != ReqMethod.POST) { + return text(ctx, 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(ctx, 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(ctx, renderForm(r.error())); + } + + // 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(ctx, "dashboard.html", session); } - 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 ReqMethod detectMethod(WebPageContext ctx) { + try { + Method m = ctx.request.getClass().getMethod("getMethod"); + Object v = m.invoke(ctx.request); + if (v != null) { + String s = String.valueOf(v).trim().toUpperCase(Locale.ROOT); + if ("GET".equals(s)) return ReqMethod.GET; + if ("POST".equals(s)) return ReqMethod.POST; + return ReqMethod.OTHER; + } + } catch (Exception ignored) { + } + + byte[] body = ctx.request.getBody(); + if (body != null && body.length > 0) return ReqMethod.POST; + return ReqMethod.GET; } - private static WebResponsePacket redirect302(String location, String session) { + private static WebResourceResponsePacket ok(WebPageContext ctx, String html) { + byte[] body = Html.utf8(html); Map headers = HeaderMaps.mutable(); - headers.put("location", location); + headers.put("content-length", String.valueOf(body.length)); + return new WebResourceResponsePacket(outHeader(ctx), 200, "text/html; charset=utf-8", headers, body, null); + } + + private static WebResourceResponsePacket text(WebPageContext ctx, int code, String msg) { + byte[] body = (msg == null ? "" : msg).getBytes(StandardCharsets.UTF_8); + Map headers = HeaderMaps.mutable(); + headers.put("content-length", String.valueOf(body.length)); + return new WebResourceResponsePacket(outHeader(ctx), code, "text/plain; charset=utf-8", headers, body, null); + } + + private static WebResourceResponsePacket redirect302(WebPageContext ctx, 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]); + return new WebResourceResponsePacket(outHeader(ctx), 302, "text/plain; charset=utf-8", headers, new byte[0], null); } private static String renderForm(String errOrOk) { @@ -138,7 +214,7 @@ public final class register implements WebPage { continue; } if (c == '%' && i + 2 < s.length()) { - int hi = hex(s.charAt(i + 1)); + int hi = hex(c = s.charAt(i + 1)); int lo = hex(s.charAt(i + 2)); if (hi >= 0 && lo >= 0) { tmp[n++] = (byte) ((hi << 4) | lo); @@ -147,7 +223,7 @@ public final class register implements WebPage { } } - byte[] b = String.valueOf(c).getBytes(StandardCharsets.UTF_8); + byte[] b = String.valueOf(s.charAt(i)).getBytes(StandardCharsets.UTF_8); for (byte bb : b) tmp[n++] = bb; } @@ -161,61 +237,18 @@ public final class register implements WebPage { 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() + private static WebPacketHeader outHeader(WebPageContext ctx) { + WebPacketHeader in = (ctx != null && ctx.request != null) ? ctx.request.getHeader() : null; + if (in == null) { + return new WebPacketHeader(0, 0, 0, 0, WebPacketFlags.RESOURCE, System.currentTimeMillis()); + } + return new WebPacketHeader( + in.getRequestId(), + in.getTabId(), + in.getPageId(), + in.getFrameId(), + in.getFlags() | WebPacketFlags.RESOURCE, + System.currentTimeMillis() ); - 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); } } \ No newline at end of file