diff options
Diffstat (limited to 'src/web/mod_http_fileserver_log.erl')
-rw-r--r-- | src/web/mod_http_fileserver_log.erl | 199 |
1 files changed, 105 insertions, 94 deletions
diff --git a/src/web/mod_http_fileserver_log.erl b/src/web/mod_http_fileserver_log.erl index b76f947c8..8f634f487 100644 --- a/src/web/mod_http_fileserver_log.erl +++ b/src/web/mod_http_fileserver_log.erl @@ -1,30 +1,35 @@ --module (mod_http_fileserver_log). +-module(mod_http_fileserver_log). --behaviour (gen_server). +-behaviour(gen_server). --export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). +-export([init/1, handle_call/3, handle_cast/2, + handle_info/2, terminate/2, code_change/3]). --export ([start_link/2,start/2, stop/1, add_to_log/4,reopen_log/1]). +-export([start_link/2, start/2, stop/1, add_to_log/4, + reopen_log/1]). -include("ejabberd.hrl"). + -include("jlib.hrl"). + -include("ejabberd_http.hrl"). + -include_lib("kernel/include/file.hrl"). -define(PROCNAME, ejabberd_mod_http_fileserver_log). --record(state, {host,accesslog, accesslogfd}). +-record(state, {host = <<"">>, + accesslog :: binary(), + accesslogfd :: file:io_device()}). + %% Public API start(Host, Filename) -> - Proc =gen_mod:get_module_proc(Host, ?PROCNAME), - ChildSpec = - {Proc, - {?MODULE, start_link, [Host, Filename]}, - transient, % if process crashes abruptly, it gets restarted - 1000, - worker, - [?MODULE]}, + Proc = gen_mod:get_module_proc(Host, ?PROCNAME), + ChildSpec = {Proc, + {?MODULE, start_link, [Host, Filename]}, + transient, % if process crashes abruptly, it gets restarted + 1000, worker, [?MODULE]}, supervisor:start_child(ejabberd_sup, ChildSpec). stop(Host) -> @@ -35,133 +40,139 @@ stop(Host) -> start_link(Host, Filename) -> Proc = gen_mod:get_module_proc(Host, ?PROCNAME), - gen_server:start_link({local, Proc}, ?MODULE, [Host, Filename], []). + gen_server:start_link({local, Proc}, ?MODULE, + [Host, Filename], []). -add_to_log(Host,FileSize, Code, Request) -> - gen_server:cast(gen_mod:get_module_proc(Host, ?PROCNAME), +-spec add_to_log(binary(), non_neg_integer(), non_neg_integer(), + http_request()) -> ok. + +add_to_log(Host, FileSize, Code, Request) -> + gen_server:cast(gen_mod:get_module_proc(Host, + ?PROCNAME), {add_to_log, FileSize, Code, Request}). - + +-spec reopen_log(binary()) -> ok. + reopen_log(Host) -> - gen_server:cast(gen_mod:get_module_proc(Host, ?PROCNAME), reopen_log). + gen_server:cast(gen_mod:get_module_proc(Host, + ?PROCNAME), + reopen_log). %% Server implementation, a.k.a.: callbacks init([Host, Filename]) -> try try_open_log(Filename, Host) of - AccessLogFD -> - ?DEBUG("File opened !", []), - {ok, #state{host = Host, - accesslog = Filename, - accesslogfd = AccessLogFD}} + AccessLogFD -> + ?DEBUG("File opened !", []), + {ok, + #state{host = Host, accesslog = Filename, + accesslogfd = AccessLogFD}} catch - throw:Reason -> - {stop, Reason} + Reason -> {stop, Reason} end. try_open_log(FN, Host) -> FD = try open_log(FN) of - FD1 -> FD1 + FD1 -> FD1 catch - throw:{cannot_open_accesslog, FN, Reason} -> - ?ERROR_MSG("Cannot open access log file: ~p~nReason: ~p", [FN, Reason]), - undefined + {cannot_open_accesslog, FN, Reason} -> + ?ERROR_MSG("Cannot open access log file: ~p~nReason: ~p", + [FN, Reason]), + undefined end, - %HostB = list_to_binary(Host), - ejabberd_hooks:add(reopen_log_hook, Host, ?MODULE, reopen_log, 50), + ejabberd_hooks:add(reopen_log_hook, Host, ?MODULE, + reopen_log, 50), FD. - + handle_call(_Request, _From, State) -> - {reply, ok, State}. + {reply, ok, State}. -handle_cast({add_to_log, FileSize, Code, Request}, State) -> - add_to_log2(State#state.accesslogfd, FileSize, Code, Request), +handle_cast({add_to_log, FileSize, Code, Request}, + State) -> + add_to_log2(State#state.accesslogfd, FileSize, Code, + Request), {noreply, State}; handle_cast(reopen_log, State) -> - FD2 = reopen_log(State#state.accesslog, State#state.accesslogfd), + FD2 = reopen_log(State#state.accesslog, + State#state.accesslogfd), {noreply, State#state{accesslogfd = FD2}}; -handle_cast(_Msg, State) -> - {noreply, State}. +handle_cast(_Msg, State) -> {noreply, State}. -handle_info(_Info, State) -> - {noreply, State}. +handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, State) -> close_log(State#state.accesslogfd), - ejabberd_hooks:delete(reopen_log_hook, State#state.host, ?MODULE, reopen_log, 50), + ejabberd_hooks:delete(reopen_log_hook, State#state.host, + ?MODULE, reopen_log, 50), ok. -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - - +code_change(_OldVsn, State, _Extra) -> {ok, State}. + %%---------------------------------------------------------------------- %% Log file %%---------------------------------------------------------------------- +-spec open_log(binary()) -> file:io_device(). + open_log(FN) -> case file:open(FN, [append]) of - {ok, FD} -> - FD; - {error, Reason} -> - throw({cannot_open_accesslog, FN, Reason}) + {ok, FD} -> FD; + {error, Reason} -> + throw({cannot_open_accesslog, FN, Reason}) end. -close_log(FD) -> - file:close(FD). +close_log(FD) -> file:close(FD). -reopen_log(undefined, undefined) -> - ok; +reopen_log(undefined, undefined) -> ok; reopen_log(FN, FD) -> ?DEBUG("reopening logs", []), close_log(FD), open_log(FN). - +-spec add_to_log2(file:io_device(), non_neg_integer(), non_neg_integer(), + http_request()) -> ok. add_to_log2(undefined, _FileSize, _Code, _Request) -> ok; add_to_log2(File, FileSize, Code, Request) -> - {{Year, Month, Day}, {Hour, Minute, Second}} = calendar:local_time(), - IP = ip_to_string(element(1, Request#request.ip)), - Path = join(Request#request.path, "/"), - Query = case join(lists:map(fun(E) -> lists:concat([element(1, E), "=", element(2, E)]) end, - Request#request.q), "&") of - [] -> - ""; - String -> - [$? | String] + {{Year, Month, Day}, {Hour, Minute, Second}} = + calendar:local_time(), + IP = jlib:ip_to_list(Request#request.ip), + Path = str:join(Request#request.path, <<"/">>), + Query = case str:join( + lists:flatmap(fun ({nokey, []}) -> + []; + ({K, V}) -> + [<<K/binary, $=, V/binary>>] + end, Request#request.q), + <<"&">>) of + <<"">> -> <<"">>; + String -> <<$?, String/binary>> end, - UserAgent = find_header('User-Agent', Request#request.headers, "-"), - Referer = find_header('Referer', Request#request.headers, "-"), - %% Pseudo Combined Apache log format: - %% 127.0.0.1 - - [28/Mar/2007:18:41:55 +0200] "GET / HTTP/1.1" 302 303 "-" "tsung" - %% TODO some fields are harcoded/missing: - %% The date/time integers should have always 2 digits. For example day "7" should be "07" - %% Month should be 3*letter, not integer 1..12 - %% Missing time zone = (`+' | `-') 4*digit - %% Missing protocol version: HTTP/1.1 - %% For reference: http://httpd.apache.org/docs/2.2/logs.html - io:format(File, "~s - - [~p/~p/~p:~p:~p:~p] \"~s /~s~s\" ~p ~p ~p ~p~n", - [IP, Day, Month, Year, Hour, Minute, Second, Request#request.method, Path, Query, Code, - FileSize, Referer, UserAgent]). - + UserAgent = find_header('User-Agent', + Request#request.headers, <<"-">>), + Referer = find_header('Referer', + Request#request.headers, <<"-">>), + io:format(File, + <<"~s - - [~p/~p/~p:~p:~p:~p] \"~s /~s~s\" " + "~p ~p \"~s\" \"~s\"~n">>, + [IP, Day, Month, Year, Hour, Minute, Second, + Request#request.method, Path, Query, Code, FileSize, + escape_quote(Referer), escape_quote(UserAgent)]). + find_header(Header, Headers, Default) -> case lists:keysearch(Header, 1, Headers) of - {value, {_, Value}} -> Value; - false -> Default + {value, {_, Value}} -> Value; + false -> Default end. - -join([], _) -> - ""; -join([E], _) -> - E; -join([H | T], Separator) -> - lists:foldl(fun(E, Acc) -> lists:concat([Acc, Separator, E]) end, H, T). - -%% Convert IP address tuple to string representation. Accepts either -%% IPv4 or IPv6 address tuples. -ip_to_string(Address) when size(Address) == 4 -> - join(tuple_to_list(Address), "."); -ip_to_string(Address) when size(Address) == 8 -> - Parts = lists:map(fun (Int) -> io_lib:format("~.16B", [Int]) end, tuple_to_list(Address)), - string:to_lower(lists:flatten(join(Parts, ":"))).
\ No newline at end of file + + +escape_quote(B) -> + escape_quote(B, <<>>). + +escape_quote(<<$", Rest/binary>>, Acc) -> + escape_quote(Rest, <<Acc/binary, $\\, $">>); +escape_quote(<<C, Rest/binary>>, Acc) -> + escape_quote(Rest, <<Acc/binary, C>>); +escape_quote(<<>>, Acc) -> + Acc. |