%%% BEGIN openflax/handler/ls.erl %%% %%% %%% openflax - Open Source web server for Erlang/OTP %%% Copyright (c)2003 Cat's Eye Technologies. All rights reserved. %%% %%% Redistribution and use in source and binary forms, with or without %%% modification, are permitted provided that the following conditions %%% are met: %%% %%% Redistributions of source code must retain the above copyright %%% notice, this list of conditions and the following disclaimer. %%% %%% Redistributions in binary form must reproduce the above copyright %%% notice, this list of conditions and the following disclaimer in %%% the documentation and/or other materials provided with the %%% distribution. %%% %%% Neither the name of Cat's Eye Technologies nor the names of its %%% contributors may be used to endorse or promote products derived %%% from this software without specific prior written permission. %%% %%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND %%% CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, %%% INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF %%% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE %%% DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE %%% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, %%% OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, %%% PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, %%% OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON %%% ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, %%% OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY %%% OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE %%% POSSIBILITY OF SUCH DAMAGE. %% @doc Simple directory listing handler for OpenFlax. %% %%

This handler implements the displaying of simple directory listings %% in a web page.

%% %%

This handler requires openflax.handler.file %% and openflax.handler.template.

%% %%

This handler supports the following configuration options in %% the conf() passed to it:

%% %% %% @end -module(openflax.handler.ls). -vsn('$Id: ls.erl 31 2004-04-23 07:00:11Z catseye $'). -author('cpressey@catseye.mine.nu'). -copyright('Copyright (c)2003 Cat`s Eye Technologies. All rights reserved.'). -behaviour(openflax.handler). -export([start/1, serve/1, stop/1]). % behaviour -import(lists). -import(string). -import(regexp). -import(filename). -import(file). -import(filelib). %% @spec start(conf()) -> conf() %% @doc Initializes the directory server. start(Conf) -> openflax.conf:new(). %% @spec stop(conf()) -> conf() %% @doc Shuts down the directory server. stop(Conf) -> Conf. %% @spec serve(conf()) -> {response(), conf()} %% @doc Serves a simple listing of a world-readable, %% world-executable directory to the connected user agent. serve(Conf) -> % Ensure directory is world-readable and -executable DirName = openflax.handler.file:document(Conf), case {openflax.handler.file:is_world_readable(DirName), openflax.handler.file:is_world_executable(DirName)} of {true, true} -> serve1(Conf, DirName); _ -> {not_found, Conf} end. serve1(Conf, DirName) -> % Serve index file in this directory, if it exists IndexFile = openflax.conf:get_string(cfg_ls_index_file, Conf), FileName = filename:join(DirName, IndexFile), case openflax.handler.file:is_accessible(FileName, Conf) of true -> % set basic-resource and content-type BasicResource = openflax.conf:get_string(sreq_basic_resource, Conf), ContentType = openflax.conf:get_string(cfg_ls_index_ctype, Conf), Conf0 = openflax.conf:put_value(sreq_basic_resource, filename:join(BasicResource, IndexFile), Conf), Conf1 = openflax.conf:put_value(res_content_type, ContentType, Conf0), {openflax.handler.file, Conf1}; false -> serve2(Conf, DirName) end. serve2(Conf, DirName) -> % read index.pub, if it exists IndexPub = filename:join([DirName, "index.pub"]), Conf0 = case file:consult(IndexPub) of {ok, List} -> lists:foldl(fun({Key, Value}, Acc) -> openflax.conf:put_value(Key, Value, Acc) end, Conf, List); _ -> Conf end, serve3(Conf0, DirName). serve3(Conf, DirName) -> % Serve the actual directory listing BasicResource = openflax.conf:get_string(sreq_basic_resource, Conf), EachFile = openflax.conf:get_string(cfg_ls_template_each_file, Conf), EachDir = openflax.conf:get_string(cfg_ls_template_each_subdir, Conf), Filter = openflax.conf:get_string(cfg_ls_filter, Conf), Keyword = openflax.conf:get_string(arg_keyword, Conf), FileDescs = case openflax.conf:get_value(cfg_ls_filedescs, Conf) of {ok, FD} -> FD; _ -> [] end, Links = case openflax.conf:get_value(cfg_ls_links, Conf) of {ok, LS} -> LS; _ -> [] end, {ok, Dir} = file:list_dir(DirName), Body = lists:foldl(fun (FileName, Acc) -> AppName = case string:rchr(FileName, $-) of 0 -> FileName; N -> string:left(FileName, N-1) end, FullFileName = filename:join([DirName, FileName]), Conf0 = openflax.conf:put_value(tpl_ls_filename, FileName, Conf), FileLink = urlize(FileName), Conf1 = openflax.conf:put_value(tpl_ls_filelink, FileLink, Conf0), Conf2 = openflax.conf:put_value(tpl_ls_filesize, openflax.string:from_term(filelib:file_size(FullFileName)), Conf1), Conf3 = openflax.conf:put_value(tpl_ls_filedate, openflax.string:from_term(filelib:last_modified(FullFileName)), Conf2), {FileDesc, Keywords} = case lists:keysearch(AppName, 1, FileDescs) of {value, {_, F}} -> {F, []}; {value, {_, F, Prereq, K}} -> {F, K}; {value, {_, F, Prereq, K, _}} -> {F, K}; _ -> {"", []} end, Conf4 = openflax.conf:put_value(tpl_ls_filedesc, FileDesc, Conf3), KeywordsString = [ ["", atom_to_list(KW), " "] || KW <- Keywords ], Conf5 = openflax.conf:put_value(tpl_ls_filekeywords, KeywordsString, Conf4), FConf = Conf5, case {matches(Keyword, Keywords), filelib:is_dir(FullFileName), openflax.handler.file:is_world_readable(FullFileName), openflax.handler.file:is_world_executable(FullFileName), openflax.handler.file:is_world_writeable(FullFileName), filter(FileName, Filter, true)} of {true, true, true, true, false, true} -> [Acc, openflax.handler.template:fill_out(EachDir, FConf)]; {true, false, true, _, false, true} -> [Acc, openflax.handler.template:fill_out(EachFile, FConf)]; _ -> Acc end end, "", lists:sort(Dir)), LinksBody = lists:foldl(fun ({LinkURL, LinkDesc, LinkKeywords}, Acc) -> case matches(Keyword, LinkKeywords) of true -> [Acc, "", LinkDesc, "
"]; false -> Acc end; (_, Acc) -> Acc end, "", Links), Conf0 = openflax.conf:put_value(tpl_ls_body, Body, Conf), Conf1 = openflax.conf:put_value(tpl_ls_links, LinksBody, Conf0), Conf2 = openflax.conf:put_value(tpl_ls_hierarchy, html_hierarchy(BasicResource), Conf1), KeywordNote = case Keyword of "" -> ""; _ -> "showing entries with keyword " ++ Keyword ++ "; show all" end, Conf3 = openflax.conf:put_value(tpl_ls_keyword, KeywordNote, Conf2), {openflax.handler.template, Conf3}. filter(FileName, [], Acc) -> Acc; filter(FileName, [{Pattern, Boolean} | Tail], Acc) -> Acc0 = case regexp:first_match(FileName, regexp:sh_to_awk(Pattern)) of {match, _, _} -> Boolean; _ -> Acc end, filter(FileName, Tail, Acc0). matches("", _) -> true; matches(Keyword, Keywords) -> lists:member(list_to_atom(Keyword), Keywords). urlize(FileName) -> FileName0 = lists:map(fun ($ ) -> "%20"; ($?) -> "%3f"; ($#) -> "%23"; (Else) -> Else end, FileName), lists:flatten(FileName0). html_hierarchy(Root) -> DirList = filename:split(Root), {Text, _} = lists:foldl(fun(X, {A, B}) -> Y = case X of "/" -> "/"; O -> O ++ "/" end, G = B ++ Y, {A ++ "" ++ Y ++ " ", G} end, {"", ""}, DirList), lists:flatten(Text). escape_hex(List) -> lists:reverse(escape_hex(List, [])). escape_hex([$ | T], Acc) -> escape_hex(T, [$0, $2, $% | Acc]); escape_hex([H | T], Acc) -> escape_hex(T, [H | Acc]). %%% END of openflax/handler/ls.erl %%%