mirror of
https://github.com/Ysurac/openmptcprouter-feeds.git
synced 2025-03-09 15:40:03 +00:00
Add macvlan support
This commit is contained in:
parent
40fdd921b9
commit
9f8643647e
255 changed files with 134998 additions and 0 deletions
649
luci-base/luasrc/http/protocol.lua
Normal file
649
luci-base/luasrc/http/protocol.lua
Normal file
|
@ -0,0 +1,649 @@
|
|||
-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
|
||||
-- Licensed to the public under the Apache License 2.0.
|
||||
|
||||
-- This class contains several functions useful for http message- and content
|
||||
-- decoding and to retrive form data from raw http messages.
|
||||
module("luci.http.protocol", package.seeall)
|
||||
|
||||
local ltn12 = require("luci.ltn12")
|
||||
|
||||
HTTP_MAX_CONTENT = 1024*8 -- 8 kB maximum content size
|
||||
|
||||
-- the "+" sign to " " - and return the decoded string.
|
||||
function urldecode( str, no_plus )
|
||||
|
||||
local function __chrdec( hex )
|
||||
return string.char( tonumber( hex, 16 ) )
|
||||
end
|
||||
|
||||
if type(str) == "string" then
|
||||
if not no_plus then
|
||||
str = str:gsub( "+", " " )
|
||||
end
|
||||
|
||||
str = str:gsub( "%%([a-fA-F0-9][a-fA-F0-9])", __chrdec )
|
||||
end
|
||||
|
||||
return str
|
||||
end
|
||||
|
||||
-- from given url or string. Returns a table with urldecoded values.
|
||||
-- Simple parameters are stored as string values associated with the parameter
|
||||
-- name within the table. Parameters with multiple values are stored as array
|
||||
-- containing the corresponding values.
|
||||
function urldecode_params( url, tbl )
|
||||
|
||||
local params = tbl or { }
|
||||
|
||||
if url:find("?") then
|
||||
url = url:gsub( "^.+%?([^?]+)", "%1" )
|
||||
end
|
||||
|
||||
for pair in url:gmatch( "[^&;]+" ) do
|
||||
|
||||
-- find key and value
|
||||
local key = urldecode( pair:match("^([^=]+)") )
|
||||
local val = urldecode( pair:match("^[^=]+=(.+)$") )
|
||||
|
||||
-- store
|
||||
if type(key) == "string" and key:len() > 0 then
|
||||
if type(val) ~= "string" then val = "" end
|
||||
|
||||
if not params[key] then
|
||||
params[key] = val
|
||||
elseif type(params[key]) ~= "table" then
|
||||
params[key] = { params[key], val }
|
||||
else
|
||||
table.insert( params[key], val )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return params
|
||||
end
|
||||
|
||||
function urlencode( str )
|
||||
|
||||
local function __chrenc( chr )
|
||||
return string.format(
|
||||
"%%%02x", string.byte( chr )
|
||||
)
|
||||
end
|
||||
|
||||
if type(str) == "string" then
|
||||
str = str:gsub(
|
||||
"([^a-zA-Z0-9$_%-%.%~])",
|
||||
__chrenc
|
||||
)
|
||||
end
|
||||
|
||||
return str
|
||||
end
|
||||
|
||||
-- separated by "&". Tables are encoded as parameters with multiple values by
|
||||
-- repeating the parameter name with each value.
|
||||
function urlencode_params( tbl )
|
||||
local enc = ""
|
||||
|
||||
for k, v in pairs(tbl) do
|
||||
if type(v) == "table" then
|
||||
for i, v2 in ipairs(v) do
|
||||
enc = enc .. ( #enc > 0 and "&" or "" ) ..
|
||||
urlencode(k) .. "=" .. urlencode(v2)
|
||||
end
|
||||
else
|
||||
enc = enc .. ( #enc > 0 and "&" or "" ) ..
|
||||
urlencode(k) .. "=" .. urlencode(v)
|
||||
end
|
||||
end
|
||||
|
||||
return enc
|
||||
end
|
||||
|
||||
-- (Internal function)
|
||||
-- Initialize given parameter and coerce string into table when the parameter
|
||||
-- already exists.
|
||||
local function __initval( tbl, key )
|
||||
if tbl[key] == nil then
|
||||
tbl[key] = ""
|
||||
elseif type(tbl[key]) == "string" then
|
||||
tbl[key] = { tbl[key], "" }
|
||||
else
|
||||
table.insert( tbl[key], "" )
|
||||
end
|
||||
end
|
||||
|
||||
-- (Internal function)
|
||||
-- Initialize given file parameter.
|
||||
local function __initfileval( tbl, key, filename, fd )
|
||||
if tbl[key] == nil then
|
||||
tbl[key] = { file=filename, fd=fd, name=key, "" }
|
||||
else
|
||||
table.insert( tbl[key], "" )
|
||||
end
|
||||
end
|
||||
|
||||
-- (Internal function)
|
||||
-- Append given data to given parameter, either by extending the string value
|
||||
-- or by appending it to the last string in the parameter's value table.
|
||||
local function __appendval( tbl, key, chunk )
|
||||
if type(tbl[key]) == "table" then
|
||||
tbl[key][#tbl[key]] = tbl[key][#tbl[key]] .. chunk
|
||||
else
|
||||
tbl[key] = tbl[key] .. chunk
|
||||
end
|
||||
end
|
||||
|
||||
-- (Internal function)
|
||||
-- Finish the value of given parameter, either by transforming the string value
|
||||
-- or - in the case of multi value parameters - the last element in the
|
||||
-- associated values table.
|
||||
local function __finishval( tbl, key, handler )
|
||||
if handler then
|
||||
if type(tbl[key]) == "table" then
|
||||
tbl[key][#tbl[key]] = handler( tbl[key][#tbl[key]] )
|
||||
else
|
||||
tbl[key] = handler( tbl[key] )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Table of our process states
|
||||
local process_states = { }
|
||||
|
||||
-- Extract "magic", the first line of a http message.
|
||||
-- Extracts the message type ("get", "post" or "response"), the requested uri
|
||||
-- or the status code if the line descripes a http response.
|
||||
process_states['magic'] = function( msg, chunk, err )
|
||||
|
||||
if chunk ~= nil then
|
||||
-- ignore empty lines before request
|
||||
if #chunk == 0 then
|
||||
return true, nil
|
||||
end
|
||||
|
||||
-- Is it a request?
|
||||
local method, uri, http_ver = chunk:match("^([A-Z]+) ([^ ]+) HTTP/([01]%.[019])$")
|
||||
|
||||
-- Yup, it is
|
||||
if method then
|
||||
|
||||
msg.type = "request"
|
||||
msg.request_method = method:lower()
|
||||
msg.request_uri = uri
|
||||
msg.http_version = tonumber( http_ver )
|
||||
msg.headers = { }
|
||||
|
||||
-- We're done, next state is header parsing
|
||||
return true, function( chunk )
|
||||
return process_states['headers']( msg, chunk )
|
||||
end
|
||||
|
||||
-- Is it a response?
|
||||
else
|
||||
|
||||
local http_ver, code, message = chunk:match("^HTTP/([01]%.[019]) ([0-9]+) ([^\r\n]+)$")
|
||||
|
||||
-- Is a response
|
||||
if code then
|
||||
|
||||
msg.type = "response"
|
||||
msg.status_code = code
|
||||
msg.status_message = message
|
||||
msg.http_version = tonumber( http_ver )
|
||||
msg.headers = { }
|
||||
|
||||
-- We're done, next state is header parsing
|
||||
return true, function( chunk )
|
||||
return process_states['headers']( msg, chunk )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Can't handle it
|
||||
return nil, "Invalid HTTP message magic"
|
||||
end
|
||||
|
||||
|
||||
-- Extract headers from given string.
|
||||
process_states['headers'] = function( msg, chunk )
|
||||
|
||||
if chunk ~= nil then
|
||||
|
||||
-- Look for a valid header format
|
||||
local hdr, val = chunk:match( "^([A-Za-z][A-Za-z0-9%-_]+): +(.+)$" )
|
||||
|
||||
if type(hdr) == "string" and hdr:len() > 0 and
|
||||
type(val) == "string" and val:len() > 0
|
||||
then
|
||||
msg.headers[hdr] = val
|
||||
|
||||
-- Valid header line, proceed
|
||||
return true, nil
|
||||
|
||||
elseif #chunk == 0 then
|
||||
-- Empty line, we won't accept data anymore
|
||||
return false, nil
|
||||
else
|
||||
-- Junk data
|
||||
return nil, "Invalid HTTP header received"
|
||||
end
|
||||
else
|
||||
return nil, "Unexpected EOF"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- data line by line with the trailing \r\n stripped of.
|
||||
function header_source( sock )
|
||||
return ltn12.source.simplify( function()
|
||||
|
||||
local chunk, err, part = sock:receive("*l")
|
||||
|
||||
-- Line too long
|
||||
if chunk == nil then
|
||||
if err ~= "timeout" then
|
||||
return nil, part
|
||||
and "Line exceeds maximum allowed length"
|
||||
or "Unexpected EOF"
|
||||
else
|
||||
return nil, err
|
||||
end
|
||||
|
||||
-- Line ok
|
||||
elseif chunk ~= nil then
|
||||
|
||||
-- Strip trailing CR
|
||||
chunk = chunk:gsub("\r$","")
|
||||
|
||||
return chunk, nil
|
||||
end
|
||||
end )
|
||||
end
|
||||
|
||||
-- Content-Type. Stores all extracted data associated with its parameter name
|
||||
-- in the params table withing the given message object. Multiple parameter
|
||||
-- values are stored as tables, ordinary ones as strings.
|
||||
-- If an optional file callback function is given then it is feeded with the
|
||||
-- file contents chunk by chunk and only the extracted file name is stored
|
||||
-- within the params table. The callback function will be called subsequently
|
||||
-- with three arguments:
|
||||
-- o Table containing decoded (name, file) and raw (headers) mime header data
|
||||
-- o String value containing a chunk of the file data
|
||||
-- o Boolean which indicates wheather the current chunk is the last one (eof)
|
||||
function mimedecode_message_body( src, msg, filecb )
|
||||
|
||||
if msg and msg.env.CONTENT_TYPE then
|
||||
msg.mime_boundary = msg.env.CONTENT_TYPE:match("^multipart/form%-data; boundary=(.+)$")
|
||||
end
|
||||
|
||||
if not msg.mime_boundary then
|
||||
return nil, "Invalid Content-Type found"
|
||||
end
|
||||
|
||||
|
||||
local tlen = 0
|
||||
local inhdr = false
|
||||
local field = nil
|
||||
local store = nil
|
||||
local lchunk = nil
|
||||
|
||||
local function parse_headers( chunk, field )
|
||||
|
||||
local stat
|
||||
repeat
|
||||
chunk, stat = chunk:gsub(
|
||||
"^([A-Z][A-Za-z0-9%-_]+): +([^\r\n]+)\r\n",
|
||||
function(k,v)
|
||||
field.headers[k] = v
|
||||
return ""
|
||||
end
|
||||
)
|
||||
until stat == 0
|
||||
|
||||
chunk, stat = chunk:gsub("^\r\n","")
|
||||
|
||||
-- End of headers
|
||||
if stat > 0 then
|
||||
if field.headers["Content-Disposition"] then
|
||||
if field.headers["Content-Disposition"]:match("^form%-data; ") then
|
||||
field.name = field.headers["Content-Disposition"]:match('name="(.-)"')
|
||||
field.file = field.headers["Content-Disposition"]:match('filename="(.+)"$')
|
||||
end
|
||||
end
|
||||
|
||||
if not field.headers["Content-Type"] then
|
||||
field.headers["Content-Type"] = "text/plain"
|
||||
end
|
||||
|
||||
if field.name and field.file and filecb then
|
||||
__initval( msg.params, field.name )
|
||||
__appendval( msg.params, field.name, field.file )
|
||||
|
||||
store = filecb
|
||||
elseif field.name and field.file then
|
||||
local nxf = require "nixio"
|
||||
local fd = nxf.mkstemp(field.name)
|
||||
__initfileval ( msg.params, field.name, field.file, fd )
|
||||
if fd then
|
||||
store = function(hdr, buf, eof)
|
||||
fd:write(buf)
|
||||
if (eof) then
|
||||
fd:seek(0, "set")
|
||||
end
|
||||
end
|
||||
else
|
||||
store = function( hdr, buf, eof )
|
||||
__appendval( msg.params, field.name, buf )
|
||||
end
|
||||
end
|
||||
elseif field.name then
|
||||
__initval( msg.params, field.name )
|
||||
|
||||
store = function( hdr, buf, eof )
|
||||
__appendval( msg.params, field.name, buf )
|
||||
end
|
||||
else
|
||||
store = nil
|
||||
end
|
||||
|
||||
return chunk, true
|
||||
end
|
||||
|
||||
return chunk, false
|
||||
end
|
||||
|
||||
local function snk( chunk )
|
||||
|
||||
tlen = tlen + ( chunk and #chunk or 0 )
|
||||
|
||||
if msg.env.CONTENT_LENGTH and tlen > tonumber(msg.env.CONTENT_LENGTH) + 2 then
|
||||
return nil, "Message body size exceeds Content-Length"
|
||||
end
|
||||
|
||||
if chunk and not lchunk then
|
||||
lchunk = "\r\n" .. chunk
|
||||
|
||||
elseif lchunk then
|
||||
local data = lchunk .. ( chunk or "" )
|
||||
local spos, epos, found
|
||||
|
||||
repeat
|
||||
spos, epos = data:find( "\r\n--" .. msg.mime_boundary .. "\r\n", 1, true )
|
||||
|
||||
if not spos then
|
||||
spos, epos = data:find( "\r\n--" .. msg.mime_boundary .. "--\r\n", 1, true )
|
||||
end
|
||||
|
||||
|
||||
if spos then
|
||||
local predata = data:sub( 1, spos - 1 )
|
||||
|
||||
if inhdr then
|
||||
predata, eof = parse_headers( predata, field )
|
||||
|
||||
if not eof then
|
||||
return nil, "Invalid MIME section header"
|
||||
elseif not field.name then
|
||||
return nil, "Invalid Content-Disposition header"
|
||||
end
|
||||
end
|
||||
|
||||
if store then
|
||||
store( field, predata, true )
|
||||
end
|
||||
|
||||
|
||||
field = { headers = { } }
|
||||
found = found or true
|
||||
|
||||
data, eof = parse_headers( data:sub( epos + 1, #data ), field )
|
||||
inhdr = not eof
|
||||
end
|
||||
until not spos
|
||||
|
||||
if found then
|
||||
-- We found at least some boundary. Save
|
||||
-- the unparsed remaining data for the
|
||||
-- next chunk.
|
||||
lchunk, data = data, nil
|
||||
else
|
||||
-- There was a complete chunk without a boundary. Parse it as headers or
|
||||
-- append it as data, depending on our current state.
|
||||
if inhdr then
|
||||
lchunk, eof = parse_headers( data, field )
|
||||
inhdr = not eof
|
||||
else
|
||||
-- We're inside data, so append the data. Note that we only append
|
||||
-- lchunk, not all of data, since there is a chance that chunk
|
||||
-- contains half a boundary. Assuming that each chunk is at least the
|
||||
-- boundary in size, this should prevent problems
|
||||
store( field, lchunk, false )
|
||||
lchunk, chunk = chunk, nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
return ltn12.pump.all( src, snk )
|
||||
end
|
||||
|
||||
-- Content-Type. Stores all extracted data associated with its parameter name
|
||||
-- in the params table withing the given message object. Multiple parameter
|
||||
-- values are stored as tables, ordinary ones as strings.
|
||||
function urldecode_message_body( src, msg )
|
||||
|
||||
local tlen = 0
|
||||
local lchunk = nil
|
||||
|
||||
local function snk( chunk )
|
||||
|
||||
tlen = tlen + ( chunk and #chunk or 0 )
|
||||
|
||||
if msg.env.CONTENT_LENGTH and tlen > tonumber(msg.env.CONTENT_LENGTH) + 2 then
|
||||
return nil, "Message body size exceeds Content-Length"
|
||||
elseif tlen > HTTP_MAX_CONTENT then
|
||||
return nil, "Message body size exceeds maximum allowed length"
|
||||
end
|
||||
|
||||
if not lchunk and chunk then
|
||||
lchunk = chunk
|
||||
|
||||
elseif lchunk then
|
||||
local data = lchunk .. ( chunk or "&" )
|
||||
local spos, epos
|
||||
|
||||
repeat
|
||||
spos, epos = data:find("^.-[;&]")
|
||||
|
||||
if spos then
|
||||
local pair = data:sub( spos, epos - 1 )
|
||||
local key = pair:match("^(.-)=")
|
||||
local val = pair:match("=([^%s]*)%s*$")
|
||||
|
||||
if key and #key > 0 then
|
||||
__initval( msg.params, key )
|
||||
__appendval( msg.params, key, val )
|
||||
__finishval( msg.params, key, urldecode )
|
||||
end
|
||||
|
||||
data = data:sub( epos + 1, #data )
|
||||
end
|
||||
until not spos
|
||||
|
||||
lchunk = data
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
return ltn12.pump.all( src, snk )
|
||||
end
|
||||
|
||||
-- version, message headers and resulting CGI environment variables from the
|
||||
-- given ltn12 source.
|
||||
function parse_message_header( src )
|
||||
|
||||
local ok = true
|
||||
local msg = { }
|
||||
|
||||
local sink = ltn12.sink.simplify(
|
||||
function( chunk )
|
||||
return process_states['magic']( msg, chunk )
|
||||
end
|
||||
)
|
||||
|
||||
-- Pump input data...
|
||||
while ok do
|
||||
|
||||
-- get data
|
||||
ok, err = ltn12.pump.step( src, sink )
|
||||
|
||||
-- error
|
||||
if not ok and err then
|
||||
return nil, err
|
||||
|
||||
-- eof
|
||||
elseif not ok then
|
||||
|
||||
-- Process get parameters
|
||||
if ( msg.request_method == "get" or msg.request_method == "post" ) and
|
||||
msg.request_uri:match("?")
|
||||
then
|
||||
msg.params = urldecode_params( msg.request_uri )
|
||||
else
|
||||
msg.params = { }
|
||||
end
|
||||
|
||||
-- Populate common environment variables
|
||||
msg.env = {
|
||||
CONTENT_LENGTH = msg.headers['Content-Length'];
|
||||
CONTENT_TYPE = msg.headers['Content-Type'] or msg.headers['Content-type'];
|
||||
REQUEST_METHOD = msg.request_method:upper();
|
||||
REQUEST_URI = msg.request_uri;
|
||||
SCRIPT_NAME = msg.request_uri:gsub("?.+$","");
|
||||
SCRIPT_FILENAME = ""; -- XXX implement me
|
||||
SERVER_PROTOCOL = "HTTP/" .. string.format("%.1f", msg.http_version);
|
||||
QUERY_STRING = msg.request_uri:match("?")
|
||||
and msg.request_uri:gsub("^.+?","") or ""
|
||||
}
|
||||
|
||||
-- Populate HTTP_* environment variables
|
||||
for i, hdr in ipairs( {
|
||||
'Accept',
|
||||
'Accept-Charset',
|
||||
'Accept-Encoding',
|
||||
'Accept-Language',
|
||||
'Connection',
|
||||
'Cookie',
|
||||
'Host',
|
||||
'Referer',
|
||||
'User-Agent',
|
||||
} ) do
|
||||
local var = 'HTTP_' .. hdr:upper():gsub("%-","_")
|
||||
local val = msg.headers[hdr]
|
||||
|
||||
msg.env[var] = val
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return msg
|
||||
end
|
||||
|
||||
-- This function will examine the Content-Type within the given message object
|
||||
-- to select the appropriate content decoder.
|
||||
-- Currently the application/x-www-urlencoded and application/form-data
|
||||
-- mime types are supported. If the encountered content encoding can't be
|
||||
-- handled then the whole message body will be stored unaltered as "content"
|
||||
-- property within the given message object.
|
||||
function parse_message_body( src, msg, filecb )
|
||||
-- Is it multipart/mime ?
|
||||
if msg.env.REQUEST_METHOD == "POST" and msg.env.CONTENT_TYPE and
|
||||
msg.env.CONTENT_TYPE:match("^multipart/form%-data")
|
||||
then
|
||||
|
||||
return mimedecode_message_body( src, msg, filecb )
|
||||
|
||||
-- Is it application/x-www-form-urlencoded ?
|
||||
elseif msg.env.REQUEST_METHOD == "POST" and msg.env.CONTENT_TYPE and
|
||||
msg.env.CONTENT_TYPE:match("^application/x%-www%-form%-urlencoded")
|
||||
then
|
||||
return urldecode_message_body( src, msg, filecb )
|
||||
|
||||
|
||||
-- Unhandled encoding
|
||||
-- If a file callback is given then feed it chunk by chunk, else
|
||||
-- store whole buffer in message.content
|
||||
else
|
||||
|
||||
local sink
|
||||
|
||||
-- If we have a file callback then feed it
|
||||
if type(filecb) == "function" then
|
||||
local meta = {
|
||||
name = "raw",
|
||||
encoding = msg.env.CONTENT_TYPE
|
||||
}
|
||||
sink = function( chunk )
|
||||
if chunk then
|
||||
return filecb(meta, chunk, false)
|
||||
else
|
||||
return filecb(meta, nil, true)
|
||||
end
|
||||
end
|
||||
-- ... else append to .content
|
||||
else
|
||||
msg.content = ""
|
||||
msg.content_length = 0
|
||||
|
||||
sink = function( chunk )
|
||||
if chunk then
|
||||
if ( msg.content_length + #chunk ) <= HTTP_MAX_CONTENT then
|
||||
msg.content = msg.content .. chunk
|
||||
msg.content_length = msg.content_length + #chunk
|
||||
return true
|
||||
else
|
||||
return nil, "POST data exceeds maximum allowed length"
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- Pump data...
|
||||
while true do
|
||||
local ok, err = ltn12.pump.step( src, sink )
|
||||
|
||||
if not ok and err then
|
||||
return nil, err
|
||||
elseif not ok then -- eof
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
statusmsg = {
|
||||
[200] = "OK",
|
||||
[206] = "Partial Content",
|
||||
[301] = "Moved Permanently",
|
||||
[302] = "Found",
|
||||
[304] = "Not Modified",
|
||||
[400] = "Bad Request",
|
||||
[403] = "Forbidden",
|
||||
[404] = "Not Found",
|
||||
[405] = "Method Not Allowed",
|
||||
[408] = "Request Time-out",
|
||||
[411] = "Length Required",
|
||||
[412] = "Precondition Failed",
|
||||
[416] = "Requested range not satisfiable",
|
||||
[500] = "Internal Server Error",
|
||||
[503] = "Server Unavailable",
|
||||
}
|
142
luci-base/luasrc/http/protocol.luadoc
Normal file
142
luci-base/luasrc/http/protocol.luadoc
Normal file
|
@ -0,0 +1,142 @@
|
|||
---[[
|
||||
LuCI http protocol class.
|
||||
|
||||
This class contains several functions useful for http message- and content
|
||||
decoding and to retrive form data from raw http messages.
|
||||
]]
|
||||
module "luci.http.protocol"
|
||||
|
||||
---[[
|
||||
Decode an urlencoded string - optionally without decoding
|
||||
|
||||
the "+" sign to " " - and return the decoded string.
|
||||
@class function
|
||||
@name urldecode
|
||||
@param str Input string in x-www-urlencoded format
|
||||
@param no_plus Don't decode "+" signs to spaces
|
||||
@return The decoded string
|
||||
@see urlencode
|
||||
]]
|
||||
|
||||
---[[
|
||||
Extract and split urlencoded data pairs, separated bei either "&" or ";"
|
||||
|
||||
from given url or string. Returns a table with urldecoded values.
|
||||
Simple parameters are stored as string values associated with the parameter
|
||||
name within the table. Parameters with multiple values are stored as array
|
||||
containing the corresponding values.
|
||||
@class function
|
||||
@name urldecode_params
|
||||
@param url The url or string which contains x-www-urlencoded form data
|
||||
@param tbl Use the given table for storing values (optional)
|
||||
@return Table containing the urldecoded parameters
|
||||
@see urlencode_params
|
||||
]]
|
||||
|
||||
---[[
|
||||
Encode given string to x-www-urlencoded format.
|
||||
|
||||
@class function
|
||||
@name urlencode
|
||||
@param str String to encode
|
||||
@return String containing the encoded data
|
||||
@see urldecode
|
||||
]]
|
||||
|
||||
---[[
|
||||
Encode each key-value-pair in given table to x-www-urlencoded format,
|
||||
|
||||
separated by "&". Tables are encoded as parameters with multiple values by
|
||||
repeating the parameter name with each value.
|
||||
@class function
|
||||
@name urlencode_params
|
||||
@param tbl Table with the values
|
||||
@return String containing encoded values
|
||||
@see urldecode_params
|
||||
]]
|
||||
|
||||
---[[
|
||||
Creates a ltn12 source from the given socket. The source will return it's
|
||||
|
||||
data line by line with the trailing \r\n stripped of.
|
||||
@class function
|
||||
@name header_source
|
||||
@param sock Readable network socket
|
||||
@return Ltn12 source function
|
||||
]]
|
||||
|
||||
---[[
|
||||
Decode a mime encoded http message body with multipart/form-data
|
||||
|
||||
Content-Type. Stores all extracted data associated with its parameter name
|
||||
in the params table withing the given message object. Multiple parameter
|
||||
values are stored as tables, ordinary ones as strings.
|
||||
If an optional file callback function is given then it is feeded with the
|
||||
file contents chunk by chunk and only the extracted file name is stored
|
||||
within the params table. The callback function will be called subsequently
|
||||
with three arguments:
|
||||
o Table containing decoded (name, file) and raw (headers) mime header data
|
||||
o String value containing a chunk of the file data
|
||||
o Boolean which indicates wheather the current chunk is the last one (eof)
|
||||
@class function
|
||||
@name mimedecode_message_body
|
||||
@param src Ltn12 source function
|
||||
@param msg HTTP message object
|
||||
@param filecb File callback function (optional)
|
||||
@return Value indicating successful operation (not nil means "ok")
|
||||
@return String containing the error if unsuccessful
|
||||
@see parse_message_header
|
||||
]]
|
||||
|
||||
---[[
|
||||
Decode an urlencoded http message body with application/x-www-urlencoded
|
||||
|
||||
Content-Type. Stores all extracted data associated with its parameter name
|
||||
in the params table withing the given message object. Multiple parameter
|
||||
values are stored as tables, ordinary ones as strings.
|
||||
@class function
|
||||
@name urldecode_message_body
|
||||
@param src Ltn12 source function
|
||||
@param msg HTTP message object
|
||||
@return Value indicating successful operation (not nil means "ok")
|
||||
@return String containing the error if unsuccessful
|
||||
@see parse_message_header
|
||||
]]
|
||||
|
||||
---[[
|
||||
Try to extract an http message header including information like protocol
|
||||
|
||||
version, message headers and resulting CGI environment variables from the
|
||||
given ltn12 source.
|
||||
@class function
|
||||
@name parse_message_header
|
||||
@param src Ltn12 source function
|
||||
@return HTTP message object
|
||||
@see parse_message_body
|
||||
]]
|
||||
|
||||
---[[
|
||||
Try to extract and decode a http message body from the given ltn12 source.
|
||||
|
||||
This function will examine the Content-Type within the given message object
|
||||
to select the appropriate content decoder.
|
||||
Currently the application/x-www-urlencoded and application/form-data
|
||||
mime types are supported. If the encountered content encoding can't be
|
||||
handled then the whole message body will be stored unaltered as "content"
|
||||
property within the given message object.
|
||||
@class function
|
||||
@name parse_message_body
|
||||
@param src Ltn12 source function
|
||||
@param msg HTTP message object
|
||||
@param filecb File data callback (optional, see mimedecode_message_body())
|
||||
@return Value indicating successful operation (not nil means "ok")
|
||||
@return String containing the error if unsuccessful
|
||||
@see parse_message_header
|
||||
]]
|
||||
|
||||
---[[
|
||||
Table containing human readable messages for several http status codes.
|
||||
|
||||
@class table
|
||||
]]
|
||||
|
110
luci-base/luasrc/http/protocol/conditionals.lua
Normal file
110
luci-base/luasrc/http/protocol/conditionals.lua
Normal file
|
@ -0,0 +1,110 @@
|
|||
-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
|
||||
-- Licensed to the public under the Apache License 2.0.
|
||||
|
||||
-- This class provides basic ETag handling and implements most of the
|
||||
-- conditional HTTP/1.1 headers specified in RFC2616 Sct. 14.24 - 14.28 .
|
||||
module("luci.http.protocol.conditionals", package.seeall)
|
||||
|
||||
local date = require("luci.http.protocol.date")
|
||||
|
||||
|
||||
function mk_etag( stat )
|
||||
if stat ~= nil then
|
||||
return string.format( '"%x-%x-%x"', stat.ino, stat.size, stat.mtime )
|
||||
end
|
||||
end
|
||||
|
||||
-- Test whether the given message object contains an "If-Match" header and
|
||||
-- compare it against the given stat object.
|
||||
function if_match( req, stat )
|
||||
local h = req.headers
|
||||
local etag = mk_etag( stat )
|
||||
|
||||
-- Check for matching resource
|
||||
if type(h['If-Match']) == "string" then
|
||||
for ent in h['If-Match']:gmatch("([^, ]+)") do
|
||||
if ( ent == '*' or ent == etag ) and stat ~= nil then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false, 412
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Test whether the given message object contains an "If-Modified-Since" header
|
||||
-- and compare it against the given stat object.
|
||||
function if_modified_since( req, stat )
|
||||
local h = req.headers
|
||||
|
||||
-- Compare mtimes
|
||||
if type(h['If-Modified-Since']) == "string" then
|
||||
local since = date.to_unix( h['If-Modified-Since'] )
|
||||
|
||||
if stat == nil or since < stat.mtime then
|
||||
return true
|
||||
end
|
||||
|
||||
return false, 304, {
|
||||
["ETag"] = mk_etag( stat );
|
||||
["Date"] = date.to_http( os.time() );
|
||||
["Last-Modified"] = date.to_http( stat.mtime )
|
||||
}
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Test whether the given message object contains an "If-None-Match" header and
|
||||
-- compare it against the given stat object.
|
||||
function if_none_match( req, stat )
|
||||
local h = req.headers
|
||||
local etag = mk_etag( stat )
|
||||
local method = req.env and req.env.REQUEST_METHOD or "GET"
|
||||
|
||||
-- Check for matching resource
|
||||
if type(h['If-None-Match']) == "string" then
|
||||
for ent in h['If-None-Match']:gmatch("([^, ]+)") do
|
||||
if ( ent == '*' or ent == etag ) and stat ~= nil then
|
||||
if method == "GET" or method == "HEAD" then
|
||||
return false, 304, {
|
||||
["ETag"] = etag;
|
||||
["Date"] = date.to_http( os.time() );
|
||||
["Last-Modified"] = date.to_http( stat.mtime )
|
||||
}
|
||||
else
|
||||
return false, 412
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- The If-Range header is currently not implemented due to the lack of general
|
||||
-- byte range stuff in luci.http.protocol . This function will always return
|
||||
-- false, 412 to indicate a failed precondition.
|
||||
function if_range( req, stat )
|
||||
-- Sorry, no subranges (yet)
|
||||
return false, 412
|
||||
end
|
||||
|
||||
-- Test whether the given message object contains an "If-Unmodified-Since"
|
||||
-- header and compare it against the given stat object.
|
||||
function if_unmodified_since( req, stat )
|
||||
local h = req.headers
|
||||
|
||||
-- Compare mtimes
|
||||
if type(h['If-Unmodified-Since']) == "string" then
|
||||
local since = date.to_unix( h['If-Unmodified-Since'] )
|
||||
|
||||
if stat ~= nil and since <= stat.mtime then
|
||||
return false, 412
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
85
luci-base/luasrc/http/protocol/conditionals.luadoc
Normal file
85
luci-base/luasrc/http/protocol/conditionals.luadoc
Normal file
|
@ -0,0 +1,85 @@
|
|||
---[[
|
||||
LuCI http protocol implementation - HTTP/1.1 bits.
|
||||
|
||||
This class provides basic ETag handling and implements most of the
|
||||
conditional HTTP/1.1 headers specified in RFC2616 Sct. 14.24 - 14.28 .
|
||||
]]
|
||||
module "luci.http.protocol.conditionals"
|
||||
|
||||
---[[
|
||||
Implement 14.19 / ETag.
|
||||
|
||||
@class function
|
||||
@name mk_etag
|
||||
@param stat A file.stat structure
|
||||
@return String containing the generated tag suitable for ETag headers
|
||||
]]
|
||||
|
||||
---[[
|
||||
14.24 / If-Match
|
||||
|
||||
Test whether the given message object contains an "If-Match" header and
|
||||
compare it against the given stat object.
|
||||
@class function
|
||||
@name if_match
|
||||
@param req HTTP request message object
|
||||
@param stat A file.stat object
|
||||
@return Boolean indicating whether the precondition is ok
|
||||
@return Alternative status code if the precondition failed
|
||||
]]
|
||||
|
||||
---[[
|
||||
14.25 / If-Modified-Since
|
||||
|
||||
Test whether the given message object contains an "If-Modified-Since" header
|
||||
and compare it against the given stat object.
|
||||
@class function
|
||||
@name if_modified_since
|
||||
@param req HTTP request message object
|
||||
@param stat A file.stat object
|
||||
@return Boolean indicating whether the precondition is ok
|
||||
@return Alternative status code if the precondition failed
|
||||
@return Table containing extra HTTP headers if the precondition failed
|
||||
]]
|
||||
|
||||
---[[
|
||||
14.26 / If-None-Match
|
||||
|
||||
Test whether the given message object contains an "If-None-Match" header and
|
||||
compare it against the given stat object.
|
||||
@class function
|
||||
@name if_none_match
|
||||
@param req HTTP request message object
|
||||
@param stat A file.stat object
|
||||
@return Boolean indicating whether the precondition is ok
|
||||
@return Alternative status code if the precondition failed
|
||||
@return Table containing extra HTTP headers if the precondition failed
|
||||
]]
|
||||
|
||||
---[[
|
||||
14.27 / If-Range
|
||||
|
||||
The If-Range header is currently not implemented due to the lack of general
|
||||
byte range stuff in luci.http.protocol . This function will always return
|
||||
false, 412 to indicate a failed precondition.
|
||||
@class function
|
||||
@name if_range
|
||||
@param req HTTP request message object
|
||||
@param stat A file.stat object
|
||||
@return Boolean indicating whether the precondition is ok
|
||||
@return Alternative status code if the precondition failed
|
||||
]]
|
||||
|
||||
---[[
|
||||
14.28 / If-Unmodified-Since
|
||||
|
||||
Test whether the given message object contains an "If-Unmodified-Since"
|
||||
header and compare it against the given stat object.
|
||||
@class function
|
||||
@name if_unmodified_since
|
||||
@param req HTTP request message object
|
||||
@param stat A file.stat object
|
||||
@return Boolean indicating whether the precondition is ok
|
||||
@return Alternative status code if the precondition failed
|
||||
]]
|
||||
|
87
luci-base/luasrc/http/protocol/date.lua
Normal file
87
luci-base/luasrc/http/protocol/date.lua
Normal file
|
@ -0,0 +1,87 @@
|
|||
-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
|
||||
-- Licensed to the public under the Apache License 2.0.
|
||||
|
||||
-- This class contains functions to parse, compare and format http dates.
|
||||
module("luci.http.protocol.date", package.seeall)
|
||||
|
||||
require("luci.sys.zoneinfo")
|
||||
|
||||
|
||||
MONTHS = {
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
|
||||
"Sep", "Oct", "Nov", "Dec"
|
||||
}
|
||||
|
||||
function tz_offset(tz)
|
||||
|
||||
if type(tz) == "string" then
|
||||
|
||||
-- check for a numeric identifier
|
||||
local s, v = tz:match("([%+%-])([0-9]+)")
|
||||
if s == '+' then s = 1 else s = -1 end
|
||||
if v then v = tonumber(v) end
|
||||
|
||||
if s and v then
|
||||
return s * 60 * ( math.floor( v / 100 ) * 60 + ( v % 100 ) )
|
||||
|
||||
-- lookup symbolic tz
|
||||
elseif luci.sys.zoneinfo.OFFSET[tz:lower()] then
|
||||
return luci.sys.zoneinfo.OFFSET[tz:lower()]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- bad luck
|
||||
return 0
|
||||
end
|
||||
|
||||
function to_unix(date)
|
||||
|
||||
local wd, day, mon, yr, hr, min, sec, tz = date:match(
|
||||
"([A-Z][a-z][a-z]), ([0-9]+) " ..
|
||||
"([A-Z][a-z][a-z]) ([0-9]+) " ..
|
||||
"([0-9]+):([0-9]+):([0-9]+) " ..
|
||||
"([A-Z0-9%+%-]+)"
|
||||
)
|
||||
|
||||
if day and mon and yr and hr and min and sec then
|
||||
-- find month
|
||||
local month = 1
|
||||
for i = 1, 12 do
|
||||
if MONTHS[i] == mon then
|
||||
month = i
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- convert to epoch time
|
||||
return tz_offset(tz) + os.time( {
|
||||
year = yr,
|
||||
month = month,
|
||||
day = day,
|
||||
hour = hr,
|
||||
min = min,
|
||||
sec = sec
|
||||
} )
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
function to_http(time)
|
||||
return os.date( "%a, %d %b %Y %H:%M:%S GMT", time )
|
||||
end
|
||||
|
||||
function compare(d1, d2)
|
||||
|
||||
if d1:match("[^0-9]") then d1 = to_unix(d1) end
|
||||
if d2:match("[^0-9]") then d2 = to_unix(d2) end
|
||||
|
||||
if d1 == d2 then
|
||||
return 0
|
||||
elseif d1 < d2 then
|
||||
return -1
|
||||
else
|
||||
return 1
|
||||
end
|
||||
end
|
46
luci-base/luasrc/http/protocol/date.luadoc
Normal file
46
luci-base/luasrc/http/protocol/date.luadoc
Normal file
|
@ -0,0 +1,46 @@
|
|||
---[[
|
||||
LuCI http protocol implementation - date helper class.
|
||||
|
||||
This class contains functions to parse, compare and format http dates.
|
||||
]]
|
||||
module "luci.http.protocol.date"
|
||||
|
||||
---[[
|
||||
Return the time offset in seconds between the UTC and given time zone.
|
||||
|
||||
@class function
|
||||
@name tz_offset
|
||||
@param tz Symbolic or numeric timezone specifier
|
||||
@return Time offset to UTC in seconds
|
||||
]]
|
||||
|
||||
---[[
|
||||
Parse given HTTP date string and convert it to unix epoch time.
|
||||
|
||||
@class function
|
||||
@name to_unix
|
||||
@param data String containing the date
|
||||
@return Unix epoch time
|
||||
]]
|
||||
|
||||
---[[
|
||||
Convert the given unix epoch time to valid HTTP date string.
|
||||
|
||||
@class function
|
||||
@name to_http
|
||||
@param time Unix epoch time
|
||||
@return String containing the formatted date
|
||||
]]
|
||||
|
||||
---[[
|
||||
Compare two dates which can either be unix epoch times or HTTP date strings.
|
||||
|
||||
@class function
|
||||
@name compare
|
||||
@param d1 The first date or epoch time to compare
|
||||
@param d2 The first date or epoch time to compare
|
||||
@return -1 - if d1 is lower then d2
|
||||
@return 0 - if both dates are equal
|
||||
@return 1 - if d1 is higher then d2
|
||||
]]
|
||||
|
78
luci-base/luasrc/http/protocol/mime.lua
Normal file
78
luci-base/luasrc/http/protocol/mime.lua
Normal file
|
@ -0,0 +1,78 @@
|
|||
-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
|
||||
-- Licensed to the public under the Apache License 2.0.
|
||||
|
||||
-- This class provides functions to guess mime types from file extensions and
|
||||
-- vice versa.
|
||||
module("luci.http.protocol.mime", package.seeall)
|
||||
|
||||
require("luci.util")
|
||||
|
||||
MIME_TYPES = {
|
||||
["txt"] = "text/plain";
|
||||
["js"] = "text/javascript";
|
||||
["css"] = "text/css";
|
||||
["htm"] = "text/html";
|
||||
["html"] = "text/html";
|
||||
["patch"] = "text/x-patch";
|
||||
["c"] = "text/x-csrc";
|
||||
["h"] = "text/x-chdr";
|
||||
["o"] = "text/x-object";
|
||||
["ko"] = "text/x-object";
|
||||
|
||||
["bmp"] = "image/bmp";
|
||||
["gif"] = "image/gif";
|
||||
["png"] = "image/png";
|
||||
["jpg"] = "image/jpeg";
|
||||
["jpeg"] = "image/jpeg";
|
||||
["svg"] = "image/svg+xml";
|
||||
|
||||
["zip"] = "application/zip";
|
||||
["pdf"] = "application/pdf";
|
||||
["xml"] = "application/xml";
|
||||
["xsl"] = "application/xml";
|
||||
["doc"] = "application/msword";
|
||||
["ppt"] = "application/vnd.ms-powerpoint";
|
||||
["xls"] = "application/vnd.ms-excel";
|
||||
["odt"] = "application/vnd.oasis.opendocument.text";
|
||||
["odp"] = "application/vnd.oasis.opendocument.presentation";
|
||||
["pl"] = "application/x-perl";
|
||||
["sh"] = "application/x-shellscript";
|
||||
["php"] = "application/x-php";
|
||||
["deb"] = "application/x-deb";
|
||||
["iso"] = "application/x-cd-image";
|
||||
["tgz"] = "application/x-compressed-tar";
|
||||
|
||||
["mp3"] = "audio/mpeg";
|
||||
["ogg"] = "audio/x-vorbis+ogg";
|
||||
["wav"] = "audio/x-wav";
|
||||
|
||||
["mpg"] = "video/mpeg";
|
||||
["mpeg"] = "video/mpeg";
|
||||
["avi"] = "video/x-msvideo";
|
||||
}
|
||||
|
||||
-- "application/octet-stream" if the extension is unknown.
|
||||
function to_mime(filename)
|
||||
if type(filename) == "string" then
|
||||
local ext = filename:match("[^%.]+$")
|
||||
|
||||
if ext and MIME_TYPES[ext:lower()] then
|
||||
return MIME_TYPES[ext:lower()]
|
||||
end
|
||||
end
|
||||
|
||||
return "application/octet-stream"
|
||||
end
|
||||
|
||||
-- given mime-type is unknown.
|
||||
function to_ext(mimetype)
|
||||
if type(mimetype) == "string" then
|
||||
for ext, type in luci.util.kspairs( MIME_TYPES ) do
|
||||
if type == mimetype then
|
||||
return ext
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
34
luci-base/luasrc/http/protocol/mime.luadoc
Normal file
34
luci-base/luasrc/http/protocol/mime.luadoc
Normal file
|
@ -0,0 +1,34 @@
|
|||
---[[
|
||||
LuCI http protocol implementation - mime helper class.
|
||||
|
||||
This class provides functions to guess mime types from file extensions and
|
||||
vice versa.
|
||||
]]
|
||||
module "luci.http.protocol.mime"
|
||||
|
||||
---[[
|
||||
MIME mapping table containg extension - mimetype relations.
|
||||
|
||||
@class table
|
||||
]]
|
||||
|
||||
---[[
|
||||
Extract extension from a filename and return corresponding mime-type or
|
||||
|
||||
"application/octet-stream" if the extension is unknown.
|
||||
@class function
|
||||
@name to_mime
|
||||
@param filename The filename for which the mime type is guessed
|
||||
@return String containign the determined mime type
|
||||
]]
|
||||
|
||||
---[[
|
||||
Return corresponding extension for a given mime type or nil if the
|
||||
|
||||
given mime-type is unknown.
|
||||
@class function
|
||||
@name to_ext
|
||||
@param mimetype The mimetype to retrieve the extension from
|
||||
@return String with the extension or nil for unknown type
|
||||
]]
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue