X-Git-Url: https://gitweb.ps.run/chirp/blobdiff_plain/13381fcc62fea793dc9c4af62e4b59927b9ae94f..d8f54dd8ac187349c1194871a55d4675f28e5a43:/src/main.zig diff --git a/src/main.zig b/src/main.zig index 0800e2b..164606f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -29,8 +29,11 @@ const User = struct { id: UserId, name: Username, display_name: DisplayName, + description: UserDescription, password_hash: PasswordHash, + posts: PostList, + following: UserList, followers: UserList, @@ -82,6 +85,7 @@ const PostId = enum(u64) { _ }; const Timestamp = i64; const Username = std.BoundedArray(u8, 32); const DisplayName = std.BoundedArray(u8, 64); +const UserDescription = std.BoundedArray(u8, 1024); const PasswordHash = std.BoundedArray(u8, 128); const SessionToken = u64; const CookieValue = std.BoundedArray(u8, 128); @@ -107,28 +111,19 @@ fn reencode(comptime T: type, text: []const u8) !T { const c = text[idx]; if (c == '+') { try result.append(' '); - } else if (c == '%') { - // special case of &#... - // assume only &#, no &#x - if (idx + 6 < text.len and std.mem.eql(u8, text[idx .. idx + 6], "%26%23")) { - const num_start = idx + 6; - var num_end = num_start; - while (num_end < text.len and std.ascii.isDigit(text[num_end])) { - num_end += 1; - } + } else if (c == '%' and idx + 2 < text.len) { + const allow = &[_]u8{ 0x26, 0x23, 0x3b, 0x0a }; - if (num_end + 2 < text.len and - text[num_end] == '%' and - text[num_end + 1] == '3' and - std.ascii.toLower(text[num_end + 2]) == 'b') - { - try std.fmt.format(result.writer(), "&#{s};", .{text[num_start..num_end]}); - idx = num_end + 2; - continue; - } + const escaped_value = std.fmt.parseUnsigned(u8, text[idx + 1 .. idx + 3], 16) catch continue; + + if (escaped_value == 0x0d) { + try std.fmt.format(result.writer(), "
", .{}); + } else if (std.mem.indexOfScalar(u8, allow, escaped_value) != null) { + try std.fmt.format(result.writer(), "{c}", .{escaped_value}); + } else { + try std.fmt.format(result.writer(), "&#x{x};", .{escaped_value}); } - try std.fmt.format(result.writer(), "&#x{s};", .{text[idx + 1 .. idx + 3]}); idx += 2; } else { try result.append(c); @@ -223,6 +218,7 @@ const Chirp = struct { .id = user_id, .name = username_array, .display_name = display_name, + .description = try UserDescription.init(0), .password_hash = try hash_password(password), .posts = try PostList.init(txn), .following = try UserList.init(txn), @@ -513,10 +509,16 @@ fn html_form(res: *http.Response, action: []const u8, inputs: anytype) !void { inline for (inputs) |input| { switch (@typeInfo(@TypeOf(input))) { - .Struct => { - try res.write("", .{}); + .Struct => |s| { + if (s.fields.len == 3) { + try res.write("<{s} ", .{input[0]}); + try res.write(input[1], input[2]); + try res.write(">", .{input[0]}); + } else { + try res.write("", .{}); + } }, else => { try res.write("{s}{s}", .{ id, name.constSlice(), if (list_view.has(post_id) catch false) " *" else "" }); } try res.write("", .{}); - try res.write("", .{@intFromEnum(post_id)}); - try res.write("", .{}); + try res.write("", .{@intFromEnum(post_id)}); + try res.write("", .{}); try res.write("", .{}); } @@ -784,8 +786,8 @@ fn write_profile(res: *http.Response, txn: lmdb.Txn, logged_in: ?Login, user: Us try res.write("", .{ id, name.constSlice(), if (list_view.has(user.id) catch false) " *" else "" }); } try res.write("", .{}); - try res.write("", .{@intFromEnum(user.id)}); - try res.write("", .{}); + try res.write("", .{@intFromEnum(user.id)}); + try res.write("", .{}); try res.write("", .{}); } try res.write( @@ -814,6 +816,15 @@ fn write_profile(res: *http.Response, txn: lmdb.Txn, logged_in: ?Login, user: Us , .{}); } + if (user.description.len > 0) { + try res.write( + \\
+ // \\« {s} » + \\{s} + \\
+ , .{user.description.constSlice()}); + } + try res.write("
", .{}); } fn write_posts(res: *http.Response, txn: lmdb.Txn, logged_in: ?Login, post_list: PostList, options: struct { @@ -1258,6 +1269,11 @@ const GET = struct { .{ "type=\"text\" name=\"display_name\" placeholder=\"{s}\"", .{login.user.display_name.constSlice()} }, "type=\"submit\" value=\"Change\"", }); + try self.res.write("
Description: ", .{}); + try html_form(self.res, "/set_description", .{ + .{ "textarea", "type=\"text\" name=\"description\" placeholder=\"{s}\"", .{login.user.description.constSlice()} }, + "type=\"submit\" value=\"Change\"", + }); try self.res.write("
Password: ", .{}); try html_form(self.res, "/set_password", .{ "type=\"text\" name=\"password\"", @@ -1381,6 +1397,18 @@ const POST = struct { user.display_name = display_name; try users.put(login.user.id, user); } + pub fn @"/set_description"(self: Self, args: struct { description: []const u8 }) !void { + const login = self.logged_in orelse return error.NotLoggedIn; + const description = try reencode(UserDescription, args.description); + + const txn = try self.env.txn(); + defer txn.commit() catch {}; + + const users = try Db.users(txn); + var user = login.user; + user.description = description; + try users.put(login.user.id, user); + } pub fn @"/set_password"(self: Self, args: struct { password: []const u8 }) !void { const login = self.logged_in orelse return error.NotLoggedIn; @@ -1653,9 +1681,10 @@ pub fn main() !void { server.wait(); while (true) { const req = (server.next_request(&req_buffer) catch break) orelse break; - handle_request(env, req) catch { - try handle_error(env, req); - }; + // handle_request(env, req) catch { + // try handle_error(env, req); + // }; + try handle_request(env, req); } } // const ThreadCount = 1;