diff options
Diffstat (limited to 'server/src/3rd/lua-uri/uri')
-rw-r--r-- | server/src/3rd/lua-uri/uri/_login.lua | 96 | ||||
-rw-r--r-- | server/src/3rd/lua-uri/uri/_relative.lua | 81 | ||||
-rw-r--r-- | server/src/3rd/lua-uri/uri/_util.lua | 128 | ||||
-rw-r--r-- | server/src/3rd/lua-uri/uri/data.lua | 116 | ||||
-rw-r--r-- | server/src/3rd/lua-uri/uri/file.lua | 72 | ||||
-rw-r--r-- | server/src/3rd/lua-uri/uri/file/unix.lua | 26 | ||||
-rw-r--r-- | server/src/3rd/lua-uri/uri/file/win32.lua | 34 | ||||
-rw-r--r-- | server/src/3rd/lua-uri/uri/ftp.lua | 53 | ||||
-rw-r--r-- | server/src/3rd/lua-uri/uri/http.lua | 32 | ||||
-rw-r--r-- | server/src/3rd/lua-uri/uri/https.lua | 9 | ||||
-rw-r--r-- | server/src/3rd/lua-uri/uri/pop.lua | 111 | ||||
-rw-r--r-- | server/src/3rd/lua-uri/uri/rtsp.lua | 9 | ||||
-rw-r--r-- | server/src/3rd/lua-uri/uri/rtspu.lua | 7 | ||||
-rw-r--r-- | server/src/3rd/lua-uri/uri/telnet.lua | 38 | ||||
-rw-r--r-- | server/src/3rd/lua-uri/uri/urn.lua | 131 | ||||
-rw-r--r-- | server/src/3rd/lua-uri/uri/urn/isbn.lua | 67 | ||||
-rw-r--r-- | server/src/3rd/lua-uri/uri/urn/issn.lua | 65 | ||||
-rw-r--r-- | server/src/3rd/lua-uri/uri/urn/oid.lua | 62 |
18 files changed, 1137 insertions, 0 deletions
diff --git a/server/src/3rd/lua-uri/uri/_login.lua b/server/src/3rd/lua-uri/uri/_login.lua new file mode 100644 index 00000000..4e9e6844 --- /dev/null +++ b/server/src/3rd/lua-uri/uri/_login.lua @@ -0,0 +1,96 @@ +local M = { _NAME = "uri._login" } +local Util = require "uri._util" +local URI = require "uri" +Util.subclass_of(M, URI) + +-- Generic terminal logins. This is used as a base class for 'telnet' and +-- 'ftp' URL schemes. + +local function _valid_userinfo (userinfo) + if userinfo then + local colon = userinfo:find(":") + if colon and userinfo:find(":", colon + 1) then + return nil, "only one colon allowed in userinfo" + end + end + return true +end + +-- TODO - this is a bit of a hack because currently subclasses are required +-- to know whether their superclass has one of these that needs calling. +-- It should be called from 'init' before anything more specific is done, +-- and it has the same calling convention. +-- According to RFC 1738 there should be at most one colon in the userinfo. +-- I apply that restriction for schemes where it's used for a username/password +-- pair. +function M.init_base (self) + local host = self:host() + if not host or host == "" then + return nil, "host missing from login URI" + end + + local ok, err = _valid_userinfo(self:userinfo()) + if not ok then return nil, err end + + return self +end + +function M.userinfo (self, ...) + if select("#", ...) > 0 then + local ok, err = _valid_userinfo(...) + if not ok then error("invalid userinfo value (" .. err .. ")") end + end + return M._SUPER.userinfo(self, ...) +end + +function M.username (self, ...) + local info = M._SUPER.userinfo(self) + local old, colon + if info then + local colon = info and info:find(":") + old = colon and info:sub(1, colon - 1) or info + old = Util.uri_decode(old) + end + + if select('#', ...) > 0 then + local pass = colon and info:sub(colon) or "" -- includes colon + local new = ... + if not new then + M._SUPER.userinfo(self, nil) + else + -- Escape anything that's not allowed in a userinfo, and also + -- colon, because that indicates the end of the username. + new = Util.uri_encode(new, "^A-Za-z0-9%-._~!$&'()*+,;=") + M._SUPER.userinfo(self, new .. pass) + end + end + + return old +end + +function M.password (self, ...) + local info = M._SUPER.userinfo(self) + local old, colon + if info then + colon = info and info:find(":") + old = colon and info:sub(colon + 1) or nil + if old then old = Util.uri_decode(old) end + end + + if select('#', ...) > 0 then + local new = ... + local user = colon and info:sub(1, colon - 1) or info + if not new then + M._SUPER.userinfo(self, user) + else + if not user then user = "" end + new = Util.uri_encode(new, "^A-Za-z0-9%-._~!$&'()*+,;=") + M._SUPER.userinfo(self, user .. ":" .. new) + end + end + + return old +end + +return M +-- vi:ts=4 sw=4 expandtab diff --git a/server/src/3rd/lua-uri/uri/_relative.lua b/server/src/3rd/lua-uri/uri/_relative.lua new file mode 100644 index 00000000..8cd53ca7 --- /dev/null +++ b/server/src/3rd/lua-uri/uri/_relative.lua @@ -0,0 +1,81 @@ +local M = { _NAME = "uri._relative" } +local Util = require "uri._util" +local URI = require "uri" +Util.subclass_of(M, URI) + +-- There needs to be an 'init' method in this class, to because the base-class +-- one expects there to be a 'scheme' value. +function M.init (self) + return self +end + +function M.scheme (self, ...) + if select("#", ...) > 0 then + error("relative URI references can't have a scheme, perhaps you" .. + " need to resolve this against an absolute URI instead") + end + return nil +end + +function M.is_relative () return true end + +-- This implements the algorithm from RFC 3986 section 5.2.3 +-- Note that this takes an additional argument which appears to be required +-- by the algorithm, but isn't shown when it is used in the RFC. +local function _merge_paths (base, r, base_has_auth) + if base_has_auth and base == "" then + return "/" .. r + end + + return base:gsub("[^/]+$", "", 1) .. r +end + +local function _do_resolve (self, base) + if type(base) == "string" then base = assert(URI:new(base)) end + setmetatable(self, URI) + + if self:host() or self:userinfo() or self:port() then + -- network path reference, just needs a scheme + self:path(Util.remove_dot_segments(self:path())) + self:scheme(base:scheme()) + return + end + + local path = self:path() + if path == "" then + self:path(base:path()) + if not self:query() then self:query(base:query()) end + else + if path:find("^/") then + self:path(Util.remove_dot_segments(path)) + else + local base_has_auth = base:host() or base:userinfo() or base:port() + local merged = _merge_paths(base:path(), path, base_has_auth) + self:path(Util.remove_dot_segments(merged)) + end + end + self:host(base:host()) + self:userinfo(base:userinfo()) + self:port(base:port()) + self:scheme(base:scheme()) +end + +function M.resolve (self, base) + local orig = tostring(self) + local ok, result = pcall(_do_resolve, self, base) + if ok then return end + + -- If the resolving causes an exception, it means that the resulting URI + -- would be invalid, so we restore self to its original state and rethrow + -- the exception. + local restored = assert(URI:new(orig)) + for k in pairs(self) do self[k] = nil end + for k, v in pairs(restored) do self[k] = v end + setmetatable(self, getmetatable(restored)) + error("resolved URI reference would be invalid: " .. result) +end + +function M.relativize (self, base) end -- already relative + +return M +-- vi:ts=4 sw=4 expandtab diff --git a/server/src/3rd/lua-uri/uri/_util.lua b/server/src/3rd/lua-uri/uri/_util.lua new file mode 100644 index 00000000..16a3b289 --- /dev/null +++ b/server/src/3rd/lua-uri/uri/_util.lua @@ -0,0 +1,128 @@ +local M = { _NAME = "uri._util" } + +-- Build a char->hex map +local escapes = {} +for i = 0, 255 do + escapes[string.char(i)] = string.format("%%%02X", i) +end + +function M.uri_encode (text, patn) + if not text then return end + if not patn then + -- Default unsafe characters. RFC 2732 ^(uric - reserved) + -- TODO - this should be updated to the latest RFC. + patn = "^A-Za-z0-9%-_.!~*'()" + end + return (text:gsub("([" .. patn .. "])", + function (chr) return escapes[chr] end)) +end + +function M.uri_decode (str, patn) + -- Note from RFC1630: "Sequences which start with a percent sign + -- but are not followed by two hexadecimal characters are reserved + -- for future extension" + if not str then return end + if patn then patn = "[" .. patn .. "]" end + return (str:gsub("%%(%x%x)", function (hex) + local char = string.char(tonumber(hex, 16)) + return (patn and not char:find(patn)) and "%" .. hex or char + end)) +end + +-- This is the remove_dot_segments algorithm from RFC 3986 section 5.2.4. +-- The input buffer is 's', the output buffer 'path'. +function M.remove_dot_segments (s) + local path = "" + + while s ~= "" do + if s:find("^%.%.?/") then -- A + s = s:gsub("^%.%.?/", "", 1) + elseif s:find("^/%./") or s == "/." then -- B + s = s:gsub("^/%./?", "/", 1) + elseif s:find("^/%.%./") or s == "/.." then -- C + s = s:gsub("^/%.%./?", "/", 1) + if path:find("/") then + path = path:gsub("/[^/]*$", "", 1) + else + path = "" + end + elseif s == "." or s == ".." then -- D + s = "" + else -- E + local _, p, seg = s:find("^(/?[^/]*)") + s = s:sub(p + 1) + path = path .. seg + end + end + + return path +end + +-- TODO - wouldn't this be better as a method on string? s:split(patn) +function M.split (patn, s, max) + if s == "" then return {} end + + local i, j = 1, string.find(s, patn) + if not j then return { s } end + + local list = {} + while true do + if #list + 1 == max then list[max] = s:sub(i); return list end + list[#list + 1] = s:sub(i, j - 1) + i = j + 1 + j = string.find(s, patn, i) + if not j then + list[#list + 1] = s:sub(i) + break + end + end + return list +end + +function M.attempt_require (modname) + local ok, result = pcall(require, modname) + if ok then + return result + elseif type(result) == "string" and + result:find("module '.*' not found") then + return nil + else + error(result) + end +end + +function M.subclass_of (class, baseclass) + class.__index = class + class.__tostring = baseclass.__tostring + class._SUPER = baseclass + setmetatable(class, baseclass) +end + +function M.do_class_changing_change (uri, baseclass, changedesc, newvalue, + changefunc) + local tmpuri = {} + setmetatable(tmpuri, baseclass) + for k, v in pairs(uri) do tmpuri[k] = v end + changefunc(tmpuri, newvalue) + tmpuri._uri = nil + + local foo, err = tmpuri:init() + if not foo then + error("URI not valid after " .. changedesc .. " changed to '" .. + newvalue .. "': " .. err) + end + + setmetatable(uri, getmetatable(tmpuri)) + for k in pairs(uri) do uri[k] = nil end + for k, v in pairs(tmpuri) do uri[k] = v end +end + +function M.uri_part_not_allowed (class, method) + class[method] = function (self, new) + if new then error(method .. " not allowed on this kind of URI") end + return self["_" .. method] + end +end + +return M +-- vi:ts=4 sw=4 expandtab diff --git a/server/src/3rd/lua-uri/uri/data.lua b/server/src/3rd/lua-uri/uri/data.lua new file mode 100644 index 00000000..c425621a --- /dev/null +++ b/server/src/3rd/lua-uri/uri/data.lua @@ -0,0 +1,116 @@ +local M = { _NAME = "uri.data" } +local Util = require "uri._util" +local URI = require "uri" +Util.subclass_of(M, URI) + +-- This implements the 'data' scheme defined in RFC 2397. + +local Filter = Util.attempt_require("datafilter") + +local function _valid_base64 (data) return data:find("^[0-9a-zA-Z/+]*$") end + +local function _split_path (path) + local _, _, mediatype, data = path:find("^([^,]*),(.*)") + if not mediatype then return "must have comma in path" end + local base64 = false + if mediatype:find(";base64$") then + base64 = true + mediatype = mediatype:sub(1, -8) + end + if base64 and not _valid_base64(data) then + return "illegal character in base64 encoding" + end + return nil, mediatype, base64, data +end + +function M.init (self) + if M._SUPER.host(self) then + return nil, "data URIs may not have authority parts" + end + local err, mediatype, base64, data = _split_path(M._SUPER.path(self)) + if err then return nil, "invalid data URI (" .. err .. ")" end + return self +end + +function M.data_media_type (self, ...) + local _, old, base64, data = _split_path(M._SUPER.path(self)) + + if select('#', ...) > 0 then + local new = ... or "" + new = Util.uri_encode(new, "^A-Za-z0-9%-._~!$&'()*+;=:@/") + if base64 then new = new .. ";base64" end + M._SUPER.path(self, new .. "," .. data) + end + + if old ~= "" then + if old:find("^;") then old = "text/plain" .. old end + return Util.uri_decode(old) + else + return "text/plain;charset=US-ASCII" -- default type + end +end + +local function _urienc_len (s) + local num_unsafe_chars = s:gsub("[A-Za-z0-9%-._~!$&'()*+,;=:@/]", ""):len() + local num_safe_chars = s:len() - num_unsafe_chars + return num_safe_chars + num_unsafe_chars * 3 +end + +local function _base64_len (s) + local num_blocks = (s:len() + 2) / 3 + num_blocks = num_blocks - num_blocks % 1 + return num_blocks * 4 + + 7 -- because of ";base64" marker +end + +local function _do_filter (algorithm, input) + return Filter[algorithm](input) +end + +function M.data_bytes (self, ...) + local _, mediatype, base64, old = _split_path(M._SUPER.path(self)) + if base64 then + if not Filter then + error("'datafilter' Lua module required to decode base64 data") + end + old = _do_filter("base64_decode", old) + else + old = Util.uri_decode(old) + end + + if select('#', ...) > 0 then + local new = ... or "" + local urienc_len = _urienc_len(new) + local base64_len = _base64_len(new) + if base64_len < urienc_len and Filter then + mediatype = mediatype .. ";base64" + new = _do_filter("base64_encode", new) + else + new = new:gsub("%%", "%%25") + end + M._SUPER.path(self, mediatype .. "," .. new) + end + + return old +end + +function M.path (self, ...) + local old = M._SUPER.path(self) + + if select('#', ...) > 0 then + local new = ... + if not new then error("there must be a path in a data URI") end + local err = _split_path(new) + if err then error("invalid data URI (" .. err .. ")") end + M._SUPER.path(self, new) + 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") + +return M +-- vi:ts=4 sw=4 expandtab diff --git a/server/src/3rd/lua-uri/uri/file.lua b/server/src/3rd/lua-uri/uri/file.lua new file mode 100644 index 00000000..271cb3ed --- /dev/null +++ b/server/src/3rd/lua-uri/uri/file.lua @@ -0,0 +1,72 @@ +local M = { _NAME = "uri.file" } +local Util = require "uri._util" +local URI = require "uri" +Util.subclass_of(M, URI) + +function M.init (self) + if self:userinfo() or self:port() then + return nil, "usernames and passwords are not allowed in HTTP URIs" + end + + local host = self:host() + local path = self:path() + if host then + if host:lower() == "localhost" then self:host("") end + else + if not path:find("^/") then + return nil, "file URIs must contain a host, even if it's empty" + end + self:host("") + end + + if path == "" then self:path("/") end + + return self +end + +function M.host (self, ...) + local old = M._SUPER.host(self) + + if select('#', ...) > 0 then + local new = ... + if not new then error("file URIs must have an authority part") end + if new:lower() == "localhost" then new = "" end + M._SUPER.host(self, new) + end + + return old +end + +function M.path (self, ...) + local old = M._SUPER.path(self) + + if select('#', ...) > 0 then + local new = ... + if not new or new == "" then new = "/" end + M._SUPER.path(self, new) + end + + return old +end + +local function _os_implementation (os) + local FileImpl = Util.attempt_require("uri.file." .. os:lower()) + if not FileImpl then + error("no file URI implementation for operating system " .. os) + end + return FileImpl +end + +function M.filesystem_path (self, os) + return _os_implementation(os).filesystem_path(self) +end + +function M.make_file_uri (path, os) + return _os_implementation(os).make_file_uri(path) +end + +Util.uri_part_not_allowed(M, "userinfo") +Util.uri_part_not_allowed(M, "port") + +return M +-- vi:ts=4 sw=4 expandtab diff --git a/server/src/3rd/lua-uri/uri/file/unix.lua b/server/src/3rd/lua-uri/uri/file/unix.lua new file mode 100644 index 00000000..2ffe1504 --- /dev/null +++ b/server/src/3rd/lua-uri/uri/file/unix.lua @@ -0,0 +1,26 @@ +local M = { _NAME = "uri.file.unix" } +local URI = require "uri" +local Util = require "uri._util" + +function M.filesystem_path (uri) + if uri:host() ~= "" then + error("a file URI with a host name can't be converted to a Unix path") + end + local path = uri:path() + if path:find("%%00") or path:find("%%2F") then + error("Unix paths cannot contain encoded null bytes or slashes") + end + return Util.uri_decode(path) +end + +function M.make_file_uri (path) + if not path:find("^/") then + error("Unix relative paths can't be converted to file URIs") + end + path = path:gsub("//+", "/") + path = Util.uri_encode(path, "^A-Za-z0-9%-._~!$&'()*+,;=:@/") + return assert(URI:new("file://" .. path)) +end + +return M +-- vi:ts=4 sw=4 expandtab diff --git a/server/src/3rd/lua-uri/uri/file/win32.lua b/server/src/3rd/lua-uri/uri/file/win32.lua new file mode 100644 index 00000000..bdacbe15 --- /dev/null +++ b/server/src/3rd/lua-uri/uri/file/win32.lua @@ -0,0 +1,34 @@ +local M = { _NAME = "uri.file.win32" } +local URI = require "uri" +local Util = require "uri._util" + +function M.filesystem_path (uri) + local host = uri:host() + local path = Util.uri_decode(uri:path()) + if host ~= "" then path = "//" .. host .. path end + if path:find("^/[A-Za-z]|/") or path:find("^/[A-Za-z]|$") then + path = path:gsub("|", ":", 1) + end + if path:find("^/[A-Za-z]:/") then + path = path:sub(2) + elseif path:find("^/[A-Za-z]:$") then + path = path:sub(2) .. "/" + end + path = path:gsub("/", "\\") + return path +end + +function M.make_file_uri (path) + if path:find("^[A-Za-z]:$") then path = path .. "\\" end + local _, _, host, hostpath = path:find("^\\\\([A-Za-z.]+)\\(.*)$") + host = host or "" + hostpath = hostpath or path + hostpath = hostpath:gsub("\\", "/") + :gsub("//+", "/") + hostpath = Util.uri_encode(hostpath, "^A-Za-z0-9%-._~!$&'()*+,;=:@/") + if not hostpath:find("^/") then hostpath = "/" .. hostpath end + return assert(URI:new("file://" .. host .. hostpath)) +end + +return M +-- vi:ts=4 sw=4 expandtab diff --git a/server/src/3rd/lua-uri/uri/ftp.lua b/server/src/3rd/lua-uri/uri/ftp.lua new file mode 100644 index 00000000..2d9e3f6c --- /dev/null +++ b/server/src/3rd/lua-uri/uri/ftp.lua @@ -0,0 +1,53 @@ +local M = { _NAME = "uri.ftp" } +local Util = require "uri._util" +local LoginURI = require "uri._login" +Util.subclass_of(M, LoginURI) + +function M.default_port () return 21 end + +function M.init (self) + self, err = M._SUPER.init_base(self) + if not self then return nil, err end + + local host = self:host() + if not host or host == "" then + return nil, "FTP URIs must have a hostname" + end + + -- I don't think there's any distinction in FTP URIs between empty path + -- and the root directory, so probably best to normalize as we do for HTTP. + if self:path() == "" then self:path("/") end + + return self +end + +function M.path (self, ...) + local old = M._SUPER.path(self) + + if select("#", ...) > 0 then + local new = ... + if not new or new == "" then new = "/" end + M._SUPER.path(self, new) + end + + return old +end + +function M.ftp_typecode (self, ...) + local path = M._SUPER.path(self) + local _, _, withouttype, old = path:find("^(.*);type=(.*)$") + if not withouttype then withouttype = path end + if old == "" then old = nil end + + if select("#", ...) > 0 then + local new = ... + if not new then new = "" end + if new ~= "" then new = ";type=" .. new end + M._SUPER.path(self, withouttype .. new) + end + + return old +end + +return M +-- vi:ts=4 sw=4 expandtab diff --git a/server/src/3rd/lua-uri/uri/http.lua b/server/src/3rd/lua-uri/uri/http.lua new file mode 100644 index 00000000..91f7a57f --- /dev/null +++ b/server/src/3rd/lua-uri/uri/http.lua @@ -0,0 +1,32 @@ +local M = { _NAME = "uri.http" } +local Util = require "uri._util" +local URI = require "uri" +Util.subclass_of(M, URI) + +-- This implementation is based on RFC 2616 section 3.2 and RFC 1738 +-- section 3.3. +-- +-- An HTTP URI with a 'userinfo' field is considered invalid, because it isn't +-- shown in the syntax given in RFC 2616, and is explicitly disallowed by +-- RFC 1738. + +function M.default_port () return 80 end + +function M.init (self) + if self:userinfo() then + return nil, "usernames and passwords are not allowed in HTTP URIs" + end + + -- RFC 2616 section 3.2.3 says that this is OK, but not that using the + -- redundant slash is canonical. I'm adding it because browsers tend to + -- treat the version with the extra slash as the normalized form, and + -- the initial slash is always present in an HTTP GET request. + if self:path() == "" then self:path("/") end + + return self +end + +Util.uri_part_not_allowed(M, "userinfo") + +return M +-- vi:ts=4 sw=4 expandtab diff --git a/server/src/3rd/lua-uri/uri/https.lua b/server/src/3rd/lua-uri/uri/https.lua new file mode 100644 index 00000000..0c4c8bc3 --- /dev/null +++ b/server/src/3rd/lua-uri/uri/https.lua @@ -0,0 +1,9 @@ +local M = { _NAME = "uri.https" } +local Util = require "uri._util" +local Http = require "uri.http" +Util.subclass_of(M, Http) + +function M.default_port () return 443 end + +return M +-- vi:ts=4 sw=4 expandtab diff --git a/server/src/3rd/lua-uri/uri/pop.lua b/server/src/3rd/lua-uri/uri/pop.lua new file mode 100644 index 00000000..e42d9d41 --- /dev/null +++ b/server/src/3rd/lua-uri/uri/pop.lua @@ -0,0 +1,111 @@ +local M = { _NAME = "uri.pop" } +local URI = require "uri" +local Util = require "uri._util" +Util.subclass_of(M, URI) + +-- This is the set of characters must be encoded in a POP userinfo, which +-- unlike for other schemes includes the ';' character. +local _POP_USERINFO_ENCODE = "^A-Za-z0-9%-._~%%!$&'()*+,=:" + +function M.default_port () return 110 end + +local function _update_userinfo (self, old, new) + if new then + local _, _, user, auth = new:find("^(.*);[Aa][Uu][Tt][Hh]=(.*)$") + if not user then user = new end + if user == "" then return "pop user name must not be empty" end + user = Util.uri_encode(user, _POP_USERINFO_ENCODE) + if auth then + if auth == "" then return "pop auth type must not be empty" end + if auth == "*" then auth = nil end + auth = Util.uri_encode(auth, _POP_USERINFO_ENCODE) + end + new = user .. (auth and ";auth=" .. auth or "") + end + + if new ~= old then M._SUPER.userinfo(self, new) end + return nil +end + +function M.init (self) + if M._SUPER.path(self) ~= "" then + return nil, "pop URIs must have an empty path" + end + + local userinfo = M._SUPER.userinfo(self) + local err = _update_userinfo(self, userinfo, userinfo) + if err then return nil, err end + + return self +end + +function M.userinfo (self, ...) + local old = M._SUPER.userinfo(self) + + if select('#', ...) > 0 then + local new = ... + local err = _update_userinfo(self, old, new) + if err then error(err) end + end + + return old +end + +function M.path (self, new) + if new and new ~= "" then error("POP URIs must have an empty path") end + return "" +end + +local function _decode_userinfo (self) + local old = M._SUPER.userinfo(self) + if not old then return nil, nil end + local _, _, old_user, old_auth = old:find("^(.*);auth=(.*)$") + if not old_user then old_user = old end + return old_user, old_auth +end + +function M.pop_user (self, ...) + local old_user, old_auth = _decode_userinfo(self) + + if select('#', ...) > 0 then + local new = ... + if new == "" then error("pop user name must not be empty") end + if not new and old_auth then + error("pop user name required when an auth type is specified") + end + if new then + new = Util.uri_encode(new, _POP_USERINFO_ENCODE) + if old_auth then new = new .. ";auth=" .. old_auth end + end + M._SUPER.userinfo(self, new) + end + + return Util.uri_decode(old_user) +end + +function M.pop_auth (self, ...) + local old_user, old_auth = _decode_userinfo(self) + + if select('#', ...) > 0 then + local new = ... + if not new or new == "" + then error("pop auth type must not be empty") + end + if new == "*" then new = nil end + if new and not old_user then + error("pop auth type can't be specified without user name") + end + if new then + new = old_user .. ";auth=" .. + Util.uri_encode(new, _POP_USERINFO_ENCODE) + else + new = old_user + end + M._SUPER.userinfo(self, new) + end + + return old_auth and Util.uri_decode(old_auth) or "*" +end + +return M +-- vi:ts=4 sw=4 expandtab diff --git a/server/src/3rd/lua-uri/uri/rtsp.lua b/server/src/3rd/lua-uri/uri/rtsp.lua new file mode 100644 index 00000000..03c71485 --- /dev/null +++ b/server/src/3rd/lua-uri/uri/rtsp.lua @@ -0,0 +1,9 @@ +local M = { _NAME = "uri.rtsp" } +local Util = require "uri._util" +local HttpURI = require "uri.http" +Util.subclass_of(M, HttpURI) + +function M.default_port () return 554 end + +return M +-- vi:ts=4 sw=4 expandtab diff --git a/server/src/3rd/lua-uri/uri/rtspu.lua b/server/src/3rd/lua-uri/uri/rtspu.lua new file mode 100644 index 00000000..16f5e3ee --- /dev/null +++ b/server/src/3rd/lua-uri/uri/rtspu.lua @@ -0,0 +1,7 @@ +local M = { _NAME = "uri.rtspu" } +local Util = require "uri._util" +local RtspURI = require "uri.rtsp" +Util.subclass_of(M, RtspURI) + +return M +-- vi:ts=4 sw=4 expandtab diff --git a/server/src/3rd/lua-uri/uri/telnet.lua b/server/src/3rd/lua-uri/uri/telnet.lua new file mode 100644 index 00000000..339e21ee --- /dev/null +++ b/server/src/3rd/lua-uri/uri/telnet.lua @@ -0,0 +1,38 @@ +local M = { _NAME = "uri.telnet" } +local Util = require "uri._util" +local LoginURI = require "uri._login" +Util.subclass_of(M, LoginURI) + +function M.default_port () return 23 end + +function M.init (self) + self, err = M._SUPER.init_base(self) + if not self then return nil, err end + + -- RFC 4248 does not discuss what a path longer than '/' might mean, and + -- there are no examples with anything significant in the path, so I'm + -- assuming that extra information in the path is not allowed. + local path = M._SUPER.path(self) + if path ~= "" and path ~= "/" then + return nil, "superfluous information in path of telnet URI" + end + + -- RFC 4248 section 2 says that the '/' can be omitted. I chose to + -- normalize to having it there, since the example shown in the RFC has + -- it, and this is consistent with the way I treat HTTP URIs. + if path == "" then self:path("/") end + + return self +end + +-- The path is always '/', so setting it won't do anything, but we do throw +-- an exception on an attempt to set it to anything invalid. +function M.path (self, new) + if new and new ~= "" and new ~= "/" then + error("invalid path for telnet URI") + end + return "/" +end + +return M +-- vi:ts=4 sw=4 expandtab diff --git a/server/src/3rd/lua-uri/uri/urn.lua b/server/src/3rd/lua-uri/uri/urn.lua new file mode 100644 index 00000000..aa4b1776 --- /dev/null +++ b/server/src/3rd/lua-uri/uri/urn.lua @@ -0,0 +1,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 diff --git a/server/src/3rd/lua-uri/uri/urn/isbn.lua b/server/src/3rd/lua-uri/uri/urn/isbn.lua new file mode 100644 index 00000000..5f0bdb69 --- /dev/null +++ b/server/src/3rd/lua-uri/uri/urn/isbn.lua @@ -0,0 +1,67 @@ +local M = { _NAME = "uri.urn.isbn" } +local Util = require "uri._util" +local URN = require "uri.urn" +Util.subclass_of(M, URN) + +-- This implements the 'isbn' NID defined in RFC 3187, and is consistent +-- with the same NID suggested in RFC 2288. + +local function _valid_isbn (isbn) + if not isbn:find("^[-%d]+[%dXx]$") then return nil, "invalid character" end + local ISBN = Util.attempt_require("isbn") + if ISBN then return ISBN:new(isbn) end + return isbn +end + +local function _normalize_isbn (isbn) + isbn = isbn:gsub("%-", ""):upper() + local ISBN = Util.attempt_require("isbn") + if ISBN then return tostring(ISBN:new(isbn)) end + return isbn +end + +function M.init (self) + local nss = self:nss() + local ok, msg = _valid_isbn(nss) + if not ok then return nil, "invalid ISBN value (" .. msg .. ")" end + self:nss(_normalize_isbn(nss)) + return self +end + +function M.nss (self, new) + local old = M._SUPER.nss(self) + + if new then + local ok, msg = _valid_isbn(new) + if not ok then + error("bad ISBN value '" .. new .. "' (" .. msg .. ")") + end + M._SUPER.nss(self, _normalize_isbn(new)) + end + + return old +end + +function M.isbn_digits (self, new) + local old = self:nss():gsub("%-", "") + + if new then + local ok, msg = _valid_isbn(new) + if not ok then + error("bad ISBN value '" .. new .. "' (" .. msg .. ")") + end + self._SUPER.nss(self, _normalize_isbn(new)) + end + + return old +end + +function M.isbn (self, new) + local ISBN = require "isbn" + local old = ISBN:new(self:nss()) + if new then self:nss(tostring(new)) end + return old +end + +return M +-- vi:ts=4 sw=4 expandtab diff --git a/server/src/3rd/lua-uri/uri/urn/issn.lua b/server/src/3rd/lua-uri/uri/urn/issn.lua new file mode 100644 index 00000000..c5f37f8c --- /dev/null +++ b/server/src/3rd/lua-uri/uri/urn/issn.lua @@ -0,0 +1,65 @@ +local M = { _NAME = "uri.urn.issn" } +local Util = require "uri._util" +local URN = require "uri.urn" +Util.subclass_of(M, URN) + +local function _parse_issn (issn) + local _, _, nums1, nums2, checksum + = issn:find("^(%d%d%d%d)-?(%d%d%d)([%dxX])$") + if checksum == "x" then checksum = "X" end + return nums1, nums2, checksum +end + +local function _valid_issn (issn) + local nums1, nums2, actual_checksum = _parse_issn(issn) + if not nums1 then return nil, "invalid ISSN syntax" end + local nums = nums1 .. nums2 + + local expected_checksum = 0 + for i = 1, 7 do + expected_checksum = expected_checksum + tonumber(nums:sub(i, i)) * (9 - i) + end + expected_checksum = (11 - expected_checksum % 11) % 11 + expected_checksum = (expected_checksum == 10) and "X" + or tostring(expected_checksum) + if actual_checksum ~= expected_checksum then + return nil, "wrong checksum, expected " .. expected_checksum + end + + return true +end + +local function _normalize_issn (issn) + local nums1, nums2, checksum = _parse_issn(issn) + return nums1 .. "-" .. nums2 .. checksum +end + +function M.init (self) + local nss = self:nss() + local ok, msg = _valid_issn(nss) + if not ok then return nil, "bad NSS value for ISSN URI (" .. msg .. ")" end + M._SUPER.nss(self, _normalize_issn(nss)) + return self +end + +function M.nss (self, new) + local old = M._SUPER.nss(self) + + if new then + local ok, msg = _valid_issn(new) + if not ok then + error("bad ISSN value '" .. new .. "' (" .. msg .. ")") + end + M._SUPER.nss(self, _normalize_issn(new)) + end + + return old +end + +function M.issn_digits (self, new) + local old = self:nss(new) + return old:sub(1, 4) .. old:sub(6, 9) +end + +return M +-- vi:ts=4 sw=4 expandtab diff --git a/server/src/3rd/lua-uri/uri/urn/oid.lua b/server/src/3rd/lua-uri/uri/urn/oid.lua new file mode 100644 index 00000000..37110cda --- /dev/null +++ b/server/src/3rd/lua-uri/uri/urn/oid.lua @@ -0,0 +1,62 @@ +local M = { _NAME = "uri.urn.oid" } +local Util = require "uri._util" +local URN = require "uri.urn" +Util.subclass_of(M, URN) + +-- This implements RFC 3061. + +local function _valid_oid (oid) + if oid == "" then return nil, "OID can't be zero-length" end + if not oid:find("^[.0-9]*$") then return nil, "bad character in OID" end + if oid:find("%.%.") then return nil, "missing number in OID" end + if oid:find("^0[^.]") or oid:find("%.0[^.]") then + return nil, "OID numbers shouldn't have leading zeros" + end + return true +end + +function M.init (self) + local nss = self:nss() + local ok, msg = _valid_oid(nss) + if not ok then return nil, "bad NSS value for OID URI (" .. msg .. ")" end + return self +end + +function M.nss (self, new) + local old = M._SUPER.nss(self) + + if new then + local ok, msg = _valid_oid(new) + if not ok then + error("bad OID value '" .. new .. "' (" .. msg .. ")") + end + M._SUPER.nss(self, new) + end + + return old +end + +function M.oid_numbers (self, new) + local old = Util.split("%.", self:nss()) + for i = 1, #old do old[i] = tonumber(old[i]) end + + if new then + if type(new) ~= "table" then error("expected array of numbers") end + local nss = "" + for _, n in ipairs(new) do + if type(n) == "string" and n:find("^%d+$") then n = tonumber(n) end + if type(n) ~= "number" then error("bad type for number in OID") end + n = n - n % 1 + if n < 0 then error("negative numbers not allowed in OID") end + if nss ~= "" then nss = nss .. "." end + nss = nss .. n + end + if nss == "" then error("no numbers in new OID value") end + self:nss(nss) + end + + return old +end + +return M +-- vi:ts=4 sw=4 expandtab |