-- windmill.mearie.org multiview emulation script
-- last updated on 2009-06-22
--
-- adapted (and heavily modified) from Svilen Spasov's:
-- http://blog.svilen.com/2009/04/using-modnegotiation-with-lighttpd.html
-- only implements Accept-Language detection, which is best used so far.
local EXTENSIONS = {'.php', '.html', '.js', '.css', '.xhtml', '.xml'}
local LANGUAGES = {'.en', '.ko', '.ja', ''}
local INDEXFILE = 'index'
function parse_acceptlang(spec)
--print('Accept-Language was ' .. spec)
spec = spec .. ','
local exts = {}
while spec ~= '' do
-- extract first specification from the string.
local lang, q, tail = spec:match('^%s*([%w%-]+)%s*;%s*q%s*=%s*([%d%.]+)%s*,%s*(.*)$')
if not lang then
lang, tail = spec:match('^%s*([%w%-]+)%s*,%s*(.*)$')
if not lang then return false end -- invalid specification: stop parsing.
q = 1.0 -- assume q=1.0
else
q = tonumber(q)
if not q then return false end -- invalid q-value
end
-- for sake of simplicity, we assume implicit * at the end of specification.
-- (so we don't have to return 406 Not Acceptable, but it is not complying)
if lang ~= '*' then
table.insert(exts, {'.' .. lang, q, #exts})
end
spec = tail
end
-- sort the list by q-value and relative order.
table.sort(exts, function(lhs, rhs)
return (lhs[2] > rhs[2] or (lhs[2] == rhs[2] and lhs[3] > rhs[3]))
end)
-- now replace LANGUAGES by new list.
local newlangs = {}
local used = {}
for _, entry in ipairs(exts) do
local ext = entry[1]
if not used[ext] then
table.insert(newlangs, ext)
used[ext] = true
end
end
for _, ext in ipairs(LANGUAGES) do
if not used[ext] then
table.insert(newlangs, ext)
end
end
LANGUAGES = newlangs
--print('replacing LANGUAGES with ' .. table.concat(newlangs, '/'))
end
function apply_constraint(filename, exts)
for _, ext in ipairs(exts) do
if ext ~= '' and filename:sub(-ext:len()) == ext then
return filename:sub(1, -1-ext:len()), ext
end
end
return filename, nil
end
local stat = lighty.stat(lighty.env['physical.path'])
if not stat or stat.is_dir then
local path = lighty.env['physical.rel-path']
local origpath = path
local uri = lighty.env['request.uri']
local docroot = lighty.env['physical.doc-root']
while uri ~= '' do
-- remove last directory/URI element from uri and path until URI is empty.
uri = uri:match('^(.-)[/\\]+[^/\\]*$')
local prevpath = path
local part
path, part = path:match('^(.-)[/\\]+([^/\\]*)$')
if part == "" then
-- since we are removing consecutive slashes at once, this case can
-- only appear at the first time, i.e. stat.is_dir was true.
part = INDEXFILE
end
local stat = lighty.stat(docroot .. path)
if stat then
-- if it is just a file, we may continue as is since lighty will
-- split the original path correctly.
if stat.is_dir then
-- we now have to parse Accept-Language header if exists.
if lighty.request['Accept-Language'] then
parse_acceptlang(lighty.request['Accept-Language'])
end
-- apply possible constraints from the requested file name.
local part, langext = apply_constraint(part, LANGUAGES)
local part, typeext = apply_constraint(part, EXTENSIONS)
if langext then LANGUAGES = {langext} end
if typeext then EXTENSIONS = {typeext} end
--print('final LANGUAGES: ' .. table.concat(LANGUAGES, '/'))
--print('final EXTENSIONS: ' .. table.concat(EXTENSIONS, '/'))
-- search for appropriate alternative path. ("path/part" didn't exist!)
local newpath = path .. '/' .. part
local rewritten = false
for i, ext in ipairs(LANGUAGES) do
local newpath = newpath .. ext
for i, ext in ipairs(EXTENSIONS) do
local newpath = newpath .. ext
if lighty.stat(docroot .. newpath) then
-- re-append stripped path elements. (lighty will split this
-- correctly and treat it as PATH_INFO.)
newpath = newpath .. origpath:sub(prevpath:len() + 1)
lighty.env['physical.rel-path'] = newpath
lighty.env['physical.path'] = docroot .. newpath
rewritten = true
break
end
end
if rewritten then
break
end
end
end
-- if the search has been failed, lighty will issue 404 correctly.
break
end
end
end
-- vim: ts=4 sw=4