💾 Archived View for gemini.conman.org › extensions › GLV-1 › handlers › zip.lua captured on 2023-11-14 at 10:06:44.

View Raw

More Information

⬅️ Previous capture (2022-06-04)

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

-- ************************************************************************
--
--    The ZIP file handler
--    Copyright 2020 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 init handler
-- luacheck: ignore 611

local syslog = require "org.conman.syslog"
local fsys   = require "org.conman.fsys"
local magic  = require "org.conman.fsys.magic"
local zipr   = require "org.conman.zip.read"
local zlib   = require "zlib"
local lpeg   = require "lpeg"
local uurl   = require "GLV-1.url-util"
local io     = require "io"
local string = require "string"
local table  = require "table"

local ipairs = ipairs
local pairs  = pairs

_ENV = {}

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

local pfile =                             lpeg.P"/" * lpeg.P(-1) * lpeg.Cc('redirect')
            + (lpeg.P(1) - lpeg.P"/")^1 * lpeg.P"/" * lpeg.P(-1) * lpeg.Cc(true)
            + (lpeg.P(1) - lpeg.P"/")^1             * lpeg.P(-1) * lpeg.Cc(true)
            +                                         lpeg.P(-1) * lpeg.Cc(true)
            + lpeg.Cc(false)
            
local decr do
  local char = (lpeg.P"\r" - lpeg.P"\n") / "\n"
             + lpeg.P(1)
  decr       = lpeg.Cs(char^0)
end

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

function init(iconf,hconf,gconf)
  if not iconf.directory then
    return false,"missing directory specification"
  end
  
  local gmime = gconf.mime or {}
  local hmime = hconf.mime or {}
  
  for ext,mimetype in pairs(gmime) do
    if hmime[ext] == nil then
      hmime[ext] = mimetype
    end
  end
  
  if not iconf.mime then
    iconf.mime = hmime
  else
    for ext,mimetype in pairs(hmime) do
      if iconf.mime[ext] == nil then
        iconf.mime[ext] = mimetype
      end
    end
  end
  
  return true
end

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

function handler(conf,_,_,pathinfo,ios)
  local match do
    match = {}
    match[1],match[2] = pathinfo:match("^(/.*%.zip)/(.*)")
  end
  
  local filename = string.format("%s/%s",conf.directory,match[1])
  local zf,err = io.open(filename,"rb")
  if not zf then
    syslog('error',"%s: %s",filename,err)
    ios:write("40\r\n")
    return 40
  end
  
  local eocd  = zipr.eocd(zf)
  local listd = {}
  local listf = {}
  
  for entry in zipr.dir(zf,eocd) do
    local s,e = entry.name:find(match[2],1,true)
    if s then
      local w = pfile:match(entry.name,e+1)
      if w then
        if entry.usize == 0 then
          table.insert(listd,entry)
        else
          table.insert(listf,entry)
        end
        
        entry._dname = entry.name:sub(e+1,-1)
        if w == 'redirect' then
          entry._redirect = true
          break
        end
      end
    end
  end
  
  if #listd == 0 and #listf == 0 then
    ios:write("51\r\n")
    return 51
  elseif listd[1] and listd[1]._redirect then
    ios:write("31 ",uurl.esc_path:match(match[1] .. "/" .. listd[1].name),"\r\n")
    return 31
  elseif #listd == 0 and #listf == 1 then
    local _     = zipr.file(zf,listf[1])
    local cdata = zf:read(listf[1].csize)
    local udata = zlib.decompress(cdata,-15)
    local mime  = conf.mime[fsys.extension(listf[1].name)] or magic(udata,true)
    
    if mime:find("^text/") then
      udata = decr:match(udata)
    end
    
    ios:write("20 ",mime,"\r\n",udata)
    return 20
  else
    ios:write(
        "20 text/gemini\r\n",
        string.format("Index of %s/%s\r\n",match[1],match[2]),
        "---------------------------\r\n",
        "\r\n"
    )
    
    table.sort(listd,function(a,b) return a.name < b.name end)
    table.sort(listf,function(a,b) return a.name < b.name end)
    
    for _,entry in ipairs(listd) do
      if #entry._dname > 0 then
      
        ios:write(string.format("=> %s\t%s",uurl.esc_path:match(entry._dname),entry._dname),"\r\n")
      end
    end
    
    if #listd > 0 then
      ios:write("\r\n")
    end
    
    for _,entry in ipairs(listf) do
      ios:write(string.format("=> %s\t%s (%d bytes)",uurl.esc_path:match(entry._dname),entry._dname,entry.usize),"\r\n")
    end
    
    ios:write(
        "\r\n",
        "---------------------------\r\n",
        "GLV-1.12556\r\n"
    )
    
    return 20
  end
end

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

return _ENV