Tiny gopher daemon written in Lua.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

145 lignes
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