You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lua-uri/uri/urn.lua

134 lines
3.9 KiB

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