atproto pds in zig pds.zat.dev
pds atproto
Something went wrong. Try again.
Zig
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130const std = @import("std");const zat = @import("zat");
pub const HostedHandleError = error{ InvalidHandle, DisallowedTld, ReservedHandle, UnsupportedDomain, InvalidHostedHandle,};
pub fn normalize(allocator: std.mem.Allocator, raw: []const u8) ![]const u8 { const normalized = try allocator.alloc(u8, raw.len); errdefer allocator.free(normalized); _ = std.ascii.lowerString(normalized, raw); if (zat.Handle.parse(normalized) == null) return HostedHandleError.InvalidHandle; try validateTld(normalized); return normalized;}
pub fn isHosted(handle: []const u8, raw_domains: []const u8) bool { var domains = std.mem.splitScalar(u8, raw_domains, ','); while (domains.next()) |raw_domain| { const domain = std.mem.trim(u8, raw_domain, &std.ascii.whitespace); if (domain.len == 0 or domain[0] != '.') continue; if (handle.len > domain.len and std.mem.endsWith(u8, handle, domain)) return true; } return false;}
pub fn isHostedAuthority(handle: []const u8, raw_domains: []const u8) bool { if (isHosted(handle, raw_domains)) return true; var domains = std.mem.splitScalar(u8, raw_domains, ','); while (domains.next()) |raw_domain| { const domain = std.mem.trim(u8, raw_domain, &std.ascii.whitespace); if (domain.len > 1 and domain[0] == '.' and std.mem.eql(u8, handle, domain[1..])) return true; } return false;}
pub fn validateHosted(handle: []const u8, raw_domains: []const u8) HostedHandleError!void { var domains = std.mem.splitScalar(u8, raw_domains, ','); while (domains.next()) |raw_domain| { const domain = std.mem.trim(u8, raw_domain, &std.ascii.whitespace); if (domain.len == 0 or domain[0] != '.') continue; if (handle.len <= domain.len or !std.mem.endsWith(u8, handle, domain)) continue;
const name = handle[0 .. handle.len - domain.len]; if (name.len < 3 or name.len > 18 or std.mem.indexOfScalar(u8, name, '.') != null) { return HostedHandleError.InvalidHostedHandle; } if (isReserved(name)) return HostedHandleError.ReservedHandle; return; } return HostedHandleError.UnsupportedDomain;}
fn validateTld(handle: []const u8) HostedHandleError!void { const disallowed = [_][]const u8{ ".alt", ".arpa", ".example", ".internal", ".invalid", ".local", ".localhost", ".onion", }; for (disallowed) |suffix| { if (std.mem.endsWith(u8, handle, suffix)) return HostedHandleError.DisallowedTld; }}
fn isReserved(name: []const u8) bool { const reserved = [_][]const u8{ "account", "accounts", "admin", "api", "at", "atp", "atproto", "auth", "bsky", "bluesky", "did", "help", "handle", "lex", "lexicon", "mail", "mod", "moderation", "nsid", "oauth", "pds", "plc", "repo", "root", "status", "support", "system", "tid", "webmaster", "www", "xrpc", }; for (reserved) |value| { if (std.mem.eql(u8, name, value)) return true; } return false;}
pub fn validateDomains(raw_domains: []const u8) error{InvalidHandleDomain}!void { var count: usize = 0; var domains = std.mem.splitScalar(u8, raw_domains, ','); while (domains.next()) |raw_domain| { const domain = std.mem.trim(u8, raw_domain, &std.ascii.whitespace); if (domain.len < 2 or domain[0] != '.') return error.InvalidHandleDomain;
var example_buf: [zat.Handle.max_length]u8 = undefined; const example = std.fmt.bufPrint(&example_buf, "abc{s}", .{domain}) catch return error.InvalidHandleDomain; if (zat.Handle.parse(example) == null) return error.InvalidHandleDomain; count += 1; } if (count == 0) return error.InvalidHandleDomain;}
test "hosted handles require one bounded account label" { try validateHosted("alice.pds.example.com", ".pds.example.com"); try std.testing.expectError(error.InvalidHostedHandle, validateHosted("a.pds.example.com", ".pds.example.com")); try std.testing.expectError(error.InvalidHostedHandle, validateHosted("nested.alice.pds.example.com", ".pds.example.com")); try std.testing.expectError(error.ReservedHandle, validateHosted("admin.pds.example.com", ".pds.example.com")); try std.testing.expectError(error.UnsupportedDomain, validateHosted("alice.example.com", ".pds.example.com"));}
test "normalization lowercases without accepting surrounding whitespace" { const normalized = try normalize(std.testing.allocator, "Alice.Example.COM"); defer std.testing.allocator.free(normalized); try std.testing.expectEqualStrings("alice.example.com", normalized); try std.testing.expectError(error.InvalidHandle, normalize(std.testing.allocator, " alice.example.com ")); try std.testing.expectError(error.DisallowedTld, normalize(std.testing.allocator, "alice.localhost"));}
test "hosted authority includes the configured base domain" { try std.testing.expect(isHostedAuthority("alice.pds.example.com", ".pds.example.com")); try std.testing.expect(isHostedAuthority("pds.example.com", ".pds.example.com")); try std.testing.expect(!isHostedAuthority("alice.example.com", ".pds.example.com"));}
test "handle domains are leading-dot suffixes" { try validateDomains(".pds.example.com,.test.example"); try std.testing.expectError(error.InvalidHandleDomain, validateDomains("pds.example.com")); try std.testing.expectError(error.InvalidHandleDomain, validateDomains(".example.com,example.com")); try std.testing.expectError(error.InvalidHandleDomain, validateDomains(""));}