const z = @import( "std" ); const zap = @import( "zap" ); const net = @import( "net-util.zig" ); const json = @import( "json" ); const getty = @import( "getty" ); const ArenaAllocator = z.heap.ArenaAllocator; const GettyResult = getty.de.Result; pub var gpa = z.heap.GeneralPurposeAllocator( .{ .thread_safe = true, .stack_trace_frames = 64 } ){}; pub const alloc = gpa.allocator(); const Status = zap.StatusCode; const Request = zap.Request; const pbkdf2 = z.crypto.pwhash.pbkdf2; const aes256Encrypt = z.crypto.aead.aes_gcm.Aes256Gcm.encrypt; const aes256Decrypt = z.crypto.aead.aes_gcm.Aes256Gcm.decrypt; pub const @"json.ignore.unknown" = struct { pub const attributes = .{ .Container = .{ .ignore_unknown_fields = true }, }; }; pub fn MergeTypes( comptime t1: type, comptime t2: type ) type { const t1info = @typeInfo( t1 ).Struct; const t2info = @typeInfo( t2 ).Struct; comptime if( t1info.is_tuple ) return t2; comptime if( t2info.is_tuple ) return t1; var t3info = t1info; t3info.fields = t1info.fields ++ t2info.fields; var t1s = @typeInfo( t1 ); t1s.Struct = t3info; return @Type( t1s ); } pub fn JsonResponse( comptime t: type ) type { return struct { v: t, @"#alloc": ArenaAllocator, pub fn deinit( self: @This() ) void { self.@"#alloc".deinit(); } }; } ///freed by caller pub fn jsonParse( comptime t: type, str: []const u8 ) !JsonResponse(t) { return jsonParseAlloc( t, str, alloc ); } ///freed by caller pub fn jsonParseAlloc( comptime t: type, str: []const u8, a: z.mem.Allocator ) !JsonResponse(t) { var arena = ArenaAllocator.init( a ); const allocator = arena.allocator(); const ret = json.fromSlice( allocator, t, str ) catch |e| { arena.deinit(); return e; }; var obj: JsonResponse(t) = undefined; obj.v = ret.value; obj.@"#alloc" = arena; return obj; } ///has to be freed using page_allocator pub fn jsonStringify( obj: anytype ) ![]const u8 { var arr = z.ArrayList( u8 ).init( alloc ); try json.toWriter( alloc, obj, arr.writer() ); return arr.toOwnedSlice(); } pub fn jsonStringifyAlloc( obj: anytype, a: z.mem.Allocator ) ![]const u8 { var arr = z.ArrayList( u8 ).init( a ); try json.toWriter( alloc, obj, arr.writer() ); return arr.toOwnedSlice(); } ///has to be freed by caller with page_allocator ///10mb max pub fn readFile( path: []const u8 ) ![]const u8 { return readFileAlloc( path, alloc ); } pub fn readFileAlloc( path: []const u8, a: z.mem.Allocator ) ![]const u8 { const file = try z.fs.cwd().openFile( path, .{} ); defer file.close(); var reader = z.io.bufferedReader( file.reader() ); var stream = reader.reader(); const contents = try stream.readAllAlloc( a, 1024 * 10000 ); return contents[0..contents.len]; } pub fn fileExists( path: []const u8 ) bool { const file = z.fs.cwd().openFile( path, .{} ) catch return false; _ = file.stat() catch return false; file.close(); return true; } pub fn writeFile( path: []const u8, contents: []const u8 ) !void { z.fs.cwd().deleteFile( path ) catch |e| { switch (e) { error.FileNotFound => {}, else => return e } }; const file = try z.fs.cwd().createFile( path, .{} ); defer file.close(); var writer = file.writer(); writer.writeAll( contents ) catch return error.OutOfMemory; } pub fn deleteFile( path: []const u8 ) !void { z.fs.cwd().deleteFile( path ) catch |e| { switch (e) { error.FileNotFound => {}, else => return e } }; } pub fn hexToBytesAlloc( hex: []const u8, a: z.mem.Allocator ) ![]const u8 { var bytes = try a.alloc( u8, hex.len / 2 ); var i: u32 = 0; while( i < hex.len ) : (i += 2) { z.debug.print( "byte: {s}\n", .{ hex[i..i+1] } ); const byte = try z.fmt.parseInt( u8, hex[i..i+1], 16 ); bytes[i/2] = byte; } return bytes; } pub fn hexToBytes( hex: []const u8 ) ![]const u8 { return hexToBytesAlloc( hex, alloc ); } ///EXTREMELY GAY but zig crypto lib has a bug that makes it not compile. ///IMPORTANT: FIX THIS!!!!! pub fn readFileCrypto( path: []const u8 ) ![]const u8 { const proc = try z.process.Child.run( .{ .allocator = alloc, .argv = &.{ "node", "../chat-reader.cjs", "read", path } } ); defer alloc.free( proc.stdout ); defer alloc.free( proc.stderr ); if( proc.stdout[0] == '0' ) return error.FileNotFound; const buf = alloc.dupe( u8, proc.stdout ); return buf; } pub fn writeFileCrypto( path: []const u8, buf: []const u8 ) ![]const u8 { const proc = try z.process.Child.run( .{ .allocator = alloc, .argv = &.{ "node", "../chat-reader.cjs", "write", path, buf } } ); defer alloc.free( proc.stdout ); defer alloc.free( proc.stderr ); const ret = alloc.dupe( u8, proc.stdout ); return ret; } pub const Validator = struct { pub fn isValidChar( c: u8 ) bool { if( c >= 'a' and c <= 'z' ) return true; if( c >= 'A' and c <= 'Z' ) return true; if( c >= '0' and c <= '9' ) return true; if( c == '-' or c == '_' ) return true; if( c == '.' ) return true; if( c == '@' ) return true; return false; } pub fn isValidCharSystem( c: u8 ) bool { const allowlist = ",;:/?=!#$^&*()+~[]{}<>|` "; for( allowlist ) |allowc| { if( allowc == c ) return true; } return isValidChar( c ); } pub fn isValidCharFont( c: u8 ) bool { if( c == ' ' ) return true; if( !isValidChar( c ) ) return false; return true; } pub fn isValidCharEmail( c: u8 ) bool { if( !isValidChar( c ) ) return false; return true; } pub fn isValidStrSystem( str: []const u8 ) bool { if( str.len > 2048 ) return false; for( str ) |c| { if( !isValidCharSystem( c ) ) return false; } return true; } pub fn isValidStrNickname( str: []const u8 ) bool { if( str.len > 24 ) return false; if( str.len < 2 ) return false; for( str ) |c| { if( !isValidCharEmail( c ) ) return false; } return true; } pub fn isValidStrEmail( str: []const u8 ) bool { if( str.len > 127 ) return false; if( str.len < 5 ) return false; var has_at = false; var has_dot = false; for( str ) |c| { if( !isValidCharEmail( c ) ) return false; if( c == '@' ) has_at = true; if( c == '.' ) has_dot = true; } return true; } pub fn isValidStrFont( str: []const u8 ) bool { if( str.len > 64 ) return false; if( str.len < 3 ) return false; for( str ) |c| { if( !isValidCharFont( c ) ) return false; } return true; } };