ejabberd-contrib/mod_muc_log_http/src/mod_muc_log_http.erl

254 lines
6.7 KiB
Erlang

%%%----------------------------------------------------------------------
%%% 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("<address>ejabberd/~s Server</address>", [?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("<a href='~s'>~s</a>", [D, E])
++ " &gt; ",
{N-1, Res2}
end,
{length(DirNest)-1, ""},
DirNest),
Title1 = lists:foldl(
fun(E, Res) ->
Res ++ E ++ " &gt; "
end,
"",
DirNest),
Title = io_lib:format("<title>~s</title>", [Title1]),
Indexof = io_lib:format("<h1>~s</h1>", [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("<a href='~s'>~s</a>~s ~s ~10s ~s~n",
[Filename2, Filename2, FillSpace, Time, Size, Description]),
Res ++ FileString
end,
"",
Listing4),
Content = "<html><head>"
++ Title
++ "</head><body><pre>"
++ Indexof
++ Header
++ "<hr>"
++ Files
++ "<hr></pre>"
++ Address
++ "</body></html>",
{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}.