Last active
July 8, 2016 16:42
-
-
Save max1220/493f21e2e68061f799dfd1298e0bee8c to your computer and use it in GitHub Desktop.
helper function common for all CGI scripts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- helper function common for all CGI | |
local t_insert = table.insert | |
local t_concat = table.concat | |
local t_remove = table.remove | |
local s_gfind = string.gfind or string.gmatch | |
local s_gsub = string.gsub | |
local s_char = string.char | |
local s_format = string.format | |
local i_write = io.write | |
local o_getenv = os.getenv | |
local _G = _G or _ENV | |
local tostring = tostring | |
-- Use LFS if it's possible | |
local ok, lfs = pcall(require, "lfs") | |
if not ok then | |
lfs = nil | |
end | |
function kvtostring(t, kToStr, strToNum) | |
-- Converts table elements and optional indexes to strings | |
--[[ | |
kvtostring({hello=123, foo=true}) --> {hello="123", foo="true"} | |
]] | |
local ret = {} | |
for k,v in pairs(t) do | |
if kToStr then | |
ret[tostring(k)] = tostring(v) | |
else | |
ret[k] = tostring(v) | |
end | |
end | |
return ret | |
end | |
function tconcat(t, sep) | |
return t_concat(kvtostring(t,false, true), sep) | |
end | |
function tcontains(t, v) | |
for k,t_v in pairs(t) do | |
if t_v == v then | |
return k, v | |
end | |
end | |
return false | |
end | |
function filter(t, f) | |
-- Filters a Table based on a function | |
--[[ | |
filter({42, 1337, 123, 1234}, function(v) if v%2 == 0 then return true end end) --> {42, 1234} | |
]] | |
local newt = {} | |
for k,v in ipairs(t) do | |
if f(v, k) then | |
table.insert(newt, v) | |
end | |
end | |
end | |
function color(str, color) | |
local colors = { black=0, red=1, green=2, yellow=3, blue=4, magenta=5, cyan=6, white=7 } | |
local color = color or colors.red | |
if type(color) == "string" then | |
if colors[color] then | |
color = 90 + colors[color] | |
elseif color:sub(1, #"bright") == "bright" and colors[color:sub(#"bright" + 1)] then | |
color = "1;" .. 90 + colors[color:sub(#"bright" + 1)] | |
end | |
end | |
return string.char(27) .. "[" .. color .. "m" .. tostring(str) .. string.char(27) .. "[m" | |
end | |
function logger(logpath, logdate, dateformat, stderr, logcolor) | |
-- Creates a function that logs to a file. | |
--[[ | |
example.lua: | |
log = loggerToFile("log.txt", false) | |
log("Hello World!") | |
log.txt: | |
Hello World! | |
]] | |
local dateformat = dateformat or "%c" | |
local logfile = io.open(logpath, "a") | |
if not logfile then | |
logfile = assert(io.open(logpath, "a"), "Can't open logfile!") | |
end | |
local function log(...) | |
local logstr = "" | |
if logdate then | |
logstr = os.date(dateformat) .. "\t" | |
end | |
logstr = logstr .. tconcat({...}, "\t") | |
if stderr then | |
io.stderr:write(color(logstr, logcolor), "\n") | |
end | |
logfile:write(logstr, "\n") | |
logfile:flush() | |
end | |
local function logf(str, ...) | |
return log(str:format(...)) | |
end | |
return log, logf | |
end | |
function pack(...) | |
-- Takes all arguments and packs them in a table. (Reverse unpack) | |
--[[ | |
pack("foo", "bar", "buzz") --> {"foo, "bar", "buzz"} | |
]] | |
return {...} | |
end | |
function htmlescape(str) | |
-- Removes HTML controll characters from a string | |
--[[ | |
name = "<script>doBadStuff()</script>" | |
htmlescape("Hello " .. name .. "!") --> "Hello <script>doBadStuff()</script>!" | |
]] | |
str = s_gsub(str, "&", "&") | |
for k,v in pairs({["<"]="<", [">"]=">", ['"']=""", ["'"]="'"}) do | |
str = s_gsub(str, k, v) | |
end | |
return str | |
end | |
function templateFromFile(filepath, removeUnused, escape) | |
-- Creates a template, which is a function that renders parameters into a template. | |
--[[ | |
example.template: | |
<html> | |
<head> | |
<title>{{title}}</title> | |
</head> | |
<body> | |
{{body}} | |
</body> | |
</html> | |
example.lua: | |
mainTemplate = templateFromFile("example.template") | |
function get(name) | |
return mainTemplate{ | |
title = "Hello World!", | |
body = "<h1>Hello World!</h1>" | |
} | |
end | |
]]-- | |
local file = assert(io.open(filepath, "r"), "Can't open template file!") | |
local template = file:read("*a") | |
return function(inserts) | |
local ret = template | |
for k,v in pairs(inserts) do | |
if escape then | |
ret = s_gsub(ret, "{{" .. htmlescape(tostring(k)) .. "}}", htmlescape(tostring(v))) | |
else | |
ret = s_gsub(ret, "{{" .. tostring(k) .. "}}", tostring(v)) | |
end | |
end | |
if removeUnused then | |
return (s_gsub(ret, "({{.-}})", "")) | |
else | |
return ret | |
end | |
end | |
end | |
function unescape (str) | |
-- Removes URL escape characters(Converts "+" to " " and "%XX" to it's correct character value) | |
--[[ | |
unescape("Hello+World%21") --> "Hello World!" | |
]] | |
str = s_gsub(str, "+", " ") | |
str = s_gsub(str, "%%(..)", function (hex) | |
return s_char(tonumber(hex, 16)) | |
end) | |
return str | |
end | |
function urldecode(str) | |
-- Seperates URL encoded key=value pairs to a lua table | |
--[[ | |
urldecode("foo=bar&list=a&list=b&list=c") --> { foo="bar", list={ "a", "b", "c" } } | |
]] | |
local parms = {} | |
for name, value in s_gfind(str, "([^&=]+)=([^&=]+)") do | |
local name = unescape(name) | |
local value = unescape(value) | |
if type(parms[name]) == "table" then | |
t_insert(parms[name], value) | |
elseif parms[name] then | |
local orig = parms[name] | |
parms[name] = {orig, value} | |
else | |
parms[name] = value | |
end | |
end | |
return parms | |
end | |
function urlencode(str) | |
-- Makes a string URL-useable by replacing illegal digits with their correct notation | |
--[[ | |
urlencode("Hello World!") --> "Hello+World%21" | |
]] | |
if (str) then | |
-- str = string.gsub (str, "\n", "\r\n") | |
str = s_gsub (str, "([^%w %-%_%.%~])", function (c) | |
return string.format ("%%%02X", string.byte(c)) | |
end) | |
str = s_gsub (str, " ", "+") | |
end | |
return str | |
end | |
function CSVtoTable(str, indexes) | |
-- Reads a CSV string into a table. | |
--[[ | |
example.lua: | |
csv = readFile("example.csv") | |
for _,fruit in pairs(CSVtoTable(csv, {"name", "color"})) do | |
printf("The fruit %q has the color %q", fruit.name, fruit.color) | |
end | |
example.csv: | |
#format: [name],[color] | |
apple,red | |
banana,yellow | |
tomato,red | |
]] | |
local indexes = indexes or {} | |
local function lineToTable(line) | |
local parts = {} | |
while true do | |
if line:find(",") then | |
t_insert(parts, line:sub(1, line:find(",") - 1)) | |
line = line:sub(line:find(",") + 1, -1) | |
else | |
t_insert(parts, line) | |
break | |
end | |
end | |
local ret = {} | |
for k,v in pairs(parts) do | |
if indexes[k] then | |
ret[indexes[k]] = v | |
else | |
ret[k] = v | |
end | |
end | |
return ret | |
end | |
local ret = {} | |
for line in s_gfind(str, "[^\r\n]+") do | |
if trim(line):sub(1,1) ~= "#" then | |
t_insert(ret, lineToTable(line)) | |
end | |
end | |
return ret | |
end | |
function trim(str) | |
-- Trims(removes leading/trailing whitespace) a string | |
--[[ | |
trim(" Hello World! ") --> "Hello World!" | |
]] | |
return string.match(str, "^%s*(.-)%s*$") | |
end | |
function readFile(path) | |
-- Reads a file, returns it's content | |
--[[ | |
example.lua: | |
readFile("example.txt") --> "Hello World!" | |
example.txt: | |
Hello World! | |
]] | |
local file = assert(io.open(path, "r")) | |
local content = file:read("*a") | |
file:close() | |
return content | |
end | |
function writeFile(path, content) | |
-- Writes a file | |
--[[ | |
example.lua: | |
writeFile("example.txt", "Hello World!") | |
example.txt: | |
Hello World! | |
]] | |
local file = assert(io.open(path, "w"), "Can't open file for writing!") | |
file:write((assert(content, "Missing Content!"))) | |
file:close() | |
return true | |
end | |
if lfs then | |
function scanDir(pwd) | |
local ret = {files = {}, dirs = {}, pwd = pwd} | |
for file in lfs.dir(pwd) do | |
if file ~= "." and file ~= ".." then | |
local attrs = lfs.attributes(pwd .. "/" .. file) | |
if attrs and attrs.mode == "directory" then | |
ret.dirs[file] = scanDir(pwd .. "/" .. file) | |
elseif attrs and attrs.mode == "file" then | |
table.insert(ret.files, file) | |
else | |
print("Not path or directory: ", file) | |
end | |
end | |
end | |
return ret | |
end | |
end | |
function concater(...) | |
local t = {...} | |
return function(append, ...) | |
if append and type(append) == "string" then | |
table.insert(t, append .. table.concat({...})) | |
end | |
return table.concat(t) | |
end | |
end | |
function printf(str, ...) | |
-- prints a formated string. (For format, see lua's string.format) | |
--[[ | |
example.lua: | |
printf("Hello %s!", "World") --> prints "Hello World!" | |
]] | |
print(s_format(str, ...)) | |
end | |
function dispatchHandlers(errhandler, prefix) | |
-- Reads the CGI parameters, and calls a global handler function(e.g. HTTP_GET). If an error occurs, errhandler is called. | |
--[[ | |
cgi-bin/example.lua: | |
function HTTP_GET(parms) | |
io.write("Content-type: text/plain\n\nParameter listing:") | |
for k,v in pairs(parms) do | |
io.write(" ", tostring(k), "\t", tostring(v)) | |
end | |
end | |
dispatchHandlers() | |
]] | |
local getenv | |
if type(fakeenv) == "table" then | |
getenv = function(key) | |
return fakeenv[key] | |
end | |
else | |
getenv = o_getenv | |
end | |
local errhandler = errhandler or function(err) | |
i_write("Content-type: text/plain\n\n An error occured!\n " .. ("="):rep(78) .. "\nThe occured error was:\n\n" .. tostring(err)) | |
os.exit(1) | |
end | |
local prefix = prefix or "HTTP_" | |
local req_method = assert(os.getenv("REQUEST_METHOD"), "Missing REQUEST_METHOD!"):upper() | |
local request_handler = assert(_G[prefix .. req_method], "Unknown REQUEST_METHOD!") | |
local query_parms = urldecode(assert(os.getenv("QUERY_STRING"), "Missing QUERY_STRING!")) | |
local ret = pack(pcall(request_handler, query_parms)) | |
if t_remove(ret, 1) then | |
i_write(unpack(kvtostring(ret))) | |
else | |
i_write(errhandler(unpack(kvtostring(ret)))) | |
end | |
end | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment