-- ************************************************************************ -- -- Read a file just prior to sending it -- 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 . -- -- Comments, questions and criticisms can be sent to: sean@conman.org -- -- ************************************************************************ -- luacheck: ignore 611 local syslog = require "org.conman.syslog" local mimetype = require "org.conman.parsers.mimetype" local fsys = require "org.conman.fsys" local magic = require "org.conman.fsys.magic" local url = require "org.conman.parsers.url.gopher" + require "org.conman.parsers.url" local mklink = require "port70.mklink" local cgi = require "port70.cgi" local lpeg = require "lpeg" local io = require "io" local table = require "table" local require = require local type = type local assert = assert local pcall = pcall -- ************************************************************************ local parseline do local Cc = lpeg.Cc local C = lpeg.C local P = lpeg.P local R = lpeg.R local entry = P"\t" * C(R" ~"^1) * P"\t"^1 -- type * C(R" ~"^1) * P"\t"^1 -- selector * C(R" \255"^0) -- display local code = P"\t" * C"Lua{" -- type * Cc"" -- selector * Cc"" -- display local info = Cc"info" -- type * Cc"" -- selector * C(R" \255"^0) -- display parseline = entry + code + info end local cleanpath do local char = lpeg.P"/"^1 / "/" + lpeg.P(1) cleanpath = lpeg.Cs(char^0) end -- ************************************************************************ local function execblock(name,file,ios) local acc = {} repeat local line = file:read("*L") table.insert(acc,line) until line:match "}Lua" table.remove(acc) local code = table.concat(acc," ") local env = { require = require } local f,err = load(code,name,"t",env) if not f then syslog('error',"%s: %s",name,err) ios:write(mklink { type = 'info', display = "Nothing in particular right now" }) else local okay,data = pcall(f) if not okay then syslog('error',"name=%q err=%q",name,data) else if type(data) == 'table' then ios:write(table.concat(data,"\r\n")) else ios:write(data) end end end end -- ************************************************************************ return function(filename,ext,info,request,ios) assert(ios) assert(ios.write) assert(ios._drain) filename = cleanpath:match(filename) if not filename then syslog('warning',"readfile() bad cleanpath") ios:write(mklink { type = 'error' , display = "Selector not found" , selector = request.selector }) return false end if info.cgi and fsys.access(filename,"rx") then return cgi(filename,info,request,ios) end if filename:match(ext) then local file = io.open(filename,"r") if not file then ios:write(mklink { type = 'error' , display = "Selector not found" , selector = request.selector }) return false end for line in file:lines() do local gtype,selector,display = parseline:match(line) if gtype == 'url' then local uri = url:match(selector) if uri.scheme == 'gopher' then uri.display = display ios:write(mklink(uri)) else ios:write(mklink { type = 'html', display = display, selector = "URL:" .. selector }) end elseif gtype == 'Lua{' then execblock(filename,file,ios) else ios:write(mklink { type = gtype, display = display, selector = selector }) end end file:close() return true end local mime = mimetype:match(magic(filename)) if not mime then syslog('error',"%s: failed to get MIME type",filename) ios:write(mklink { type = 'error' , display = "Selector not found" , selector = request.selector }) return false elseif mime.type:match "^text/" then local file,err = io.open(filename,"r") if not file then syslog('error',"io.open(%q) = %s",filename,err) ios:write(mklink { type = 'error' , display = "Selector not found" , selector = request.selector }) return false end for line in file:lines() do ios:write(line,"\r\n") end file:close() return true else local file,err = io.open(filename,"rb") if not file then syslog('error',"io.open(%q) = %s",filename,err) ios:write(mklink { type = 'error' , display = "Selector not found" , selector = request.selector }) return false end repeat local data = file:read(1024) if data then ios:write(data) end until not data file:close() return true,true end end .