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
|