if os.getenv("comspec") then
sql_library = "../cgilua53/lua_sqlite3.dll"
elseif os.getenv("lua_sql") then
sql_library = os.getenv("lua_sql")
else
sql_library = "../cgilua53/lua_sql.so"
end
binary_library = "../cgilua53/binstd.dll"
package.path = package.path .. ";../cgilua53/?.lua"
function print_errors(script_name, f)
local ok,err = xpcall(f, debug.traceback)
if not ok then
header()
print("Problem in " .. script_name .. "
")
print(string.gsub(err, "\n", "
"))
os.exit()
end
end
print_errors ("cgi.lua",function()
write = io.write
local insert = table.insert
local find = string.find
local sub = string.sub
local len = string.len
local write = io.write
--iterator that returns the pairs in order
function sorted_pairs(t, f)
local a = {}
for n in pairs(t) do
table.insert(a, n)
end
table.sort(a, f)
local i = 0
local iter = function()
i = i + 1
if a[i] == nil then
return nil
else
return a[i], t[a[i]]
end
end
return iter
end
--use this function returns each part of the arg seperated by the seperator in a table element. That is, split("apple banana"," ") would return {"apple","banana")
function split(arg, seperator, magic)
seperator = seperator or " "
local t = {}
local i, j = string.find(arg, seperator, 1, not magic)
if not i then
table.insert(t, arg)
end
while i do
table.insert(t, string.sub(arg, 1, i - 1))
arg = string.sub(arg, j + 1)
i, j = string.find(arg, seperator, 1, not magic)
if not i then
table.insert(t, arg)
end
end
return t
end
--returns the string passed in but with the extra spaces to left and right trimmed
local hex = "0123456789ABCDEF"
function trim(s)
while string.sub(s, 1, 1) == " " do
s = string.sub(s, 2)
end
while string.sub(s, -1, -1) == " " do
s = string.sub(s, 1, -2)
end
return s
end
--function(filename): reads an entire textfile into a varible which is returned
function slurp(filename)
local old_input = io.input()
io.input(filename)
local result = io.read "*a"
io.close(io.input())
pcall(io.input, old_input)
return result
end
--reads an entire datafile into a variable which is returned
function file2data(filename)
local file_handle = io.open(filename, "rb")
local result = file_handle:read "a"
file_handle:close()
return result
end
--writes the contents of a string to a file
function string2file(string, filename)
local file_handle = io.open(filename, "w")
file_handle:write(string)
file_handle:close()
end
--writes data to file
function data2file(data, filename)
local file_handle = io.open(filename, "wb")
file_handle:write(data)
file_handle:close()
end
--returns true if file is readable
function file_exists(name)
local f = io.open(name, "r")
if f ~= nil then
io.close(f)
return true
else
return false
end
end
function convert_from_hex(s)
local result = 0
for i = 1,len(s) do
local value = find(hex,sub(s, i, i)) or 0
result = result * 16 + value - 1
end
return string.char(result)
end
-- this will replace all %xx with characters to decode
function decode_from_url(value)
value = string.gsub(value, "%+", " ") -- + becomes space
value = string.gsub(value, "%%(..)", convert_from_hex) --%99 percent and two characters
return value
end
-- make a string url safe by replacing non alphanumeric characters with %xx where xx is a hex code but allow /
function encode_to_url(value)
return string.gsub(value, "[^%w/]", function (a)
return string.upper(string.format("%%%02x", string.byte(a))) -- %% is literal percent 0 is leading zero, 2 digits, x specifies hexidecimal
end) --non alpha numeric digits
end
function query_string_to_table(s)
--split from string
local t = split(s, "&")
local result = {}
local x
for k, v in pairs(t) do
x = split(v, "=")
local value=x[2]
if value ~= nil then
--convert from hex
value = decode_from_url(value)
result[x[1]] = value
end
end
return result
end
function cookie_string_to_table(s)
--split from string
local t = split(s, ";") --this is different
local result = {}
local x
for k, v in pairs(t) do
x = split(v, "=")
local value = x[2]
if value ~= nil then
--convert from hex
value = decode_from_url(value)
result[trim(x[1])] = value
end
end
return result
end
--POST elements are tables with name and data and other keys
function multipart_form_to_table(s)
s = s or INPUT
local content_type = os.getenv "CONTENT_TYPE"
content_type = string.gsub(content_type, '\"', "") --no quotes
local boundary = "--" .. sub(content_type, string.find(content_type, "boundary=") + #"boundary=") .. "\r\n"
s = sub(s, 1, -(#boundary + 3))
local result = {}
for k_item, v_item in ipairs(split(s, boundary)) do
local t = {}
local position = 1
local done = false
-- repeat finding the next line until blank, then put that into data
repeat
local new_position = string.find(v_item, "\r\n", position, true)
if not new_position then
new_position = #v_item
end
local v = sub(v_item, position, new_position - 1)
if sub(v, 1, 19) == "Content-Disposition" then
local content_disp = split(v, "; ")
for k_content_disp, v_content_disp in pairs(content_disp) do
local value = split(v_content_disp, "=")
if value[2] then
t[value[1]] = sub(value[2], 2, -2) -- removing quotes
end
end
end
if sub(v, 1, 12) == "Content-Type" then
t["Content-Type"] = sub(v, 15)
end
if v == "" then
t.data = sub(v_item, position + 2, -3)
done = true
end
if not done then
position = new_position + 2
end
until done
if t.name then
result[t.name] = t
end
end
return result
end
--encode from string to something safe to display on a form, that is, turn & into &, turn > int > and turn < into <
--this will sanitize strings going to html
function escape_html(s)
s = tostring(s)
s = string.gsub(s, "&", "&")
s = string.gsub(s, "'", "'")
s = string.gsub(s, '"', """)
s = string.gsub(s, ">", ">")
s = string.gsub(s, "<", "<")
s = string.gsub(s, "\n", "
")
s = string.gsub(s, "&(#%d+;)", "&%1")
return s
end
--encode from string to something safe to display on a form, that is, turn & into &, turn > int > and turn < into <
--this will sanitize strings going to html
--no turning \n to
function escape_html2(s)
s = tostring(s)
s = string.gsub(s, "&", "&")
s = string.gsub(s, "'", "'")
s = string.gsub(s, '"', """)
s = string.gsub(s, ">", ">")
s = string.gsub(s, "<", "<")
s = string.gsub(s, "&(#%d+;)", "&%1")
return s
end
function table_to_table(t)
local result = '
'
for k, v in sorted_pairs(t) do
result = result .. '| ' .. escape_html(k) .. ' | ' .. escape_html(v) .. " |
"
end
write(result .. "
")
end
pack = table.pack
--runs a string as lua code. if die is true, then throws an error on errors. Otherwise, returns false, and errors. Will return true and all results if there is a result. Will return all results if die is true (true isn't needed, since any error would have caused an error instead of a result)
function eval(expr, die)
local ok, result = load(expr)
if not ok then
if die then
error(result,2)
else
return false, result
end
end
local t = pack(pcall(ok))
ok = t[1] --first result is status code
table.remove(t, 1) --toss it, now t is a table of results
if not ok then
if die then
error(t[1], 2)
else
return false, t[1]
end
end
if die then
return table.unpack(t)
end
return true, table.unpack(t)
end
--runs a string as lua code. if die is true, then throws an error on errors. Otherwise, returns false, and errors. Will return true and all results if there is a result. Will return all results if die is true (true isn't needed, since any error would have caused an error instead of a result)
--env parameters is the environment
function eval2(expr, env, die)
local ok, result = load(expr, expr, "t", env)
if not ok then
if die then
error(result,2)
else
return false, result
end
end
local t = pack(pcall(ok))
ok = t[1] --first result is status code
table.remove(t, 1) --toss it, now t is a table of results
if not ok then
if die then
error(t[1], 2)
else
return false, t[1]
end
end
if die then
return table.unpack(t)
end
return true, table.unpack(t)
end
--io.write everything in a table, and tables under it, in a format that can be dofile to recreate the table. Can't be endlessly recursive, or contain formats it doesn't understand. f is a function that can be run to try to make sense of formats other than number,string, table and boolean.
function serialize(o,f)
if type(o) == "number" then
io.write(o)
elseif type(o) == "string" then
io.write(string.format("%q", o))
elseif type(o) == "boolean" then
io.write(tostring(o))
elseif type(o) == "table" then
io.write("{\n")
for k, v in pairs(o) do
io.write(" [")
serialize(k, f)
io.write("] = ")
serialize(v, f)
io.write(", \n")
end
io.write("}\n")
else
if f then
f(o)
else
error("cannot serialize a " .. type(o), 2)
end
end
end
--This returns a string variable created out of the list of values given
--for example.. serial(1,3,"alpha",true,{"elephant"})
--returns the string '1,3,"alpha",true,{ [1] = "elephant", }'
function serial(...)
local arg = table.pack(...)
local s = {}
local insert = table.insert
for i = 1, arg.n do
local o = arg[i]
local t = type(o)
if t == "number" then
insert(s, o)
elseif t == "string" then
insert(s, string.format("%q", o))
elseif t == "boolean" then
insert(s, tostring(o))
elseif t == "nil" then
insert(s, "nil")
elseif t == "table" then
local a
a="{"
for k, v in pairs(o) do
a = a.." [" .. serial(k) .. '] = ' .. serial(v) .. ', '
end
a = a .. "}"
insert(s, a)
else
error("cannot serial a " .. t, 2)
end
end
return table.concat(s, ',')
end
--clones a table, meaning to create another seperate table with the same values. can't be endlessly recursive
function copy_table(source)
if type(source) ~= "table" then
error("copy_table - argument must be a table", 2)
end
local dest = {}
for k, v in pairs(source) do
if type(v) == "table" then
dest[k] = copy_table(v)
else
dest[k] = v
end
end
return dest
end
local database_name
function query_and_close(query, dbname)
if not sql_open then
package.loadlib(sql_library, "lua_sqlite3")()
end
if dbname then
database_name = dbname
end
local db = sql_open(database_name)
local result = sql_query(db, query)
sql_close(db)
return result
end
function initial_query(query, dbname)
if not sql_open then
package.loadlib(sql_library, "lua_sqlite3")()
end
if dbname then
database_name = dbname
end
local db = sql_open(database_name)
local result = sql_query(db, query)
return result, db
end
function display_query(query, dbname, nullvalue)
local result = query_and_close(query, dbname)
nullvalue = nullvalue or "(null)"
print ""
--collecting every header in every row, due to nil results destroying the header
local headers = {}
for k, v in pairs(result) do
for k, v in pairs(v) do
headers[k] = true
end
end
--printing the headers out
for k, v in sorted_pairs(headers) do
print "| "
print (escape_html(k))
print " | "
end
--now the values, including the nil results
for k, v in ipairs(result) do
print ""
for h, _ in sorted_pairs(headers) do
print "| "
print (escape_html(v[h] or nullvalue))
print " | "
end
print "
"
end
print "
"
end
--checks if the table is empty
function is_empty(tabl)
if type(tabl) ~= "table" then
error("is_empty: not a table", 2)
end
for _ in pairs(tabl) do
return false
end
return true
end
--given string s with single quotes such as let's return 'let''s'
--this should sanitize to a database query
function escape_string(s)
if s == nil then
return "NULL"
end
s = tostring(s)
s = string.gsub(s, "'", "''")
return "'" .. s .. "'"
end
function escape_number(n)
return tonumber(n) or "NULL"
end
local header_written
function header()
if not header_written then
write "Content-type: text/html\n\n"
header_written = true
end
end
--sets a cookie header
function cookie(name, contents, expire_days)
local cookie_time = os.date("!%a, %d-%b-%Y %H:%M:%S GMT",(os.time() + expire_days * 24 * 60 * 60))
local value = "Set-Cookie: " .. encode_to_url(name) .. "=" .. encode_to_url(contents) .. ";expires=" .. cookie_time .. "\n"
io.write(value)
return value
end
function debug_log(string) --don't name this debug, that's a libary
local file = io.open("debug.txt", "a")
file:write(tostring(string) .."\n")
file:close()
end
--main
if os.getenv("CONTENT_TYPE") == "application/x-www-form-urlencoded" then
INPUT = io.read("*a")
POST = query_string_to_table(INPUT)
elseif os.getenv("CONTENT_TYPE") ~= nil then
if os.getenv("comspec") then
package.loadlib(binary_library, "luaopen_binstd")()
end
INPUT = io.read("*a")
POST = multipart_form_to_table()
else
POST = {}
end
GET = query_string_to_table(os.getenv("QUERY_STRING") or "")
COOKIE = cookie_string_to_table(os.getenv("HTTP_COOKIE") or "")
end)