|
|
@@ -0,0 +1,134 @@ |
|
|
|
local socket = require "socket" |
|
|
|
local fs = require "lfs" |
|
|
|
|
|
|
|
local tArgs = {...} |
|
|
|
|
|
|
|
local pcount = 0 |
|
|
|
local threads = {} |
|
|
|
|
|
|
|
-- initial configuration |
|
|
|
local config = {} |
|
|
|
config.path = "/var/gopher" |
|
|
|
config.hostname = "shadowkat.net" |
|
|
|
config.port = 70 |
|
|
|
config.bindport = 7000 |
|
|
|
config.dirinfo = true |
|
|
|
config.timer = 0.1 |
|
|
|
|
|
|
|
-- load the config as a lua script |
|
|
|
if tArgs[1] then |
|
|
|
local f = io.open(tArgs[1],"rb") |
|
|
|
local fn = load(f:read("*a")) |
|
|
|
f:close() |
|
|
|
if fn then |
|
|
|
for k,v in pairs(fn()) do |
|
|
|
config[k] = v |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
local function cleanPath(p) -- canonicalizes the path in theory |
|
|
|
local t,o = {},"" |
|
|
|
for s in p:gmatch("[^/]+") do |
|
|
|
if s == ".." then |
|
|
|
t[#t] = nil |
|
|
|
else |
|
|
|
t[#t+1] = s |
|
|
|
end |
|
|
|
end |
|
|
|
for k,v in pairs(t) do |
|
|
|
o=o.."/"..v |
|
|
|
end |
|
|
|
return o |
|
|
|
end |
|
|
|
|
|
|
|
local function logerr(msg) |
|
|
|
-- todo: proper error logging logic |
|
|
|
print("error: "..msg) |
|
|
|
end |
|
|
|
|
|
|
|
local function detectft(path) -- tries to detect the file type |
|
|
|
local attr = fs.attributes(path) |
|
|
|
if attr.mode:sub(1,3) == "dir" then |
|
|
|
return "1" |
|
|
|
end |
|
|
|
if path:sub(-4) == ".png" or path:sub(-4) == ".jpg" or path:sub(-5) == ".jpeg" or path:sub(-4) == ".bmp" or path:sub(-4) == "gif" then |
|
|
|
return "I" |
|
|
|
end |
|
|
|
if path:sub(-5) == ".html" or path:sub(-4) == ".htm" then |
|
|
|
return "h" |
|
|
|
end |
|
|
|
return "0" |
|
|
|
end |
|
|
|
|
|
|
|
local function handleConnect(client) |
|
|
|
client:settimeout(0) |
|
|
|
threads[pcount+1] = coroutine.create(function() local w,err = pcall(function() |
|
|
|
repeat |
|
|
|
coroutine.yield() |
|
|
|
line=client:receive() |
|
|
|
until line |
|
|
|
print(line) |
|
|
|
local path,args = config.path .. cleanPath(line) |
|
|
|
local attr = fs.attributes(path) |
|
|
|
if attr then |
|
|
|
if attr.mode:sub(1,3) == "dir" then |
|
|
|
if lfs.attributes(path.."/.gopherdir.cgi") then |
|
|
|
local f = io.popen(path.."/.gopherdir.cgi") |
|
|
|
coroutine.yield() |
|
|
|
client:send(f:read("*a")) |
|
|
|
f:close() |
|
|
|
elseif lfs.attributes(path.."/.gopherdir") then |
|
|
|
local f = io.open(path.."/.gopherdir") |
|
|
|
client:send(f:read("*a")) |
|
|
|
f:close() |
|
|
|
else |
|
|
|
if config.dirinfo then |
|
|
|
client:send(string.format("i%s\ni%s\n",config.hostname,cleanPath(line))) |
|
|
|
end |
|
|
|
for file in lfs.dir(path) do |
|
|
|
if file:sub(1,1) ~= "." then |
|
|
|
client:send(string.format("%s%s\t%s\t%s\t%d\n",detectft(path.."/"..file),file,string.format("%s/%s",cleanPath(line),file),config.hostname,config.port)) |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
else |
|
|
|
if path:sub(-4) == ".cgi" then |
|
|
|
local f = io.popen(path) |
|
|
|
coroutine.yield() |
|
|
|
client:send(f:read("*a")) |
|
|
|
f:close() |
|
|
|
else |
|
|
|
local f = io.open(path) |
|
|
|
client:send(f:read("*a")) |
|
|
|
f:close() |
|
|
|
end |
|
|
|
end |
|
|
|
else |
|
|
|
client:send("Not found.") |
|
|
|
end |
|
|
|
client:close() |
|
|
|
end) |
|
|
|
if not w then |
|
|
|
logerr(err) |
|
|
|
client:close() |
|
|
|
end |
|
|
|
end) |
|
|
|
pcount=pcount+1 |
|
|
|
end |
|
|
|
|
|
|
|
local server = socket.bind("*",config.bindport) |
|
|
|
server:settimeout(config.timer) |
|
|
|
while true do -- totally not a scheduler |
|
|
|
client = server:accept() |
|
|
|
if client then |
|
|
|
handleConnect(client) |
|
|
|
end |
|
|
|
for k,v in pairs(threads) do |
|
|
|
if coroutine.status(v) == "dead" then |
|
|
|
threads[k] = nil |
|
|
|
else |
|
|
|
coroutine.resume(v) |
|
|
|
end |
|
|
|
end |
|
|
|
end |