💾 Archived View for gemini.conman.org › extensions › GLV-1 › handlers › torture.lua captured on 2023-09-28 at 19:24:37.

View Raw

More Information

⬅️ Previous capture (2022-06-04)

🚧 View Differences

-=-=-=-=-=-=-

-- ************************************************************************
--
--    CGI interface.
--    Copyright 2019 by Sean Conner.  All Rights Reserved.
--
--    This program is free software: you can redistribute it and/or modify
--    it under the terms of the GNU General Public License as published by
--    the Free Software Foundation, either version 3 of the License, or
--    (at your option) any later version.
--
--    This program is distributed in the hope that it will be useful,
--    but WITHOUT ANY WARRANTY; without even the implied warranty of
--    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--    GNU General Public License for more details.
--
--    You should have received a copy of the GNU General Public License
--    along with this program.  If not, see <http://www.gnu.org/licenses/>.
--
--    Comments, questions and criticisms can be sent to: sean@conman.org
--
-- ************************************************************************
-- luacheck: globals handler
-- luacheck: ignore 611

local syslog = require "org.conman.syslog"
local fsys   = require "org.conman.fsys"
local abnf   = require "org.conman.parsers.abnf"
local uurl   = require "GLV-1.url-util"
local lpeg   = require "lpeg"
local io     = require "io"
local string = require "string"

local tonumber = tonumber

_ENV = {}

-- ************************************************************************

local parse_headers do
  local Cf = lpeg.Cf
  local Cg = lpeg.Cg
  local Cp = lpeg.Cp
  local Cs = lpeg.Cs
  local Ct = lpeg.Ct
  local C  = lpeg.C
  local P  = lpeg.P
  local R  = lpeg.R
  local S  = lpeg.S
  
  local H do
    local text = R("AZ","az") / function(c) return P(c:lower()) + P(c:upper()) end
               + P(1)         / function(c) return P(c) end
               
    H = function(s)
      local pattern = Cf(text^1,function(acc,pat) return acc * pat end)
      return pattern:match(s) / s
    end
  end
  
  local LWSP         = (abnf.WSP + abnf.CRLF * abnf.WSP)
  local text         = LWSP^1 / " "
                     + abnf.VCHAR
  local separator    = S'()<>@,;:\\"/[]?={}\t '
  local token        = (abnf.VCHAR - separator)^1
  local number       = R"09"^1 / tonumber
  local title        = H"Title"        * P":" * LWSP * Cs(text^1) * abnf.CRLF
  local status       = H"Status"       * P":" * LWSP * number     * abnf.CRLF
  local content_type = H"Content-Type" * P":" * LWSP * Cs(text^1) * abnf.CRLF
  local location     = H"Location"     * P":" * LWSP * Cs(text^1) * abnf.CRLF
  local reason       = H"Error"        * P":" * LWSP * Cs(text^1) * abnf.CRLF
  local generic      = C(token)        * P":" * LWSP * C(text^0)  * abnf.CRLF
  local headers      = title + status + content_type + location + reason + generic
  parse_headers      = Cf(Ct"" * Cg(headers)^1,function(acc,name,value)
                         acc[name] = value
                         return acc
                       end)
                     * abnf.CRLF * Cp()
end

-- ************************************************************************

function handler(conf,_,loc,pathinfo,ios)
  local function write_status(headers,textfield)
    ios:write(string.format("%d %s\r\n",headers['Status'],headers[textfield]))
  end
  
  if pathinfo == "" then
    loc.path = loc.path .. "/"
    ios:write("31 ",uurl.toa(loc),"\r\n")
    return 31
  end
  
  if not pathinfo:match "^/" then
    ios:write("51\r\n")
    return 51
  end
  
  pathinfo = pathinfo:sub(2,-1)
  
  if pathinfo == "" then
    pathinfo = "0000"
  end
  
  local fname    = conf.directory .. "/" .. pathinfo
  local file,err = io.open(fname,"r")
  if not file then
    syslog('error',"%s = %s",fname,err)
    ios:write("40\r\n")
    return 40
  end
  
  local headers = ""
  for line in file:lines() do
    headers = headers .. line .. "\n"
    if line == "" then break end
  end
  
  if not headers then
    syslog('error',"bad headers")
    ios:write("40\r\n")
    return 40
  end
  
  headers = parse_headers:match(headers)
  
  if not headers then
    syslog('error',"cannot parse headers")
    ios:write("40\r\n")
    return 40
  end
  
  if headers['Status'] == 39 then
    write_status(headers,'Location')
  elseif headers['Status'] == 49 then
    write_status(headers,'Error')
  elseif headers['Status'] == 58 then
    write_status(headers,'Error')
  else
    write_status(headers,'Content-Type')
    
    repeat
      local data = file:read(1024)
      if data then ios:write(data) end
    until not data
    
    if pathinfo == "0000" then
      ios:write("\r\nList of tests\r\n\r\n")
      
      for filename in fsys.gexpand(conf.directory .. "/[0-9][0-9][0-9][0-9]") do
        local f   = io.open(filename,"r")
        local hdr = ""
        for line in f:lines() do
          hdr = hdr .. line .. "\n"
          if line == "" then break end
        end
        
        hdr = parse_headers:match(hdr)
        f:close()
        
        local name = fsys.basename(filename)
        ios:write(
          string.format("=> %s %s %s\r\n",
                  name,
                  name,
                  hdr['Title']
          ))
      end
    end
    
    file:close()
  end
  
  return headers['Status']
end

-- ************************************************************************

return _ENV