From 022699f899cd0f2004123d35245f9e60455f653d Mon Sep 17 00:00:00 2001 From: XeonSquared Date: Thu, 20 Dec 2018 11:23:29 +1100 Subject: [PATCH] daemon and stuff --- example.cfg | 10 ++++ gopherd.lua | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 example.cfg create mode 100644 gopherd.lua diff --git a/example.cfg b/example.cfg new file mode 100644 index 0000000..1627091 --- /dev/null +++ b/example.cfg @@ -0,0 +1,10 @@ +local config = {} + +config.path = "/var/gopher" -- the root path of the dir to be served +config.hostname = "shadowkat.net" -- the hostname of the server +config.port = 70 -- the external port of the server +config.bindport = 7000 -- the port to actually bind to +config.dirinfo = true -- whether to output some extra info in automatic directory indexes +config.timer = 0.1 -- main loop timer + +return config diff --git a/gopherd.lua b/gopherd.lua new file mode 100644 index 0000000..7f27d72 --- /dev/null +++ b/gopherd.lua @@ -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