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 132 133
|
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 .. ")", 2)
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 .. ")", 2)
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 .. ")", 2)
end
local _, _, newnid, newnss = path:find("^([^:]+):(.*)")
if not newnid then error("bad path for URN, no NID part found", 2) end
local ok, msg = _valid_nid(newnid)
if not ok then
error("invalid namespace identifier (" .. msg .. ")", 2)
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
|