Tiny gopher daemon written in Lua.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

145 lines
3.3KB

  1. local socket = require "socket"
  2. local fs = require "lfs"
  3. local tArgs = {...}
  4. local pcount = 0
  5. local threads = {}
  6. -- initial configuration
  7. local config = {}
  8. config.path = "/var/www/gopher"
  9. config.hostname = "shadowkat.net"
  10. config.port = 70
  11. config.bindport = 7000
  12. config.dirinfo = true
  13. config.timer = 0.1
  14. config.cgi = true
  15. --config.cgipattern = ".*" -- todo
  16. -- load the config as a lua script
  17. if tArgs[1] then
  18. local f = io.open(tArgs[1],"rb")
  19. local fn = load(f:read("*a"))
  20. f:close()
  21. if fn then
  22. for k,v in pairs(fn()) do
  23. config[k] = v
  24. end
  25. end
  26. end
  27. local function cleanPath(p) -- canonicalizes the path in theory
  28. local t,o = {},""
  29. for s in p:gmatch("[^/]+") do
  30. if s == ".." then
  31. t[#t] = nil
  32. else
  33. t[#t+1] = s
  34. end
  35. end
  36. for k,v in pairs(t) do
  37. o=o.."/"..v
  38. end
  39. return o
  40. end
  41. local function logerr(msg)
  42. -- todo: proper error logging logic
  43. print("error: "..msg)
  44. end
  45. local function detectft(path) -- tries to detect the file type
  46. local attr = fs.attributes(path)
  47. if attr.mode:sub(1,3) == "dir" then
  48. return "1"
  49. end
  50. 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
  51. return "I"
  52. elseif path:sub(-5) == ".html" or path:sub(-4) == ".htm" then
  53. return "h"
  54. elseif path:sub(-10) == ".gopherdir" then
  55. return "1"
  56. end
  57. return "0"
  58. end
  59. local function handleConnect(client)
  60. client:settimeout(0)
  61. threads[pcount+1] = coroutine.create(function() local w,err = pcall(function()
  62. local host,port = client:getsockname()
  63. repeat
  64. coroutine.yield()
  65. line=client:receive()
  66. until line
  67. print(string.format("%s:%d: %s",host,port,line))
  68. local path,args = config.path .. cleanPath(line)
  69. local attr = fs.attributes(path)
  70. if attr then
  71. if attr.mode:sub(1,3) == "dir" then
  72. if lfs.attributes(path.."/.gopherdir.cgi") and config.cgi then
  73. local f = io.popen(path.."/.gopherdir.cgi")
  74. coroutine.yield()
  75. client:send(f:read("*a"))
  76. f:close()
  77. elseif lfs.attributes(path.."/.gopherdir") then
  78. local f = io.open(path.."/.gopherdir")
  79. client:send(f:read("*a"))
  80. f:close()
  81. else
  82. if config.dirinfo then
  83. client:send(string.format("i%s\ni%s\n",config.hostname,cleanPath(line)))
  84. end
  85. for file in lfs.dir(path) do
  86. if file:sub(1,1) ~= "." then
  87. 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))
  88. end
  89. end
  90. end
  91. else
  92. if path:sub(-4) == ".cgi" and config.cgi then
  93. local f = io.popen(path)
  94. coroutine.yield()
  95. client:send(f:read("*a"))
  96. f:close()
  97. else
  98. local f = io.open(path)
  99. client:send(f:read("*a"))
  100. f:close()
  101. end
  102. end
  103. else
  104. client:send("Not found.")
  105. end
  106. client:close()
  107. end)
  108. if not w then
  109. logerr(err)
  110. client:close()
  111. end
  112. end)
  113. pcount=pcount+1
  114. end
  115. local server = socket.bind("*",config.bindport)
  116. while true do -- totally not a scheduler
  117. client = server:accept()
  118. if client then
  119. server:settimeout(config.timer)
  120. handleConnect(client)
  121. end
  122. local c = 0
  123. for k,v in pairs(threads) do
  124. if coroutine.status(v) == "dead" then
  125. threads[k] = nil
  126. else
  127. coroutine.resume(v)
  128. c = c + 1
  129. end
  130. end
  131. if c == 0 then
  132. -- if there are no clients connected, make server:accept() block forever
  133. server:settimeout(inf)
  134. end
  135. end