---[[
+-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
+-- Licensed to the public under the Apache License 2.0.
-HTTP protocol implementation for LuCI
-(c) 2008 Freifunk Leipzig / Jo-Philipp Wich <xm@leipzig.freifunk.net>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-$Id$
-
-]]--
-
---- 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", package.seeall)
local ltn12 = require("luci.ltn12")
+local util = require("luci.util")
HTTP_MAX_CONTENT = 1024*8 -- 8 kB maximum content size
---- Decode an urlencoded string - optionally without decoding
--- the "+" sign to " " - and return the decoded string.
--- @param str Input string in x-www-urlencoded format
--- @param no_plus Don't decode "+" signs to spaces
--- @return The decoded string
--- @see urlencode
-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
-
---- 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.
--- @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
function urldecode_params( url, tbl )
local params = tbl or { }
for pair in url:gmatch( "[^&;]+" ) do
-- find key and value
- local key = urldecode( pair:match("^([^=]+)") )
- local val = urldecode( pair:match("^[^=]+=(.+)$") )
+ local key = util.urldecode( pair:match("^([^=]+)") )
+ local val = util.urldecode( pair:match("^[^=]+=(.+)$") )
-- store
if type(key) == "string" and key:len() > 0 then
return params
end
---- Encode given string to x-www-urlencoded format.
--- @param str String to encode
--- @return String containing the encoded data
--- @see urldecode
-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
-
---- 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.
--- @param tbl Table with the values
--- @return String containing encoded values
--- @see urldecode_params
function urlencode_params( tbl )
local enc = ""
if type(v) == "table" then
for i, v2 in ipairs(v) do
enc = enc .. ( #enc > 0 and "&" or "" ) ..
- urlencode(k) .. "=" .. urlencode(v2)
+ util.urlencode(k) .. "=" .. util.urlencode(v2)
end
else
enc = enc .. ( #enc > 0 and "&" or "" ) ..
- urlencode(k) .. "=" .. urlencode(v)
+ util.urlencode(k) .. "=" .. util.urlencode(v)
end
end
-- (Internal function)
-- Initialize given parameter and coerce string into table when the parameter
-- already exists.
--- @param tbl Table where parameter should be created
--- @param key Parameter name
--- @return Always nil
local function __initval( tbl, key )
if tbl[key] == nil then
tbl[key] = ""
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.
--- @param tbl Table containing the previously initialized parameter value
--- @param key Parameter name
--- @param chunk String containing the data to append
--- @return Always nil
--- @see __initval
local function __appendval( tbl, key, chunk )
if type(tbl[key]) == "table" then
tbl[key][#tbl[key]] = tbl[key][#tbl[key]] .. chunk
-- 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.
--- @param tbl Table containing the previously initialized parameter value
--- @param key Parameter name
--- @param handler Function which transforms the parameter value
--- @return Always nil
--- @see __initval
--- @see __appendval
local function __finishval( tbl, key, handler )
if handler then
if type(tbl[key]) == "table" then
end
---- 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.
--- @param sock Readable network socket
--- @return Ltn12 source function
function header_source( sock )
return ltn12.source.simplify( function()
end )
end
---- 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
+-- in the params table within 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
-- 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)
--- @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
function mimedecode_message_body( src, msg, filecb )
if msg and msg.env.CONTENT_TYPE then
__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 )
return ltn12.pump.all( src, snk )
end
---- 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
+-- in the params table within the given message object. Multiple parameter
-- values are stored as tables, ordinary ones as strings.
--- @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
function urldecode_message_body( src, msg )
local tlen = 0
return ltn12.pump.all( src, snk )
end
---- Try to extract an http message header including information like protocol
-- version, message headers and resulting CGI environment variables from the
-- given ltn12 source.
--- @param src Ltn12 source function
--- @return HTTP message object
--- @see parse_message_body
function parse_message_header( src )
local ok = true
return msg
end
---- 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.
--- @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
function parse_message_body( src, msg, filecb )
-- Is it multipart/mime ?
if msg.env.REQUEST_METHOD == "POST" and msg.env.CONTENT_TYPE and
-- If we have a file callback then feed it
if type(filecb) == "function" then
- sink = filecb
-
+ 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, err )
+ sink = function( chunk )
if chunk then
if ( msg.content_length + #chunk ) <= HTTP_MAX_CONTENT then
msg.content = msg.content .. chunk
end
end
---- Table containing human readable messages for several http status codes.
--- @class table
statusmsg = {
[200] = "OK",
[206] = "Partial Content",