%%%---------------------------------------------------------------------- %%% File : mod_muc_log_http.erl %%% Author : Badlop, Massimiliano Mirra %%% Purpose : MUC logs simple file server %%% Created : %%% Id : %%%---------------------------------------------------------------------- -module(mod_muc_log_http). -behaviour(gen_mod). -export([ start/2, stop/1, loop/1, process/2 ]). -include("ejabberd.hrl"). -include("jlib.hrl"). -include("ejabberd_http.hrl"). -include("mod_muc_room.hrl"). -include("logger.hrl"). -include_lib("kernel/include/file.hrl"). -define(PROCNAME, mod_muc_log_http). % TODO: % - If chatroom is password protected, ask password % - If chatroom is only for members, ask for username and password %% Copied from mod_muc/mod_muc.erl -record(muc_online_room, {name_host, pid}). %%%---------------------------------------------------------------------- %%% REQUEST HANDLERS %%%---------------------------------------------------------------------- process(LocalPath, Request) -> serve(LocalPath, Request). serve(LocalPathBin, #request{host = Host} = Request) -> Proc = gen_mod:get_module_proc(Host, ?PROCNAME), Proc ! {get_docroot, self()}, receive DocRoot -> ok end, LocalPath = [binary_to_list(LPB) || LPB <- LocalPathBin], FileName = filename:join(filename:split(DocRoot) ++ LocalPath), case file:read_file(FileName) of {ok, FileContents} -> ?DEBUG("Delivering content.", []), {200, [{"Server", "ejabberd"}, {"Last-Modified", last_modified(FileName)}, {"Content-Type", content_type(FileName)}], FileContents}; {error, eisdir} -> FileNameIndex = FileName ++ "/index.html", case file:read_file_info(FileNameIndex) of {ok, _FileInfo} -> serve(LocalPath ++ ["index.html"], Request); {error, _Error} -> show_dir_listing(FileName, LocalPath) end; {error, Error} -> ?DEBUG("Delivering error: ~p", [Error]), case Error of eacces -> {403, [], "Forbidden"}; enoent -> {404, [], "Not found"}; _Else -> {404, [], atom_to_list(Error)} end end. %%%---------------------------------------------------------------------- %%% Dir listing %%%---------------------------------------------------------------------- build_datetimelist(DateTime) -> {{Ye, Mo, Da}, {Ho, Mi, Se}} = DateTime, Nums = [Mo, Da, Ho, Mi, Se], Nums2 = [fill(Num) || Num <- Nums], [integer_to_list(Ye)] ++ Nums2. fill(Num) when Num < 10 -> io_lib:format("0~p", [Num]); fill(Num) -> io_lib:format("~p", [Num]). %%%---------------------------------------------------------------------- %%% MUC INFO %%%---------------------------------------------------------------------- get_room_pid(Name, Host) -> case ets:lookup(muc_online_room, {Name, Host}) of [] -> unknown; [Room_ets] -> Room_ets#muc_online_room.pid end. get_room_config(Room_pid) -> {ok, C} = gen_fsm:sync_send_all_state_event(Room_pid, get_config), C. %%%---------------------------------------------------------------------- %%% MUC LIST %%%---------------------------------------------------------------------- show_dir_listing(DirName, LocalPath) -> Header = io_lib:format("Name Last modified Size Description~n", []), Address = io_lib:format("
ejabberd/~s Server
", [?VERSION]), {ok, Listing} = file:list_dir(DirName), Listing2 = lists:sort(Listing), Listing3 = case LocalPath of [] -> lists:filter( fun(RoomFull) -> case string:tokens(RoomFull, "@") of [Room, Host] -> case get_room_pid(Room, Host) of unknown -> true; Room_pid -> (get_room_config(Room_pid))#config.public end; _ -> false % Don't show files that are not rooms end end, Listing2); _ -> Listing2 end, Listing4 = lists:map( fun(RoomFull) -> Desc = case string:tokens(RoomFull, "@") of [Room, Host] -> case get_room_pid(Room, Host) of unknown -> "-"; Room_pid -> (get_room_config(Room_pid))#config.title end; _ -> "-" end, {RoomFull, Desc} end, Listing3), DirNest = ["Chatroom logs" | LocalPath], {_, Indexof1} = lists:foldl( fun(E, {N, Res}) -> D = string:copies("../", N), Res2 = Res ++ io_lib:format("~s", [D, E]) ++ " > ", {N-1, Res2} end, {length(DirNest)-1, ""}, DirNest), Title1 = lists:foldl( fun(E, Res) -> Res ++ E ++ " > " end, "", DirNest), Title = io_lib:format("~s", [Title1]), Indexof = io_lib:format("

~s

", [Indexof1]), Files = lists:foldl( fun({Filename, Description}, Res) -> {ok, Fi} = file:read_file_info(DirName ++ "/" ++ Filename), {Filename2, Size} = case Fi#file_info.type of directory -> {Filename ++ "/", "-"}; _ -> {Filename, integer_to_list(Fi#file_info.size)} end, DateTimeList = build_datetimelist(Fi#file_info.ctime), Time = io_lib:format("~s-~s-~s ~s:~s:~s", DateTimeList), FillSpace = lists:flatten(lists:duplicate(50-length(Filename2), " ")), FileString = io_lib:format("~s~s ~s ~10s ~s~n", [Filename2, Filename2, FillSpace, Time, Size, Description]), Res ++ FileString end, "", Listing4), Content = "" ++ Title ++ "
"
		++ Indexof
		++ Header
		++ "
" ++ Files ++ "
" ++ Address ++ "", {200, [{"Server", "ejabberd"}, {"Content-Type", "text/html"}], Content}. %%%---------------------------------------------------------------------- %%% UTILITIES %%%---------------------------------------------------------------------- content_type(Filename) -> case string:to_lower(filename:extension(Filename)) of ".jpg" -> "image/jpeg"; ".jpeg" -> "image/jpeg"; ".gif" -> "image/gif"; ".png" -> "image/png"; ".html" -> "text/html"; ".css" -> "text/css"; ".txt" -> "text/plain"; ".xul" -> "application/vnd.mozilla.xul+xml"; ".jar" -> "application/java-archive"; ".xpi" -> "application/x-xpinstall"; _Else -> "application/octet-stream" end. last_modified(FileName) -> {ok, FileInfo} = file:read_file_info(FileName), Then = FileInfo#file_info.mtime, httpd_util:rfc1123_date(Then). loop(DocRoot) -> receive {get_docroot, Pid} -> Pid ! DocRoot, loop(DocRoot); stop -> ok end. %%%---------------------------------------------------------------------- %%% BEHAVIOUR CALLBACKS %%%---------------------------------------------------------------------- start(Host, _Opts) -> DocRootBin = gen_mod:get_module_opt(Host, mod_muc_log, outdir, fun(A) -> A end, <<"www/muc">>), Proc = gen_mod:get_module_proc(Host, ?PROCNAME), DocRoot = binary_to_list(DocRootBin), catch register(Proc, spawn(?MODULE, loop, [DocRoot])), ok. stop(Host) -> Proc = gen_mod:get_module_proc(Host, ?PROCNAME), exit(whereis(Proc), stop), {wait, Proc}.