diff options
Diffstat (limited to 'backend/api/src/util.zig')
| -rw-r--r-- | backend/api/src/util.zig | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/backend/api/src/util.zig b/backend/api/src/util.zig new file mode 100644 index 0000000..19fa456 --- /dev/null +++ b/backend/api/src/util.zig @@ -0,0 +1,271 @@ +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; + } +}; |
