summaryrefslogtreecommitdiff
path: root/server/src/3rd/lua-uri/uri/urn.lua
blob: aa4b1776c2d07fee123d1477cb4d719b745ac8ae (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
local M = { _NAME = "uri.urn" }
local Util = require "uri._util"
local URI = require "uri"
Util.subclass_of(M, URI)

-- This implements RFC 2141, and attempts to change the class of the URI object
-- to one of its subclasses for further validation and normalization of the
-- namespace-specific string.

-- Check NID syntax matches RFC 2141 section 2.1.
local function _valid_nid (nid)
    if nid == "" then return nil, "missing completely" end
    if nid:len() > 32 then return nil, "too long" end
    if not nid:find("^[A-Za-z0-9][-A-Za-z0-9]*$") then
        return nil, "contains illegal character"
    end
    if nid:lower() == "urn" then return nil, "'urn' is reserved" end
    return true
end

-- Check NSS syntax matches RFC 2141 section 2.2.
local function _valid_nss (nss)
    if nss == "" then return nil, "can't be empty" end
    if nss:find("[^A-Za-z0-9()+,%-.:=@;$_!*'/%%]") then
        return nil, "contains illegal character"
    end
    return true
end

local function _validate_and_normalize_path (path)
    local _, _, nid, nss = path:find("^([^:]+):(.*)$")
    if not nid then return nil, "illegal path syntax for URN" end

    local ok, msg = _valid_nid(nid)
    if not ok then
        return nil, "invalid namespace identifier (" .. msg .. ")"
    end
    ok, msg = _valid_nss(nss)
    if not ok then
        return nil, "invalid namespace specific string (" .. msg .. ")"
    end

    return nid:lower() .. ":" .. nss
end

-- TODO - this should check that percent-encoded bytes are valid UTF-8
function M.init (self)
    if M._SUPER.query(self) then
        return nil, "URNs may not have query parts"
    end
    if M._SUPER.host(self) then
        return nil, "URNs may not have authority parts"
    end

    local path, msg = _validate_and_normalize_path(self:path())
    if not path then return nil, msg end
    M._SUPER.path(self, path)

    local nid_class
        = Util.attempt_require("uri.urn." .. self:nid():gsub("%-", "_"))
    if nid_class then
        setmetatable(self, nid_class)
        if self.init ~= M.init then return self:init() end
    end

    return self
end

function M.nid (self, new)
    local _, _, old = self:path():find("^([^:]+)")

    if new then
        new = new:lower()
        if new ~= old then
            local ok, msg = _valid_nid(new)
            if not ok then
                error("invalid namespace identifier (" .. msg .. ")")
            end
        end
        Util.do_class_changing_change(self, M, "NID", new, function (uri, new)
            M._SUPER.path(uri, new .. ":" .. uri:nss())
        end)
    end

    return old
end

function M.nss (self, new)
    local _, _, old = self:path():find(":(.*)")

    if new and new ~= old then
        local ok, msg = _valid_nss(new)
        if not ok then
            error("invalid namespace specific string (" .. msg .. ")")
        end
        M._SUPER.path(self, self:nid() .. ":" .. new)
    end

    return old
end

function M.path (self, new)
    local old = M._SUPER.path(self)

    if new and new ~= old then
        local path, msg = _validate_and_normalize_path(new)
        if not path then
            error("invalid path for URN '" .. new .. "' (" ..msg .. ")")
        end
        local _, _, newnid, newnss = path:find("^([^:]+):(.*)")
        if not newnid then error("bad path for URN, no NID part found") end
        local ok, msg = _valid_nid(newnid)
        if not ok then error("invalid namespace identifier (" .. msg .. ")") end
        if newnid:lower() == self:nid() then
            self:nss(newnss)
        else
            Util.do_class_changing_change(self, M, "path", path,
                function (uri, new) M._SUPER.path(uri, new) end)
        end
    end

    return old
end

Util.uri_part_not_allowed(M, "userinfo")
Util.uri_part_not_allowed(M, "host")
Util.uri_part_not_allowed(M, "port")
Util.uri_part_not_allowed(M, "query")

return M
-- vi:ts=4 sw=4 expandtab