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 .. '" end write(result .. "
' .. escape_html(k) .. '' .. escape_html(v) .. "
") 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 "" end --now the values, including the nil results for k, v in ipairs(result) do print "" for h, _ in sorted_pairs(headers) do print "" end print "" end print "
" print (escape_html(k)) print "
" print (escape_html(v[h] or nullvalue)) 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)