webmcp

changeset 203:c6ef9991b911

Removed CGI support; Created draft for new MCP program to be used with "Moonbridge Network Server for Lua Applications"
author jbe
date Fri Jan 09 01:57:20 2015 +0100 (2015-01-09)
parents eceb6f56e9ed
children b059efd81649
files Makefile framework/bin/mcp.lua framework/cgi-bin/webmcp-wrapper.lua framework/cgi-bin/webmcp.lua framework/env/__init.lua libraries/rocketcgi/rocketcgi.lua
line diff
     1.1 --- a/Makefile	Mon Aug 25 20:00:01 2014 +0200
     1.2 +++ b/Makefile	Fri Jan 09 01:57:20 2015 +0100
     1.3 @@ -30,7 +30,6 @@
     1.4  	ln -s -f ../../libraries/mondelefant/mondelefant_native.so framework/lib/
     1.5  	ln -s -f ../../libraries/mondelefant/mondelefant_atom_connector.lua framework/lib/
     1.6  	ln -s -f ../../libraries/multirand/multirand.so framework/lib/
     1.7 -	ln -s -f ../../libraries/rocketcgi/rocketcgi.lua framework/lib/
     1.8  	ln -s -f ../../libraries/nihil/nihil.lua framework/lib/
     1.9  	ln -s -f ../../libraries/luatex/luatex.lua framework/lib/
    1.10  
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/framework/bin/mcp.lua	Fri Jan 09 01:57:20 2015 +0100
     2.3 @@ -0,0 +1,420 @@
     2.4 +#!/usr/bin/env moonbridge
     2.5 +
     2.6 +WEBMCP_VERSION = "2.0.0_devel"
     2.7 +
     2.8 +-- check if interactive mode
     2.9 +if listen then  -- defined by moonbridge
    2.10 +  WEBMCP_MODE = "listen"
    2.11 +else
    2.12 +  WEBMCP_MODE = "interactive"
    2.13 +end
    2.14 +
    2.15 +-- store extra command line arguments
    2.16 +local extraargs = {select(3, ...)}
    2.17 +
    2.18 +-- determine framework and bath path from command line arguments
    2.19 +-- or print usage synopsis (if applicable)
    2.20 +do
    2.21 +  local arg1, arg2 = ...
    2.22 +  local helpout
    2.23 +  if
    2.24 +    arg1 == "-h" or arg1 == "--help" or
    2.25 +    arg2 == "-h" or arg2 == "--help"
    2.26 +  then
    2.27 +    helpout = io.stdout
    2.28 +  elseif
    2.29 +    (WEBMCP_MODE == "listen" and (#extraargs < 2 or #extraargs % 2 ~= 0)) or
    2.30 +    (WEBMCP_MODE == "interactive" and arg2 == nil)
    2.31 +  then
    2.32 +    helpout = io.stderr
    2.33 +  end
    2.34 +  helpout:write("Usage: moonbridge -- mcp.lua <framework path> <app base path> <app name 1> <config name 1> [<app name 2> <config name 2> ...]\n")
    2.35 +  helpout:write("   or: lua -i mcp.lua <framework path> <app base path> [<config name>]\n")
    2.36 +  if helpout then
    2.37 +    if helpout == io.stderr then
    2.38 +      return 1
    2.39 +    else
    2.40 +      return 0
    2.41 +    end
    2.42 +  end
    2.43 +  local function append_trailing_slash(str)
    2.44 +    return string.sub(str, "([^/])$", function(last) return last .. "/" end)
    2.45 +  end
    2.46 +  WEBMCP_FRAMEWORK_PATH = append_trailing_slash(arg1)
    2.47 +  WEBMCP_BASE_PATH      = append_trailing_slash(arg2)
    2.48 +end
    2.49 +
    2.50 +-- setup search paths for libraries
    2.51 +do
    2.52 +  package.path = WEBMCP_FRAMEWORK_PATH .. "lib/?.lua;" .. package.path
    2.53 +  -- find out which file name extension shared libraries have
    2.54 +  local slib_exts = {}
    2.55 +  for ext in string.gmatch(package.cpath, "%?%.([A-Za-z0-9_-]+)") do
    2.56 +    slib_exts[ext] = true
    2.57 +  end
    2.58 +  local paths = {}
    2.59 +  for ext in pairs(slib_exts) do
    2.60 +    paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "accelerator/?." .. ext
    2.61 +  end
    2.62 +  for ext in pairs(slib_exts) do
    2.63 +    paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "lib/?." .. ext
    2.64 +  end
    2.65 +  paths[#paths+1] = package.cpath
    2.66 +  package.cpath = table.concat(paths, ";")
    2.67 +end
    2.68 +
    2.69 +-- autoloader system for WebMCP environment "$WEBMCP_FRAMEWORK_PATH/env/",
    2.70 +-- application environment extensions "$WEBMCP_BASE_PATH/env/"
    2.71 +-- and models "$WEBMCP_BASE_PATH/model/"
    2.72 +do
    2.73 +  local weakkey_mt = { __mode = "k" }
    2.74 +  local autoloader_category = setmetatable({}, weakkey_mt)
    2.75 +  local autoloader_path     = setmetatable({}, weakkey_mt)
    2.76 +  local autoloader_mt       = {}
    2.77 +  local function install_autoloader(self, category, path)
    2.78 +    autoloader_category[self] = category
    2.79 +    autoloader_path[self]     = path
    2.80 +    setmetatable(self, autoloader_mt)
    2.81 +  end
    2.82 +  local function try_exec(filename)
    2.83 +    local file = io.open(filename, "r")
    2.84 +    if file then
    2.85 +      local filedata = file:read("*a")
    2.86 +      io.close(file)
    2.87 +      local func, errmsg = load(filedata, "=" .. filename)
    2.88 +      if func then
    2.89 +        func()
    2.90 +        return true
    2.91 +      else
    2.92 +        error(errmsg, 0)
    2.93 +      end
    2.94 +    else
    2.95 +      return false
    2.96 +    end
    2.97 +  end
    2.98 +  local function compose_path_string(base, path, key)
    2.99 +    if #path == 0 then
   2.100 +      return base .. "/" .. key
   2.101 +    else
   2.102 +      return base .. table.concat(path, "/") .. "/" .. key
   2.103 +    end
   2.104 +  end
   2.105 +  function autoloader_mt.__index(self, key)
   2.106 +    local category, base_path, merge_base_path, file_key
   2.107 +    local merge = false
   2.108 +    if
   2.109 +      string.find(key, "^[a-z_][A-Za-z0-9_]*$") and
   2.110 +      not string.find(key, "^__")
   2.111 +    then
   2.112 +      category        = "env"
   2.113 +      base_path       = WEBMCP_FRAMEWORK_PATH .. "env/"
   2.114 +      merge           = true
   2.115 +      merge_base_path = WEBMCP_BASE_PATH .. "env/"
   2.116 +      file_key        = key
   2.117 +    elseif string.find(key, "^[A-Z][A-Za-z0-9]*$") then
   2.118 +      category        = "model"
   2.119 +      base_path       = WEBMCP_BASE_PATH .. "model/"
   2.120 +      local first = true
   2.121 +      file_key = string.gsub(key, "[A-Z]",
   2.122 +        function(c)
   2.123 +          if first then
   2.124 +            first = false
   2.125 +            return string.lower(c)
   2.126 +          else
   2.127 +            return "_" .. string.lower(c)
   2.128 +          end
   2.129 +        end
   2.130 +      )
   2.131 +    else
   2.132 +      return
   2.133 +    end
   2.134 +    local required_category = autoloader_category[self]
   2.135 +    if required_category and required_category ~= category then return end
   2.136 +    local path = autoloader_path[self]
   2.137 +    local path_string = compose_path_string(base_path, path, file_key)
   2.138 +    local merge_path_string
   2.139 +    if merge then
   2.140 +      merge_path_string = compose_path_string(
   2.141 +        merge_base_path, path, file_key
   2.142 +      )
   2.143 +    end
   2.144 +    local function try_dir(dirname)
   2.145 +      local dir = io.open(dirname)
   2.146 +      if dir then
   2.147 +        io.close(dir)
   2.148 +        local obj = {}
   2.149 +        local sub_path = {}
   2.150 +        for i = 1, #path do sub_path[i] = path[i] end
   2.151 +        sub_path[#path+1] = file_key
   2.152 +        install_autoloader(obj, category, sub_path)
   2.153 +        rawset(self, key, obj)
   2.154 +        try_exec(path_string .. "/__init.lua")
   2.155 +        if merge then try_exec(merge_path_string .. "/__init.lua") end
   2.156 +        return true
   2.157 +      else
   2.158 +        return false
   2.159 +      end
   2.160 +    end
   2.161 +    if merge and try_exec(merge_path_string .. ".lua") then
   2.162 +    elseif merge and try_dir(merge_path_string .. "/") then
   2.163 +    elseif try_exec(path_string .. ".lua") then
   2.164 +    elseif try_dir(path_string .. "/") then
   2.165 +    else end
   2.166 +    return rawget(self, key)
   2.167 +  end
   2.168 +  install_autoloader(_G, nil, {})
   2.169 +  try_exec(WEBMCP_FRAMEWORK_PATH .. "env/__init.lua")
   2.170 +  try_exec(WEBMCP_BASE_PATH .. "env/__init.lua")
   2.171 +end
   2.172 +
   2.173 +-- prohibit (unintended) definition of new global variables
   2.174 +_ENV = setmetatable({}, {
   2.175 +  __index = _G,
   2.176 +  __newindex = function()
   2.177 +    error("Setting of global variable prohibited")
   2.178 +  end
   2.179 +})
   2.180 +
   2.181 +-- interactive console mode
   2.182 +if WEBMCP_MODE == "interactive" then
   2.183 +  trace.disable()  -- avoids memory leakage (TODO: needs general solution for moonbridge?)
   2.184 +  local config_name = select(3, ...)
   2.185 +  if config_name then
   2.186 +    execute.config(config_name)
   2.187 +  end
   2.188 +  return
   2.189 +end
   2.190 +
   2.191 +-- TODO: moonbridge support below this line and in env/request and env/slot
   2.192 +
   2.193 +local success, error_info = xpcall(
   2.194 +  function()
   2.195 +
   2.196 +    -- execute configuration file
   2.197 +    do
   2.198 +      local config_name = request.get_config_name()
   2.199 +      if config_name then
   2.200 +        execute.config(config_name)
   2.201 +      end
   2.202 +    end
   2.203 +
   2.204 +    -- restore slots if coming from http redirect
   2.205 +    if cgi.params.tempstore then
   2.206 +      trace.restore_slots{}
   2.207 +      local blob = tempstore.pop(cgi.params.tempstore)
   2.208 +      if blob then slot.restore_all(blob) end
   2.209 +    end
   2.210 +
   2.211 +    local function file_exists(filename)
   2.212 +      local file = io.open(filename, "r")
   2.213 +      if file then
   2.214 +        io.close(file)
   2.215 +        return true
   2.216 +      else
   2.217 +        return false
   2.218 +      end
   2.219 +    end
   2.220 +
   2.221 +    if request.is_404() then
   2.222 +      request.set_status("404 Not Found")
   2.223 +      if request.get_404_route() then
   2.224 +        request.forward(request.get_404_route())
   2.225 +      else
   2.226 +        error("No 404 page set.")
   2.227 +      end
   2.228 +    elseif request.get_action() then
   2.229 +      trace.request{
   2.230 +        module = request.get_module(),
   2.231 +        action = request.get_action()
   2.232 +      }
   2.233 +      if
   2.234 +        request.get_404_route() and
   2.235 +        not file_exists(
   2.236 +          encode.action_file_path{
   2.237 +            module = request.get_module(),
   2.238 +            action = request.get_action()
   2.239 +          }
   2.240 +        )
   2.241 +      then
   2.242 +        request.set_status("404 Not Found")
   2.243 +        request.forward(request.get_404_route())
   2.244 +      else
   2.245 +        if cgi.method ~= "POST" then
   2.246 +          request.set_status("405 Method Not Allowed")
   2.247 +          cgi.add_header("Allow: POST")
   2.248 +          error("Tried to invoke an action with a GET request.")
   2.249 +        end
   2.250 +        local action_status = execute.filtered_action{
   2.251 +          module = request.get_module(),
   2.252 +          action = request.get_action(),
   2.253 +        }
   2.254 +        if not request.is_rerouted() then
   2.255 +          local routing_mode, routing_module, routing_view
   2.256 +          routing_mode   = cgi.params["_webmcp_routing." .. action_status .. ".mode"]
   2.257 +          routing_module = cgi.params["_webmcp_routing." .. action_status .. ".module"]
   2.258 +          routing_view   = cgi.params["_webmcp_routing." .. action_status .. ".view"]
   2.259 +          routing_anchor = cgi.params["_webmcp_routing." .. action_status .. ".anchor"]
   2.260 +          if not (routing_mode or routing_module or routing_view) then
   2.261 +            action_status = "default"
   2.262 +            routing_mode   = cgi.params["_webmcp_routing.default.mode"]
   2.263 +            routing_module = cgi.params["_webmcp_routing.default.module"]
   2.264 +            routing_view   = cgi.params["_webmcp_routing.default.view"]
   2.265 +            routing_anchor = cgi.params["_webmcp_routing.default.anchor"]
   2.266 +          end
   2.267 +          assert(routing_module, "Routing information has no module.")
   2.268 +          assert(routing_view,   "Routing information has no view.")
   2.269 +          if routing_mode == "redirect" then
   2.270 +            local routing_params = {}
   2.271 +            for key, value in pairs(cgi.params) do
   2.272 +              local status, stripped_key = string.match(
   2.273 +                key, "^_webmcp_routing%.([^%.]*)%.params%.(.*)$"
   2.274 +              )
   2.275 +              if status == action_status then
   2.276 +                routing_params[stripped_key] = value
   2.277 +              end
   2.278 +            end
   2.279 +            request.redirect{
   2.280 +              module = routing_module,
   2.281 +              view   = routing_view,
   2.282 +              id     = cgi.params["_webmcp_routing." .. action_status .. ".id"],
   2.283 +              params = routing_params,
   2.284 +              anchor = routing_anchor
   2.285 +            }
   2.286 +          elseif routing_mode == "forward" then
   2.287 +            request.forward{ module = routing_module, view = routing_view }
   2.288 +          else
   2.289 +            error("Missing or unknown routing mode in request parameters.")
   2.290 +          end
   2.291 +        end
   2.292 +      end
   2.293 +    else
   2.294 +      -- no action
   2.295 +      trace.request{
   2.296 +        module = request.get_module(),
   2.297 +        view   = request.get_view()
   2.298 +      }
   2.299 +      if
   2.300 +        request.get_404_route() and
   2.301 +        not file_exists(
   2.302 +          encode.view_file_path{
   2.303 +            module = request.get_module(),
   2.304 +            view   = request.get_view()
   2.305 +          }
   2.306 +        )
   2.307 +      then
   2.308 +        request.set_status("404 Not Found")
   2.309 +        request.forward(request.get_404_route())
   2.310 +      end
   2.311 +    end
   2.312 +
   2.313 +    if not request.get_redirect_data() then
   2.314 +      request.process_forward()
   2.315 +      local view = request.get_view()
   2.316 +      if string.find(view, "^_") then
   2.317 +        error("Tried to call a private view (prefixed with underscore).")
   2.318 +      end
   2.319 +      execute.filtered_view{
   2.320 +        module = request.get_module(),
   2.321 +        view   = view,
   2.322 +      }
   2.323 +    end
   2.324 +
   2.325 +    -- force error due to missing absolute base URL until its too late to display error message
   2.326 +    --if request.get_redirect_data() then
   2.327 +    --  request.get_absolute_baseurl()
   2.328 +    --end
   2.329 +
   2.330 +  end,
   2.331 +
   2.332 +  function(errobj)
   2.333 +    return {
   2.334 +      errobj = errobj,
   2.335 +      stacktrace = string.gsub(
   2.336 +        debug.traceback('', 2),
   2.337 +        "^\r?\n?stack traceback:\r?\n?", ""
   2.338 +      )
   2.339 +    }
   2.340 +  end
   2.341 +)
   2.342 +
   2.343 +if not success then trace.error{} end
   2.344 +
   2.345 +-- laufzeitermittlung
   2.346 +trace.exectime{ real = extos.monotonic_hires_time(), cpu = os.clock() }
   2.347 +
   2.348 +slot.select('trace', trace.render)  -- render trace information
   2.349 +
   2.350 +local redirect_data = request.get_redirect_data()
   2.351 +
   2.352 +-- log error and switch to error layout, unless success
   2.353 +if not success then
   2.354 +  local errobj     = error_info.errobj
   2.355 +  local stacktrace = error_info.stacktrace
   2.356 +  if not request.get_status() and not request.get_json_request_slots() then
   2.357 +    request.set_status("500 Internal Server Error")
   2.358 +  end
   2.359 +  slot.set_layout('system_error')
   2.360 +  slot.select('system_error', function()
   2.361 +    if getmetatable(errobj) == mondelefant.errorobject_metatable then
   2.362 +      slot.put(
   2.363 +        "<p>Database error of class <b>",
   2.364 +        encode.html(errobj.code),
   2.365 +        "</b> occured:<br/><b>",
   2.366 +        encode.html(errobj.message),
   2.367 +        "</b></p>"
   2.368 +      )
   2.369 +    else
   2.370 +      slot.put("<p><b>", encode.html(tostring(errobj)), "</b></p>")
   2.371 +    end
   2.372 +    slot.put("<p>Stack trace follows:<br/>")
   2.373 +    slot.put(encode.html_newlines(encode.html(stacktrace)))
   2.374 +    slot.put("</p>")
   2.375 +  end)
   2.376 +elseif redirect_data then
   2.377 +  local redirect_params = {}
   2.378 +  for key, value in pairs(redirect_data.params) do
   2.379 +    redirect_params[key] = value
   2.380 +  end
   2.381 +  local slot_dump = slot.dump_all()
   2.382 +  if slot_dump ~= "" then
   2.383 +    redirect_params.tempstore = tempstore.save(slot_dump)
   2.384 +  end
   2.385 +  local json_request_slots = request.get_json_request_slots()
   2.386 +  if json_request_slots then
   2.387 +    redirect_params["_webmcp_json_slots[]"] = json_request_slots  
   2.388 +  end
   2.389 +  cgi.redirect(
   2.390 +    encode.url{
   2.391 +      base   = request.get_absolute_baseurl(),
   2.392 +      module = redirect_data.module,
   2.393 +      view   = redirect_data.view,
   2.394 +      id     = redirect_data.id,
   2.395 +      params = redirect_params,
   2.396 +      anchor = redirect_data.anchor
   2.397 +    }
   2.398 +  )
   2.399 +  cgi.send_data()
   2.400 +end
   2.401 +
   2.402 +if not success or not redirect_data then
   2.403 +
   2.404 +  local http_status = request.get_status()
   2.405 +  if http_status then
   2.406 +    cgi.set_status(http_status)
   2.407 +  end
   2.408 +
   2.409 +  local json_request_slots = request.get_json_request_slots()
   2.410 +  if json_request_slots then
   2.411 +    cgi.set_content_type('application/json')
   2.412 +    local data = {}
   2.413 +    for idx, slot_ident in ipairs(json_request_slots) do
   2.414 +      data[slot_ident] = slot.get_content(slot_ident)
   2.415 +    end
   2.416 +    cgi.send_data(encode.json(data))
   2.417 +  else
   2.418 +    cgi.set_content_type(slot.get_content_type())
   2.419 +    cgi.send_data(slot.render_layout())
   2.420 +  end
   2.421 +end
   2.422 +
   2.423 +exit()
     3.1 --- a/framework/cgi-bin/webmcp-wrapper.lua	Mon Aug 25 20:00:01 2014 +0200
     3.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.3 @@ -1,23 +0,0 @@
     3.4 -#!/usr/bin/env lua
     3.5 -
     3.6 -local func, errmsg = loadfile('webmcp.lua')
     3.7 -
     3.8 -if func then
     3.9 -  local result
    3.10 -  result, errmsg = pcall(func)
    3.11 -  if result then
    3.12 -    errmsg = nil
    3.13 -  end
    3.14 -end
    3.15 -
    3.16 -if errmsg and not (cgi and cgi.data_sent) then
    3.17 -  print('Status: 500 Internal Server Error')
    3.18 -  print('Content-type: text/plain')
    3.19 -  print()
    3.20 -  print('500 Internal Server Error')
    3.21 -  print()
    3.22 -  print(errmsg)
    3.23 -  print()
    3.24 -  print('(caught by webmcp-wrapper.lua)')
    3.25 -  os.exit(1)
    3.26 -end
     4.1 --- a/framework/cgi-bin/webmcp.lua	Mon Aug 25 20:00:01 2014 +0200
     4.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.3 @@ -1,489 +0,0 @@
     4.4 -#!/usr/bin/env lua
     4.5 -
     4.6 -_WEBMCP_VERSION = "1.2.6"
     4.7 -
     4.8 --- Lua 5.1 compatibility
     4.9 -if not table.unpack then
    4.10 -  table.unpack = unpack
    4.11 -end
    4.12 -do
    4.13 -  local old_load = load
    4.14 -  function load(ld, ...)
    4.15 -    if type(ld) == "string" then
    4.16 -      local done = false
    4.17 -      local func = function()
    4.18 -        if not done then
    4.19 -          done = true
    4.20 -          return ld
    4.21 -        end
    4.22 -      end
    4.23 -      return old_load(func, ...)
    4.24 -    else
    4.25 -      return old_load(ld, ...)
    4.26 -    end
    4.27 -  end
    4.28 -end
    4.29 -
    4.30 --- include "../lib/" in search path for libraries
    4.31 -if not WEBMCP_PATH then
    4.32 -  WEBMCP_PATH = "../"
    4.33 -end
    4.34 -do
    4.35 -  package.path = WEBMCP_PATH .. 'lib/?.lua;' .. package.path
    4.36 -  -- find out which file name extension shared libraries have
    4.37 -  local slib_exts = {}
    4.38 -  for ext in string.gmatch(package.cpath, "%?%.([A-Za-z0-9_-]+)") do
    4.39 -    slib_exts[ext] = true
    4.40 -  end
    4.41 -  local paths = {}
    4.42 -  for ext in pairs(slib_exts) do
    4.43 -    paths[#paths+1] = WEBMCP_PATH .. "accelerator/?." .. ext
    4.44 -  end
    4.45 -  for ext in pairs(slib_exts) do
    4.46 -    paths[#paths+1] = WEBMCP_PATH .. "lib/?." .. ext
    4.47 -  end
    4.48 -  paths[#paths+1] = package.cpath
    4.49 -  package.cpath = table.concat(paths, ";")
    4.50 -end
    4.51 -
    4.52 --- load os extensions for lua
    4.53 --- (should happen as soon as possible due to run time measurement)
    4.54 -extos = require 'extos'
    4.55 -
    4.56 --- load nihil library
    4.57 -nihil = require 'nihil'
    4.58 -
    4.59 --- load random generator library
    4.60 -multirand = require 'multirand'
    4.61 -
    4.62 --- load rocketcgi library and map it to cgi, unless interactive
    4.63 -do
    4.64 -  local option = os.getenv("WEBMCP_INTERACTIVE")
    4.65 -  if option and option ~= "" and option ~= "0" then
    4.66 -    cgi = nil
    4.67 -  else
    4.68 -    rocketcgi = require 'rocketcgi'  -- TODO: no "rocketcgi" alias
    4.69 -    cgi = rocketcgi
    4.70 -  end
    4.71 -end
    4.72 -
    4.73 --- load database access library with object relational mapper
    4.74 -mondelefant = require 'mondelefant'
    4.75 -mondelefant.connection_prototype.error_objects = true
    4.76 -
    4.77 --- load type system "atom"
    4.78 -atom = require 'atom'
    4.79 -
    4.80 --- load JSON library
    4.81 -json = require 'json'
    4.82 -
    4.83 --- load mondelefant atom connector
    4.84 -require 'mondelefant_atom_connector'
    4.85 -
    4.86 ---[[--
    4.87 -cloned_table =  -- newly generated table
    4.88 -table.new(
    4.89 -  table_or_nil  -- keys of a given table will be copied to the new table
    4.90 -)
    4.91 -
    4.92 -If a table is given, then a cloned table is returned.
    4.93 -If nil is given, then a new empty table is returned.
    4.94 -
    4.95 ---]]--
    4.96 -function table.new(tbl)
    4.97 -  new_tbl = {}
    4.98 -  if tbl then
    4.99 -    for key, value in pairs(tbl) do
   4.100 -      new_tbl[key] = value
   4.101 -    end
   4.102 -  end
   4.103 -  return new_tbl
   4.104 -end
   4.105 ---//--
   4.106 -
   4.107 ---[[--
   4.108 -at_exit(
   4.109 -  func  -- function to be called before the process is ending
   4.110 -)
   4.111 -
   4.112 -Registers a function to be called before the CGI process is exiting.
   4.113 ---]]--
   4.114 -do
   4.115 -  local exit_handlers = {}
   4.116 -  function at_exit(func)
   4.117 -    table.insert(exit_handlers, func)
   4.118 -  end
   4.119 -  function exit(code)
   4.120 -    for i = #exit_handlers, 1, -1 do
   4.121 -      exit_handlers[i]()
   4.122 -    end
   4.123 -    os.exit(code)
   4.124 -  end
   4.125 -end
   4.126 ---//--
   4.127 -
   4.128 ---[[--
   4.129 -app  -- table to store an application state
   4.130 -
   4.131 -'app' is a global table for storing any application state data
   4.132 ---]]--
   4.133 -app = {}
   4.134 ---//--
   4.135 -
   4.136 ---[[--
   4.137 -config  -- table to store application configuration
   4.138 -
   4.139 -'config' is a global table, which can be modified by a config file of an application to modify the behaviour of that application.
   4.140 ---]]--
   4.141 -config = {}
   4.142 ---//--
   4.143 -
   4.144 --- autoloader system for WebMCP environment "../env/",
   4.145 --- application environment extensions "$WEBMCP_APP_BASE/env/"
   4.146 --- and models "$WEBMCP_APP_BASE/model/"
   4.147 -do
   4.148 -  local app_base = os.getenv("WEBMCP_APP_BASEPATH")
   4.149 -  if not app_base then
   4.150 -    error(
   4.151 -      "Failed to initialize autoloader " ..
   4.152 -      "due to unset WEBMCP_APP_BASEPATH environment variable."
   4.153 -    )
   4.154 -  end
   4.155 -  local weakkey_mt = { __mode = "k" }
   4.156 -  local autoloader_category = setmetatable({}, weakkey_mt)
   4.157 -  local autoloader_path     = setmetatable({}, weakkey_mt)
   4.158 -  local autoloader_mt       = {}
   4.159 -  local function install_autoloader(self, category, path)
   4.160 -    autoloader_category[self] = category
   4.161 -    autoloader_path[self]     = path
   4.162 -    setmetatable(self, autoloader_mt)
   4.163 -  end
   4.164 -  local function try_exec(filename)
   4.165 -    local file = io.open(filename, "r")
   4.166 -    if file then
   4.167 -      local filedata = file:read("*a")
   4.168 -      io.close(file)
   4.169 -      local func, errmsg = load(filedata, "=" .. filename)
   4.170 -      if func then
   4.171 -        func()
   4.172 -        return true
   4.173 -      else
   4.174 -        error(errmsg, 0)
   4.175 -      end
   4.176 -    else
   4.177 -      return false
   4.178 -    end
   4.179 -  end
   4.180 -  local function compose_path_string(base, path, key)
   4.181 -    return string.gsub(
   4.182 -      base .. table.concat(path, "/") .. "/" .. key, "/+", "/"
   4.183 -    )
   4.184 -  end
   4.185 -  function autoloader_mt.__index(self, key)
   4.186 -    local category, base_path, merge_base_path, file_key
   4.187 -    local merge = false
   4.188 -    if
   4.189 -      string.find(key, "^[a-z_][A-Za-z0-9_]*$") and
   4.190 -      not string.find(key, "^__")
   4.191 -    then
   4.192 -      category        = "env"
   4.193 -      base_path       = WEBMCP_PATH .. "/env/"
   4.194 -      merge           = true
   4.195 -      merge_base_path = app_base .. "/env/"
   4.196 -      file_key        = key
   4.197 -    elseif string.find(key, "^[A-Z][A-Za-z0-9]*$") then
   4.198 -      category        = "model"
   4.199 -      base_path       = app_base .. "/model/"
   4.200 -      local first = true
   4.201 -      file_key = string.gsub(key, "[A-Z]",
   4.202 -        function(c)
   4.203 -          if first then
   4.204 -            first = false
   4.205 -            return string.lower(c)
   4.206 -          else
   4.207 -            return "_" .. string.lower(c)
   4.208 -          end
   4.209 -        end
   4.210 -      )
   4.211 -    else
   4.212 -      return
   4.213 -    end
   4.214 -    local required_category = autoloader_category[self]
   4.215 -    if required_category and required_category ~= category then return end
   4.216 -    local path = autoloader_path[self]
   4.217 -    local path_string = compose_path_string(base_path, path, file_key)
   4.218 -    local merge_path_string
   4.219 -    if merge then
   4.220 -      merge_path_string = compose_path_string(
   4.221 -        merge_base_path, path, file_key
   4.222 -      )
   4.223 -    end
   4.224 -    local function try_dir(dirname)
   4.225 -      local dir = io.open(dirname)
   4.226 -      if dir then
   4.227 -        io.close(dir)
   4.228 -        local obj = {}
   4.229 -        local sub_path = {}
   4.230 -        for i, v in ipairs(path) do sub_path[i] = v end
   4.231 -        table.insert(sub_path, file_key)
   4.232 -        install_autoloader(obj, category, sub_path)
   4.233 -        rawset(self, key, obj)
   4.234 -        try_exec(path_string .. "/__init.lua")
   4.235 -        if merge then try_exec(merge_path_string .. "/__init.lua") end
   4.236 -        return true
   4.237 -      else
   4.238 -        return false
   4.239 -      end
   4.240 -    end
   4.241 -    if merge and try_exec(merge_path_string .. ".lua") then
   4.242 -    elseif merge and try_dir(merge_path_string .. "/") then
   4.243 -    elseif try_exec(path_string .. ".lua") then
   4.244 -    elseif try_dir(path_string .. "/") then
   4.245 -    else end
   4.246 -    return rawget(self, key)
   4.247 -  end
   4.248 -  install_autoloader(_G, nil, {})
   4.249 -  try_exec(WEBMCP_PATH .. "env/__init.lua")
   4.250 -end
   4.251 -
   4.252 --- interactive console mode
   4.253 -if not cgi then
   4.254 -  trace.disable()  -- avoids memory leakage
   4.255 -  local config_name = request.get_config_name()
   4.256 -  if config_name then
   4.257 -    execute.config(config_name)
   4.258 -  end
   4.259 -  return
   4.260 -end
   4.261 -
   4.262 -local success, error_info = xpcall(
   4.263 -  function()
   4.264 -
   4.265 -    -- execute configuration file
   4.266 -    do
   4.267 -      local config_name = request.get_config_name()
   4.268 -      if config_name then
   4.269 -        execute.config(config_name)
   4.270 -      end
   4.271 -    end
   4.272 -
   4.273 -    -- restore slots if coming from http redirect
   4.274 -    if cgi.params.tempstore then
   4.275 -      trace.restore_slots{}
   4.276 -      local blob = tempstore.pop(cgi.params.tempstore)
   4.277 -      if blob then slot.restore_all(blob) end
   4.278 -    end
   4.279 -
   4.280 -    local function file_exists(filename)
   4.281 -      local file = io.open(filename, "r")
   4.282 -      if file then
   4.283 -        io.close(file)
   4.284 -        return true
   4.285 -      else
   4.286 -        return false
   4.287 -      end
   4.288 -    end
   4.289 -
   4.290 -    if request.is_404() then
   4.291 -      request.set_status("404 Not Found")
   4.292 -      if request.get_404_route() then
   4.293 -        request.forward(request.get_404_route())
   4.294 -      else
   4.295 -        error("No 404 page set.")
   4.296 -      end
   4.297 -    elseif request.get_action() then
   4.298 -      trace.request{
   4.299 -        module = request.get_module(),
   4.300 -        action = request.get_action()
   4.301 -      }
   4.302 -      if
   4.303 -        request.get_404_route() and
   4.304 -        not file_exists(
   4.305 -          encode.action_file_path{
   4.306 -            module = request.get_module(),
   4.307 -            action = request.get_action()
   4.308 -          }
   4.309 -        )
   4.310 -      then
   4.311 -        request.set_status("404 Not Found")
   4.312 -        request.forward(request.get_404_route())
   4.313 -      else
   4.314 -        if cgi.method ~= "POST" then
   4.315 -          request.set_status("405 Method Not Allowed")
   4.316 -          cgi.add_header("Allow: POST")
   4.317 -          error("Tried to invoke an action with a GET request.")
   4.318 -        end
   4.319 -        local action_status = execute.filtered_action{
   4.320 -          module = request.get_module(),
   4.321 -          action = request.get_action(),
   4.322 -        }
   4.323 -        if not request.is_rerouted() then
   4.324 -          local routing_mode, routing_module, routing_view
   4.325 -          routing_mode   = cgi.params["_webmcp_routing." .. action_status .. ".mode"]
   4.326 -          routing_module = cgi.params["_webmcp_routing." .. action_status .. ".module"]
   4.327 -          routing_view   = cgi.params["_webmcp_routing." .. action_status .. ".view"]
   4.328 -          routing_anchor = cgi.params["_webmcp_routing." .. action_status .. ".anchor"]
   4.329 -          if not (routing_mode or routing_module or routing_view) then
   4.330 -            action_status = "default"
   4.331 -            routing_mode   = cgi.params["_webmcp_routing.default.mode"]
   4.332 -            routing_module = cgi.params["_webmcp_routing.default.module"]
   4.333 -            routing_view   = cgi.params["_webmcp_routing.default.view"]
   4.334 -            routing_anchor = cgi.params["_webmcp_routing.default.anchor"]
   4.335 -          end
   4.336 -          assert(routing_module, "Routing information has no module.")
   4.337 -          assert(routing_view,   "Routing information has no view.")
   4.338 -          if routing_mode == "redirect" then
   4.339 -            local routing_params = {}
   4.340 -            for key, value in pairs(cgi.params) do
   4.341 -              local status, stripped_key = string.match(
   4.342 -                key, "^_webmcp_routing%.([^%.]*)%.params%.(.*)$"
   4.343 -              )
   4.344 -              if status == action_status then
   4.345 -                routing_params[stripped_key] = value
   4.346 -              end
   4.347 -            end
   4.348 -            request.redirect{
   4.349 -              module = routing_module,
   4.350 -              view   = routing_view,
   4.351 -              id     = cgi.params["_webmcp_routing." .. action_status .. ".id"],
   4.352 -              params = routing_params,
   4.353 -              anchor = routing_anchor
   4.354 -            }
   4.355 -          elseif routing_mode == "forward" then
   4.356 -            request.forward{ module = routing_module, view = routing_view }
   4.357 -          else
   4.358 -            error("Missing or unknown routing mode in request parameters.")
   4.359 -          end
   4.360 -        end
   4.361 -      end
   4.362 -    else
   4.363 -      -- no action
   4.364 -      trace.request{
   4.365 -        module = request.get_module(),
   4.366 -        view   = request.get_view()
   4.367 -      }
   4.368 -      if
   4.369 -        request.get_404_route() and
   4.370 -        not file_exists(
   4.371 -          encode.view_file_path{
   4.372 -            module = request.get_module(),
   4.373 -            view   = request.get_view()
   4.374 -          }
   4.375 -        )
   4.376 -      then
   4.377 -        request.set_status("404 Not Found")
   4.378 -        request.forward(request.get_404_route())
   4.379 -      end
   4.380 -    end
   4.381 -
   4.382 -    if not request.get_redirect_data() then
   4.383 -      request.process_forward()
   4.384 -      local view = request.get_view()
   4.385 -      if string.find(view, "^_") then
   4.386 -        error("Tried to call a private view (prefixed with underscore).")
   4.387 -      end
   4.388 -      execute.filtered_view{
   4.389 -        module = request.get_module(),
   4.390 -        view   = view,
   4.391 -      }
   4.392 -    end
   4.393 -
   4.394 -    -- force error due to missing absolute base URL until its too late to display error message
   4.395 -    --if request.get_redirect_data() then
   4.396 -    --  request.get_absolute_baseurl()
   4.397 -    --end
   4.398 -
   4.399 -  end,
   4.400 -
   4.401 -  function(errobj)
   4.402 -    return {
   4.403 -      errobj = errobj,
   4.404 -      stacktrace = string.gsub(
   4.405 -        debug.traceback('', 2),
   4.406 -        "^\r?\n?stack traceback:\r?\n?", ""
   4.407 -      )
   4.408 -    }
   4.409 -  end
   4.410 -)
   4.411 -
   4.412 -if not success then trace.error{} end
   4.413 -
   4.414 --- laufzeitermittlung
   4.415 -trace.exectime{ real = extos.monotonic_hires_time(), cpu = os.clock() }
   4.416 -
   4.417 -slot.select('trace', trace.render)  -- render trace information
   4.418 -
   4.419 -local redirect_data = request.get_redirect_data()
   4.420 -
   4.421 --- log error and switch to error layout, unless success
   4.422 -if not success then
   4.423 -  local errobj     = error_info.errobj
   4.424 -  local stacktrace = error_info.stacktrace
   4.425 -  if not request.get_status() and not request.get_json_request_slots() then
   4.426 -    request.set_status("500 Internal Server Error")
   4.427 -  end
   4.428 -  slot.set_layout('system_error')
   4.429 -  slot.select('system_error', function()
   4.430 -    if getmetatable(errobj) == mondelefant.errorobject_metatable then
   4.431 -      slot.put(
   4.432 -        "<p>Database error of class <b>",
   4.433 -        encode.html(errobj.code),
   4.434 -        "</b> occured:<br/><b>",
   4.435 -        encode.html(errobj.message),
   4.436 -        "</b></p>"
   4.437 -      )
   4.438 -    else
   4.439 -      slot.put("<p><b>", encode.html(tostring(errobj)), "</b></p>")
   4.440 -    end
   4.441 -    slot.put("<p>Stack trace follows:<br/>")
   4.442 -    slot.put(encode.html_newlines(encode.html(stacktrace)))
   4.443 -    slot.put("</p>")
   4.444 -  end)
   4.445 -elseif redirect_data then
   4.446 -  local redirect_params = {}
   4.447 -  for key, value in pairs(redirect_data.params) do
   4.448 -    redirect_params[key] = value
   4.449 -  end
   4.450 -  local slot_dump = slot.dump_all()
   4.451 -  if slot_dump ~= "" then
   4.452 -    redirect_params.tempstore = tempstore.save(slot_dump)
   4.453 -  end
   4.454 -  local json_request_slots = request.get_json_request_slots()
   4.455 -  if json_request_slots then
   4.456 -    redirect_params["_webmcp_json_slots[]"] = json_request_slots  
   4.457 -  end
   4.458 -  cgi.redirect(
   4.459 -    encode.url{
   4.460 -      base   = request.get_absolute_baseurl(),
   4.461 -      module = redirect_data.module,
   4.462 -      view   = redirect_data.view,
   4.463 -      id     = redirect_data.id,
   4.464 -      params = redirect_params,
   4.465 -      anchor = redirect_data.anchor
   4.466 -    }
   4.467 -  )
   4.468 -  cgi.send_data()
   4.469 -end
   4.470 -
   4.471 -if not success or not redirect_data then
   4.472 -
   4.473 -  local http_status = request.get_status()
   4.474 -  if http_status then
   4.475 -    cgi.set_status(http_status)
   4.476 -  end
   4.477 -
   4.478 -  local json_request_slots = request.get_json_request_slots()
   4.479 -  if json_request_slots then
   4.480 -    cgi.set_content_type('application/json')
   4.481 -    local data = {}
   4.482 -    for idx, slot_ident in ipairs(json_request_slots) do
   4.483 -      data[slot_ident] = slot.get_content(slot_ident)
   4.484 -    end
   4.485 -    cgi.send_data(encode.json(data))
   4.486 -  else
   4.487 -    cgi.set_content_type(slot.get_content_type())
   4.488 -    cgi.send_data(slot.render_layout())
   4.489 -  end
   4.490 -end
   4.491 -
   4.492 -exit()
     5.1 --- a/framework/env/__init.lua	Mon Aug 25 20:00:01 2014 +0200
     5.2 +++ b/framework/env/__init.lua	Fri Jan 09 01:57:20 2015 +0100
     5.3 @@ -1,3 +1,4 @@
     5.4 +-- string localization function
     5.5  function _(text, replacements)
     5.6    local text = locale._get_translation_table()[text] or text
     5.7    if replacements then
     5.8 @@ -14,3 +15,35 @@
     5.9      return text
    5.10    end
    5.11  end
    5.12 +
    5.13 +--[[--
    5.14 +cloned_table =  -- newly generated table
    5.15 +table.new(
    5.16 +  table_or_nil  -- keys of a given table will be copied to the new table
    5.17 +)
    5.18 +
    5.19 +If a table is given, then a cloned table is returned.
    5.20 +If nil is given, then a new empty table is returned.
    5.21 +
    5.22 +--]]--
    5.23 +function table.new(tbl)
    5.24 +  new_tbl = {}
    5.25 +  if tbl then
    5.26 +    for key, value in pairs(tbl) do
    5.27 +      new_tbl[key] = value
    5.28 +    end
    5.29 +  end
    5.30 +  return new_tbl
    5.31 +end
    5.32 +--//--
    5.33 +
    5.34 +-- load libraries
    5.35 +extos       = require 'extos'
    5.36 +nihil       = require 'nihil'
    5.37 +multirand   = require 'multirand'
    5.38 +mondelefant = require 'mondelefant'
    5.39 +mondelefant.connection_prototype.error_objects = true
    5.40 +atom        = require 'atom'
    5.41 +json        = require 'json'
    5.42 +require 'mondelefant_atom_connector'
    5.43 +
     6.1 --- a/libraries/rocketcgi/rocketcgi.lua	Mon Aug 25 20:00:01 2014 +0200
     6.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.3 @@ -1,313 +0,0 @@
     6.4 -#!/usr/bin/env lua
     6.5 -
     6.6 -local assert         = assert
     6.7 -local collectgarbage = collectgarbage
     6.8 -local error          = error
     6.9 -local getmetatable   = getmetatable
    6.10 -local ipairs         = ipairs
    6.11 -local next           = next
    6.12 -local pairs          = pairs
    6.13 -local print          = print
    6.14 -local rawequal       = rawequal
    6.15 -local rawget         = rawget
    6.16 -local rawlen         = rawlen
    6.17 -local rawset         = rawset
    6.18 -local select         = select
    6.19 -local setmetatable   = setmetatable
    6.20 -local tonumber       = tonumber
    6.21 -local tostring       = tostring
    6.22 -local type           = type
    6.23 -
    6.24 -local io     = io
    6.25 -local math   = math
    6.26 -local os     = os
    6.27 -local string = string
    6.28 -local table  = table
    6.29 -
    6.30 -local _M = {}
    6.31 -if _ENV then
    6.32 -  _ENV = _M
    6.33 -else
    6.34 -  _G[...] = _M
    6.35 -  setfenv(1, _M)
    6.36 -end
    6.37 -
    6.38 -data_sent = false
    6.39 -
    6.40 ---[[--
    6.41 -rocketcgi.add_header(
    6.42 -  string_part1,        -- string
    6.43 -  string_part2,        -- optional second part of string to be concatted
    6.44 -  ...
    6.45 -)
    6.46 -
    6.47 -Sends a header line to the browser. Multiple arguments are concatted to form a single string.
    6.48 -
    6.49 ---]]--
    6.50 -function add_header(...)
    6.51 -  if data_sent then
    6.52 -    error("Can not add header after data has been sent.", 2)
    6.53 -  end
    6.54 -  io.stdout:write(...)
    6.55 -  io.stdout:write("\r\n")
    6.56 -end
    6.57 ---//--
    6.58 -
    6.59 ---[[--
    6.60 -rocketcgi.send_data(
    6.61 -  string_part1,       -- string
    6.62 -  string_part2,       -- optional second part of string to be concatted
    6.63 -  ...
    6.64 -)
    6.65 -
    6.66 -Sends document data to the browser. Multiple arguments are concatted to form a single string.
    6.67 -
    6.68 ---]]--
    6.69 -function send_data(...)
    6.70 -  if not data_sent then
    6.71 -    io.stdout:write("\r\n")
    6.72 -    data_sent = true
    6.73 -  end
    6.74 -  io.stdout:write(...)
    6.75 -end
    6.76 ---//--
    6.77 -
    6.78 ---[[--
    6.79 -rocketcgi.set_status(
    6.80 -  status               -- Status code and description, e.g. "404 Not Found"
    6.81 -)
    6.82 -
    6.83 -Sends a header line to the browser, indicating a given HTTP status.
    6.84 -
    6.85 ---]]--
    6.86 -function set_status(status)
    6.87 -  add_header("Status: ", status)
    6.88 -end
    6.89 ---//--
    6.90 -
    6.91 ---[[--
    6.92 -rocketcgi.redirect(
    6.93 -  status             -- Absolute URL to redirect the browser to
    6.94 -)
    6.95 -
    6.96 -Redirects the browser to the given absolute URL, using a 303 Redirect.
    6.97 -
    6.98 ---]]--
    6.99 -function redirect(location)
   6.100 -  set_status("303 See Other")
   6.101 -  add_header("Location: ", location)
   6.102 -end
   6.103 ---//--
   6.104 -
   6.105 ---[[--
   6.106 -rocketcgi.set_content_type(
   6.107 -  content_type               -- MIME content type
   6.108 -)
   6.109 -
   6.110 -Sends a header line specifying the content-type to the browser.
   6.111 -
   6.112 ---]]--
   6.113 -function set_content_type(content_type)
   6.114 -  add_header("Content-Type: ", content_type)
   6.115 -end
   6.116 ---//--
   6.117 -
   6.118 ---[[--
   6.119 -rocketcgi.set_cookie{
   6.120 -  name   = name,       -- name of cookie
   6.121 -  value  = value,      -- value of cookie
   6.122 -  domain = domain,     -- domain where cookie is transmitted
   6.123 -  path   = path,       -- path where cookie is transmitted
   6.124 -  secure = secure      -- boolean, indicating if cookie should only be transmitted over HTTPS
   6.125 -}
   6.126 -
   6.127 -Sends a header line setting a cookie. NOTE: Currently only session cookies are supported.
   6.128 -
   6.129 ---]]--
   6.130 -function set_cookie(args)
   6.131 -  assert(string.find(args.name, "^[0-9A-Za-z%%._~-]+$"), "Illegal cookie name")
   6.132 -  assert(string.find(args.value, "^[0-9A-Za-z%%._~-]+$"), "Illegal cookie value")
   6.133 -  local parts = {"Set-Cookie: " .. args.name .. "=" .. args.value}
   6.134 -  if args.domain then
   6.135 -    assert(
   6.136 -      string.find(args.path, "^[0-9A-Za-z%%/._~-]+$"),
   6.137 -      "Illegal cookie domain"
   6.138 -    )
   6.139 -    parts[#parts+1] = "domain=" .. args.domain
   6.140 -  end
   6.141 -  if args.path then
   6.142 -    assert(
   6.143 -      string.find(args.path, "^[0-9A-Za-z%%/._~-]+$"),
   6.144 -      "Illegal cookie path"
   6.145 -    )
   6.146 -    parts[#parts+1] = "path=" .. args.path
   6.147 -  end
   6.148 -  if args.secure then
   6.149 -    parts[#parts+1] = "secure"
   6.150 -  end
   6.151 -  add_header(table.concat(parts, "; "))
   6.152 -end
   6.153 ---//--
   6.154 -
   6.155 -method = os.getenv("REQUEST_METHOD") or false
   6.156 -query = os.getenv("QUERY_STRING") or false
   6.157 -cookie_data = os.getenv("HTTP_COOKIE") or false
   6.158 -post_data = io.stdin:read("*a") or false
   6.159 -post_contenttype = os.getenv("CONTENT_TYPE") or false
   6.160 -params = {}
   6.161 -get_params = {}
   6.162 -cookies = {}
   6.163 -post_params = {}
   6.164 -post_filenames = {}
   6.165 -post_types = {}
   6.166 -
   6.167 -local urldecode
   6.168 -do
   6.169 -  local b0 = string.byte("0")
   6.170 -  local b9 = string.byte("9")
   6.171 -  local bA = string.byte("A")
   6.172 -  local bF = string.byte("F")
   6.173 -  local ba = string.byte("a")
   6.174 -  local bf = string.byte("f")
   6.175 -  function urldecode(str)
   6.176 -    return (
   6.177 -      string.gsub(
   6.178 -        string.gsub(str, "%+", " "),
   6.179 -        "%%([0-9A-Fa-f][0-9A-Fa-f])",
   6.180 -        function(hex)
   6.181 -          local n1, n2 = string.byte(hex, 1, 2)
   6.182 -          if n1 >= b0 and n1 <= b9 then n1 = n1 - b0
   6.183 -          elseif n1 >= bA and n1 <= bF then n1 = n1 - bA + 10
   6.184 -          elseif n1 >= ba and n1 <= bf then n1 = n1 - ba + 10
   6.185 -          else return end
   6.186 -          if n2 >= b0 and n2 <= b9 then n2 = n2 - b0
   6.187 -          elseif n2 >= bA and n2 <= bF then n2 = n2 - bA + 10
   6.188 -          elseif n2 >= ba and n2 <= bf then n2 = n2 - ba + 10
   6.189 -          else return end
   6.190 -          return string.char(n1 * 16 + n2)
   6.191 -        end
   6.192 -      )
   6.193 -    )
   6.194 -  end
   6.195 -end
   6.196 -
   6.197 -local function proc_param(tbl, key, value)
   6.198 -  if string.find(key, "%[%]$") then
   6.199 -    if tbl[key] then
   6.200 -      table.insert(tbl[key], value)
   6.201 -    else
   6.202 -      local list = { value }
   6.203 -      params[key] = list
   6.204 -      tbl[key] = list
   6.205 -    end
   6.206 -  else
   6.207 -    params[key] = value
   6.208 -    tbl[key] = value
   6.209 -  end
   6.210 -end
   6.211 -
   6.212 -local function read_urlencoded_form(tbl, data)
   6.213 -  for rawkey, rawvalue in string.gmatch(data, "([^=&]*)=([^=&]*)") do
   6.214 -    proc_param(tbl, urldecode(rawkey), urldecode(rawvalue))
   6.215 -  end
   6.216 -end
   6.217 -
   6.218 -if query then
   6.219 -  read_urlencoded_form(get_params, query)
   6.220 -end
   6.221 -
   6.222 -if cookie_data then
   6.223 -  for rawkey, rawvalue in string.gmatch(cookie_data, "([^=; ]*)=([^=; ]*)") do
   6.224 -    cookies[urldecode(rawkey)] = urldecode(rawvalue)
   6.225 -  end
   6.226 -end
   6.227 -
   6.228 -if post_contenttype and (
   6.229 -  post_contenttype == "application/x-www-form-urlencoded" or
   6.230 -  string.match(post_contenttype, "^application/x%-www%-form%-urlencoded[ ;]")
   6.231 -) then
   6.232 -  read_urlencoded_form(post_params, post_data)
   6.233 -elseif post_contenttype then
   6.234 -  local boundary = string.match(
   6.235 -    post_contenttype,
   6.236 -    '^multipart/form%-data[ \t]*;[ \t]*boundary="([^"]+)"'
   6.237 -  ) or string.match(
   6.238 -      post_contenttype,
   6.239 -      '^multipart/form%-data[ \t]*;[ \t]*boundary=([^"; \t]+)'
   6.240 -  )
   6.241 -  if boundary then
   6.242 -    local parts = {}
   6.243 -    do
   6.244 -      local boundary = "\r\n--" .. boundary
   6.245 -      local post_data = "\r\n" .. post_data
   6.246 -      local pos1, pos2 = string.find(post_data, boundary, 1, true)
   6.247 -      while true do
   6.248 -        local ind = string.sub(post_data, pos2 + 1, pos2 + 2)
   6.249 -        if ind == "\r\n" then
   6.250 -          local pos3, pos4 = string.find(post_data, boundary, pos2 + 1, true)
   6.251 -          if pos3 then
   6.252 -            parts[#parts + 1] = string.sub(post_data, pos2 + 3, pos3 - 1)
   6.253 -          else
   6.254 -            error("Illegal POST data.")
   6.255 -          end
   6.256 -          pos1, pos2 = pos3, pos4
   6.257 -        elseif ind == "--" then
   6.258 -          break
   6.259 -        else
   6.260 -          error("Illegal POST data.")
   6.261 -        end
   6.262 -      end
   6.263 -    end
   6.264 -    for i, part in ipairs(parts) do
   6.265 -      local pos = 1
   6.266 -      local name, filename, contenttype
   6.267 -      while true do
   6.268 -        local header
   6.269 -        do
   6.270 -          local oldpos = pos
   6.271 -          pos = string.find(part, "\r\n", oldpos, true)
   6.272 -          if not pos then
   6.273 -            error("Illegal POST data.")
   6.274 -          end
   6.275 -          if pos == oldpos then break end
   6.276 -          header = string.sub(part, oldpos, pos - 1)
   6.277 -          pos = pos + 2
   6.278 -        end
   6.279 -        if string.find(
   6.280 -          string.lower(header),
   6.281 -          "^content%-disposition:[ \t]*form%-data[ \t]*;"
   6.282 -        ) then
   6.283 -          -- TODO: handle all cases correctly
   6.284 -          name = string.match(header, ';[ \t]*name="([^"]*)"') or
   6.285 -            string.match(header, ';[ \t]*name=([^"; \t]+)')
   6.286 -          filename = string.match(header, ';[ \t]*filename="([^"]*)"') or
   6.287 -            string.match(header, ';[ \t]*filename=([^"; \t]+)')
   6.288 -        else
   6.289 -          local dummy, subpos = string.find(
   6.290 -            string.lower(header),
   6.291 -            "^content%-type:[ \t]*"
   6.292 -          )
   6.293 -          if subpos then
   6.294 -            contenttype = string.sub(header, subpos + 1, #header)
   6.295 -          end
   6.296 -        end
   6.297 -      end
   6.298 -      local content = string.sub(part, pos + 2, #part)
   6.299 -      if not name then
   6.300 -        error("Illegal POST data.")
   6.301 -      end
   6.302 -      proc_param(post_params, name, content)
   6.303 -      post_filenames[name] = filename
   6.304 -      post_types[name] = contenttype
   6.305 -    end
   6.306 -  end
   6.307 -end
   6.308 -
   6.309 -if post_data and #post_data > 262144 then
   6.310 -  post_data = nil
   6.311 -  collectgarbage("collect")
   6.312 -else
   6.313 -  --post_data = nil  --allow access to "post_data" for usual requests
   6.314 -end
   6.315 -
   6.316 -return _M

Impressum / About Us