Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/lib/support/share
diff options
context:
space:
mode:
Diffstat (limited to 'lib/support/share')
-rw-r--r--lib/support/share/ltn12.lua292
-rw-r--r--lib/support/share/mime.lua87
-rw-r--r--lib/support/share/socket.lua133
-rw-r--r--lib/support/share/socket/ftp.lua281
-rw-r--r--lib/support/share/socket/http.lua350
-rw-r--r--lib/support/share/socket/smtp.lua251
-rw-r--r--lib/support/share/socket/tp.lua123
-rw-r--r--lib/support/share/socket/url.lua297
8 files changed, 0 insertions, 1814 deletions
diff --git a/lib/support/share/ltn12.lua b/lib/support/share/ltn12.lua
deleted file mode 100644
index b42689a..0000000
--- a/lib/support/share/ltn12.lua
+++ /dev/null
@@ -1,292 +0,0 @@
------------------------------------------------------------------------------
--- LTN12 - Filters, sources, sinks and pumps.
--- LuaSocket toolkit.
--- Author: Diego Nehab
--- RCS ID: $Id: ltn12.lua,v 1.31 2006/04/03 04:45:42 diego Exp $
------------------------------------------------------------------------------
-
------------------------------------------------------------------------------
--- Declare module
------------------------------------------------------------------------------
-local string = require("string")
-local table = require("table")
-local base = _G
-module("ltn12")
-
-filter = {}
-source = {}
-sink = {}
-pump = {}
-
--- 2048 seems to be better in windows...
-BLOCKSIZE = 2048
-_VERSION = "LTN12 1.0.1"
-
------------------------------------------------------------------------------
--- Filter stuff
------------------------------------------------------------------------------
--- returns a high level filter that cycles a low-level filter
-function filter.cycle(low, ctx, extra)
- base.assert(low)
- return function(chunk)
- local ret
- ret, ctx = low(ctx, chunk, extra)
- return ret
- end
-end
-
--- chains a bunch of filters together
--- (thanks to Wim Couwenberg)
-function filter.chain(...)
- local n = table.getn(arg)
- local top, index = 1, 1
- local retry = ""
- return function(chunk)
- retry = chunk and retry
- while true do
- if index == top then
- chunk = arg[index](chunk)
- if chunk == "" or top == n then return chunk
- elseif chunk then index = index + 1
- else
- top = top+1
- index = top
- end
- else
- chunk = arg[index](chunk or "")
- if chunk == "" then
- index = index - 1
- chunk = retry
- elseif chunk then
- if index == n then return chunk
- else index = index + 1 end
- else base.error("filter returned inappropriate nil") end
- end
- end
- end
-end
-
------------------------------------------------------------------------------
--- Source stuff
------------------------------------------------------------------------------
--- create an empty source
-local function empty()
- return nil
-end
-
-function source.empty()
- return empty
-end
-
--- returns a source that just outputs an error
-function source.error(err)
- return function()
- return nil, err
- end
-end
-
--- creates a file source
-function source.file(handle, io_err)
- if handle then
- return function()
- local chunk = handle:read(BLOCKSIZE)
- if not chunk then handle:close() end
- return chunk
- end
- else return source.error(io_err or "unable to open file") end
-end
-
--- turns a fancy source into a simple source
-function source.simplify(src)
- base.assert(src)
- return function()
- local chunk, err_or_new = src()
- src = err_or_new or src
- if not chunk then return nil, err_or_new
- else return chunk end
- end
-end
-
--- creates string source
-function source.string(s)
- if s then
- local i = 1
- return function()
- local chunk = string.sub(s, i, i+BLOCKSIZE-1)
- i = i + BLOCKSIZE
- if chunk ~= "" then return chunk
- else return nil end
- end
- else return source.empty() end
-end
-
--- creates rewindable source
-function source.rewind(src)
- base.assert(src)
- local t = {}
- return function(chunk)
- if not chunk then
- chunk = table.remove(t)
- if not chunk then return src()
- else return chunk end
- else
- table.insert(t, chunk)
- end
- end
-end
-
-function source.chain(src, f)
- base.assert(src and f)
- local last_in, last_out = "", ""
- local state = "feeding"
- local err
- return function()
- if not last_out then
- base.error('source is empty!', 2)
- end
- while true do
- if state == "feeding" then
- last_in, err = src()
- if err then return nil, err end
- last_out = f(last_in)
- if not last_out then
- if last_in then
- base.error('filter returned inappropriate nil')
- else
- return nil
- end
- elseif last_out ~= "" then
- state = "eating"
- if last_in then last_in = "" end
- return last_out
- end
- else
- last_out = f(last_in)
- if last_out == "" then
- if last_in == "" then
- state = "feeding"
- else
- base.error('filter returned ""')
- end
- elseif not last_out then
- if last_in then
- base.error('filter returned inappropriate nil')
- else
- return nil
- end
- else
- return last_out
- end
- end
- end
- end
-end
-
--- creates a source that produces contents of several sources, one after the
--- other, as if they were concatenated
--- (thanks to Wim Couwenberg)
-function source.cat(...)
- local src = table.remove(arg, 1)
- return function()
- while src do
- local chunk, err = src()
- if chunk then return chunk end
- if err then return nil, err end
- src = table.remove(arg, 1)
- end
- end
-end
-
------------------------------------------------------------------------------
--- Sink stuff
------------------------------------------------------------------------------
--- creates a sink that stores into a table
-function sink.table(t)
- t = t or {}
- local f = function(chunk, err)
- if chunk then table.insert(t, chunk) end
- return 1
- end
- return f, t
-end
-
--- turns a fancy sink into a simple sink
-function sink.simplify(snk)
- base.assert(snk)
- return function(chunk, err)
- local ret, err_or_new = snk(chunk, err)
- if not ret then return nil, err_or_new end
- snk = err_or_new or snk
- return 1
- end
-end
-
--- creates a file sink
-function sink.file(handle, io_err)
- if handle then
- return function(chunk, err)
- if not chunk then
- handle:close()
- return 1
- else return handle:write(chunk) end
- end
- else return sink.error(io_err or "unable to open file") end
-end
-
--- creates a sink that discards data
-local function null()
- return 1
-end
-
-function sink.null()
- return null
-end
-
--- creates a sink that just returns an error
-function sink.error(err)
- return function()
- return nil, err
- end
-end
-
--- chains a sink with a filter
-function sink.chain(f, snk)
- base.assert(f and snk)
- return function(chunk, err)
- if chunk ~= "" then
- local filtered = f(chunk)
- local done = chunk and ""
- while true do
- local ret, snkerr = snk(filtered, err)
- if not ret then return nil, snkerr end
- if filtered == done then return 1 end
- filtered = f(done)
- end
- else return 1 end
- end
-end
-
------------------------------------------------------------------------------
--- Pump stuff
------------------------------------------------------------------------------
--- pumps one chunk from the source to the sink
-function pump.step(src, snk)
- local chunk, src_err = src()
- local ret, snk_err = snk(chunk, src_err)
- if chunk and ret then return 1
- else return nil, src_err or snk_err end
-end
-
--- pumps all data from a source to a sink, using a step function
-function pump.all(src, snk, step)
- base.assert(src and snk)
- step = step or pump.step
- while true do
- local ret, err = step(src, snk)
- if not ret then
- if err then return nil, err
- else return 1 end
- end
- end
-end
-
diff --git a/lib/support/share/mime.lua b/lib/support/share/mime.lua
deleted file mode 100644
index 169eda2..0000000
--- a/lib/support/share/mime.lua
+++ /dev/null
@@ -1,87 +0,0 @@
------------------------------------------------------------------------------
--- MIME support for the Lua language.
--- Author: Diego Nehab
--- Conforming to RFCs 2045-2049
--- RCS ID: $Id: mime.lua,v 1.29 2007/06/11 23:44:54 diego Exp $
------------------------------------------------------------------------------
-
------------------------------------------------------------------------------
--- Declare module and import dependencies
------------------------------------------------------------------------------
-local base = _G
-local ltn12 = require("ltn12")
-local mime = require("mime.core")
-local io = require("io")
-local string = require("string")
-module("mime")
-
--- encode, decode and wrap algorithm tables
-encodet = {}
-decodet = {}
-wrapt = {}
-
--- creates a function that chooses a filter by name from a given table
-local function choose(table)
- return function(name, opt1, opt2)
- if base.type(name) ~= "string" then
- name, opt1, opt2 = "default", name, opt1
- end
- local f = table[name or "nil"]
- if not f then
- base.error("unknown key (" .. base.tostring(name) .. ")", 3)
- else return f(opt1, opt2) end
- end
-end
-
--- define the encoding filters
-encodet['base64'] = function()
- return ltn12.filter.cycle(b64, "")
-end
-
-encodet['quoted-printable'] = function(mode)
- return ltn12.filter.cycle(qp, "",
- (mode == "binary") and "=0D=0A" or "\r\n")
-end
-
--- define the decoding filters
-decodet['base64'] = function()
- return ltn12.filter.cycle(unb64, "")
-end
-
-decodet['quoted-printable'] = function()
- return ltn12.filter.cycle(unqp, "")
-end
-
-local function format(chunk)
- if chunk then
- if chunk == "" then return "''"
- else return string.len(chunk) end
- else return "nil" end
-end
-
--- define the line-wrap filters
-wrapt['text'] = function(length)
- length = length or 76
- return ltn12.filter.cycle(wrp, length, length)
-end
-wrapt['base64'] = wrapt['text']
-wrapt['default'] = wrapt['text']
-
-wrapt['quoted-printable'] = function()
- return ltn12.filter.cycle(qpwrp, 76, 76)
-end
-
--- function that choose the encoding, decoding or wrap algorithm
-encode = choose(encodet)
-decode = choose(decodet)
-wrap = choose(wrapt)
-
--- define the end-of-line normalization filter
-function normalize(marker)
- return ltn12.filter.cycle(eol, 0, marker)
-end
-
--- high level stuffing filter
-function stuff()
- return ltn12.filter.cycle(dot, 2)
-end
diff --git a/lib/support/share/socket.lua b/lib/support/share/socket.lua
deleted file mode 100644
index 211adcd..0000000
--- a/lib/support/share/socket.lua
+++ /dev/null
@@ -1,133 +0,0 @@
------------------------------------------------------------------------------
--- LuaSocket helper module
--- Author: Diego Nehab
--- RCS ID: $Id: socket.lua,v 1.22 2005/11/22 08:33:29 diego Exp $
------------------------------------------------------------------------------
-
------------------------------------------------------------------------------
--- Declare module and import dependencies
------------------------------------------------------------------------------
-local base = _G
-local string = require("string")
-local math = require("math")
-local socket = require("socket.core")
-module("socket")
-
------------------------------------------------------------------------------
--- Exported auxiliar functions
------------------------------------------------------------------------------
-function connect(address, port, laddress, lport)
- local sock, err = socket.tcp()
- if not sock then return nil, err end
- if laddress then
- local res, err = sock:bind(laddress, lport, -1)
- if not res then return nil, err end
- end
- local res, err = sock:connect(address, port)
- if not res then return nil, err end
- return sock
-end
-
-function bind(host, port, backlog)
- local sock, err = socket.tcp()
- if not sock then return nil, err end
- sock:setoption("reuseaddr", true)
- local res, err = sock:bind(host, port)
- if not res then return nil, err end
- res, err = sock:listen(backlog)
- if not res then return nil, err end
- return sock
-end
-
-try = newtry()
-
-function choose(table)
- return function(name, opt1, opt2)
- if base.type(name) ~= "string" then
- name, opt1, opt2 = "default", name, opt1
- end
- local f = table[name or "nil"]
- if not f then base.error("unknown key (".. base.tostring(name) ..")", 3)
- else return f(opt1, opt2) end
- end
-end
-
------------------------------------------------------------------------------
--- Socket sources and sinks, conforming to LTN12
------------------------------------------------------------------------------
--- create namespaces inside LuaSocket namespace
-sourcet = {}
-sinkt = {}
-
-BLOCKSIZE = 2048
-
-sinkt["close-when-done"] = function(sock)
- return base.setmetatable({
- getfd = function() return sock:getfd() end,
- dirty = function() return sock:dirty() end
- }, {
- __call = function(self, chunk, err)
- if not chunk then
- sock:close()
- return 1
- else return sock:send(chunk) end
- end
- })
-end
-
-sinkt["keep-open"] = function(sock)
- return base.setmetatable({
- getfd = function() return sock:getfd() end,
- dirty = function() return sock:dirty() end
- }, {
- __call = function(self, chunk, err)
- if chunk then return sock:send(chunk)
- else return 1 end
- end
- })
-end
-
-sinkt["default"] = sinkt["keep-open"]
-
-sink = choose(sinkt)
-
-sourcet["by-length"] = function(sock, length)
- return base.setmetatable({
- getfd = function() return sock:getfd() end,
- dirty = function() return sock:dirty() end
- }, {
- __call = function()
- if length <= 0 then return nil end
- local size = math.min(socket.BLOCKSIZE, length)
- local chunk, err = sock:receive(size)
- if err then return nil, err end
- length = length - string.len(chunk)
- return chunk
- end
- })
-end
-
-sourcet["until-closed"] = function(sock)
- local done
- return base.setmetatable({
- getfd = function() return sock:getfd() end,
- dirty = function() return sock:dirty() end
- }, {
- __call = function()
- if done then return nil end
- local chunk, err, partial = sock:receive(socket.BLOCKSIZE)
- if not err then return chunk
- elseif err == "closed" then
- sock:close()
- done = 1
- return partial
- else return nil, err end
- end
- })
-end
-
-
-sourcet["default"] = sourcet["until-closed"]
-
-source = choose(sourcet)
-
diff --git a/lib/support/share/socket/ftp.lua b/lib/support/share/socket/ftp.lua
deleted file mode 100644
index 598f65d..0000000
--- a/lib/support/share/socket/ftp.lua
+++ /dev/null
@@ -1,281 +0,0 @@
------------------------------------------------------------------------------
--- FTP support for the Lua language
--- LuaSocket toolkit.
--- Author: Diego Nehab
--- RCS ID: $Id: ftp.lua,v 1.45 2007/07/11 19:25:47 diego Exp $
------------------------------------------------------------------------------
-
------------------------------------------------------------------------------
--- Declare module and import dependencies
------------------------------------------------------------------------------
-local base = _G
-local table = require("table")
-local string = require("string")
-local math = require("math")
-local socket = require("socket")
-local url = require("socket.url")
-local tp = require("socket.tp")
-local ltn12 = require("ltn12")
-module("socket.ftp")
-
------------------------------------------------------------------------------
--- Program constants
------------------------------------------------------------------------------
--- timeout in seconds before the program gives up on a connection
-TIMEOUT = 60
--- default port for ftp service
-PORT = 21
--- this is the default anonymous password. used when no password is
--- provided in url. should be changed to your e-mail.
-USER = "ftp"
-PASSWORD = "anonymous@anonymous.org"
-
------------------------------------------------------------------------------
--- Low level FTP API
------------------------------------------------------------------------------
-local metat = { __index = {} }
-
-function open(server, port, create)
- local tp = socket.try(tp.connect(server, port or PORT, TIMEOUT, create))
- local f = base.setmetatable({ tp = tp }, metat)
- -- make sure everything gets closed in an exception
- f.try = socket.newtry(function() f:close() end)
- return f
-end
-
-function metat.__index:portconnect()
- self.try(self.server:settimeout(TIMEOUT))
- self.data = self.try(self.server:accept())
- self.try(self.data:settimeout(TIMEOUT))
-end
-
-function metat.__index:pasvconnect()
- self.data = self.try(socket.tcp())
- self.try(self.data:settimeout(TIMEOUT))
- self.try(self.data:connect(self.pasvt.ip, self.pasvt.port))
-end
-
-function metat.__index:login(user, password)
- self.try(self.tp:command("user", user or USER))
- local code, reply = self.try(self.tp:check{"2..", 331})
- if code == 331 then
- self.try(self.tp:command("pass", password or PASSWORD))
- self.try(self.tp:check("2.."))
- end
- return 1
-end
-
-function metat.__index:pasv()
- self.try(self.tp:command("pasv"))
- local code, reply = self.try(self.tp:check("2.."))
- local pattern = "(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)"
- local a, b, c, d, p1, p2 = socket.skip(2, string.find(reply, pattern))
- self.try(a and b and c and d and p1 and p2, reply)
- self.pasvt = {
- ip = string.format("%d.%d.%d.%d", a, b, c, d),
- port = p1*256 + p2
- }
- if self.server then
- self.server:close()
- self.server = nil
- end
- return self.pasvt.ip, self.pasvt.port
-end
-
-function metat.__index:port(ip, port)
- self.pasvt = nil
- if not ip then
- ip, port = self.try(self.tp:getcontrol():getsockname())
- self.server = self.try(socket.bind(ip, 0))
- ip, port = self.try(self.server:getsockname())
- self.try(self.server:settimeout(TIMEOUT))
- end
- local pl = math.mod(port, 256)
- local ph = (port - pl)/256
- local arg = string.gsub(string.format("%s,%d,%d", ip, ph, pl), "%.", ",")
- self.try(self.tp:command("port", arg))
- self.try(self.tp:check("2.."))
- return 1
-end
-
-function metat.__index:send(sendt)
- self.try(self.pasvt or self.server, "need port or pasv first")
- -- if there is a pasvt table, we already sent a PASV command
- -- we just get the data connection into self.data
- if self.pasvt then self:pasvconnect() end
- -- get the transfer argument and command
- local argument = sendt.argument or
- url.unescape(string.gsub(sendt.path or "", "^[/\\]", ""))
- if argument == "" then argument = nil end
- local command = sendt.command or "stor"
- -- send the transfer command and check the reply
- self.try(self.tp:command(command, argument))
- local code, reply = self.try(self.tp:check{"2..", "1.."})
- -- if there is not a a pasvt table, then there is a server
- -- and we already sent a PORT command
- if not self.pasvt then self:portconnect() end
- -- get the sink, source and step for the transfer
- local step = sendt.step or ltn12.pump.step
- local readt = {self.tp.c}
- local checkstep = function(src, snk)
- -- check status in control connection while downloading
- local readyt = socket.select(readt, nil, 0)
- if readyt[tp] then code = self.try(self.tp:check("2..")) end
- return step(src, snk)
- end
- local sink = socket.sink("close-when-done", self.data)
- -- transfer all data and check error
- self.try(ltn12.pump.all(sendt.source, sink, checkstep))
- if string.find(code, "1..") then self.try(self.tp:check("2..")) end
- -- done with data connection
- self.data:close()
- -- find out how many bytes were sent
- local sent = socket.skip(1, self.data:getstats())
- self.data = nil
- return sent
-end
-
-function metat.__index:receive(recvt)
- self.try(self.pasvt or self.server, "need port or pasv first")
- if self.pasvt then self:pasvconnect() end
- local argument = recvt.argument or
- url.unescape(string.gsub(recvt.path or "", "^[/\\]", ""))
- if argument == "" then argument = nil end
- local command = recvt.command or "retr"
- self.try(self.tp:command(command, argument))
- local code = self.try(self.tp:check{"1..", "2.."})
- if not self.pasvt then self:portconnect() end
- local source = socket.source("until-closed", self.data)
- local step = recvt.step or ltn12.pump.step
- self.try(ltn12.pump.all(source, recvt.sink, step))
- if string.find(code, "1..") then self.try(self.tp:check("2..")) end
- self.data:close()
- self.data = nil
- return 1
-end
-
-function metat.__index:cwd(dir)
- self.try(self.tp:command("cwd", dir))
- self.try(self.tp:check(250))
- return 1
-end
-
-function metat.__index:type(type)
- self.try(self.tp:command("type", type))
- self.try(self.tp:check(200))
- return 1
-end
-
-function metat.__index:greet()
- local code = self.try(self.tp:check{"1..", "2.."})
- if string.find(code, "1..") then self.try(self.tp:check("2..")) end
- return 1
-end
-
-function metat.__index:quit()
- self.try(self.tp:command("quit"))
- self.try(self.tp:check("2.."))
- return 1
-end
-
-function metat.__index:close()
- if self.data then self.data:close() end
- if self.server then self.server:close() end
- return self.tp:close()
-end
-
------------------------------------------------------------------------------
--- High level FTP API
------------------------------------------------------------------------------
-local function override(t)
- if t.url then
- local u = url.parse(t.url)
- for i,v in base.pairs(t) do
- u[i] = v
- end
- return u
- else return t end
-end
-
-local function tput(putt)
- putt = override(putt)
- socket.try(putt.host, "missing hostname")
- local f = open(putt.host, putt.port, putt.create)
- f:greet()
- f:login(putt.user, putt.password)
- if putt.type then f:type(putt.type) end
- f:pasv()
- local sent = f:send(putt)
- f:quit()
- f:close()
- return sent
-end
-
-local default = {
- path = "/",
- scheme = "ftp"
-}
-
-local function parse(u)
- local t = socket.try(url.parse(u, default))
- socket.try(t.scheme == "ftp", "wrong scheme '" .. t.scheme .. "'")
- socket.try(t.host, "missing hostname")
- local pat = "^type=(.)$"
- if t.params then
- t.type = socket.skip(2, string.find(t.params, pat))
- socket.try(t.type == "a" or t.type == "i",
- "invalid type '" .. t.type .. "'")
- end
- return t
-end
-
-local function sput(u, body)
- local putt = parse(u)
- putt.source = ltn12.source.string(body)
- return tput(putt)
-end
-
-put = socket.protect(function(putt, body)
- if base.type(putt) == "string" then return sput(putt, body)
- else return tput(putt) end
-end)
-
-local function tget(gett)
- gett = override(gett)
- socket.try(gett.host, "missing hostname")
- local f = open(gett.host, gett.port, gett.create)
- f:greet()
- f:login(gett.user, gett.password)
- if gett.type then f:type(gett.type) end
- f:pasv()
- f:receive(gett)
- f:quit()
- return f:close()
-end
-
-local function sget(u)
- local gett = parse(u)
- local t = {}
- gett.sink = ltn12.sink.table(t)
- tget(gett)
- return table.concat(t)
-end
-
-command = socket.protect(function(cmdt)
- cmdt = override(cmdt)
- socket.try(cmdt.host, "missing hostname")
- socket.try(cmdt.command, "missing command")
- local f = open(cmdt.host, cmdt.port, cmdt.create)
- f:greet()
- f:login(cmdt.user, cmdt.password)
- f.try(f.tp:command(cmdt.command, cmdt.argument))
- if cmdt.check then f.try(f.tp:check(cmdt.check)) end
- f:quit()
- return f:close()
-end)
-
-get = socket.protect(function(gett)
- if base.type(gett) == "string" then return sget(gett)
- else return tget(gett) end
-end)
-
diff --git a/lib/support/share/socket/http.lua b/lib/support/share/socket/http.lua
deleted file mode 100644
index ad8db1e..0000000
--- a/lib/support/share/socket/http.lua
+++ /dev/null
@@ -1,350 +0,0 @@
------------------------------------------------------------------------------
--- HTTP/1.1 client support for the Lua language.
--- LuaSocket toolkit.
--- Author: Diego Nehab
--- RCS ID: $Id: http.lua,v 1.71 2007/10/13 23:55:20 diego Exp $
------------------------------------------------------------------------------
-
------------------------------------------------------------------------------
--- Declare module and import dependencies
--------------------------------------------------------------------------------
-local socket = require("socket")
-local url = require("socket.url")
-local ltn12 = require("ltn12")
-local mime = require("mime")
-local string = require("string")
-local base = _G
-local table = require("table")
-module("socket.http")
-
------------------------------------------------------------------------------
--- Program constants
------------------------------------------------------------------------------
--- connection timeout in seconds
-TIMEOUT = 60
--- default port for document retrieval
-PORT = 80
--- user agent field sent in request
-USERAGENT = socket._VERSION
-
------------------------------------------------------------------------------
--- Reads MIME headers from a connection, unfolding where needed
------------------------------------------------------------------------------
-local function receiveheaders(sock, headers)
- local line, name, value, err
- headers = headers or {}
- -- get first line
- line, err = sock:receive()
- if err then return nil, err end
- -- headers go until a blank line is found
- while line ~= "" do
- -- get field-name and value
- name, value = socket.skip(2, string.find(line, "^(.-):%s*(.*)"))
- if not (name and value) then return nil, "malformed reponse headers" end
- name = string.lower(name)
- -- get next line (value might be folded)
- line, err = sock:receive()
- if err then return nil, err end
- -- unfold any folded values
- while string.find(line, "^%s") do
- value = value .. line
- line = sock:receive()
- if err then return nil, err end
- end
- -- save pair in table
- if headers[name] then headers[name] = headers[name] .. ", " .. value
- else headers[name] = value end
- end
- return headers
-end
-
------------------------------------------------------------------------------
--- Extra sources and sinks
------------------------------------------------------------------------------
-socket.sourcet["http-chunked"] = function(sock, headers)
- return base.setmetatable({
- getfd = function() return sock:getfd() end,
- dirty = function() return sock:dirty() end
- }, {
- __call = function()
- -- get chunk size, skip extention
- local line, err = sock:receive()
- if err then return nil, err end
- local size = base.tonumber(string.gsub(line, ";.*", ""), 16)
- if not size then return nil, "invalid chunk size" end
- -- was it the last chunk?
- if size > 0 then
- -- if not, get chunk and skip terminating CRLF
- local chunk, err, part = sock:receive(size)
- if chunk then sock:receive() end
- return chunk, err
- else
- -- if it was, read trailers into headers table
- headers, err = receiveheaders(sock, headers)
- if not headers then return nil, err end
- end
- end
- })
-end
-
-socket.sinkt["http-chunked"] = function(sock)
- return base.setmetatable({
- getfd = function() return sock:getfd() end,
- dirty = function() return sock:dirty() end
- }, {
- __call = function(self, chunk, err)
- if not chunk then return sock:send("0\r\n\r\n") end
- local size = string.format("%X\r\n", string.len(chunk))
- return sock:send(size .. chunk .. "\r\n")
- end
- })
-end
-
------------------------------------------------------------------------------
--- Low level HTTP API
------------------------------------------------------------------------------
-local metat = { __index = {} }
-
-function open(host, port, create)
- -- create socket with user connect function, or with default
- local c = socket.try((create or socket.tcp)())
- local h = base.setmetatable({ c = c }, metat)
- -- create finalized try
- h.try = socket.newtry(function() h:close() end)
- -- set timeout before connecting
- h.try(c:settimeout(TIMEOUT))
- h.try(c:connect(host, port or PORT))
- -- here everything worked
- return h
-end
-
-function metat.__index:sendrequestline(method, uri)
- local reqline = string.format("%s %s HTTP/1.1\r\n", method or "GET", uri)
- return self.try(self.c:send(reqline))
-end
-
-function metat.__index:sendheaders(headers)
- local h = "\r\n"
- for i, v in base.pairs(headers) do
- h = i .. ": " .. v .. "\r\n" .. h
- end
- self.try(self.c:send(h))
- return 1
-end
-
-function metat.__index:sendbody(headers, source, step)
- source = source or ltn12.source.empty()
- step = step or ltn12.pump.step
- -- if we don't know the size in advance, send chunked and hope for the best
- local mode = "http-chunked"
- if headers["content-length"] then mode = "keep-open" end
- return self.try(ltn12.pump.all(source, socket.sink(mode, self.c), step))
-end
-
-function metat.__index:receivestatusline()
- local status = self.try(self.c:receive(5))
- -- identify HTTP/0.9 responses, which do not contain a status line
- -- this is just a heuristic, but is what the RFC recommends
- if status ~= "HTTP/" then return nil, status end
- -- otherwise proceed reading a status line
- status = self.try(self.c:receive("*l", status))
- local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)"))
- return self.try(base.tonumber(code), status)
-end
-
-function metat.__index:receiveheaders()
- return self.try(receiveheaders(self.c))
-end
-
-function metat.__index:receivebody(headers, sink, step)
- sink = sink or ltn12.sink.null()
- step = step or ltn12.pump.step
- local length = base.tonumber(headers["content-length"])
- local t = headers["transfer-encoding"] -- shortcut
- local mode = "default" -- connection close
- if t and t ~= "identity" then mode = "http-chunked"
- elseif base.tonumber(headers["content-length"]) then mode = "by-length" end
- return self.try(ltn12.pump.all(socket.source(mode, self.c, length),
- sink, step))
-end
-
-function metat.__index:receive09body(status, sink, step)
- local source = ltn12.source.rewind(socket.source("until-closed", self.c))
- source(status)
- return self.try(ltn12.pump.all(source, sink, step))
-end
-
-function metat.__index:close()
- return self.c:close()
-end
-
------------------------------------------------------------------------------
--- High level HTTP API
------------------------------------------------------------------------------
-local function adjusturi(reqt)
- local u = reqt
- -- if there is a proxy, we need the full url. otherwise, just a part.
- if not reqt.proxy and not PROXY then
- u = {
- path = socket.try(reqt.path, "invalid path 'nil'"),
- params = reqt.params,
- query = reqt.query,
- fragment = reqt.fragment
- }
- end
- return url.build(u)
-end
-
-local function adjustproxy(reqt)
- local proxy = reqt.proxy or PROXY
- if proxy then
- proxy = url.parse(proxy)
- return proxy.host, proxy.port or 3128
- else
- return reqt.host, reqt.port
- end
-end
-
-local function adjustheaders(reqt)
- -- default headers
- local lower = {
- ["user-agent"] = USERAGENT,
- ["host"] = reqt.host,
- ["connection"] = "close, TE",
- ["te"] = "trailers"
- }
- -- if we have authentication information, pass it along
- if reqt.user and reqt.password then
- lower["authorization"] =
- "Basic " .. (mime.b64(reqt.user .. ":" .. reqt.password))
- end
- -- override with user headers
- for i,v in base.pairs(reqt.headers or lower) do
- lower[string.lower(i)] = v
- end
- return lower
-end
-
--- default url parts
-local default = {
- host = "",
- port = PORT,
- path ="/",
- scheme = "http"
-}
-
-local function adjustrequest(reqt)
- -- parse url if provided
- local nreqt = reqt.url and url.parse(reqt.url, default) or {}
- -- explicit components override url
- for i,v in base.pairs(reqt) do nreqt[i] = v end
- if nreqt.port == "" then nreqt.port = 80 end
- socket.try(nreqt.host and nreqt.host ~= "",
- "invalid host '" .. base.tostring(nreqt.host) .. "'")
- -- compute uri if user hasn't overriden
- nreqt.uri = reqt.uri or adjusturi(nreqt)
- -- ajust host and port if there is a proxy
- nreqt.host, nreqt.port = adjustproxy(nreqt)
- -- adjust headers in request
- nreqt.headers = adjustheaders(nreqt)
- return nreqt
-end
-
-local function shouldredirect(reqt, code, headers)
- return headers.location and
- string.gsub(headers.location, "%s", "") ~= "" and
- (reqt.redirect ~= false) and
- (code == 301 or code == 302) and
- (not reqt.method or reqt.method == "GET" or reqt.method == "HEAD")
- and (not reqt.nredirects or reqt.nredirects < 5)
-end
-
-local function shouldreceivebody(reqt, code)
- if reqt.method == "HEAD" then return nil end
- if code == 204 or code == 304 then return nil end
- if code >= 100 and code < 200 then return nil end
- return 1
-end
-
--- forward declarations
-local trequest, tredirect
-
-function tredirect(reqt, location)
- local result, code, headers, status = trequest {
- -- the RFC says the redirect URL has to be absolute, but some
- -- servers do not respect that
- url = url.absolute(reqt.url, location),
- source = reqt.source,
- sink = reqt.sink,
- headers = reqt.headers,
- proxy = reqt.proxy,
- nredirects = (reqt.nredirects or 0) + 1,
- create = reqt.create
- }
- -- pass location header back as a hint we redirected
- headers = headers or {}
- headers.location = headers.location or location
- return result, code, headers, status
-end
-
-function trequest(reqt)
- -- we loop until we get what we want, or
- -- until we are sure there is no way to get it
- local nreqt = adjustrequest(reqt)
- local h = open(nreqt.host, nreqt.port, nreqt.create)
- -- send request line and headers
- h:sendrequestline(nreqt.method, nreqt.uri)
- h:sendheaders(nreqt.headers)
- -- if there is a body, send it
- if nreqt.source then
- h:sendbody(nreqt.headers, nreqt.source, nreqt.step)
- end
- local code, status = h:receivestatusline()
- -- if it is an HTTP/0.9 server, simply get the body and we are done
- if not code then
- h:receive09body(status, nreqt.sink, nreqt.step)
- return 1, 200
- end
- local headers
- -- ignore any 100-continue messages
- while code == 100 do
- headers = h:receiveheaders()
- code, status = h:receivestatusline()
- end
- headers = h:receiveheaders()
- -- at this point we should have a honest reply from the server
- -- we can't redirect if we already used the source, so we report the error
- if shouldredirect(nreqt, code, headers) and not nreqt.source then
- h:close()
- return tredirect(reqt, headers.location)
- end
- -- here we are finally done
- if shouldreceivebody(nreqt, code) then
- h:receivebody(headers, nreqt.sink, nreqt.step)
- end
- h:close()
- return 1, code, headers, status
-end
-
-local function srequest(u, b)
- local t = {}
- local reqt = {
- url = u,
- sink = ltn12.sink.table(t)
- }
- if b then
- reqt.source = ltn12.source.string(b)
- reqt.headers = {
- ["content-length"] = string.len(b),
- ["content-type"] = "application/x-www-form-urlencoded"
- }
- reqt.method = "POST"
- end
- local code, headers, status = socket.skip(1, trequest(reqt))
- return table.concat(t), code, headers, status
-end
-
-request = socket.protect(function(reqt, body)
- if base.type(reqt) == "string" then return srequest(reqt, body)
- else return trequest(reqt) end
-end)
diff --git a/lib/support/share/socket/smtp.lua b/lib/support/share/socket/smtp.lua
deleted file mode 100644
index 8f3cfcf..0000000
--- a/lib/support/share/socket/smtp.lua
+++ /dev/null
@@ -1,251 +0,0 @@
------------------------------------------------------------------------------
--- SMTP client support for the Lua language.
--- LuaSocket toolkit.
--- Author: Diego Nehab
--- RCS ID: $Id: smtp.lua,v 1.46 2007/03/12 04:08:40 diego Exp $
------------------------------------------------------------------------------
-
------------------------------------------------------------------------------
--- Declare module and import dependencies
------------------------------------------------------------------------------
-local base = _G
-local coroutine = require("coroutine")
-local string = require("string")
-local math = require("math")
-local os = require("os")
-local socket = require("socket")
-local tp = require("socket.tp")
-local ltn12 = require("ltn12")
-local mime = require("mime")
-module("socket.smtp")
-
------------------------------------------------------------------------------
--- Program constants
------------------------------------------------------------------------------
--- timeout for connection
-TIMEOUT = 60
--- default server used to send e-mails
-SERVER = "localhost"
--- default port
-PORT = 25
--- domain used in HELO command and default sendmail
--- If we are under a CGI, try to get from environment
-DOMAIN = os.getenv("SERVER_NAME") or "localhost"
--- default time zone (means we don't know)
-ZONE = "-0000"
-
----------------------------------------------------------------------------
--- Low level SMTP API
------------------------------------------------------------------------------
-local metat = { __index = {} }
-
-function metat.__index:greet(domain)
- self.try(self.tp:check("2.."))
- self.try(self.tp:command("EHLO", domain or DOMAIN))
- return socket.skip(1, self.try(self.tp:check("2..")))
-end
-
-function metat.__index:mail(from)
- self.try(self.tp:command("MAIL", "FROM:" .. from))
- return self.try(self.tp:check("2.."))
-end
-
-function metat.__index:rcpt(to)
- self.try(self.tp:command("RCPT", "TO:" .. to))
- return self.try(self.tp:check("2.."))
-end
-
-function metat.__index:data(src, step)
- self.try(self.tp:command("DATA"))
- self.try(self.tp:check("3.."))
- self.try(self.tp:source(src, step))
- self.try(self.tp:send("\r\n.\r\n"))
- return self.try(self.tp:check("2.."))
-end
-
-function metat.__index:quit()
- self.try(self.tp:command("QUIT"))
- return self.try(self.tp:check("2.."))
-end
-
-function metat.__index:close()
- return self.tp:close()
-end
-
-function metat.__index:login(user, password)
- self.try(self.tp:command("AUTH", "LOGIN"))
- self.try(self.tp:check("3.."))
- self.try(self.tp:command(mime.b64(user)))
- self.try(self.tp:check("3.."))
- self.try(self.tp:command(mime.b64(password)))
- return self.try(self.tp:check("2.."))
-end
-
-function metat.__index:plain(user, password)
- local auth = "PLAIN " .. mime.b64("\0" .. user .. "\0" .. password)
- self.try(self.tp:command("AUTH", auth))
- return self.try(self.tp:check("2.."))
-end
-
-function metat.__index:auth(user, password, ext)
- if not user or not password then return 1 end
- if string.find(ext, "AUTH[^\n]+LOGIN") then
- return self:login(user, password)
- elseif string.find(ext, "AUTH[^\n]+PLAIN") then
- return self:plain(user, password)
- else
- self.try(nil, "authentication not supported")
- end
-end
-
--- send message or throw an exception
-function metat.__index:send(mailt)
- self:mail(mailt.from)
- if base.type(mailt.rcpt) == "table" then
- for i,v in base.ipairs(mailt.rcpt) do
- self:rcpt(v)
- end
- else
- self:rcpt(mailt.rcpt)
- end
- self:data(ltn12.source.chain(mailt.source, mime.stuff()), mailt.step)
-end
-
-function open(server, port, create)
- local tp = socket.try(tp.connect(server or SERVER, port or PORT,
- TIMEOUT, create))
- local s = base.setmetatable({tp = tp}, metat)
- -- make sure tp is closed if we get an exception
- s.try = socket.newtry(function()
- s:close()
- end)
- return s
-end
-
--- convert headers to lowercase
-local function lower_headers(headers)
- local lower = {}
- for i,v in base.pairs(headers or lower) do
- lower[string.lower(i)] = v
- end
- return lower
-end
-
----------------------------------------------------------------------------
--- Multipart message source
------------------------------------------------------------------------------
--- returns a hopefully unique mime boundary
-local seqno = 0
-local function newboundary()
- seqno = seqno + 1
- return string.format('%s%05d==%05u', os.date('%d%m%Y%H%M%S'),
- math.random(0, 99999), seqno)
-end
-
--- send_message forward declaration
-local send_message
-
--- yield the headers all at once, it's faster
-local function send_headers(headers)
- local h = "\r\n"
- for i,v in base.pairs(headers) do
- h = i .. ': ' .. v .. "\r\n" .. h
- end
- coroutine.yield(h)
-end
-
--- yield multipart message body from a multipart message table
-local function send_multipart(mesgt)
- -- make sure we have our boundary and send headers
- local bd = newboundary()
- local headers = lower_headers(mesgt.headers or {})
- headers['content-type'] = headers['content-type'] or 'multipart/mixed'
- headers['content-type'] = headers['content-type'] ..
- '; boundary="' .. bd .. '"'
- send_headers(headers)
- -- send preamble
- if mesgt.body.preamble then
- coroutine.yield(mesgt.body.preamble)
- coroutine.yield("\r\n")
- end
- -- send each part separated by a boundary
- for i, m in base.ipairs(mesgt.body) do
- coroutine.yield("\r\n--" .. bd .. "\r\n")
- send_message(m)
- end
- -- send last boundary
- coroutine.yield("\r\n--" .. bd .. "--\r\n\r\n")
- -- send epilogue
- if mesgt.body.epilogue then
- coroutine.yield(mesgt.body.epilogue)
- coroutine.yield("\r\n")
- end
-end
-
--- yield message body from a source
-local function send_source(mesgt)
- -- make sure we have a content-type
- local headers = lower_headers(mesgt.headers or {})
- headers['content-type'] = headers['content-type'] or
- 'text/plain; charset="iso-8859-1"'
- send_headers(headers)
- -- send body from source
- while true do
- local chunk, err = mesgt.body()
- if err then coroutine.yield(nil, err)
- elseif chunk then coroutine.yield(chunk)
- else break end
- end
-end
-
--- yield message body from a string
-local function send_string(mesgt)
- -- make sure we have a content-type
- local headers = lower_headers(mesgt.headers or {})
- headers['content-type'] = headers['content-type'] or
- 'text/plain; charset="iso-8859-1"'
- send_headers(headers)
- -- send body from string
- coroutine.yield(mesgt.body)
-end
-
--- message source
-function send_message(mesgt)
- if base.type(mesgt.body) == "table" then send_multipart(mesgt)
- elseif base.type(mesgt.body) == "function" then send_source(mesgt)
- else send_string(mesgt) end
-end
-
--- set defaul headers
-local function adjust_headers(mesgt)
- local lower = lower_headers(mesgt.headers)
- lower["date"] = lower["date"] or
- os.date("!%a, %d %b %Y %H:%M:%S ") .. (mesgt.zone or ZONE)
- lower["x-mailer"] = lower["x-mailer"] or socket._VERSION
- -- this can't be overriden
- lower["mime-version"] = "1.0"
- return lower
-end
-
-function message(mesgt)
- mesgt.headers = adjust_headers(mesgt)
- -- create and return message source
- local co = coroutine.create(function() send_message(mesgt) end)
- return function()
- local ret, a, b = coroutine.resume(co)
- if ret then return a, b
- else return nil, a end
- end
-end
-
----------------------------------------------------------------------------
--- High level SMTP API
------------------------------------------------------------------------------
-send = socket.protect(function(mailt)
- local s = open(mailt.server, mailt.port, mailt.create)
- local ext = s:greet(mailt.domain)
- s:auth(mailt.user, mailt.password, ext)
- s:send(mailt)
- s:quit()
- return s:close()
-end)
diff --git a/lib/support/share/socket/tp.lua b/lib/support/share/socket/tp.lua
deleted file mode 100644
index 0683869..0000000
--- a/lib/support/share/socket/tp.lua
+++ /dev/null
@@ -1,123 +0,0 @@
------------------------------------------------------------------------------
--- Unified SMTP/FTP subsystem
--- LuaSocket toolkit.
--- Author: Diego Nehab
--- RCS ID: $Id: tp.lua,v 1.22 2006/03/14 09:04:15 diego Exp $
------------------------------------------------------------------------------
-
------------------------------------------------------------------------------
--- Declare module and import dependencies
------------------------------------------------------------------------------
-local base = _G
-local string = require("string")
-local socket = require("socket")
-local ltn12 = require("ltn12")
-module("socket.tp")
-
------------------------------------------------------------------------------
--- Program constants
------------------------------------------------------------------------------
-TIMEOUT = 60
-
------------------------------------------------------------------------------
--- Implementation
------------------------------------------------------------------------------
--- gets server reply (works for SMTP and FTP)
-local function get_reply(c)
- local code, current, sep
- local line, err = c:receive()
- local reply = line
- if err then return nil, err end
- code, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)"))
- if not code then return nil, "invalid server reply" end
- if sep == "-" then -- reply is multiline
- repeat
- line, err = c:receive()
- if err then return nil, err end
- current, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)"))
- reply = reply .. "\n" .. line
- -- reply ends with same code
- until code == current and sep == " "
- end
- return code, reply
-end
-
--- metatable for sock object
-local metat = { __index = {} }
-
-function metat.__index:check(ok)
- local code, reply = get_reply(self.c)
- if not code then return nil, reply end
- if base.type(ok) ~= "function" then
- if base.type(ok) == "table" then
- for i, v in base.ipairs(ok) do
- if string.find(code, v) then
- return base.tonumber(code), reply
- end
- end
- return nil, reply
- else
- if string.find(code, ok) then return base.tonumber(code), reply
- else return nil, reply end
- end
- else return ok(base.tonumber(code), reply) end
-end
-
-function metat.__index:command(cmd, arg)
- if arg then
- return self.c:send(cmd .. " " .. arg.. "\r\n")
- else
- return self.c:send(cmd .. "\r\n")
- end
-end
-
-function metat.__index:sink(snk, pat)
- local chunk, err = c:receive(pat)
- return snk(chunk, err)
-end
-
-function metat.__index:send(data)
- return self.c:send(data)
-end
-
-function metat.__index:receive(pat)
- return self.c:receive(pat)
-end
-
-function metat.__index:getfd()
- return self.c:getfd()
-end
-
-function metat.__index:dirty()
- return self.c:dirty()
-end
-
-function metat.__index:getcontrol()
- return self.c
-end
-
-function metat.__index:source(source, step)
- local sink = socket.sink("keep-open", self.c)
- local ret, err = ltn12.pump.all(source, sink, step or ltn12.pump.step)
- return ret, err
-end
-
--- closes the underlying c
-function metat.__index:close()
- self.c:close()
- return 1
-end
-
--- connect with server and return c object
-function connect(host, port, timeout, create)
- local c, e = (create or socket.tcp)()
- if not c then return nil, e end
- c:settimeout(timeout or TIMEOUT)
- local r, e = c:connect(host, port)
- if not r then
- c:close()
- return nil, e
- end
- return base.setmetatable({c = c}, metat)
-end
-
diff --git a/lib/support/share/socket/url.lua b/lib/support/share/socket/url.lua
deleted file mode 100644
index 0e31d8a..0000000
--- a/lib/support/share/socket/url.lua
+++ /dev/null
@@ -1,297 +0,0 @@
------------------------------------------------------------------------------
--- URI parsing, composition and relative URL resolution
--- LuaSocket toolkit.
--- Author: Diego Nehab
--- RCS ID: $Id: url.lua,v 1.38 2006/04/03 04:45:42 diego Exp $
------------------------------------------------------------------------------
-
------------------------------------------------------------------------------
--- Declare module
------------------------------------------------------------------------------
-local string = require("string")
-local base = _G
-local table = require("table")
-module("socket.url")
-
------------------------------------------------------------------------------
--- Module version
------------------------------------------------------------------------------
-_VERSION = "URL 1.0.1"
-
------------------------------------------------------------------------------
--- Encodes a string into its escaped hexadecimal representation
--- Input
--- s: binary string to be encoded
--- Returns
--- escaped representation of string binary
------------------------------------------------------------------------------
-function escape(s)
- return string.gsub(s, "([^A-Za-z0-9_])", function(c)
- return string.format("%%%02x", string.byte(c))
- end)
-end
-
------------------------------------------------------------------------------
--- Protects a path segment, to prevent it from interfering with the
--- url parsing.
--- Input
--- s: binary string to be encoded
--- Returns
--- escaped representation of string binary
------------------------------------------------------------------------------
-local function make_set(t)
- local s = {}
- for i,v in base.ipairs(t) do
- s[t[i]] = 1
- end
- return s
-end
-
--- these are allowed withing a path segment, along with alphanum
--- other characters must be escaped
-local segment_set = make_set {
- "-", "_", ".", "!", "~", "*", "'", "(",
- ")", ":", "@", "&", "=", "+", "$", ",",
-}
-
-local function protect_segment(s)
- return string.gsub(s, "([^A-Za-z0-9_])", function (c)
- if segment_set[c] then return c
- else return string.format("%%%02x", string.byte(c)) end
- end)
-end
-
------------------------------------------------------------------------------
--- Encodes a string into its escaped hexadecimal representation
--- Input
--- s: binary string to be encoded
--- Returns
--- escaped representation of string binary
------------------------------------------------------------------------------
-function unescape(s)
- return string.gsub(s, "%%(%x%x)", function(hex)
- return string.char(base.tonumber(hex, 16))
- end)
-end
-
------------------------------------------------------------------------------
--- Builds a path from a base path and a relative path
--- Input
--- base_path
--- relative_path
--- Returns
--- corresponding absolute path
------------------------------------------------------------------------------
-local function absolute_path(base_path, relative_path)
- if string.sub(relative_path, 1, 1) == "/" then return relative_path end
- local path = string.gsub(base_path, "[^/]*$", "")
- path = path .. relative_path
- path = string.gsub(path, "([^/]*%./)", function (s)
- if s ~= "./" then return s else return "" end
- end)
- path = string.gsub(path, "/%.$", "/")
- local reduced
- while reduced ~= path do
- reduced = path
- path = string.gsub(reduced, "([^/]*/%.%./)", function (s)
- if s ~= "../../" then return "" else return s end
- end)
- end
- path = string.gsub(reduced, "([^/]*/%.%.)$", function (s)
- if s ~= "../.." then return "" else return s end
- end)
- return path
-end
-
------------------------------------------------------------------------------
--- Parses a url and returns a table with all its parts according to RFC 2396
--- The following grammar describes the names given to the URL parts
--- <url> ::= <scheme>://<authority>/<path>;<params>?<query>#<fragment>
--- <authority> ::= <userinfo>@<host>:<port>
--- <userinfo> ::= <user>[:<password>]
--- <path> :: = {<segment>/}<segment>
--- Input
--- url: uniform resource locator of request
--- default: table with default values for each field
--- Returns
--- table with the following fields, where RFC naming conventions have
--- been preserved:
--- scheme, authority, userinfo, user, password, host, port,
--- path, params, query, fragment
--- Obs:
--- the leading '/' in {/<path>} is considered part of <path>
------------------------------------------------------------------------------
-function parse(url, default)
- -- initialize default parameters
- local parsed = {}
- for i,v in base.pairs(default or parsed) do parsed[i] = v end
- -- empty url is parsed to nil
- if not url or url == "" then return nil, "invalid url" end
- -- remove whitespace
- -- url = string.gsub(url, "%s", "")
- -- get fragment
- url = string.gsub(url, "#(.*)$", function(f)
- parsed.fragment = f
- return ""
- end)
- -- get scheme
- url = string.gsub(url, "^([%w][%w%+%-%.]*)%:",
- function(s) parsed.scheme = s; return "" end)
- -- get authority
- url = string.gsub(url, "^//([^/]*)", function(n)
- parsed.authority = n
- return ""
- end)
- -- get query stringing
- url = string.gsub(url, "%?(.*)", function(q)
- parsed.query = q
- return ""
- end)
- -- get params
- url = string.gsub(url, "%;(.*)", function(p)
- parsed.params = p
- return ""
- end)
- -- path is whatever was left
- if url ~= "" then parsed.path = url end
- local authority = parsed.authority
- if not authority then return parsed end
- authority = string.gsub(authority,"^([^@]*)@",
- function(u) parsed.userinfo = u; return "" end)
- authority = string.gsub(authority, ":([^:]*)$",
- function(p) parsed.port = p; return "" end)
- if authority ~= "" then parsed.host = authority end
- local userinfo = parsed.userinfo
- if not userinfo then return parsed end
- userinfo = string.gsub(userinfo, ":([^:]*)$",
- function(p) parsed.password = p; return "" end)
- parsed.user = userinfo
- return parsed
-end
-
------------------------------------------------------------------------------
--- Rebuilds a parsed URL from its components.
--- Components are protected if any reserved or unallowed characters are found
--- Input
--- parsed: parsed URL, as returned by parse
--- Returns
--- a stringing with the corresponding URL
------------------------------------------------------------------------------
-function build(parsed)
- local ppath = parse_path(parsed.path or "")
- local url = build_path(ppath)
- if parsed.params then url = url .. ";" .. parsed.params end
- if parsed.query then url = url .. "?" .. parsed.query end
- local authority = parsed.authority
- if parsed.host then
- authority = parsed.host
- if parsed.port then authority = authority .. ":" .. parsed.port end
- local userinfo = parsed.userinfo
- if parsed.user then
- userinfo = parsed.user
- if parsed.password then
- userinfo = userinfo .. ":" .. parsed.password
- end
- end
- if userinfo then authority = userinfo .. "@" .. authority end
- end
- if authority then url = "//" .. authority .. url end
- if parsed.scheme then url = parsed.scheme .. ":" .. url end
- if parsed.fragment then url = url .. "#" .. parsed.fragment end
- -- url = string.gsub(url, "%s", "")
- return url
-end
-
------------------------------------------------------------------------------
--- Builds a absolute URL from a base and a relative URL according to RFC 2396
--- Input
--- base_url
--- relative_url
--- Returns
--- corresponding absolute url
------------------------------------------------------------------------------
-function absolute(base_url, relative_url)
- if base.type(base_url) == "table" then
- base_parsed = base_url
- base_url = build(base_parsed)
- else
- base_parsed = parse(base_url)
- end
- local relative_parsed = parse(relative_url)
- if not base_parsed then return relative_url
- elseif not relative_parsed then return base_url
- elseif relative_parsed.scheme then return relative_url
- else
- relative_parsed.scheme = base_parsed.scheme
- if not relative_parsed.authority then
- relative_parsed.authority = base_parsed.authority
- if not relative_parsed.path then
- relative_parsed.path = base_parsed.path
- if not relative_parsed.params then
- relative_parsed.params = base_parsed.params
- if not relative_parsed.query then
- relative_parsed.query = base_parsed.query
- end
- end
- else
- relative_parsed.path = absolute_path(base_parsed.path or "",
- relative_parsed.path)
- end
- end
- return build(relative_parsed)
- end
-end
-
------------------------------------------------------------------------------
--- Breaks a path into its segments, unescaping the segments
--- Input
--- path
--- Returns
--- segment: a table with one entry per segment
------------------------------------------------------------------------------
-function parse_path(path)
- local parsed = {}
- path = path or ""
- --path = string.gsub(path, "%s", "")
- string.gsub(path, "([^/]+)", function (s) table.insert(parsed, s) end)
- for i = 1, table.getn(parsed) do
- parsed[i] = unescape(parsed[i])
- end
- if string.sub(path, 1, 1) == "/" then parsed.is_absolute = 1 end
- if string.sub(path, -1, -1) == "/" then parsed.is_directory = 1 end
- return parsed
-end
-
------------------------------------------------------------------------------
--- Builds a path component from its segments, escaping protected characters.
--- Input
--- parsed: path segments
--- unsafe: if true, segments are not protected before path is built
--- Returns
--- path: corresponding path stringing
------------------------------------------------------------------------------
-function build_path(parsed, unsafe)
- local path = ""
- local n = table.getn(parsed)
- if unsafe then
- for i = 1, n-1 do
- path = path .. parsed[i]
- path = path .. "/"
- end
- if n > 0 then
- path = path .. parsed[n]
- if parsed.is_directory then path = path .. "/" end
- end
- else
- for i = 1, n-1 do
- path = path .. protect_segment(parsed[i])
- path = path .. "/"
- end
- if n > 0 then
- path = path .. protect_segment(parsed[n])
- if parsed.is_directory then path = path .. "/" end
- end
- end
- if parsed.is_absolute then path = "/" .. path end
- return path
-end