IMPORTANT: due to a drive failure, as of 13-Mar-2021, the Mercurial repository had to be re-mirrored, which changed every commit SHA. The old SHAs and trees are backed up in the vault branches. Please migrate to the new branches as soon as you can.
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.
 
 
 
 
prosody/plugins/mod_http_files.lua

153 lines
4.5 KiB

-- Prosody IM
-- Copyright (C) 2008-2010 Matthew Wild
-- Copyright (C) 2008-2010 Waqas Hussain
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
--
module:depends("http");
local server = require"net.http.server";
local lfs = require "lfs";
local os_date = os.date;
local open = io.open;
local stat = lfs.attributes;
local build_path = require"socket.url".build_path;
local base_path = module:get_option_string("http_files_dir", module:get_option_string("http_path"));
local dir_indices = module:get_option("http_index_files", { "index.html", "index.htm" });
local directory_index = module:get_option_boolean("http_dir_listing");
local mime_map = module:shared("/*/http_files/mime").types;
if not mime_map then
mime_map = {
html = "text/html", htm = "text/html",
xml = "application/xml",
txt = "text/plain",
css = "text/css",
js = "application/javascript",
png = "image/png",
gif = "image/gif",
jpeg = "image/jpeg", jpg = "image/jpeg",
svg = "image/svg+xml",
};
module:shared("/*/http_files/mime").types = mime_map;
local mime_types, err = open(module:get_option_string("mime_types_file", "/etc/mime.types"),"r");
if mime_types then
local mime_data = mime_types:read("*a");
mime_types:close();
setmetatable(mime_map, {
__index = function(t, ext)
local typ = mime_data:match("\n(%S+)[^\n]*%s"..(ext:lower()).."%s") or "application/octet-stream";
t[ext] = typ;
return typ;
end
});
end
end
local cache = setmetatable({}, { __mode = "kv" }); -- Let the garbage collector have it if it wants to.
function serve(opts)
if type(opts) ~= "table" then -- assume path string
opts = { path = opts };
end
local base_path = opts.path;
local dir_indices = opts.index_files or dir_indices;
local directory_index = opts.directory_index;
local function serve_file(event, path)
local request, response = event.request, event.response;
local orig_path = request.path;
local full_path = base_path .. (path and "/"..path or "");
local attr = stat(full_path);
if not attr then
return 404;
end
local request_headers, response_headers = request.headers, response.headers;
local last_modified = os_date('!%a, %d %b %Y %H:%M:%S GMT', attr.modification);
response_headers.last_modified = last_modified;
local etag = ("%02x-%x-%x-%x"):format(attr.dev or 0, attr.ino or 0, attr.size or 0, attr.modification or 0);
response_headers.etag = etag;
local if_none_match = request_headers.if_none_match
local if_modified_since = request_headers.if_modified_since;
if etag == if_none_match
or (not if_none_match and last_modified == if_modified_since) then
return 304;
end
local data = cache[orig_path];
if data and data.etag == etag then
response_headers.content_type = data.content_type;
data = data.data;
elseif attr.mode == "directory" and path then
if full_path:sub(-1) ~= "/" then
local path = { is_absolute = true, is_directory = true };
for dir in orig_path:gmatch("[^/]+") do path[#path+1]=dir; end
response_headers.location = build_path(path);
return 301;
end
for i=1,#dir_indices do
if stat(full_path..dir_indices[i], "mode") == "file" then
return serve_file(event, path..dir_indices[i]);
end
end
if directory_index then
data = server._events.fire_event("directory-index", { path = request.path, full_path = full_path });
end
if not data then
return 403;
end
cache[orig_path] = { data = data, content_type = mime_map.html; etag = etag; };
response_headers.content_type = mime_map.html;
else
local f, err = open(full_path, "rb");
if f then
data, err = f:read("*a");
f:close();
end
if not data then
module:log("debug", "Could not open or read %s. Error was %s", full_path, err);
return 403;
end
local ext = full_path:match("%.([^./]+)$");
local content_type = ext and mime_map[ext];
cache[orig_path] = { data = data; content_type = content_type; etag = etag };
response_headers.content_type = content_type;
end
return response:send(data);
end
return serve_file;
end
function wrap_route(routes)
for route,handler in pairs(routes) do
if type(handler) ~= "function" then
routes[route] = serve(handler);
end
end
return routes;
end
if base_path then
module:provides("http", {
route = {
["GET /*"] = serve {
path = base_path;
directory_index = directory_index;
}
};
});
else
module:log("debug", "http_files_dir not set, assuming use by some other module");
end