aboutsummaryrefslogtreecommitdiff
path: root/src/web
diff options
context:
space:
mode:
Diffstat (limited to 'src/web')
-rw-r--r--src/web/Makefile.in37
-rw-r--r--src/web/Makefile.win3233
-rw-r--r--src/web/ejabberd_http.erl826
-rw-r--r--src/web/ejabberd_http.hrl36
-rw-r--r--src/web/ejabberd_http_bind.erl1235
-rw-r--r--src/web/ejabberd_http_poll.erl428
-rw-r--r--src/web/ejabberd_web.erl105
-rw-r--r--src/web/ejabberd_web_admin.erl2893
-rw-r--r--src/web/ejabberd_web_admin.hrl105
-rw-r--r--src/web/http_bind.hrl47
-rw-r--r--src/web/mod_http_bind.erl144
-rw-r--r--src/web/mod_http_fileserver.erl454
-rw-r--r--src/web/mod_register_web.erl565
13 files changed, 0 insertions, 6908 deletions
diff --git a/src/web/Makefile.in b/src/web/Makefile.in
deleted file mode 100644
index 151f4c476..000000000
--- a/src/web/Makefile.in
+++ /dev/null
@@ -1,37 +0,0 @@
-# $Id$
-
-CC = @CC@
-CFLAGS = @CFLAGS@
-CPPFLAGS = @CPPFLAGS@
-LDFLAGS = @LDFLAGS@
-LIBS = @LIBS@
-
-ERLANG_CFLAGS = @ERLANG_CFLAGS@
-ERLANG_LIBS = @ERLANG_LIBS@
-
-EFLAGS += -I ..
-EFLAGS += -pz ..
-
-# make debug=true to compile Erlang module with debug informations.
-ifdef debug
- EFLAGS+=+debug_info
-endif
-
-SOURCES = $(wildcard *.erl)
-OUTDIR = ..
-BEAMS = $(addprefix $(OUTDIR)/,$(SOURCES:.erl=.beam))
-
-all: $(BEAMS)
-
-$(OUTDIR)/%.beam: %.erl
- @ERLC@ -W $(EFLAGS) -o $(OUTDIR) $<
-
-clean:
- rm -f $(BEAMS)
-
-distclean: clean
- rm -f Makefile
-
-TAGS:
- etags *.erl
-
diff --git a/src/web/Makefile.win32 b/src/web/Makefile.win32
deleted file mode 100644
index 411d57ce8..000000000
--- a/src/web/Makefile.win32
+++ /dev/null
@@ -1,33 +0,0 @@
-
-include ..\Makefile.inc
-
-EFLAGS = -I .. -pz ..
-
-OUTDIR = ..
-BEAMS = ..\ejabberd_http.beam ..\ejabberd_http_bind.beam ..\ejabberd_http_poll.beam ..\ejabberd_web.beam ..\ejabberd_web_admin.beam ..\mod_http_bind.beam ..\mod_http_fileserver.beam
-
-ALL : $(BEAMS)
-
-CLEAN :
- -@erase $(BEAMS)
-
-$(OUTDIR)\ejabberd_http.beam : ejabberd_http.erl
- erlc -W $(EFLAGS) -o $(OUTDIR) ejabberd_http.erl
-
-$(OUTDIR)\ejabberd_web.beam : ejabberd_web.erl
- erlc -W $(EFLAGS) -o $(OUTDIR) ejabberd_web.erl
-
-$(OUTDIR)\ejabberd_web_admin.beam : ejabberd_web_admin.erl
- erlc -W $(EFLAGS) -o $(OUTDIR) ejabberd_web_admin.erl
-
-$(OUTDIR)\ejabberd_http_bind.beam : ejabberd_http_bind.erl
- erlc -W $(EFLAGS) -o $(OUTDIR) ejabberd_http_bind.erl
-
-$(OUTDIR)\ejabberd_http_poll.beam : ejabberd_http_poll.erl
- erlc -W $(EFLAGS) -o $(OUTDIR) ejabberd_http_poll.erl
-
-$(OUTDIR)\mod_http_bind.beam : mod_http_bind.erl
- erlc -W $(EFLAGS) -o $(OUTDIR) mod_http_bind.erl
-
-$(OUTDIR)\mod_http_fileserver.beam : mod_http_fileserver.erl
- erlc -W $(EFLAGS) -o $(OUTDIR) mod_http_fileserver.erl
diff --git a/src/web/ejabberd_http.erl b/src/web/ejabberd_http.erl
deleted file mode 100644
index 25928c0af..000000000
--- a/src/web/ejabberd_http.erl
+++ /dev/null
@@ -1,826 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File : ejabberd_http.erl
-%%% Author : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose :
-%%% Created : 27 Feb 2004 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
--module(ejabberd_http).
-
--author('alexey@process-one.net').
-
-%% External exports
--export([start/2, start_link/2, become_controller/1,
- socket_type/0, receive_headers/1, url_encode/1]).
-
-%% Callbacks
--export([init/2]).
-
--include("ejabberd.hrl").
-
--include("jlib.hrl").
-
--include("ejabberd_http.hrl").
-
--record(state, {sockmod,
- socket,
- request_method,
- request_version,
- request_path,
- request_auth,
- request_keepalive,
- request_content_length,
- request_lang = "en",
- %% XXX bard: request handlers are configured in
- %% ejabberd.cfg under the HTTP service. For example,
- %% to have the module test_web handle requests with
- %% paths starting with "/test/module":
- %%
- %% {5280, ejabberd_http, [http_poll, web_admin,
- %% {request_handlers, [{["test", "module"], mod_test_web}]}]}
- %%
- request_handlers = [],
- request_host,
- request_port,
- request_tp,
- request_headers = [],
- end_of_request = false,
- default_host,
- trail = <<>>
- }).
-
--define(XHTML_DOCTYPE,
- <<"<?xml version='1.0'?>\n<!DOCTYPE html "
- "PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//"
- "EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1"
- "-transitional.dtd\">\n">>).
-
--define(HTML_DOCTYPE,
- <<"<!DOCTYPE html PUBLIC \"-//W3C//DTD "
- "XHTML 1.0 Transitional//EN\" \"http://www.w3."
- "org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
- "">>).
-
-start(SockData, Opts) ->
- supervisor:start_child(ejabberd_http_sup,
- [SockData, Opts]).
-
-start_link(SockData, Opts) ->
- {ok,
- proc_lib:spawn_link(ejabberd_http, init,
- [SockData, Opts])}.
-
-init({SockMod, Socket}, Opts) ->
- TLSEnabled = lists:member(tls, Opts),
- TLSOpts1 = lists:filter(fun ({certfile, _}) -> true;
- (_) -> false
- end,
- Opts),
- TLSOpts = [verify_none | TLSOpts1],
- {SockMod1, Socket1} = if TLSEnabled ->
- inet:setopts(Socket, [{recbuf, 8192}]),
- {ok, TLSSocket} = tls:tcp_to_tls(Socket,
- TLSOpts),
- {tls, TLSSocket};
- true -> {SockMod, Socket}
- end,
- case SockMod1 of
- gen_tcp ->
- inet:setopts(Socket1, [{packet, http_bin}, {recbuf, 8192}]);
- _ -> ok
- end,
- Captcha = case lists:member(captcha, Opts) of
- true -> [{[<<"captcha">>], ejabberd_captcha}];
- false -> []
- end,
- Register = case lists:member(register, Opts) of
- true -> [{[<<"register">>], mod_register_web}];
- false -> []
- end,
- Admin = case lists:member(web_admin, Opts) of
- true -> [{[<<"admin">>], ejabberd_web_admin}];
- false -> []
- end,
- Bind = case lists:member(http_bind, Opts) of
- true -> [{[<<"http-bind">>], mod_http_bind}];
- false -> []
- end,
- Poll = case lists:member(http_poll, Opts) of
- true -> [{[<<"http-poll">>], ejabberd_http_poll}];
- false -> []
- end,
- DefinedHandlers = case lists:keysearch(request_handlers,
- 1, Opts)
- of
- {value, {request_handlers, H}} -> H;
- false -> []
- end,
- RequestHandlers = DefinedHandlers ++ Captcha ++ Register ++
- Admin ++ Bind ++ Poll,
- ?DEBUG("S: ~p~n", [RequestHandlers]),
-
- DefaultHost = gen_mod:get_opt(default_host, Opts, fun(A) -> A end, undefined),
-
- ?INFO_MSG("started: ~p", [{SockMod1, Socket1}]),
- State = #state{sockmod = SockMod1,
- socket = Socket1,
- default_host = DefaultHost,
- request_handlers = RequestHandlers},
- receive_headers(State).
-
-become_controller(_Pid) -> ok.
-
-socket_type() ->
- raw.
-
-send_text(State, Text) ->
- case catch
- (State#state.sockmod):send(State#state.socket, Text)
- of
- ok -> ok;
- {error, timeout} ->
- ?INFO_MSG("Timeout on ~p:send", [State#state.sockmod]),
- exit(normal);
- Error ->
- ?DEBUG("Error in ~p:send: ~p",
- [State#state.sockmod, Error]),
- exit(normal)
- end.
-
-receive_headers(#state{trail = Trail} = State) ->
- SockMod = State#state.sockmod,
- Socket = State#state.socket,
- Data = SockMod:recv(Socket, 0, 300000),
- case State#state.sockmod of
- gen_tcp ->
- NewState = process_header(State, Data),
- case NewState#state.end_of_request of
- true ->
- ok;
- _ ->
- receive_headers(NewState)
- end;
- _ ->
- case Data of
- {ok, D} ->
- parse_headers(State#state{trail = <<Trail/binary, D/binary>>});
- {error, _} ->
- ok
- end
- end.
-
-parse_headers(#state{trail = <<>>} = State) ->
- receive_headers(State);
-parse_headers(#state{request_method = Method,
- trail = Data} =
- State) ->
- PktType = case Method of
- undefined -> http;
- _ -> httph
- end,
- case erlang:decode_packet(PktType, Data) of
- {ok, Pkt, Rest} ->
- NewState = process_header(State#state{trail = Rest}, {ok, Pkt}),
- case NewState#state.end_of_request of
- true ->
- ok;
- _ ->
- parse_headers(NewState)
- end;
- {more, _} ->
- receive_headers(State#state{trail = Data});
- _ ->
- ok
- end.
-
-process_header(State, Data) ->
- SockMod = State#state.sockmod,
- Socket = State#state.socket,
- case Data of
- {ok, {http_request, Method, Uri, Version}} ->
- KeepAlive = case Version of
- {1, 1} -> true;
- _ -> false
- end,
- Path = case Uri of
- {absoluteURI, _Scheme, _Host, _Port, P} ->
- {abs_path, P};
- {abs_path, P} ->
- {abs_path, P};
- _ -> Uri
- end,
- State#state{request_method = Method,
- request_version = Version, request_path = Path,
- request_keepalive = KeepAlive};
- {ok, {http_header, _, 'Connection' = Name, _, Conn}} ->
- KeepAlive1 = case jlib:tolower(Conn) of
- <<"keep-alive">> -> true;
- <<"close">> -> false;
- _ -> State#state.request_keepalive
- end,
- State#state{request_keepalive = KeepAlive1,
- request_headers = add_header(Name, Conn, State)};
- {ok,
- {http_header, _, 'Authorization' = Name, _, Auth}} ->
- State#state{request_auth = parse_auth(Auth),
- request_headers = add_header(Name, Auth, State)};
- {ok,
- {http_header, _, 'Content-Length' = Name, _, SLen}} ->
- case catch jlib:binary_to_integer(SLen) of
- Len when is_integer(Len) ->
- State#state{request_content_length = Len,
- request_headers = add_header(Name, SLen, State)};
- _ -> State
- end;
- {ok,
- {http_header, _, 'Accept-Language' = Name, _, Langs}} ->
- State#state{request_lang = parse_lang(Langs),
- request_headers = add_header(Name, Langs, State)};
- {ok, {http_header, _, 'Host' = Name, _, Host}} ->
- State#state{request_host = Host,
- request_headers = add_header(Name, Host, State)};
- {ok, {http_header, _, Name, _, Value}} ->
- State#state{request_headers =
- add_header(Name, Value, State)};
- {ok, http_eoh}
- when State#state.request_host == undefined ->
- ?WARNING_MSG("An HTTP request without 'Host' HTTP "
- "header was received.",
- []),
- throw(http_request_no_host_header);
- {ok, http_eoh} ->
- ?DEBUG("(~w) http query: ~w ~s~n",
- [State#state.socket, State#state.request_method,
- element(2, State#state.request_path)]),
- {HostProvided, Port, TP} =
- get_transfer_protocol(SockMod,
- State#state.request_host),
- Host = get_host_really_served(State#state.default_host,
- HostProvided),
- State2 = State#state{request_host = Host,
- request_port = Port, request_tp = TP},
- Out = process_request(State2),
- send_text(State2, Out),
- case State2#state.request_keepalive of
- true ->
- case SockMod of
- gen_tcp -> inet:setopts(Socket, [{packet, http_bin}]);
- _ -> ok
- end,
- #state{sockmod = SockMod, socket = Socket,
- request_handlers = State#state.request_handlers};
- _ ->
- #state{end_of_request = true,
- request_handlers = State#state.request_handlers}
- end;
- {error, _Reason} ->
- #state{end_of_request = true,
- request_handlers = State#state.request_handlers};
- _ ->
- #state{end_of_request = true,
- request_handlers = State#state.request_handlers}
- end.
-
-add_header(Name, Value, State)->
- [{Name, Value} | State#state.request_headers].
-
-get_host_really_served(undefined, Provided) ->
- Provided;
-get_host_really_served(Default, Provided) ->
- case lists:member(Provided, ?MYHOSTS) of
- true -> Provided;
- false -> Default
- end.
-
-%% @spec (SockMod, HostPort) -> {Host::string(), Port::integer(), TP}
-%% where
-%% SockMod = gen_tcp | tls
-%% HostPort = string()
-%% TP = http | https
-%% @doc Given a socket and hostport header, return data of transfer protocol.
-%% Note that HostPort can be a string of a host like "example.org",
-%% or a string of a host and port like "example.org:5280".
-get_transfer_protocol(SockMod, HostPort) ->
- [Host | PortList] = str:tokens(HostPort, <<":">>),
- case {SockMod, PortList} of
- {gen_tcp, []} -> {Host, 80, http};
- {gen_tcp, [Port]} ->
- {Host, jlib:binary_to_integer(Port), http};
- {tls, []} -> {Host, 443, https};
- {tls, [Port]} ->
- {Host, jlib:binary_to_integer(Port), https}
- end.
-
-%% XXX bard: search through request handlers looking for one that
-%% matches the requested URL path, and pass control to it. If none is
-%% found, answer with HTTP 404.
-process([], _) ->
- ejabberd_web:error(not_found);
-process(Handlers, Request) ->
- %% Only the first element in the path prefix is checked
- [{HandlerPathPrefix, HandlerModule} | HandlersLeft] =
- Handlers,
- case lists:prefix(HandlerPathPrefix,
- Request#request.path)
- or (HandlerPathPrefix == Request#request.path)
- of
- true ->
- ?DEBUG("~p matches ~p",
- [Request#request.path, HandlerPathPrefix]),
- LocalPath = lists:nthtail(length(HandlerPathPrefix),
- Request#request.path),
- ?DEBUG("~p", [Request#request.headers]),
- R = HandlerModule:process(LocalPath, Request),
- ejabberd_hooks:run(http_request_debug,
- [{LocalPath, Request}]),
- R;
- false -> process(HandlersLeft, Request)
- end.
-
-process_request(#state{request_method = Method,
- request_path = {abs_path, Path}, request_auth = Auth,
- request_lang = Lang, request_handlers = RequestHandlers,
- request_host = Host, request_port = Port,
- request_tp = TP, request_headers = RequestHeaders,
- sockmod = SockMod,
- socket = Socket} = State)
- when Method=:='GET' orelse Method=:='HEAD' orelse Method=:='DELETE' orelse Method=:='OPTIONS' ->
- case (catch url_decode_q_split(Path)) of
- {'EXIT', _} ->
- make_bad_request(State);
- {NPath, Query} ->
- LPath = [path_decode(NPE) || NPE <- str:tokens(NPath, <<"/">>)],
- LQuery = case (catch parse_urlencoded(Query)) of
- {'EXIT', _Reason} ->
- [];
- LQ ->
- LQ
- end,
- {ok, IPHere} =
- case SockMod of
- gen_tcp ->
- inet:peername(Socket);
- _ ->
- SockMod:peername(Socket)
- end,
- XFF = proplists:get_value('X-Forwarded-For', RequestHeaders, []),
- IP = analyze_ip_xff(IPHere, XFF, Host),
- Request = #request{method = Method,
- path = LPath,
- q = LQuery,
- auth = Auth,
- lang = Lang,
- host = Host,
- port = Port,
- tp = TP,
- headers = RequestHeaders,
- ip = IP},
- %% XXX bard: This previously passed control to
- %% ejabberd_web:process_get, now passes it to a local
- %% procedure (process) that handles dispatching based on
- %% URL path prefix.
- case process(RequestHandlers, Request) of
- El when element(1, El) == xmlel ->
- make_xhtml_output(State, 200, [], El);
- {Status, Headers, El} when
- element(1, El) == xmlel ->
- make_xhtml_output(State, Status, Headers, El);
- Output when is_list(Output) or is_binary(Output) ->
- make_text_output(State, 200, [], Output);
- {Status, Headers, Output} when is_list(Output) or is_binary(Output) ->
- make_text_output(State, Status, Headers, Output)
- end
- end;
-process_request(#state{request_method = Method,
- request_path = {abs_path, Path}, request_auth = Auth,
- request_content_length = Len, request_lang = Lang,
- sockmod = SockMod, socket = Socket, request_host = Host,
- request_port = Port, request_tp = TP,
- request_headers = RequestHeaders,
- request_handlers = RequestHandlers} =
- State)
- when (Method =:= 'POST' orelse Method =:= 'PUT') andalso
- is_integer(Len) ->
- {ok, IPHere} = case SockMod of
- gen_tcp -> inet:peername(Socket);
- _ -> SockMod:peername(Socket)
- end,
- XFF = proplists:get_value('X-Forwarded-For',
- RequestHeaders, []),
- IP = analyze_ip_xff(IPHere, XFF, Host),
- case SockMod of
- gen_tcp -> inet:setopts(Socket, [{packet, 0}]);
- _ -> ok
- end,
- Data = recv_data(State, Len),
- ?DEBUG("client data: ~p~n", [Data]),
- case (catch url_decode_q_split(Path)) of
- {'EXIT', _} ->
- make_bad_request(State);
- {NPath, _Query} ->
- LPath = [path_decode(NPE) || NPE <- str:tokens(NPath, <<"/">>)],
- LQuery = case (catch parse_urlencoded(Data)) of
- {'EXIT', _Reason} ->
- [];
- LQ ->
- LQ
- end,
- Request = #request{method = Method,
- path = LPath,
- q = LQuery,
- auth = Auth,
- data = Data,
- lang = Lang,
- host = Host,
- port = Port,
- tp = TP,
- headers = RequestHeaders,
- ip = IP},
- case process(RequestHandlers, Request) of
- El when element(1, El) == xmlel ->
- make_xhtml_output(State, 200, [], El);
- {Status, Headers, El} when
- element(1, El) == xmlel ->
- make_xhtml_output(State, Status, Headers, El);
- Output when is_list(Output) or is_binary(Output) ->
- make_text_output(State, 200, [], Output);
- {Status, Headers, Output} when is_list(Output) or is_binary(Output) ->
- make_text_output(State, Status, Headers, Output)
- end
- end;
-process_request(State) -> make_bad_request(State).
-
-make_bad_request(State) ->
-%% Support for X-Forwarded-From
- make_xhtml_output(State, 400, [],
- ejabberd_web:make_xhtml([#xmlel{name = <<"h1">>,
- attrs = [],
- children =
- [{xmlcdata,
- <<"400 Bad Request">>}]}])).
-
-analyze_ip_xff(IP, [], _Host) -> IP;
-analyze_ip_xff({IPLast, Port}, XFF, Host) ->
- [ClientIP | ProxiesIPs] = str:tokens(XFF, <<", ">>) ++
- [jlib:ip_to_list(IPLast)],
- TrustedProxies = ejabberd_config:get_local_option(
- {trusted_proxies, Host},
- fun(TPs) ->
- [iolist_to_binary(TP) || TP <- TPs]
- end, []),
- IPClient = case is_ipchain_trusted(ProxiesIPs,
- TrustedProxies)
- of
- true ->
- {ok, IPFirst} = inet_parse:address(
- binary_to_list(ClientIP)),
- ?DEBUG("The IP ~w was replaced with ~w due to "
- "header X-Forwarded-For: ~s",
- [IPLast, IPFirst, XFF]),
- IPFirst;
- false -> IPLast
- end,
- {IPClient, Port}.
-
-is_ipchain_trusted(_UserIPs, all) -> true;
-is_ipchain_trusted(UserIPs, TrustedIPs) ->
- [] == UserIPs -- [<<"127.0.0.1">> | TrustedIPs].
-
-recv_data(State, Len) -> recv_data(State, Len, <<>>).
-
-recv_data(_State, 0, Acc) -> (iolist_to_binary(Acc));
-recv_data(State, Len, Acc) ->
- case State#state.trail of
- <<>> ->
- case (State#state.sockmod):recv(State#state.socket, Len,
- 300000)
- of
- {ok, Data} ->
- recv_data(State, Len - byte_size(Data), <<Acc/binary, Data/binary>>);
- _ -> <<"">>
- end;
- _ ->
- Trail = (State#state.trail),
- recv_data(State#state{trail = <<>>},
- Len - byte_size(Trail), <<Acc/binary, Trail/binary>>)
- end.
-
-make_xhtml_output(State, Status, Headers, XHTML) ->
- Data = case lists:member(html, Headers) of
- true ->
- iolist_to_binary([?HTML_DOCTYPE,
- xml:element_to_binary(XHTML)]);
- _ ->
- iolist_to_binary([?XHTML_DOCTYPE,
- xml:element_to_binary(XHTML)])
- end,
- Headers1 = case lists:keysearch(<<"Content-Type">>, 1,
- Headers)
- of
- {value, _} ->
- [{<<"Content-Length">>,
- iolist_to_binary(integer_to_list(byte_size(Data)))}
- | Headers];
- _ ->
- [{<<"Content-Type">>, <<"text/html; charset=utf-8">>},
- {<<"Content-Length">>,
- iolist_to_binary(integer_to_list(byte_size(Data)))}
- | Headers]
- end,
- HeadersOut = case {State#state.request_version,
- State#state.request_keepalive}
- of
- {{1, 1}, true} -> Headers1;
- {_, true} ->
- [{<<"Connection">>, <<"keep-alive">>} | Headers1];
- {_, false} ->
- [{<<"Connection">>, <<"close">>} | Headers1]
- end,
- Version = case State#state.request_version of
- {1, 1} -> <<"HTTP/1.1 ">>;
- _ -> <<"HTTP/1.0 ">>
- end,
- H = lists:map(fun ({Attr, Val}) ->
- [Attr, <<": ">>, Val, <<"\r\n">>];
- (_) -> []
- end,
- HeadersOut),
- SL = [Version,
- iolist_to_binary(integer_to_list(Status)), <<" ">>,
- code_to_phrase(Status), <<"\r\n">>],
- Data2 = case State#state.request_method of
- 'HEAD' -> <<"">>;
- _ -> Data
- end,
- [SL, H, <<"\r\n">>, Data2].
-
-make_text_output(State, Status, Headers, Text) ->
- make_text_output(State, Status, <<"">>, Headers, Text).
-
-make_text_output(State, Status, Reason, Headers, Text) ->
- Data = iolist_to_binary(Text),
- Headers1 = case lists:keysearch(<<"Content-Type">>, 1,
- Headers)
- of
- {value, _} ->
- [{<<"Content-Length">>,
- jlib:integer_to_binary(byte_size(Data))}
- | Headers];
- _ ->
- [{<<"Content-Type">>, <<"text/html; charset=utf-8">>},
- {<<"Content-Length">>,
- jlib:integer_to_binary(byte_size(Data))}
- | Headers]
- end,
- HeadersOut = case {State#state.request_version,
- State#state.request_keepalive}
- of
- {{1, 1}, true} -> Headers1;
- {_, true} ->
- [{<<"Connection">>, <<"keep-alive">>} | Headers1];
- {_, false} ->
- [{<<"Connection">>, <<"close">>} | Headers1]
- end,
- Version = case State#state.request_version of
- {1, 1} -> <<"HTTP/1.1 ">>;
- _ -> <<"HTTP/1.0 ">>
- end,
- H = lists:map(fun ({Attr, Val}) ->
- [Attr, <<": ">>, Val, <<"\r\n">>]
- end,
- HeadersOut),
- NewReason = case Reason of
- <<"">> -> code_to_phrase(Status);
- _ -> Reason
- end,
- SL = [Version,
- jlib:integer_to_binary(Status), <<" ">>,
- NewReason, <<"\r\n">>],
- Data2 = case State#state.request_method of
- 'HEAD' -> <<"">>;
- _ -> Data
- end,
- [SL, H, <<"\r\n">>, Data2].
-
-parse_lang(Langs) ->
- case str:tokens(Langs, <<",; ">>) of
- [First | _] -> First;
- [] -> <<"en">>
- end.
-
-% Code below is taken (with some modifications) from the yaws webserver, which
-% is distributed under the folowing license:
-%
-% This software (the yaws webserver) is free software.
-% Parts of this software is Copyright (c) Claes Wikstrom <klacke@hyber.org>
-% Any use or misuse of the source code is hereby freely allowed.
-%
-% 1. Redistributions of source code must retain the above copyright
-% notice as well as this list of conditions.
-%
-% 2. Redistributions in binary form must reproduce the above copyright
-% notice as well as this list of conditions.
-
-%% @doc Split the URL and return {Path, QueryPart}
-url_decode_q_split(Path) ->
- url_decode_q_split(Path, <<>>).
-
-url_decode_q_split(<<$?, T/binary>>, Acc) ->
- %% Don't decode the query string here, that is parsed separately.
- {path_norm_reverse(Acc), T};
-url_decode_q_split(<<H, T/binary>>, Acc) when H /= 0 ->
- url_decode_q_split(T, <<H, Acc/binary>>);
-url_decode_q_split(<<>>, Ack) ->
- {path_norm_reverse(Ack), <<>>}.
-
-%% @doc Decode a part of the URL and return string()
-path_decode(Path) -> path_decode(Path, <<>>).
-
-path_decode(<<$%, Hi, Lo, Tail/binary>>, Acc) ->
- Hex = hex_to_integer([Hi, Lo]),
- if Hex == 0 -> exit(badurl);
- true -> ok
- end,
- path_decode(Tail, <<Acc/binary, Hex>>);
-path_decode(<<H, T/binary>>, Acc) when H /= 0 ->
- path_decode(T, <<Acc/binary, H>>);
-path_decode(<<>>, Acc) -> Acc.
-
-path_norm_reverse(<<"/", T/binary>>) -> start_dir(0, <<"/">>, T);
-path_norm_reverse(T) -> start_dir(0, <<"">>, T).
-
-start_dir(N, Path, <<"..">>) -> rest_dir(N, Path, <<"">>);
-start_dir(N, Path, <<"/", T/binary>>) -> start_dir(N, Path, T);
-start_dir(N, Path, <<"./", T/binary>>) -> start_dir(N, Path, T);
-start_dir(N, Path, <<"../", T/binary>>) ->
- start_dir(N + 1, Path, T);
-start_dir(N, Path, T) -> rest_dir(N, Path, T).
-
-rest_dir(_N, Path, <<>>) ->
- case Path of
- <<>> -> <<"/">>;
- _ -> Path
- end;
-rest_dir(0, Path, <<$/, T/binary>>) ->
- start_dir(0, <<$/, Path/binary>>, T);
-rest_dir(N, Path, <<$/, T/binary>>) ->
- start_dir(N - 1, Path, T);
-rest_dir(0, Path, <<H, T/binary>>) ->
- rest_dir(0, <<H, Path/binary>>, T);
-rest_dir(N, Path, <<_H, T/binary>>) -> rest_dir(N, Path, T).
-
-%% hex_to_integer
-
-hex_to_integer(Hex) ->
- case catch list_to_integer(Hex, 16) of
- {'EXIT', _} -> old_hex_to_integer(Hex);
- X -> X
- end.
-
-old_hex_to_integer(Hex) ->
- DEHEX = fun (H) when H >= $a, H =< $f -> H - $a + 10;
- (H) when H >= $A, H =< $F -> H - $A + 10;
- (H) when H >= $0, H =< $9 -> H - $0
- end,
- lists:foldl(fun (E, Acc) -> Acc * 16 + DEHEX(E) end, 0,
- Hex).
-
-code_to_phrase(100) -> <<"Continue">>;
-code_to_phrase(101) -> <<"Switching Protocols ">>;
-code_to_phrase(200) -> <<"OK">>;
-code_to_phrase(201) -> <<"Created">>;
-code_to_phrase(202) -> <<"Accepted">>;
-code_to_phrase(203) ->
- <<"Non-Authoritative Information">>;
-code_to_phrase(204) -> <<"No Content">>;
-code_to_phrase(205) -> <<"Reset Content">>;
-code_to_phrase(206) -> <<"Partial Content">>;
-code_to_phrase(300) -> <<"Multiple Choices">>;
-code_to_phrase(301) -> <<"Moved Permanently">>;
-code_to_phrase(302) -> <<"Found">>;
-code_to_phrase(303) -> <<"See Other">>;
-code_to_phrase(304) -> <<"Not Modified">>;
-code_to_phrase(305) -> <<"Use Proxy">>;
-code_to_phrase(306) -> <<"(Unused)">>;
-code_to_phrase(307) -> <<"Temporary Redirect">>;
-code_to_phrase(400) -> <<"Bad Request">>;
-code_to_phrase(401) -> <<"Unauthorized">>;
-code_to_phrase(402) -> <<"Payment Required">>;
-code_to_phrase(403) -> <<"Forbidden">>;
-code_to_phrase(404) -> <<"Not Found">>;
-code_to_phrase(405) -> <<"Method Not Allowed">>;
-code_to_phrase(406) -> <<"Not Acceptable">>;
-code_to_phrase(407) ->
- <<"Proxy Authentication Required">>;
-code_to_phrase(408) -> <<"Request Timeout">>;
-code_to_phrase(409) -> <<"Conflict">>;
-code_to_phrase(410) -> <<"Gone">>;
-code_to_phrase(411) -> <<"Length Required">>;
-code_to_phrase(412) -> <<"Precondition Failed">>;
-code_to_phrase(413) -> <<"Request Entity Too Large">>;
-code_to_phrase(414) -> <<"Request-URI Too Long">>;
-code_to_phrase(415) -> <<"Unsupported Media Type">>;
-code_to_phrase(416) ->
- <<"Requested Range Not Satisfiable">>;
-code_to_phrase(417) -> <<"Expectation Failed">>;
-code_to_phrase(500) -> <<"Internal Server Error">>;
-code_to_phrase(501) -> <<"Not Implemented">>;
-code_to_phrase(502) -> <<"Bad Gateway">>;
-code_to_phrase(503) -> <<"Service Unavailable">>;
-code_to_phrase(504) -> <<"Gateway Timeout">>;
-code_to_phrase(505) -> <<"HTTP Version Not Supported">>.
-
-parse_auth(<<"Basic ", Auth64/binary>>) ->
- Auth = jlib:decode_base64(Auth64),
- %% Auth should be a string with the format: user@server:password
- %% Note that password can contain additional characters '@' and ':'
- case str:chr(Auth, $:) of
- 0 ->
- undefined;
- Pos ->
- {User, <<$:, Pass/binary>>} = erlang:split_binary(Auth, Pos-1),
- {User, Pass}
- end;
-parse_auth(<<_/binary>>) -> undefined.
-
-parse_urlencoded(S) ->
- parse_urlencoded(S, nokey, <<>>, key).
-
-parse_urlencoded(<<$%, Hi, Lo, Tail/binary>>, Last, Cur,
- State) ->
- Hex = hex_to_integer([Hi, Lo]),
- parse_urlencoded(Tail, Last, <<Cur/binary, Hex>>, State);
-parse_urlencoded(<<$&, Tail/binary>>, _Last, Cur, key) ->
- [{Cur, <<"">>} | parse_urlencoded(Tail,
- nokey, <<>>,
- key)]; %% cont keymode
-parse_urlencoded(<<$&, Tail/binary>>, Last, Cur, value) ->
- V = {Last, Cur},
- [V | parse_urlencoded(Tail, nokey, <<>>, key)];
-parse_urlencoded(<<$+, Tail/binary>>, Last, Cur, State) ->
- parse_urlencoded(Tail, Last, <<Cur/binary, $\s>>, State);
-parse_urlencoded(<<$=, Tail/binary>>, _Last, Cur, key) ->
- parse_urlencoded(Tail, Cur, <<>>,
- value); %% change mode
-parse_urlencoded(<<H, Tail/binary>>, Last, Cur, State) ->
- parse_urlencoded(Tail, Last, <<Cur/binary, H>>, State);
-parse_urlencoded(<<>>, Last, Cur, _State) ->
- [{Last, Cur}];
-parse_urlencoded(undefined, _, _, _) -> [].
-
-
-url_encode(A) ->
- url_encode(A, <<>>).
-
-url_encode(<<H:8, T/binary>>, Acc) when
- (H >= $a andalso H =< $z) orelse
- (H >= $A andalso H =< $Z) orelse
- (H >= $0 andalso H =< $9) orelse
- H == $_ orelse
- H == $. orelse
- H == $- orelse
- H == $/ orelse
- H == $: ->
- url_encode(T, <<Acc/binary, H>>);
-url_encode(<<H:8, T/binary>>, Acc) ->
- case integer_to_hex(H) of
- [X, Y] -> url_encode(T, <<Acc/binary, $%, X, Y>>);
- [X] -> url_encode(T, <<Acc/binary, $%, $0, X>>)
- end;
-url_encode(<<>>, Acc) ->
- Acc.
-
-
-integer_to_hex(I) ->
- case catch erlang:integer_to_list(I, 16) of
- {'EXIT', _} -> old_integer_to_hex(I);
- Int -> Int
- end.
-
-old_integer_to_hex(I) when I < 10 -> integer_to_list(I);
-old_integer_to_hex(I) when I < 16 -> [I - 10 + $A];
-old_integer_to_hex(I) when I >= 16 ->
- N = trunc(I / 16),
- old_integer_to_hex(N) ++ old_integer_to_hex(I rem 16).
-
-%% strip_spaces(String, left) ->
-%% drop_spaces(String);
diff --git a/src/web/ejabberd_http.hrl b/src/web/ejabberd_http.hrl
deleted file mode 100644
index 542dc0cdb..000000000
--- a/src/web/ejabberd_http.hrl
+++ /dev/null
@@ -1,36 +0,0 @@
-%%%----------------------------------------------------------------------
-%%%
-%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
--record(request,
- {method, % :: method(),
- path = [] :: [binary()],
- q = [] :: [{binary() | nokey, binary()}],
- us = {<<>>, <<>>} :: {binary(), binary()},
- auth :: {binary(), binary()} |
- {auth_jid, {binary(), binary()}, jlib:jid()},
- lang = <<"">> :: binary(),
- data = <<"">> :: binary(),
- ip :: {inet:ip_address(), inet:port_number()},
- host = <<"">> :: binary(),
- port = 5280 :: inet:port_number(),
- tp = http, % :: protocol(),
- headers = [] :: [{atom() | binary(), binary()}]}).
-
diff --git a/src/web/ejabberd_http_bind.erl b/src/web/ejabberd_http_bind.erl
deleted file mode 100644
index 913291672..000000000
--- a/src/web/ejabberd_http_bind.erl
+++ /dev/null
@@ -1,1235 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File : ejabberd_http_bind.erl
-%%% Author : Stefan Strigler <steve@zeank.in-berlin.de>
-%%% Purpose : Implements XMPP over BOSH (XEP-0206) (formerly known as
-%%% HTTP Binding)
-%%% Created : 21 Sep 2005 by Stefan Strigler <steve@zeank.in-berlin.de>
-%%% Modified: may 2009 by Mickael Remond, Alexey Schepin
-%%% Id : $Id: ejabberd_http_bind.erl 953 2009-05-07 10:40:40Z alexey $
-%%%----------------------------------------------------------------------
-
--module(ejabberd_http_bind).
-
--behaviour(gen_fsm).
-
-%% External exports
--export([start_link/3,
- init/1,
- handle_event/3,
- handle_sync_event/4,
- code_change/4,
- handle_info/3,
- terminate/3,
- send/2,
- send_xml/2,
- sockname/1,
- peername/1,
- setopts/2,
- controlling_process/2,
- become_controller/2,
- custom_receiver/1,
- reset_stream/1,
- change_shaper/2,
- monitor/1,
- close/1,
- start/4,
- handle_session_start/8,
- handle_http_put/7,
- http_put/7,
- http_get/2,
- prepare_response/4,
- process_request/2]).
-
--include("ejabberd.hrl").
-
--include("jlib.hrl").
-
--include("ejabberd_http.hrl").
-
--include("http_bind.hrl").
-
--record(http_bind,
- {id, pid, to, hold, wait, process_delay, version}).
-
--define(NULL_PEER, {{0, 0, 0, 0}, 0}).
-
-%% http binding request
--record(hbr, {rid,
- key,
- out}).
-
--record(state, {id,
- rid = none,
- key,
- socket,
- output = "",
- input = queue:new(),
- waiting_input = false,
- shaper_state,
- shaper_timer,
- last_receiver,
- last_poll,
- http_receiver,
- out_of_order_receiver = false,
- wait_timer,
- ctime = 0,
- timer,
- pause=0,
- unprocessed_req_list = [], % list of request that have been delayed for proper reordering: {Request, PID}
- req_list = [], % list of requests (cache)
- max_inactivity,
- max_pause,
- ip = ?NULL_PEER
- }).
-
-%% Internal request format:
--record(http_put, {rid,
- attrs,
- payload,
- payload_size,
- hold,
- stream,
- ip}).
-
-%%-define(DBGFSM, true).
--ifdef(DBGFSM).
-
--define(FSMOPTS, [{debug, [trace]}]).
-
--else.
-
--define(FSMOPTS, []).
-
--endif.
-
-%% Wait 100ms before continue processing, to allow the client provide more related stanzas.
--define(BOSH_VERSION, <<"1.8">>).
-
--define(NS_CLIENT, <<"jabber:client">>).
-
--define(NS_BOSH, <<"urn:xmpp:xbosh">>).
-
--define(NS_HTTP_BIND,
- <<"http://jabber.org/protocol/httpbind">>).
-
--define(MAX_REQUESTS, 2).
-
--define(MIN_POLLING, 2000000).
-
--define(MAX_WAIT, 3600).
-
--define(MAX_INACTIVITY, 30000).
-
--define(MAX_PAUSE, 120).
-
--define(PROCESS_DELAY_DEFAULT, 100).
-
--define(PROCESS_DELAY_MIN, 0).
-
--define(PROCESS_DELAY_MAX, 1000).
-
-%% Line copied from mod_http_bind.erl
--define(PROCNAME_MHB, ejabberd_mod_http_bind).
-
-%%%----------------------------------------------------------------------
-%%% API
-%%%----------------------------------------------------------------------
-%% TODO: If compile with no supervisor option, start the session without
-%% supervisor
-start(XMPPDomain, Sid, Key, IP) ->
- ?DEBUG("Starting session", []),
- SupervisorProc = gen_mod:get_module_proc(XMPPDomain, ?PROCNAME_MHB),
- case catch supervisor:start_child(SupervisorProc, [Sid, Key, IP]) of
- {ok, Pid} -> {ok, Pid};
- _ -> check_bind_module(XMPPDomain),
- {error, "Cannot start HTTP bind session"}
- end.
-
-start_link(Sid, Key, IP) ->
- gen_fsm:start_link(?MODULE, [Sid, Key, IP], ?FSMOPTS).
-
-send({http_bind, FsmRef, _IP}, Packet) ->
- gen_fsm:sync_send_all_state_event(FsmRef,
- {send, Packet}).
-
-send_xml({http_bind, FsmRef, _IP}, Packet) ->
- gen_fsm:sync_send_all_state_event(FsmRef,
- {send_xml, Packet}).
-
-setopts({http_bind, FsmRef, _IP}, Opts) ->
- case lists:member({active, once}, Opts) of
- true ->
- gen_fsm:send_all_state_event(FsmRef, {activate, self()});
- _ ->
- ok
- end.
-
-controlling_process(_Socket, _Pid) -> ok.
-
-custom_receiver({http_bind, FsmRef, _IP}) ->
- {receiver, ?MODULE, FsmRef}.
-
-become_controller(FsmRef, C2SPid) ->
- gen_fsm:send_all_state_event(FsmRef,
- {become_controller, C2SPid}).
-
-reset_stream({http_bind, _FsmRef, _IP}) ->
- ok.
-
-change_shaper({http_bind, FsmRef, _IP}, Shaper) ->
- gen_fsm:send_all_state_event(FsmRef,
- {change_shaper, Shaper}).
-
-monitor({http_bind, FsmRef, _IP}) ->
- erlang:monitor(process, FsmRef).
-
-close({http_bind, FsmRef, _IP}) ->
- catch gen_fsm:sync_send_all_state_event(FsmRef,
- {stop, close}).
-
-sockname(_Socket) -> {ok, ?NULL_PEER}.
-
-peername({http_bind, _FsmRef, IP}) -> {ok, IP}.
-
-
-%% Entry point for data coming from client through ejabberd HTTP server:
-process_request(Data, IP) ->
- Opts1 = ejabberd_c2s_config:get_c2s_limits(),
- Opts = [{xml_socket, true} | Opts1],
- MaxStanzaSize = case lists:keysearch(max_stanza_size, 1,
- Opts)
- of
- {value, {_, Size}} -> Size;
- _ -> infinity
- end,
- PayloadSize = iolist_size(Data),
- case catch parse_request(Data, PayloadSize,
- MaxStanzaSize)
- of
- %% No existing session:
- {ok, {<<"">>, Rid, Attrs, Payload}} ->
- case xml:get_attr_s(<<"to">>, Attrs) of
- <<"">> ->
- ?DEBUG("Session not created (Improper addressing)", []),
- {200, ?HEADER,
- <<"<body type='terminate' condition='improper-ad"
- "dressing' xmlns='",
- (?NS_HTTP_BIND)/binary, "'/>">>};
- XmppDomain ->
- Sid = sha:sha(term_to_binary({now(), make_ref()})),
- case start(XmppDomain, Sid, <<"">>, IP) of
- {error, _} ->
- {500, ?HEADER,
- <<"<body type='terminate' condition='internal-se"
- "rver-error' xmlns='",
- (?NS_HTTP_BIND)/binary,
- "'>Internal Server Error</body>">>};
- {ok, Pid} ->
- handle_session_start(Pid, XmppDomain, Sid, Rid, Attrs,
- Payload, PayloadSize, IP)
- end
- end;
- %% Existing session
- {ok, {Sid, Rid, Attrs, Payload1}} ->
- StreamStart = case xml:get_attr_s(<<"xmpp:restart">>,
- Attrs)
- of
- <<"true">> -> true;
- _ -> false
- end,
- Payload2 = case xml:get_attr_s(<<"type">>, Attrs) of
- <<"terminate">> ->
- Payload1 ++ [{xmlstreamend, <<"stream:stream">>}];
- _ -> Payload1
- end,
- handle_http_put(Sid, Rid, Attrs, Payload2, PayloadSize,
- StreamStart, IP);
- {size_limit, Sid} ->
- case mnesia:dirty_read({http_bind, Sid}) of
- {error, _} -> {404, ?HEADER, <<"">>};
- {ok, #http_bind{pid = FsmRef}} ->
- gen_fsm:sync_send_all_state_event(FsmRef,
- {stop, close}),
- {200, ?HEADER,
- <<"<body type='terminate' condition='undefined-c"
- "ondition' xmlns='",
- (?NS_HTTP_BIND)/binary, "'>Request Too Large</body>">>}
- end;
- _ ->
- ?DEBUG("Received bad request: ~p", [Data]),
- {400, ?HEADER, <<"">>}
- end.
-
-handle_session_start(Pid, XmppDomain, Sid, Rid, Attrs,
- Payload, PayloadSize, IP) ->
- ?DEBUG("got pid: ~p", [Pid]),
- Wait = case str:to_integer(xml:get_attr_s(<<"wait">>,
- Attrs))
- of
- {error, _} -> ?MAX_WAIT;
- {CWait, _} ->
- if CWait > (?MAX_WAIT) -> ?MAX_WAIT;
- true -> CWait
- end
- end,
- Hold = case str:to_integer(xml:get_attr_s(<<"hold">>,
- Attrs))
- of
- {error, _} -> (?MAX_REQUESTS) - 1;
- {CHold, _} ->
- if CHold > (?MAX_REQUESTS) - 1 -> (?MAX_REQUESTS) - 1;
- true -> CHold
- end
- end,
- Pdelay = case
- str:to_integer(xml:get_attr_s(<<"process-delay">>,
- Attrs))
- of
- {error, _} -> ?PROCESS_DELAY_DEFAULT;
- {CPdelay, _}
- when ((?PROCESS_DELAY_MIN) =< CPdelay) and
- (CPdelay =< (?PROCESS_DELAY_MAX)) ->
- CPdelay;
- {CPdelay, _} ->
- lists:max([lists:min([CPdelay, ?PROCESS_DELAY_MAX]),
- ?PROCESS_DELAY_MIN])
- end,
- Version = case catch
- list_to_float(binary_to_list(xml:get_attr_s(<<"ver">>, Attrs)))
- of
- {'EXIT', _} -> 0.0;
- V -> V
- end,
- XmppVersion = xml:get_attr_s(<<"xmpp:version">>, Attrs),
- ?DEBUG("Create session: ~p", [Sid]),
- mnesia:dirty_write(
- #http_bind{id = Sid,
- pid = Pid,
- to = {XmppDomain,
- XmppVersion},
- hold = Hold,
- wait = Wait,
- process_delay = Pdelay,
- version = Version
- }),
- handle_http_put(Sid, Rid, Attrs, Payload, PayloadSize, true, IP).
-
-%%%----------------------------------------------------------------------
-%%% Callback functions from gen_fsm
-%%%----------------------------------------------------------------------
-
-%%----------------------------------------------------------------------
-%% Func: init/1
-%% Returns: {ok, StateName, StateData} |
-%% {ok, StateName, StateData, Timeout} |
-%% ignore |
-%% {stop, StopReason}
-%%----------------------------------------------------------------------
-init([Sid, Key, IP]) ->
- ?DEBUG("started: ~p", [{Sid, Key, IP}]),
- Opts1 = ejabberd_c2s_config:get_c2s_limits(),
- Opts = [{xml_socket, true} | Opts1],
- Shaper = none,
- ShaperState = shaper:new(Shaper),
- Socket = {http_bind, self(), IP},
- ejabberd_socket:start(ejabberd_c2s, ?MODULE, Socket, Opts),
- Timer = erlang:start_timer(?MAX_INACTIVITY, self(), []),
- {ok, loop, #state{id = Sid,
- key = Key,
- socket = Socket,
- shaper_state = ShaperState,
- max_inactivity = ?MAX_INACTIVITY,
- max_pause = ?MAX_PAUSE,
- timer = Timer}}.
-
-%%----------------------------------------------------------------------
-%% Func: handle_event/3
-%% Returns: {next_state, NextStateName, NextStateData} |
-%% {next_state, NextStateName, NextStateData, Timeout} |
-%% {stop, Reason, NewStateData}
-%%----------------------------------------------------------------------
-handle_event({become_controller, C2SPid}, StateName, StateData) ->
- case StateData#state.input of
- cancel ->
- {next_state, StateName,
- StateData#state{waiting_input = C2SPid}};
- Input ->
- lists:foreach(fun (Event) -> C2SPid ! Event end,
- queue:to_list(Input)),
- {next_state, StateName,
- StateData#state{input = queue:new(),
- waiting_input = C2SPid}}
- end;
-handle_event({change_shaper, Shaper}, StateName,
- StateData) ->
- NewShaperState = shaper:new(Shaper),
- {next_state, StateName,
- StateData#state{shaper_state = NewShaperState}};
-handle_event(_Event, StateName, StateData) ->
- {next_state, StateName, StateData}.
-
-%%----------------------------------------------------------------------
-%% Func: handle_sync_event/4
-%% Returns: {next_state, NextStateName, NextStateData} |
-%% {next_state, NextStateName, NextStateData, Timeout} |
-%% {reply, Reply, NextStateName, NextStateData} |
-%% {reply, Reply, NextStateName, NextStateData, Timeout} |
-%% {stop, Reason, NewStateData} |
-%% {stop, Reason, Reply, NewStateData}
-%%----------------------------------------------------------------------
-handle_sync_event({send_xml, Packet}, _From, StateName,
- #state{http_receiver = undefined} = StateData) ->
- Output = [Packet | StateData#state.output],
- Reply = ok,
- {reply, Reply, StateName,
- StateData#state{output = Output}};
-handle_sync_event({send_xml, Packet}, _From, StateName,
- #state{out_of_order_receiver = true} = StateData) ->
- Output = [Packet | StateData#state.output],
- Reply = ok,
- {reply, Reply, StateName,
- StateData#state{output = Output}};
-handle_sync_event({send_xml, Packet}, _From, StateName,
- StateData) ->
- Output = [Packet | StateData#state.output],
- cancel_timer(StateData#state.timer),
- Timer = set_inactivity_timer(StateData#state.pause,
- StateData#state.max_inactivity),
- HTTPReply = {ok, Output},
- gen_fsm:reply(StateData#state.http_receiver, HTTPReply),
- cancel_timer(StateData#state.wait_timer),
- Rid = StateData#state.rid,
- ReqList = [#hbr{rid = Rid, key = StateData#state.key,
- out = Output}
- | [El
- || El <- StateData#state.req_list, El#hbr.rid /= Rid]],
- Reply = ok,
- {reply, Reply, StateName,
- StateData#state{output = [], http_receiver = undefined,
- req_list = ReqList, wait_timer = undefined,
- timer = Timer}};
-
-handle_sync_event({stop,close}, _From, _StateName, StateData) ->
- Reply = ok,
- {stop, normal, Reply, StateData};
-handle_sync_event({stop,stream_closed}, _From, _StateName, StateData) ->
- Reply = ok,
- {stop, normal, Reply, StateData};
-handle_sync_event({stop,Reason}, _From, _StateName, StateData) ->
- ?DEBUG("Closing bind session ~p - Reason: ~p", [StateData#state.id, Reason]),
- Reply = ok,
- {stop, normal, Reply, StateData};
-%% HTTP PUT: Receive packets from the client
-handle_sync_event(#http_put{rid = Rid}, _From,
- StateName, StateData)
- when StateData#state.shaper_timer /= undefined ->
- Pause = case
- erlang:read_timer(StateData#state.shaper_timer)
- of
- false -> 0;
- P -> P
- end,
- Reply = {wait, Pause},
- ?DEBUG("Shaper timer for RID ~p: ~p", [Rid, Reply]),
- {reply, Reply, StateName, StateData};
-handle_sync_event(#http_put{payload_size =
- PayloadSize} =
- Request,
- _From, StateName, StateData) ->
- ?DEBUG("New request: ~p", [Request]),
- {NewShaperState, NewShaperTimer} =
- update_shaper(StateData#state.shaper_state,
- PayloadSize),
- handle_http_put_event(Request, StateName,
- StateData#state{shaper_state = NewShaperState,
- shaper_timer = NewShaperTimer});
-%% HTTP GET: send packets to the client
-handle_sync_event({http_get, Rid, Wait, Hold}, From, StateName, StateData) ->
- %% setup timer
- TNow = tnow(),
- if
- (Hold > 0) and
- ((StateData#state.output == []) or (StateData#state.rid < Rid)) and
- ((TNow - StateData#state.ctime) < (Wait*1000*1000)) and
- (StateData#state.rid =< Rid) and
- (StateData#state.pause == 0) ->
- send_receiver_reply(StateData#state.http_receiver, {ok, empty}),
- cancel_timer(StateData#state.wait_timer),
- WaitTimer = erlang:start_timer(Wait * 1000, self(), []),
- %% MR: Not sure we should cancel the state timer here.
- cancel_timer(StateData#state.timer),
- {next_state, StateName, StateData#state{
- http_receiver = From,
- out_of_order_receiver = StateData#state.rid < Rid,
- wait_timer = WaitTimer,
- timer = undefined}};
- true ->
- cancel_timer(StateData#state.timer),
- Reply = {ok, StateData#state.output},
- %% save request
- ReqList = [#hbr{rid = Rid,
- key = StateData#state.key,
- out = StateData#state.output
- } |
- [El || El <- StateData#state.req_list,
- El#hbr.rid /= Rid ]
- ],
- if
- (StateData#state.http_receiver /= undefined) and
- StateData#state.out_of_order_receiver ->
- {reply, Reply, StateName, StateData#state{
- output = [],
- timer = undefined,
- req_list = ReqList,
- out_of_order_receiver = false}};
- true ->
- send_receiver_reply(StateData#state.http_receiver, {ok, empty}),
- cancel_timer(StateData#state.wait_timer),
- Timer = set_inactivity_timer(StateData#state.pause,
- StateData#state.max_inactivity),
- {reply, Reply, StateName,
- StateData#state{output = [],
- http_receiver = undefined,
- wait_timer = undefined,
- timer = Timer,
- req_list = ReqList}}
- end
- end;
-handle_sync_event(peername, _From, StateName,
- StateData) ->
- Reply = {ok, StateData#state.ip},
- {reply, Reply, StateName, StateData};
-handle_sync_event(_Event, _From, StateName,
- StateData) ->
- Reply = ok, {reply, Reply, StateName, StateData}.
-
-code_change(_OldVsn, StateName, StateData, _Extra) ->
- {ok, StateName, StateData}.
-
-%%----------------------------------------------------------------------
-%% Func: handle_info/3
-%% Returns: {next_state, NextStateName, NextStateData} |
-%% {next_state, NextStateName, NextStateData, Timeout} |
-%% {stop, Reason, NewStateData}
-%%----------------------------------------------------------------------
-%% We reached the max_inactivity timeout:
-handle_info({timeout, Timer, _}, _StateName,
- #state{id = SID, timer = Timer} = StateData) ->
- ?INFO_MSG("Session timeout. Closing the HTTP bind "
- "session: ~p",
- [SID]),
- {stop, normal, StateData};
-handle_info({timeout, WaitTimer, _}, StateName,
- #state{wait_timer = WaitTimer} = StateData) ->
- if StateData#state.http_receiver /= undefined ->
- cancel_timer(StateData#state.timer),
- Timer = set_inactivity_timer(StateData#state.pause,
- StateData#state.max_inactivity),
- gen_fsm:reply(StateData#state.http_receiver,
- {ok, empty}),
- Rid = StateData#state.rid,
- ReqList = [#hbr{rid = Rid, key = StateData#state.key,
- out = []}
- | [El
- || El <- StateData#state.req_list, El#hbr.rid /= Rid]],
- {next_state, StateName,
- StateData#state{http_receiver = undefined,
- req_list = ReqList, wait_timer = undefined,
- timer = Timer}};
- true -> {next_state, StateName, StateData}
- end;
-handle_info({timeout, ShaperTimer, _}, StateName,
- #state{shaper_timer = ShaperTimer} = StateData) ->
- {next_state, StateName, StateData#state{shaper_timer = undefined}};
-
-handle_info(_, StateName, StateData) ->
- {next_state, StateName, StateData}.
-
-%%----------------------------------------------------------------------
-%% Func: terminate/3
-%% Purpose: Shutdown the fsm
-%% Returns: any
-%%----------------------------------------------------------------------
-terminate(_Reason, _StateName, StateData) ->
- ?DEBUG("terminate: Deleting session ~s",
- [StateData#state.id]),
- mnesia:dirty_delete({http_bind, StateData#state.id}),
- send_receiver_reply(StateData#state.http_receiver,
- {ok, terminate}),
- case StateData#state.waiting_input of
- false -> ok;
- C2SPid -> gen_fsm:send_event(C2SPid, closed)
- end,
- ok.
-
-%%%----------------------------------------------------------------------
-%%% Internal functions
-%%%----------------------------------------------------------------------
-
-%% PUT / Get processing:
-handle_http_put_event(#http_put{rid = Rid,
- attrs = Attrs, hold = Hold} =
- Request,
- StateName, StateData) ->
- ?DEBUG("New request: ~p", [Request]),
- RidAllow = rid_allow(StateData#state.rid, Rid, Attrs,
- Hold, StateData#state.max_pause),
- case RidAllow of
- buffer ->
- ?DEBUG("Buffered request: ~p", [Request]),
- PendingRequests = StateData#state.unprocessed_req_list,
- Requests = lists:keydelete(Rid, 2, PendingRequests),
- ReqList = [#hbr{rid = Rid, key = StateData#state.key,
- out = []}
- | [El
- || El <- StateData#state.req_list,
- El#hbr.rid > Rid - 1 - Hold]],
- ?DEBUG("reqlist: ~p", [ReqList]),
- UnprocessedReqList = [Request | Requests],
- cancel_timer(StateData#state.timer),
- Timer = set_inactivity_timer(0,
- StateData#state.max_inactivity),
- {reply, ok, StateName,
- StateData#state{unprocessed_req_list =
- UnprocessedReqList,
- req_list = ReqList, timer = Timer},
- hibernate};
- _ ->
- process_http_put(Request, StateName, StateData,
- RidAllow)
- end.
-
-process_http_put(#http_put{rid = Rid, attrs = Attrs,
- payload = Payload, hold = Hold, stream = StreamTo,
- ip = IP} =
- Request,
- StateName, StateData, RidAllow) ->
- ?DEBUG("Actually processing request: ~p", [Request]),
- Key = xml:get_attr_s(<<"key">>, Attrs),
- NewKey = xml:get_attr_s(<<"newkey">>, Attrs),
- KeyAllow = case RidAllow of
- repeat -> true;
- false -> false;
- {true, _} ->
- case StateData#state.key of
- <<"">> -> true;
- OldKey ->
- NextKey = sha:sha(Key),
- ?DEBUG("Key/OldKey/NextKey: ~s/~s/~s",
- [Key, OldKey, NextKey]),
- if OldKey == NextKey -> true;
- true -> ?DEBUG("wrong key: ~s", [Key]), false
- end
- end
- end,
- TNow = tnow(),
- LastPoll = if Payload == [] -> TNow;
- true -> 0
- end,
- if (Payload == []) and (Hold == 0) and
- (TNow - StateData#state.last_poll < (?MIN_POLLING)) ->
- Reply = {error, polling_too_frequently},
- {reply, Reply, StateName, StateData};
- KeyAllow ->
- case RidAllow of
- false ->
- Reply = {error, not_exists},
- {reply, Reply, StateName, StateData};
- repeat ->
- ?DEBUG("REPEATING ~p", [Rid]),
- case [El#hbr.out
- || El <- StateData#state.req_list, El#hbr.rid == Rid]
- of
- [] -> {error, not_exists};
- [Out | _XS] ->
- if (Rid == StateData#state.rid) and
- (StateData#state.http_receiver /= undefined) ->
- {reply, ok, StateName, StateData};
- true ->
- Reply = {repeat, lists:reverse(Out)},
- {reply, Reply, StateName,
- StateData#state{last_poll = LastPoll}}
- end
- end;
- {true, Pause} ->
- SaveKey = if NewKey == <<"">> -> Key;
- true -> NewKey
- end,
- ?DEBUG(" -- SaveKey: ~s~n", [SaveKey]),
- ReqList1 = [El
- || El <- StateData#state.req_list,
- El#hbr.rid > Rid - 1 - Hold],
- ReqList = case lists:keymember(Rid, #hbr.rid, ReqList1)
- of
- true -> ReqList1;
- false ->
- [#hbr{rid = Rid, key = StateData#state.key,
- out = []}
- | ReqList1]
- end,
- ?DEBUG("reqlist: ~p", [ReqList]),
- cancel_timer(StateData#state.timer),
- Timer = set_inactivity_timer(Pause,
- StateData#state.max_inactivity),
- case StateData#state.waiting_input of
- false ->
- Input = lists:foldl(fun queue:in/2,
- StateData#state.input, Payload),
- Reply = ok,
- process_buffered_request(Reply, StateName,
- StateData#state{input = Input,
- rid = Rid,
- key = SaveKey,
- ctime = TNow,
- timer = Timer,
- pause = Pause,
- last_poll =
- LastPoll,
- req_list =
- ReqList,
- ip = IP});
- C2SPid ->
- case StreamTo of
- {To, <<"">>} ->
- gen_fsm:send_event(C2SPid,
- {xmlstreamstart,
- <<"stream:stream">>,
- [{<<"to">>, To},
- {<<"xmlns">>, ?NS_CLIENT},
- {<<"xmlns:stream">>,
- ?NS_STREAM}]});
- {To, Version} ->
- gen_fsm:send_event(C2SPid,
- {xmlstreamstart,
- <<"stream:stream">>,
- [{<<"to">>, To},
- {<<"xmlns">>, ?NS_CLIENT},
- {<<"version">>, Version},
- {<<"xmlns:stream">>,
- ?NS_STREAM}]});
- _ -> ok
- end,
- MaxInactivity = get_max_inactivity(StreamTo,
- StateData#state.max_inactivity),
- MaxPause = get_max_inactivity(StreamTo,
- StateData#state.max_pause),
- ?DEBUG("really sending now: ~p", [Payload]),
- lists:foreach(fun ({xmlstreamend, End}) ->
- gen_fsm:send_event(C2SPid,
- {xmlstreamend,
- End});
- (El) ->
- gen_fsm:send_event(C2SPid,
- {xmlstreamelement,
- El})
- end,
- Payload),
- Reply = ok,
- process_buffered_request(Reply, StateName,
- StateData#state{input =
- queue:new(),
- rid = Rid,
- key = SaveKey,
- ctime = TNow,
- timer = Timer,
- pause = Pause,
- last_poll =
- LastPoll,
- req_list =
- ReqList,
- max_inactivity =
- MaxInactivity,
- max_pause =
- MaxPause,
- ip = IP})
- end
- end;
- true ->
- Reply = {error, bad_key},
- {reply, Reply, StateName, StateData}
- end.
-
-process_buffered_request(Reply, StateName, StateData) ->
- Rid = StateData#state.rid,
- Requests = StateData#state.unprocessed_req_list,
- case lists:keysearch(Rid + 1, 2, Requests) of
- {value, Request} ->
- ?DEBUG("Processing buffered request: ~p", [Request]),
- NewRequests = lists:keydelete(Rid + 1, 2, Requests),
- handle_http_put_event(Request, StateName,
- StateData#state{unprocessed_req_list =
- NewRequests});
- _ -> {reply, Reply, StateName, StateData, hibernate}
- end.
-
-handle_http_put(Sid, Rid, Attrs, Payload, PayloadSize,
- StreamStart, IP) ->
- case http_put(Sid, Rid, Attrs, Payload, PayloadSize,
- StreamStart, IP)
- of
- {error, not_exists} ->
- ?DEBUG("no session associated with sid: ~p", [Sid]),
- {404, ?HEADER, <<"">>};
- {{error, Reason}, Sess} ->
- ?DEBUG("Error on HTTP put. Reason: ~p", [Reason]),
- handle_http_put_error(Reason, Sess);
- {{repeat, OutPacket}, Sess} ->
- ?DEBUG("http_put said 'repeat!' ...~nOutPacket: ~p",
- [OutPacket]),
- send_outpacket(Sess, OutPacket);
- {{wait, Pause}, _Sess} ->
- ?DEBUG("Trafic Shaper: Delaying request ~p", [Rid]),
- timer:sleep(Pause),
- handle_http_put(Sid, Rid, Attrs, Payload, PayloadSize,
- StreamStart, IP);
- {ok, Sess} ->
- prepare_response(Sess, Rid, [], StreamStart)
- end.
-
-http_put(Sid, Rid, Attrs, Payload, PayloadSize,
- StreamStart, IP) ->
- ?DEBUG("Looking for session: ~p", [Sid]),
- case mnesia:dirty_read({http_bind, Sid}) of
- [] ->
- {error, not_exists};
- [#http_bind{pid = FsmRef, hold=Hold, to={To, StreamVersion}}=Sess] ->
- NewStream =
- case StreamStart of
- true ->
- {To, StreamVersion};
- _ ->
- <<"">>
- end,
- {gen_fsm:sync_send_all_state_event(
- FsmRef, #http_put{rid = Rid, attrs = Attrs, payload = Payload,
- payload_size = PayloadSize, hold = Hold,
- stream = NewStream, ip = IP}, 30000), Sess}
- end.
-
-handle_http_put_error(Reason,
- #http_bind{pid = FsmRef, version = Version})
- when Version >= 0 ->
- gen_fsm:sync_send_all_state_event(FsmRef,
- {stop, {put_error, Reason}}),
- case Reason of
- not_exists ->
- {200, ?HEADER,
- xml:element_to_binary(#xmlel{name = <<"body">>,
- attrs =
- [{<<"xmlns">>, ?NS_HTTP_BIND},
- {<<"type">>, <<"terminate">>},
- {<<"condition">>,
- <<"item-not-found">>}],
- children = []})};
- bad_key ->
- {200, ?HEADER,
- xml:element_to_binary(#xmlel{name = <<"body">>,
- attrs =
- [{<<"xmlns">>, ?NS_HTTP_BIND},
- {<<"type">>, <<"terminate">>},
- {<<"condition">>,
- <<"item-not-found">>}],
- children = []})};
- polling_too_frequently ->
- {200, ?HEADER,
- xml:element_to_binary(#xmlel{name = <<"body">>,
- attrs =
- [{<<"xmlns">>, ?NS_HTTP_BIND},
- {<<"type">>, <<"terminate">>},
- {<<"condition">>,
- <<"policy-violation">>}],
- children = []})}
- end;
-handle_http_put_error(Reason,
- #http_bind{pid = FsmRef}) ->
- gen_fsm:sync_send_all_state_event(FsmRef,
- {stop, {put_error_no_version, Reason}}),
- case Reason of
- not_exists -> %% bad rid
- ?DEBUG("Closing HTTP bind session (Bad rid).", []),
- {404, ?HEADER, <<"">>};
- bad_key ->
- ?DEBUG("Closing HTTP bind session (Bad key).", []),
- {404, ?HEADER, <<"">>};
- polling_too_frequently ->
- ?DEBUG("Closing HTTP bind session (User polling "
- "too frequently).",
- []),
- {403, ?HEADER, <<"">>}
- end.
-
-%% Control RID ordering
-rid_allow(none, _NewRid, _Attrs, _Hold, _MaxPause) ->
- {true, 0};
-rid_allow(OldRid, NewRid, Attrs, Hold, MaxPause) ->
- ?DEBUG("Previous rid / New rid: ~p/~p",
- [OldRid, NewRid]),
- if
- %% We did not miss any packet, we can process it immediately:
- NewRid == OldRid + 1 ->
- case catch
- jlib:binary_to_integer(xml:get_attr_s(<<"pause">>,
- Attrs))
- of
- {'EXIT', _} -> {true, 0};
- Pause1 when Pause1 =< MaxPause ->
- ?DEBUG("got pause: ~p", [Pause1]), {true, Pause1};
- _ -> {true, 0}
- end;
- %% We have missed packets, we need to cached it to process it later on:
- (OldRid < NewRid) and (NewRid =< OldRid + Hold + 1) ->
- buffer;
- (NewRid =< OldRid) and (NewRid > OldRid - Hold - 1) ->
- repeat;
- true -> false
- end.
-
-update_shaper(ShaperState, PayloadSize) ->
- {NewShaperState, Pause} = shaper:update(ShaperState,
- PayloadSize),
- if Pause > 0 ->
- ShaperTimer = erlang:start_timer(Pause, self(),
- activate),
- {NewShaperState, ShaperTimer};
- true -> {NewShaperState, undefined}
- end.
-
-prepare_response(Sess, Rid, OutputEls, StreamStart) ->
- receive after Sess#http_bind.process_delay -> ok end,
- case catch http_get(Sess, Rid) of
- {ok, cancel} ->
- {200, ?HEADER,
- <<"<body type='error' xmlns='", (?NS_HTTP_BIND)/binary,
- "'/>">>};
- {ok, empty} ->
- {200, ?HEADER,
- <<"<body xmlns='", (?NS_HTTP_BIND)/binary, "'/>">>};
- {ok, terminate} ->
- {200, ?HEADER,
- <<"<body type='terminate' xmlns='",
- (?NS_HTTP_BIND)/binary, "'/>">>};
- {ok, ROutPacket} ->
- OutPacket = lists:reverse(ROutPacket),
- ?DEBUG("OutPacket: ~p", [OutputEls ++ OutPacket]),
- prepare_outpacket_response(Sess, Rid,
- OutputEls ++ OutPacket, StreamStart);
- {'EXIT', {shutdown, _}} ->
- {200, ?HEADER,
- <<"<body type='terminate' condition='system-shut"
- "down' xmlns='",
- (?NS_HTTP_BIND)/binary, "'/>">>};
- {'EXIT', _Reason} ->
- {200, ?HEADER,
- <<"<body type='terminate' xmlns='",
- (?NS_HTTP_BIND)/binary, "'/>">>}
- end.
-
-%% Send output payloads on establised sessions
-prepare_outpacket_response(Sess, _Rid, OutPacket,
- false) ->
- case catch send_outpacket(Sess, OutPacket) of
- {'EXIT', _Reason} ->
- ?DEBUG("Error in sending packet ~p ", [_Reason]),
- {200, ?HEADER,
- <<"<body type='terminate' xmlns='",
- (?NS_HTTP_BIND)/binary, "'/>">>};
- SendRes -> SendRes
- end;
-%% Handle a new session along with its output payload
-prepare_outpacket_response(#http_bind{id = Sid,
- wait = Wait, hold = Hold, to = To} =
- _Sess,
- _Rid, OutPacket, true) ->
- case OutPacket of
- [{xmlstreamstart, _, OutAttrs} | Els] ->
- AuthID = xml:get_attr_s(<<"id">>, OutAttrs),
- From = xml:get_attr_s(<<"from">>, OutAttrs),
- Version = xml:get_attr_s(<<"version">>, OutAttrs),
- OutEls = case Els of
- [] -> [];
- [{xmlstreamelement,
- #xmlel{name = <<"stream:features">>,
- attrs = StreamAttribs, children = StreamEls}}
- | StreamTail] ->
- TypedTail = [check_default_xmlns(OEl)
- || {xmlstreamelement, OEl} <- StreamTail],
- [#xmlel{name = <<"stream:features">>,
- attrs =
- [{<<"xmlns:stream">>, ?NS_STREAM}] ++
- StreamAttribs,
- children = StreamEls}]
- ++ TypedTail;
- StreamTail ->
- [check_default_xmlns(OEl)
- || {xmlstreamelement, OEl} <- StreamTail]
- end,
- case OutEls of
- [#xmlel{name = <<"stream:error">>}] ->
- {200, ?HEADER,
- <<"<body type='terminate' condition='host-unknow"
- "n' xmlns='",
- (?NS_HTTP_BIND)/binary, "'/>">>};
- _ ->
- BOSH_attribs = [{<<"authid">>, AuthID},
- {<<"xmlns:xmpp">>, ?NS_BOSH},
- {<<"xmlns:stream">>, ?NS_STREAM}]
- ++
- case OutEls of
- [] -> [];
- _ -> [{<<"xmpp:version">>, Version}]
- end,
- MaxInactivity = get_max_inactivity(To, ?MAX_INACTIVITY),
- MaxPause = get_max_pause(To),
- {200, ?HEADER,
- xml:element_to_binary(#xmlel{name = <<"body">>,
- attrs =
- [{<<"xmlns">>, ?NS_HTTP_BIND},
- {<<"sid">>, Sid},
- {<<"wait">>,
- iolist_to_binary(integer_to_list(Wait))},
- {<<"requests">>,
- iolist_to_binary(integer_to_list(Hold
- +
- 1))},
- {<<"inactivity">>,
- iolist_to_binary(integer_to_list(trunc(MaxInactivity
- /
- 1000)))},
- {<<"maxpause">>,
- iolist_to_binary(integer_to_list(MaxPause))},
- {<<"polling">>,
- iolist_to_binary(integer_to_list(trunc((?MIN_POLLING)
- /
- 1000000)))},
- {<<"ver">>, ?BOSH_VERSION},
- {<<"from">>, From},
- {<<"secure">>, <<"true">>}]
- ++ BOSH_attribs,
- children = OutEls})}
- end;
- _ ->
- {200, ?HEADER,
- <<"<body type='terminate' condition='internal-se"
- "rver-error' xmlns='",
- (?NS_HTTP_BIND)/binary, "'/>">>}
- end.
-
-http_get(#http_bind{pid = FsmRef, wait = Wait,
- hold = Hold},
- Rid) ->
- gen_fsm:sync_send_all_state_event(FsmRef,
- {http_get, Rid, Wait, Hold},
- 2 * (?MAX_WAIT) * 1000).
-
-send_outpacket(#http_bind{pid = FsmRef}, OutPacket) ->
- case OutPacket of
- [] ->
- {200, ?HEADER,
- <<"<body xmlns='", (?NS_HTTP_BIND)/binary, "'/>">>};
- [{xmlstreamend, _}] ->
- gen_fsm:sync_send_all_state_event(FsmRef,
- {stop, stream_closed}),
- {200, ?HEADER,
- <<"<body xmlns='", (?NS_HTTP_BIND)/binary, "'/>">>};
- _ ->
- AllElements = lists:all(fun ({xmlstreamelement,
- #xmlel{name = <<"stream:error">>}}) ->
- false;
- ({xmlstreamelement, _}) -> true;
- ({xmlstreamraw, _}) -> true;
- (_) -> false
- end,
- OutPacket),
- case AllElements of
- true ->
- TypedEls = lists:foldl(fun ({xmlstreamelement, El},
- Acc) ->
- Acc ++
- [xml:element_to_binary(check_default_xmlns(El))];
- ({xmlstreamraw, R}, Acc) ->
- Acc ++ [R]
- end,
- [], OutPacket),
- Body = <<"<body xmlns='", (?NS_HTTP_BIND)/binary, "'>",
- (iolist_to_binary(TypedEls))/binary, "</body>">>,
- ?DEBUG(" --- outgoing data --- ~n~s~n --- END "
- "--- ~n",
- [Body]),
- {200, ?HEADER, Body};
- false ->
- case OutPacket of
- [{xmlstreamstart, _, _} | SEls] ->
- OutEls = case SEls of
- [{xmlstreamelement,
- #xmlel{name = <<"stream:features">>,
- attrs = StreamAttribs,
- children = StreamEls}}
- | StreamTail] ->
- TypedTail = [check_default_xmlns(OEl)
- || {xmlstreamelement, OEl}
- <- StreamTail],
- [#xmlel{name = <<"stream:features">>,
- attrs =
- [{<<"xmlns:stream">>,
- ?NS_STREAM}]
- ++ StreamAttribs,
- children = StreamEls}]
- ++ TypedTail;
- StreamTail ->
- [check_default_xmlns(OEl)
- || {xmlstreamelement, OEl} <- StreamTail]
- end,
- {200, ?HEADER,
- xml:element_to_binary(#xmlel{name = <<"body">>,
- attrs =
- [{<<"xmlns">>,
- ?NS_HTTP_BIND}],
- children = OutEls})};
- _ ->
- SErrCond = lists:filter(fun ({xmlstreamelement,
- #xmlel{name =
- <<"stream:error">>}}) ->
- true;
- (_) -> false
- end,
- OutPacket),
- StreamErrCond = case SErrCond of
- [] -> null;
- [{xmlstreamelement,
- #xmlel{} = StreamErrorTag}
- | _] ->
- [StreamErrorTag]
- end,
- gen_fsm:sync_send_all_state_event(FsmRef,
- {stop,
- {stream_error,
- OutPacket}}),
- case StreamErrCond of
- null ->
- {200, ?HEADER,
- <<"<body type='terminate' condition='internal-se"
- "rver-error' xmlns='",
- (?NS_HTTP_BIND)/binary, "'/>">>};
- _ ->
- {200, ?HEADER,
- <<"<body type='terminate' condition='remote-stre"
- "am-error' xmlns='",
- (?NS_HTTP_BIND)/binary, "' ", "xmlns:stream='",
- (?NS_STREAM)/binary, "'>",
- (elements_to_string(StreamErrCond))/binary,
- "</body>">>}
- end
- end
- end
- end.
-
-parse_request(Data, PayloadSize, MaxStanzaSize) ->
- ?DEBUG("--- incoming data --- ~n~s~n --- END "
- "--- ",
- [Data]),
- case xml_stream:parse_element(Data) of
- #xmlel{name = <<"body">>, attrs = Attrs,
- children = Els} ->
- Xmlns = xml:get_attr_s(<<"xmlns">>, Attrs),
- if Xmlns /= (?NS_HTTP_BIND) -> {error, bad_request};
- true ->
- case catch
- jlib:binary_to_integer(xml:get_attr_s(<<"rid">>,
- Attrs))
- of
- {'EXIT', _} -> {error, bad_request};
- Rid ->
- FixedEls = lists:filter(fun (I) ->
- case I of
- #xmlel{} -> true;
- _ -> false
- end
- end,
- Els),
- Sid = xml:get_attr_s(<<"sid">>, Attrs),
- if PayloadSize =< MaxStanzaSize ->
- {ok, {Sid, Rid, Attrs, FixedEls}};
- true -> {size_limit, Sid}
- end
- end
- end;
- #xmlel{} -> {error, bad_request};
- {error, _Reason} -> {error, bad_request}
- end.
-
-send_receiver_reply(undefined, _Reply) -> ok;
-send_receiver_reply(Receiver, Reply) ->
- gen_fsm:reply(Receiver, Reply).
-
-%% Cancel timer and empty message queue.
-cancel_timer(undefined) -> ok;
-cancel_timer(Timer) ->
- erlang:cancel_timer(Timer),
- receive {timeout, Timer, _} -> ok after 0 -> ok end.
-
-%% If client asked for a pause (pause > 0), we apply the pause value
-%% as inactivity timer:
-set_inactivity_timer(Pause, _MaxInactivity)
- when Pause > 0 ->
- erlang:start_timer(Pause * 1000, self(), []);
-%% Otherwise, we apply the max_inactivity value as inactivity timer:
-set_inactivity_timer(_Pause, MaxInactivity) ->
- erlang:start_timer(MaxInactivity, self(), []).
-
-%% TODO: Use tail recursion and list reverse ?
-elements_to_string([]) -> [];
-elements_to_string([El | Els]) ->
- [xml:element_to_binary(El) | elements_to_string(Els)].
-
-%% @spec (To, Default::integer()) -> integer()
-%% where To = [] | {Host::string(), Version::string()}
-get_max_inactivity({Host, _}, Default) ->
- case gen_mod:get_module_opt(Host, mod_http_bind, max_inactivity,
- fun(I) when is_integer(I), I>0 -> I end,
- undefined)
- of
- Seconds when is_integer(Seconds) -> Seconds * 1000;
- undefined -> Default
- end;
-get_max_inactivity(_, Default) -> Default.
-
-get_max_pause({Host, _}) ->
- gen_mod:get_module_opt(Host, mod_http_bind, max_pause,
- fun(I) when is_integer(I), I>0 -> I end,
- ?MAX_PAUSE);
-get_max_pause(_) -> ?MAX_PAUSE.
-
-%% Current time as integer
-tnow() ->
- {TMegSec, TSec, TMSec} = now(),
- (TMegSec * 1000000 + TSec) * 1000000 + TMSec.
-
-check_default_xmlns(#xmlel{name = Name, attrs = Attrs,
- children = Els} =
- El) ->
- case xml:get_tag_attr_s(<<"xmlns">>, El) of
- <<"">> ->
- #xmlel{name = Name,
- attrs = [{<<"xmlns">>, ?NS_CLIENT} | Attrs],
- children = Els};
- _ -> El
- end;
-check_default_xmlns(El) -> El.
-
-%% Check that mod_http_bind has been defined in config file.
-%% Print a warning in log file if this is not the case.
-check_bind_module(XmppDomain) ->
- case gen_mod:is_loaded(XmppDomain, mod_http_bind) of
- true -> true;
- false ->
- ?ERROR_MSG("You are trying to use BOSH (HTTP Bind) "
- "in host ~p, but the module mod_http_bind "
- "is not started in that host. Configure "
- "your BOSH client to connect to the correct "
- "host, or add your desired host to the "
- "configuration, or check your 'modules' "
- "section in your ejabberd configuration "
- "file.",
- [XmppDomain]),
- false
- end.
diff --git a/src/web/ejabberd_http_poll.erl b/src/web/ejabberd_http_poll.erl
deleted file mode 100644
index 7648f5710..000000000
--- a/src/web/ejabberd_http_poll.erl
+++ /dev/null
@@ -1,428 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File : ejabberd_http_poll.erl
-%%% Author : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : HTTP Polling support (XEP-0025)
-%%% Created : 4 Mar 2004 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
--module(ejabberd_http_poll).
-
--author('alexey@process-one.net').
-
--behaviour(gen_fsm).
-
-%% External exports
--export([start_link/3, init/1, handle_event/3,
- handle_sync_event/4, code_change/4, handle_info/3,
- terminate/3, send/2, setopts/2, sockname/1, peername/1,
- controlling_process/2, close/1, process/2]).
-
--include("ejabberd.hrl").
-
--include("jlib.hrl").
-
--include("ejabberd_http.hrl").
-
--record(http_poll, {id :: pid() | binary(), pid :: pid()}).
-
--type poll_socket() :: #http_poll{}.
--export_type([poll_socket/0]).
-
--record(state,
- {id, key, socket, output = <<"">>, input = <<"">>,
- waiting_input = false, last_receiver, http_poll_timeout,
- timer}).
-
-%-define(DBGFSM, true).
-
--ifdef(DBGFSM).
-
--define(FSMOPTS, [{debug, [trace]}]).
-
--else.
-
--define(FSMOPTS, []).
-
--endif.
-
--define(HTTP_POLL_TIMEOUT, 300).
-
--define(CT,
- {<<"Content-Type">>, <<"text/xml; charset=utf-8">>}).
-
--define(BAD_REQUEST,
- [?CT, {<<"Set-Cookie">>, <<"ID=-3:0; expires=-1">>}]).
-
-%%%----------------------------------------------------------------------
-%%% API
-%%%----------------------------------------------------------------------
-start(ID, Key, IP) ->
- mnesia:create_table(http_poll,
- [{ram_copies, [node()]},
- {attributes, record_info(fields, http_poll)}]),
- supervisor:start_child(ejabberd_http_poll_sup, [ID, Key, IP]).
-
-start_link(ID, Key, IP) ->
- gen_fsm:start_link(?MODULE, [ID, Key, IP], ?FSMOPTS).
-
-send({http_poll, FsmRef, _IP}, Packet) ->
- gen_fsm:sync_send_all_state_event(FsmRef,
- {send, Packet}).
-
-setopts({http_poll, FsmRef, _IP}, Opts) ->
- case lists:member({active, once}, Opts) of
- true ->
- gen_fsm:send_all_state_event(FsmRef,
- {activate, self()});
- _ -> ok
- end.
-
-sockname(_Socket) -> {ok, {{0, 0, 0, 0}, 0}}.
-
-peername({http_poll, _FsmRef, IP}) -> {ok, IP}.
-
-controlling_process(_Socket, _Pid) -> ok.
-
-close({http_poll, FsmRef, _IP}) ->
- catch gen_fsm:sync_send_all_state_event(FsmRef, close).
-
-process([],
- #request{data = Data, ip = IP} = _Request) ->
- case catch parse_request(Data) of
- {ok, ID1, Key, NewKey, Packet} ->
- ID = if
- (ID1 == <<"0">>) or (ID1 == <<"mobile">>) ->
- NewID = sha:sha(term_to_binary({now(), make_ref()})),
- {ok, Pid} = start(NewID, <<"">>, IP),
- mnesia:transaction(
- fun() ->
- mnesia:write(#http_poll{id = NewID, pid = Pid})
- end),
- NewID;
- true ->
- ID1
- end,
- case http_put(ID, Key, NewKey, Packet) of
- {error, not_exists} ->
- {200, ?BAD_REQUEST, <<"">>};
- {error, bad_key} ->
- {200, ?BAD_REQUEST, <<"">>};
- ok ->
- receive
- after 100 -> ok
- end,
- case http_get(ID) of
- {error, not_exists} ->
- {200, ?BAD_REQUEST, <<"">>};
- {ok, OutPacket} ->
- if
- ID == ID1 ->
- Cookie = <<"ID=", ID/binary, "; expires=-1">>,
- {200, [?CT, {<<"Set-Cookie">>, Cookie}],
- OutPacket};
- ID1 == <<"mobile">> ->
- {200, [?CT], [ID, $\n, OutPacket]};
- true ->
- Cookie = <<"ID=", ID/binary, "; expires=-1">>,
- {200, [?CT, {<<"Set-Cookie">>, Cookie}],
- OutPacket}
- end
- end
- end;
- _ ->
- HumanHTMLxmlel = get_human_html_xmlel(),
- {200, [?CT, {<<"Set-Cookie">>, <<"ID=-2:0; expires=-1">>}], HumanHTMLxmlel}
- end;
-process(_, _Request) ->
- {400, [],
- #xmlel{name = <<"h1">>, attrs = [],
- children = [{xmlcdata, <<"400 Bad Request">>}]}}.
-
-%% Code copied from mod_http_bind.erl and customized
-get_human_html_xmlel() ->
- Heading = <<"ejabberd ",
- (iolist_to_binary(atom_to_list(?MODULE)))/binary>>,
- #xmlel{name = <<"html">>,
- attrs =
- [{<<"xmlns">>, <<"http://www.w3.org/1999/xhtml">>}],
- children =
- [#xmlel{name = <<"head">>, attrs = [],
- children =
- [#xmlel{name = <<"title">>, attrs = [],
- children = [{xmlcdata, Heading}]}]},
- #xmlel{name = <<"body">>, attrs = [],
- children =
- [#xmlel{name = <<"h1">>, attrs = [],
- children = [{xmlcdata, Heading}]},
- #xmlel{name = <<"p">>, attrs = [],
- children =
- [{xmlcdata, <<"An implementation of ">>},
- #xmlel{name = <<"a">>,
- attrs =
- [{<<"href">>,
- <<"http://xmpp.org/extensions/xep-0025.html">>}],
- children =
- [{xmlcdata,
- <<"Jabber HTTP Polling (XEP-0025)">>}]}]},
- #xmlel{name = <<"p">>, attrs = [],
- children =
- [{xmlcdata,
- <<"This web page is only informative. To "
- "use HTTP-Poll you need a Jabber/XMPP "
- "client that supports it.">>}]}]}]}.
-
-%%%----------------------------------------------------------------------
-%%% Callback functions from gen_fsm
-%%%----------------------------------------------------------------------
-
-%%----------------------------------------------------------------------
-%% Func: init/1
-%% Returns: {ok, StateName, StateData} |
-%% {ok, StateName, StateData, Timeout} |
-%% ignore |
-%% {stop, StopReason}
-%%----------------------------------------------------------------------
-init([ID, Key, IP]) ->
- ?INFO_MSG("started: ~p", [{ID, Key, IP}]),
- Opts = ejabberd_c2s_config:get_c2s_limits(),
- HTTPPollTimeout = ejabberd_config:get_local_option(
- {http_poll_timeout, ?MYNAME},
- fun(I) when is_integer(I), I>0 -> I end,
- ?HTTP_POLL_TIMEOUT) * 1000,
- Socket = {http_poll, self(), IP},
- ejabberd_socket:start(ejabberd_c2s, ?MODULE, Socket,
- Opts),
- Timer = erlang:start_timer(HTTPPollTimeout, self(), []),
- {ok, loop,
- #state{id = ID, key = Key, socket = Socket,
- http_poll_timeout = HTTPPollTimeout, timer = Timer}}.
-
-%%----------------------------------------------------------------------
-%% Func: StateName/2
-%% Returns: {next_state, NextStateName, NextStateData} |
-%% {next_state, NextStateName, NextStateData, Timeout} |
-%% {stop, Reason, NewStateData}
-%% {stop, Reason, NewStateData}
-%%----------------------------------------------------------------------
-
-%%----------------------------------------------------------------------
-%% Func: StateName/3
-%% Returns: {next_state, NextStateName, NextStateData} |
-%% {next_state, NextStateName, NextStateData, Timeout} |
-%% {reply, Reply, NextStateName, NextStateData} |
-%% {reply, Reply, NextStateName, NextStateData, Timeout} |
-%% {stop, Reason, NewStateData} |
-%% {stop, Reason, Reply, NewStateData}
-%% {stop, Reason, Reply, NewStateData}
-%%----------------------------------------------------------------------
-%state_name(Event, From, StateData) ->
-% Reply = ok,
-% {reply, Reply, state_name, StateData}.
-
-%%----------------------------------------------------------------------
-%% Func: handle_event/3
-%% Returns: {next_state, NextStateName, NextStateData} |
-%% {next_state, NextStateName, NextStateData, Timeout} |
-%% {stop, Reason, NewStateData}
-%%----------------------------------------------------------------------
-handle_event({activate, From}, StateName, StateData) ->
- case StateData#state.input of
- <<"">> ->
- {next_state, StateName,
- StateData#state{waiting_input = {From, ok}}};
- Input ->
- Receiver = From,
- Receiver !
- {tcp, StateData#state.socket, iolist_to_binary(Input)},
- {next_state, StateName,
- StateData#state{input = <<"">>, waiting_input = false,
- last_receiver = Receiver}}
- end;
-handle_event(_Event, StateName, StateData) ->
- {next_state, StateName, StateData}.
-
-%%----------------------------------------------------------------------
-%% Func: handle_sync_event/4
-%% Returns: {next_state, NextStateName, NextStateData} |
-%% {next_state, NextStateName, NextStateData, Timeout} |
-%% {reply, Reply, NextStateName, NextStateData} |
-%% {reply, Reply, NextStateName, NextStateData, Timeout} |
-%% {stop, Reason, NewStateData} |
-%% {stop, Reason, Reply, NewStateData}
-%%----------------------------------------------------------------------
-handle_sync_event({send, Packet}, _From, StateName,
- StateData) ->
- Packet2 = if is_binary(Packet) -> (Packet);
- true -> Packet
- end,
- Output = StateData#state.output ++
- [lists:flatten(Packet2)],
- Reply = ok,
- {reply, Reply, StateName,
- StateData#state{output = Output}};
-handle_sync_event(stop, _From, _StateName, StateData) ->
- Reply = ok, {stop, normal, Reply, StateData};
-handle_sync_event({http_put, Key, NewKey, Packet},
- _From, StateName, StateData) ->
- Allow = case StateData#state.key of
- <<"">> -> true;
- OldKey ->
- NextKey = jlib:encode_base64((crypto:sha(Key))),
- if OldKey == NextKey -> true;
- true -> false
- end
- end,
- if Allow ->
- case StateData#state.waiting_input of
- false ->
- Input = [StateData#state.input | Packet],
- Reply = ok,
- {reply, Reply, StateName,
- StateData#state{input = Input, key = NewKey}};
- {Receiver, _Tag} ->
- Receiver !
- {tcp, StateData#state.socket, iolist_to_binary(Packet)},
- cancel_timer(StateData#state.timer),
- Timer =
- erlang:start_timer(StateData#state.http_poll_timeout,
- self(), []),
- Reply = ok,
- {reply, Reply, StateName,
- StateData#state{waiting_input = false,
- last_receiver = Receiver, key = NewKey,
- timer = Timer}}
- end;
- true ->
- Reply = {error, bad_key},
- {reply, Reply, StateName, StateData}
- end;
-handle_sync_event(http_get, _From, StateName,
- StateData) ->
- Reply = {ok, StateData#state.output},
- {reply, Reply, StateName,
- StateData#state{output = <<"">>}};
-handle_sync_event(_Event, _From, StateName,
- StateData) ->
- Reply = ok, {reply, Reply, StateName, StateData}.
-
-code_change(_OldVsn, StateName, StateData, _Extra) ->
- {ok, StateName, StateData}.
-
-%%----------------------------------------------------------------------
-%% Func: handle_info/3
-%% Returns: {next_state, NextStateName, NextStateData} |
-%% {next_state, NextStateName, NextStateData, Timeout} |
-%% {stop, Reason, NewStateData}
-%%----------------------------------------------------------------------
-handle_info({timeout, Timer, _}, _StateName,
- #state{timer = Timer} = StateData) ->
- {stop, normal, StateData};
-handle_info(_, StateName, StateData) ->
- {next_state, StateName, StateData}.
-
-%%----------------------------------------------------------------------
-%% Func: terminate/3
-%% Purpose: Shutdown the fsm
-%% Returns: any
-%%----------------------------------------------------------------------
-terminate(_Reason, _StateName, StateData) ->
- mnesia:transaction(
- fun() ->
- mnesia:delete({http_poll, StateData#state.id})
- end),
- case StateData#state.waiting_input of
- false ->
- case StateData#state.last_receiver of
- undefined -> ok;
- Receiver ->
- Receiver ! {tcp_closed, StateData#state.socket}
- end;
- {Receiver, _Tag} ->
- Receiver ! {tcp_closed, StateData#state.socket}
- end,
- catch resend_messages(StateData#state.output),
- ok.
-
-%%%----------------------------------------------------------------------
-%%% Internal functions
-%%%----------------------------------------------------------------------
-
-http_put(ID, Key, NewKey, Packet) ->
- case mnesia:dirty_read({http_poll, ID}) of
- [] ->
- {error, not_exists};
- [#http_poll{pid = FsmRef}] ->
- gen_fsm:sync_send_all_state_event(
- FsmRef, {http_put, Key, NewKey, Packet})
- end.
-
-http_get(ID) ->
- case mnesia:dirty_read({http_poll, ID}) of
- [] ->
- {error, not_exists};
- [#http_poll{pid = FsmRef}] ->
- gen_fsm:sync_send_all_state_event(FsmRef, http_get)
- end.
-
-parse_request(Data) ->
- Comma = str:chr(Data, $,),
- Header = str:substr(Data, 1, Comma - 1),
- Packet = str:substr(Data, Comma + 1, byte_size(Data)),
- {ID, Key, NewKey} = case str:tokens(Header, <<";">>) of
- [ID1] -> {ID1, <<"">>, <<"">>};
- [ID1, Key1] -> {ID1, Key1, Key1};
- [ID1, Key1, NewKey1] -> {ID1, Key1, NewKey1}
- end,
- {ok, ID, Key, NewKey, Packet}.
-
-cancel_timer(Timer) ->
- erlang:cancel_timer(Timer),
- receive {timeout, Timer, _} -> ok after 0 -> ok end.
-
-%% Resend the polled messages
-resend_messages(Messages) ->
-%% This function is used to resend messages that have been polled but not
-%% delivered.
- lists:foreach(fun (Packet) -> resend_message(Packet)
- end,
- Messages).
-
-resend_message(Packet) ->
- #xmlel{name = Name} = ParsedPacket =
- xml_stream:parse_element(Packet),
- if Name == <<"iq">>;
- Name == <<"message">>;
- Name == <<"presence">> ->
- From = get_jid(<<"from">>, ParsedPacket),
- To = get_jid(<<"to">>, ParsedPacket),
- ?DEBUG("Resend ~p ~p ~p~n", [From, To, ParsedPacket]),
- ejabberd_router:route(From, To, ParsedPacket);
- true -> ok
- end.
-
-%% Type can be "from" or "to"
-%% Parsed packet is a parsed Jabber packet.
-get_jid(Type, ParsedPacket) ->
- case xml:get_tag_attr(Type, ParsedPacket) of
- {value, StringJid} -> jlib:string_to_jid(StringJid);
- false -> jlib:make_jid(<<"">>, <<"">>, <<"">>)
- end.
diff --git a/src/web/ejabberd_web.erl b/src/web/ejabberd_web.erl
deleted file mode 100644
index 8c7ccaf69..000000000
--- a/src/web/ejabberd_web.erl
+++ /dev/null
@@ -1,105 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File : ejabberd_web.erl
-%%% Author : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose :
-%%% Purpose :
-%%% Created : 28 Feb 2004 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
--module(ejabberd_web).
-
--author('alexey@process-one.net').
-
-%% External exports
--export([make_xhtml/1, make_xhtml/2, error/1]).
-
--include("ejabberd.hrl").
-
--include("jlib.hrl").
-
--include("ejabberd_http.hrl").
-
-%% XXX bard: there are variants of make_xhtml in ejabberd_http and
-%% ejabberd_web_admin. It might be a good idea to centralize it here
-%% and also create an ejabberd_web.hrl file holding the macros, so
-%% that third parties can use ejabberd_web as an "utility" library.
-
-make_xhtml(Els) -> make_xhtml([], Els).
-
-make_xhtml(HeadEls, Els) ->
- #xmlel{name = <<"html">>,
- attrs =
- [{<<"xmlns">>, <<"http://www.w3.org/1999/xhtml">>},
- {<<"xml:lang">>, <<"en">>}, {<<"lang">>, <<"en">>}],
- children =
- [#xmlel{name = <<"head">>, attrs = [],
- children =
- [#xmlel{name = <<"meta">>,
- attrs =
- [{<<"http-equiv">>, <<"Content-Type">>},
- {<<"content">>,
- <<"text/html; charset=utf-8">>}],
- children = []}
- | HeadEls]},
- #xmlel{name = <<"body">>, attrs = [], children = Els}]}.
-
--define(X(Name),
- #xmlel{name = Name, attrs = [], children = []}).
-
--define(XA(Name, Attrs),
- #xmlel{name = Name, attrs = Attrs, children = []}).
-
--define(XE(Name, Els),
- #xmlel{name = Name, attrs = [], children = Els}).
-
--define(XAE(Name, Attrs, Els),
- #xmlel{name = Name, attrs = Attrs, children = Els}).
-
--define(C(Text), {xmlcdata, Text}).
-
--define(XC(Name, Text), ?XE(Name, [?C(Text)])).
-
--define(XAC(Name, Attrs, Text),
- ?XAE(Name, Attrs, [?C(Text)])).
-
--define(LI(Els), ?XE(<<"li">>, Els)).
-
--define(A(URL, Els),
- ?XAE(<<"a">>, [{<<"href">>, URL}], Els)).
-
--define(AC(URL, Text), ?A(URL, [?C(Text)])).
-
--define(P, ?X(<<"p">>)).
-
--define(BR, ?X(<<"br">>)).
-
--define(INPUT(Type, Name, Value),
- ?XA(<<"input">>,
- [{<<"type">>, Type}, {<<"name">>, Name},
- {<<"value">>, Value}])).
-
-error(not_found) ->
- {404, [],
- make_xhtml([?XC(<<"h1">>, <<"404 Not Found">>)])};
-error(not_allowed) ->
- {401, [],
- make_xhtml([?XC(<<"h1">>, <<"401 Unauthorized">>)])}.
diff --git a/src/web/ejabberd_web_admin.erl b/src/web/ejabberd_web_admin.erl
deleted file mode 100644
index 73c7ab52a..000000000
--- a/src/web/ejabberd_web_admin.erl
+++ /dev/null
@@ -1,2893 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File : ejabberd_web_admin.erl
-%%% Author : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : Administration web interface
-%%% Created : 9 Apr 2004 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
-%%%% definitions
-
--module(ejabberd_web_admin).
-
--author('alexey@process-one.net').
-
-%% External exports
--export([process/2, list_users/4,
- list_users_in_diapason/4, pretty_print_xml/1,
- term_to_id/1]).
-
--include("ejabberd.hrl").
-
--include("jlib.hrl").
-
--include("ejabberd_http.hrl").
-
--include("ejabberd_web_admin.hrl").
-
--define(INPUTATTRS(Type, Name, Value, Attrs),
- ?XA(<<"input">>,
- (Attrs ++
- [{<<"type">>, Type}, {<<"name">>, Name},
- {<<"value">>, Value}]))).
-
-%%%==================================
-%%%% get_acl_access
-
-%% @spec (Path::[string()], Method) -> {HostOfRule, [AccessRule]}
-%% where Method = 'GET' | 'POST'
-
-%% All accounts can access those URLs
-get_acl_rule([], _) -> {<<"localhost">>, [all]};
-get_acl_rule([<<"style.css">>], _) ->
- {<<"localhost">>, [all]};
-get_acl_rule([<<"logo.png">>], _) ->
- {<<"localhost">>, [all]};
-get_acl_rule([<<"logo-fill.png">>], _) ->
- {<<"localhost">>, [all]};
-get_acl_rule([<<"favicon.ico">>], _) ->
- {<<"localhost">>, [all]};
-get_acl_rule([<<"additions.js">>], _) ->
- {<<"localhost">>, [all]};
-%% This page only displays vhosts that the user is admin:
-get_acl_rule([<<"vhosts">>], _) ->
- {<<"localhost">>, [all]};
-%% The pages of a vhost are only accesible if the user is admin of that vhost:
-get_acl_rule([<<"server">>, VHost | _RPath], Method)
- when Method =:= 'GET' orelse Method =:= 'HEAD' ->
- {VHost, [configure, webadmin_view]};
-get_acl_rule([<<"server">>, VHost | _RPath], 'POST') ->
- {VHost, [configure]};
-%% Default rule: only global admins can access any other random page
-get_acl_rule(_RPath, Method)
- when Method =:= 'GET' orelse Method =:= 'HEAD' ->
- {global, [configure, webadmin_view]};
-get_acl_rule(_RPath, 'POST') -> {global, [configure]}.
-
-is_acl_match(Host, Rules, Jid) ->
- lists:any(fun (Rule) ->
- allow == acl:match_rule(Host, Rule, Jid)
- end,
- Rules).
-
-%%%==================================
-%%%% Menu Items Access
-
-get_jid(Auth, HostHTTP, Method) ->
- case get_auth_admin(Auth, HostHTTP, [], Method) of
- {ok, {User, Server}} ->
- jlib:make_jid(User, Server, <<"">>);
- {unauthorized, Error} ->
- ?ERROR_MSG("Unauthorized ~p: ~p", [Auth, Error]),
- throw({unauthorized, Auth})
- end.
-
-get_menu_items(global, cluster, Lang, JID) ->
- {Base, _, Items} = make_server_menu([], [], Lang, JID),
- lists:map(fun ({URI, Name}) ->
- {<<Base/binary, URI/binary, "/">>, Name};
- ({URI, Name, _SubMenu}) ->
- {<<Base/binary, URI/binary, "/">>, Name}
- end,
- Items);
-get_menu_items(Host, cluster, Lang, JID) ->
- {Base, _, Items} = make_host_menu(Host, [], Lang, JID),
- lists:map(fun ({URI, Name}) ->
- {<<Base/binary, URI/binary, "/">>, Name};
- ({URI, Name, _SubMenu}) ->
- {<<Base/binary, URI/binary, "/">>, Name}
- end,
- Items).
-
-%% get_menu_items(Host, Node, Lang, JID) ->
-%% {Base, _, Items} = make_host_node_menu(Host, Node, Lang, JID),
-%% lists:map(
-%% fun({URI, Name}) ->
-%% {Base++URI++"/", Name};
-%% ({URI, Name, _SubMenu}) ->
-%% {Base++URI++"/", Name}
-%% end,
-%% Items
-%% ).
-
-is_allowed_path(BasePath, {Path, _}, JID) ->
- is_allowed_path(BasePath ++ [Path], JID);
-is_allowed_path(BasePath, {Path, _, _}, JID) ->
- is_allowed_path(BasePath ++ [Path], JID).
-
-is_allowed_path([<<"admin">> | Path], JID) ->
- is_allowed_path(Path, JID);
-is_allowed_path(Path, JID) ->
- {HostOfRule, AccessRule} = get_acl_rule(Path, 'GET'),
- is_acl_match(HostOfRule, AccessRule, JID).
-
-%% @spec(Path) -> URL
-%% where Path = [string()]
-%% URL = string()
-%% Convert ["admin", "user", "tom"] -> "/admin/user/tom/"
-%%path_to_url(Path) ->
-%% "/" ++ string:join(Path, "/") ++ "/".
-
-%% @spec(URL) -> Path
-%% where Path = [string()]
-%% URL = string()
-%% Convert "admin/user/tom" -> ["admin", "user", "tom"]
-url_to_path(URL) -> str:tokens(URL, <<"/">>).
-
-%%%==================================
-%%%% process/2
-
-process([<<"doc">>, LocalFile], _Request) ->
- DocPath = case os:getenv("EJABBERD_DOC_PATH") of
- P when is_list(P) -> P;
- false -> <<"/share/doc/ejabberd/">>
- end,
- FileName = filename:join(DocPath, LocalFile),
- case file:read_file(FileName) of
- {ok, FileContents} ->
- ?DEBUG("Delivering content.", []),
- {200, [{<<"Server">>, <<"ejabberd">>}], FileContents};
- {error, Error} ->
- ?DEBUG("Delivering error: ~p", [Error]),
- Help = <<" ", FileName/binary,
- " - Try to specify the path to ejabberd "
- "documentation with the environment variable "
- "EJABBERD_DOC_PATH. Check the ejabberd "
- "Guide for more information.">>,
- case Error of
- eacces -> {403, [], <<"Forbidden", Help/binary>>};
- enoent -> {404, [], <<"Not found", Help/binary>>};
- _Else ->
- {404, [], <<(iolist_to_binary(atom_to_list(Error)))/binary, Help/binary>>}
- end
- end;
-process([<<"server">>, SHost | RPath] = Path,
- #request{auth = Auth, lang = Lang, host = HostHTTP,
- method = Method} =
- Request) ->
- Host = jlib:nameprep(SHost),
- case lists:member(Host, ?MYHOSTS) of
- true ->
- case get_auth_admin(Auth, HostHTTP, Path, Method) of
- {ok, {User, Server}} ->
- AJID = get_jid(Auth, HostHTTP, Method),
- process_admin(Host,
- Request#request{path = RPath,
- auth = {auth_jid, Auth, AJID},
- us = {User, Server}});
- {unauthorized, <<"no-auth-provided">>} ->
- {401,
- [{<<"WWW-Authenticate">>,
- <<"basic realm=\"ejabberd\"">>}],
- ejabberd_web:make_xhtml([?XCT(<<"h1">>,
- <<"Unauthorized">>)])};
- {unauthorized, Error} ->
- {BadUser, _BadPass} = Auth,
- {IPT, _Port} = Request#request.ip,
- IPS = jlib:ip_to_list(IPT),
- ?WARNING_MSG("Access of ~p from ~p failed with error: ~p",
- [BadUser, IPS, Error]),
- {401,
- [{<<"WWW-Authenticate">>,
- <<"basic realm=\"auth error, retry login "
- "to ejabberd\"">>}],
- ejabberd_web:make_xhtml([?XCT(<<"h1">>,
- <<"Unauthorized">>)])}
- end;
- false -> ejabberd_web:error(not_found)
- end;
-process(RPath,
- #request{auth = Auth, lang = Lang, host = HostHTTP,
- method = Method} =
- Request) ->
- case get_auth_admin(Auth, HostHTTP, RPath, Method) of
- {ok, {User, Server}} ->
- AJID = get_jid(Auth, HostHTTP, Method),
- process_admin(global,
- Request#request{path = RPath,
- auth = {auth_jid, Auth, AJID},
- us = {User, Server}});
- {unauthorized, <<"no-auth-provided">>} ->
- {401,
- [{<<"WWW-Authenticate">>,
- <<"basic realm=\"ejabberd\"">>}],
- ejabberd_web:make_xhtml([?XCT(<<"h1">>,
- <<"Unauthorized">>)])};
- {unauthorized, Error} ->
- {BadUser, _BadPass} = Auth,
- {IPT, _Port} = Request#request.ip,
- IPS = jlib:ip_to_list(IPT),
- ?WARNING_MSG("Access of ~p from ~p failed with error: ~p",
- [BadUser, IPS, Error]),
- {401,
- [{<<"WWW-Authenticate">>,
- <<"basic realm=\"auth error, retry login "
- "to ejabberd\"">>}],
- ejabberd_web:make_xhtml([?XCT(<<"h1">>,
- <<"Unauthorized">>)])}
- end.
-
-get_auth_admin(Auth, HostHTTP, RPath, Method) ->
- case Auth of
- {SJID, Pass} ->
- {HostOfRule, AccessRule} = get_acl_rule(RPath, Method),
- case jlib:string_to_jid(SJID) of
- error -> {unauthorized, <<"badformed-jid">>};
- #jid{user = <<"">>, server = User} ->
- get_auth_account(HostOfRule, AccessRule, User, HostHTTP,
- Pass);
- #jid{user = User, server = Server} ->
- get_auth_account(HostOfRule, AccessRule, User, Server,
- Pass)
- end;
- undefined -> {unauthorized, <<"no-auth-provided">>}
- end.
-
-get_auth_account(HostOfRule, AccessRule, User, Server,
- Pass) ->
- case ejabberd_auth:check_password(User, Server, Pass) of
- true ->
- case is_acl_match(HostOfRule, AccessRule,
- jlib:make_jid(User, Server, <<"">>))
- of
- false -> {unauthorized, <<"unprivileged-account">>};
- true -> {ok, {User, Server}}
- end;
- false ->
- case ejabberd_auth:is_user_exists(User, Server) of
- true -> {unauthorized, <<"bad-password">>};
- false -> {unauthorized, <<"inexistent-account">>}
- end
- end.
-
-%%%==================================
-%%%% make_xhtml
-
-make_xhtml(Els, Host, Lang, JID) ->
- make_xhtml(Els, Host, cluster, Lang, JID).
-
-%% @spec (Els, Host, Node, Lang, JID) -> {200, [html], xmlelement()}
-%% where Host = global | string()
-%% Node = cluster | atom()
-%% JID = jid()
-make_xhtml(Els, Host, Node, Lang, JID) ->
- Base = get_base_path(Host, cluster),
- MenuItems = make_navigation(Host, Node, Lang, JID),
- {200, [html],
- #xmlel{name = <<"html">>,
- attrs =
- [{<<"xmlns">>, <<"http://www.w3.org/1999/xhtml">>},
- {<<"xml:lang">>, Lang}, {<<"lang">>, Lang}],
- children =
- [#xmlel{name = <<"head">>, attrs = [],
- children =
- [?XCT(<<"title">>, <<"ejabberd Web Admin">>),
- #xmlel{name = <<"meta">>,
- attrs =
- [{<<"http-equiv">>, <<"Content-Type">>},
- {<<"content">>,
- <<"text/html; charset=utf-8">>}],
- children = []},
- #xmlel{name = <<"script">>,
- attrs =
- [{<<"src">>,
- <<Base/binary, "/additions.js">>},
- {<<"type">>, <<"text/javascript">>}],
- children = [?C(<<" ">>)]},
- #xmlel{name = <<"link">>,
- attrs =
- [{<<"href">>,
- <<Base/binary, "favicon.ico">>},
- {<<"type">>, <<"image/x-icon">>},
- {<<"rel">>, <<"shortcut icon">>}],
- children = []},
- #xmlel{name = <<"link">>,
- attrs =
- [{<<"href">>,
- <<Base/binary, "style.css">>},
- {<<"type">>, <<"text/css">>},
- {<<"rel">>, <<"stylesheet">>}],
- children = []}]},
- ?XE(<<"body">>,
- [?XAE(<<"div">>, [{<<"id">>, <<"container">>}],
- [?XAE(<<"div">>, [{<<"id">>, <<"header">>}],
- [?XE(<<"h1">>,
- [?ACT(<<"/admin/">>,
- <<"ejabberd Web Admin">>)])]),
- ?XAE(<<"div">>, [{<<"id">>, <<"navigation">>}],
- [?XE(<<"ul">>, MenuItems)]),
- ?XAE(<<"div">>, [{<<"id">>, <<"content">>}], Els),
- ?XAE(<<"div">>, [{<<"id">>, <<"clearcopyright">>}],
- [{xmlcdata, <<"">>}])]),
- ?XAE(<<"div">>, [{<<"id">>, <<"copyrightouter">>}],
- [?XAE(<<"div">>, [{<<"id">>, <<"copyright">>}],
- [?XC(<<"p">>,
- <<"ejabberd (c) 2002-2013 ProcessOne">>)])])])]}}.
-
-get_base_path(global, cluster) -> <<"/admin/">>;
-get_base_path(Host, cluster) ->
- <<"/admin/server/", Host/binary, "/">>;
-get_base_path(global, Node) ->
- <<"/admin/node/",
- (iolist_to_binary(atom_to_list(Node)))/binary, "/">>;
-get_base_path(Host, Node) ->
- <<"/admin/server/", Host/binary, "/node/",
- (iolist_to_binary(atom_to_list(Node)))/binary, "/">>.
-
-%%%==================================
-%%%% css & images
-
-additions_js() ->
- <<"\nfunction selectAll() {\n for(i=0;i<documen"
- "t.forms[0].elements.length;i++)\n { "
- "var e = document.forms[0].elements[i];\n "
- " if(e.type == 'checkbox')\n { e.checked "
- "= true; }\n }\n}\nfunction unSelectAll() "
- "{\n for(i=0;i<document.forms[0].elements.len"
- "gth;i++)\n { var e = document.forms[0].eleme"
- "nts[i];\n if(e.type == 'checkbox')\n "
- " { e.checked = false; }\n }\n}\n">>.
-
-css(Host) ->
- Base = get_base_path(Host, cluster),
- <<"\nhtml,body {\n background: white;\n "
- " margin: 0;\n padding: 0;\n height: "
- "100%;\n}\n\n#container {\n padding: "
- "0;\n margin: 0;\n min-height: 100%;\n "
- " height: 100%;\n margin-bottom: -30px;\n}\n\n"
- "html>body #container {\n height: auto;\n}\n\n"
- "#header h1 {\n width: 100%;\n height: "
- "55px;\n padding: 0;\n margin: 0;\n "
- " background: transparent url(\"",
- Base/binary,
- "logo-fill.png\");\n}\n\n#header h1 a "
- "{\n position: absolute;\n top: 0;\n "
- " left: 0;\n width: 100%;\n height: "
- "55px;\n padding: 0;\n margin: 0;\n "
- " background: transparent url(\"",
- Base/binary,
- "logo.png\") no-repeat;\n display: block;\n "
- " text-indent: -700em;\n}\n\n#clearcopyright "
- "{\n display: block;\n width: 100%;\n "
- " height: 30px;\n}\n\n#copyrightouter "
- "{\n display: table;\n width: 100%;\n "
- " height: 30px;\n}\n\n#copyright {\n "
- " display: table-cell;\n vertical-align: "
- "bottom;\n width: 100%;\n height: 30px;\n}\n\n"
- "#copyright p {\n margin-left: 0;\n "
- " margin-right: 0;\n margin-top: 5px;\n "
- " margin-bottom: 0;\n padding-left: "
- "0;\n padding-right: 0;\n padding-top: "
- "1px;\n padding-bottom: 1px;\n width: "
- "100%;\n color: #ffffff;\n background-color: "
- "#fe8a00;\n font-family: Verdana, Arial, "
- "Helvetica, sans-serif; \n font-size: "
- "7pt;\n font-weight: bold;\n text-align: "
- "center;\n}\n\n#navigation ul {\n position: "
- "absolute;\n top: 65px;\n left: 0;\n "
- " padding: 0 1px 1px 1px;\n margin: "
- "0;\n font-family: Verdana, Arial, Helvetica, "
- "sans-serif; \n font-size: 8pt;\n font-weigh"
- "t: bold;\n border-top: 1px solid #d47911;\n "
- " width: 17em;\n}\n\n#navigation ul li "
- "{\n list-style: none;\n margin: 0;\n "
- " text-align: left;\n display: inline;\n}\n\n"
- "#navigation ul li a {\n margin: 0;\n "
- " display: block;\n padding: 3px 6px "
- "3px 9px;\n border-left: 1em solid #ffc78c;\n "
- " border-right: 1px solid #d47911;\n "
- " border-bottom: 1px solid #d47911;\n "
- " background: #ffe3c9;\n text-decoration: "
- "none;\n}\n\n#navigation ul li a:link "
- "{\n color: #844;\n}\n\n#navigation "
- "ul li a:visited {\n color: #766;\n}\n\n#navig"
- "ation ul li a:hover {\n border-color: "
- "#fc8800;\n color: #FFF;\n background: "
- "#332;\n}\n\nul li #navhead a, ul li "
- "#navheadsub a, ul li #navheadsubsub "
- "a {\n text-align: center;\n border-top: "
- "1px solid #d47911;\n border-bottom: "
- "2px solid #d47911;\n background: #FED6A6;\n}\n\n"
- "#navheadsub, #navitemsub {\n border-left: "
- "7px solid white;\n margin-left: 2px;\n}\n\n#"
- "navheadsubsub, #navitemsubsub {\n border-lef"
- "t: 14px solid white;\n margin-left: "
- "4px;\n}\n\n#lastactivity li {\n font-weight: "
- "bold;\n border: 1px solid #d6760e;\n "
- " background-color: #fff2e8;\n padding: "
- "2px;\n margin-bottom: -1px;\n}\n\ntd.copy "
- "{\n color: #ffffff;\n background-color: "
- "#fe8a00;\n font-family: Verdana, Arial, "
- "Helvetica, sans-serif; \n font-size: "
- "7pt;\n font-weight: bold;\n text-align: "
- "center;\n}\n\ninput {\n font-family: "
- "Verdana, Arial, Helvetica, sans-serif; "
- "\n font-size: 10pt;\n border: 1px "
- "solid #d6760e;\n color: #723202;\n "
- " background-color: #fff2e8;\n vertical-align"
- ": middle;\n margin-bottom: 0px;\n "
- "padding: 0.1em;\n}\n\ninput[type=submit] "
- "{\n font-family: Verdana, Arial, Helvetica, "
- "sans-serif; \n font-size: 8pt;\n font-weigh"
- "t: bold;\n color: #ffffff;\n background-col"
- "or: #fe8a00;\n border: 1px solid #d6760e;\n}\n\n"
- "textarea {\n font-family: Verdana, "
- "Arial, Helvetica, sans-serif; \n font-size: "
- "10pt;\n border: 1px solid #d6760e;\n "
- " color: #723202;\n background-color: "
- "#fff2e8;\n}\n\nselect {\n border: 1px "
- "solid #d6760e;\n color: #723202;\n "
- " background-color: #fff2e8;\n vertical-align"
- ": middle;\n margin-bottom: 0px; \n "
- " padding: 0.1em;\n}\n\nthead {\n color: "
- "#000000;\n background-color: #ffffff;\n "
- " font-family: Verdana, Arial, Helvetica, "
- "sans-serif; \n font-size: 10pt;\n "
- "font-weight: bold;\n}\n\ntr.head {\n "
- " color: #ffffff;\n background-color: "
- "#3b547a;\n font-family: Verdana, Arial, "
- "Helvetica, sans-serif; \n font-size: "
- "9pt;\n font-weight: bold;\n text-align: "
- "center;\n}\n\ntr.oddraw {\n color: "
- "#412c75;\n background-color: #ccd4df;\n "
- " font-family: Verdana, Arial, Helvetica, "
- "sans-serif; \n font-size: 9pt;\n font-weigh"
- "t: normal;\n text-align: center;\n}\n\ntr.ev"
- "enraw {\n color: #412c75;\n background-colo"
- "r: #dbe0e8;\n font-family: Verdana, "
- "Arial, Helvetica, sans-serif; \n font-size: "
- "9pt;\n font-weight: normal;\n text-align: "
- "center;\n}\n\ntd.leftheader {\n color: "
- "#412c75;\n background-color: #ccccc1;\n "
- " font-family: Verdana, Arial, Helvetica, "
- "sans-serif; \n font-size: 9pt;\n font-weigh"
- "t: bold;\n padding-left: 5px;\n padding-top"
- ": 2px;\n padding-bottom: 2px;\n margin-top: "
- "0px;\n margin-bottom: 0px;\n}\n\ntd.leftcont"
- "ent {\n color: #000044;\n background-color: "
- "#e6e6df;\n font-family: Verdana, Arial, "
- "Helvetica, sans-serif; \n font-size: "
- "7pt;\n font-weight: normal;\n padding-left: "
- "5px;\n padding-right: 5px;\n padding-top: "
- "2px;\n padding-bottom: 2px;\n margin-top: "
- "0px;\n margin-bottom: 0px;\n}\n\ntd.rightcon"
- "tent {\n color: #000044;\n font-family: "
- "Verdana, Arial, Helvetica, sans-serif; "
- "\n font-size: 10pt;\n font-weight: "
- "normal;\n text-align: justify;\n padding-le"
- "ft: 10px;\n padding-right: 10px;\n "
- " padding-bottom: 5px;\n}\n\n\nh1 {\n "
- " color: #000044;\n font-family: Verdana, "
- "Arial, Helvetica, sans-serif; \n font-size: "
- "14pt;\n font-weight: bold;\n text-align: "
- "center;\n padding-top: 2px;\n padding-botto"
- "m: 2px;\n margin-top: 0px;\n margin-bottom: "
- "0px;\n}\n\nh2 {\n color: #000044;\n "
- " font-family: Verdana, Arial, Helvetica, "
- "sans-serif; \n font-size: 12pt;\n "
- "font-weight: bold;\n text-align: center;\n "
- " padding-top: 2px;\n padding-bottom: "
- "2px;\n margin-top: 0px;\n margin-bottom: "
- "0px;\n}\n\nh3 {\n color: #000044;\n "
- " font-family: Verdana, Arial, Helvetica, "
- "sans-serif; \n font-size: 10pt;\n "
- "font-weight: bold;\n text-align: left;\n "
- " padding-top: 20px;\n padding-bottom: "
- "2px;\n margin-top: 0px;\n margin-bottom: "
- "0px;\n}\n\n#content a:link {\n color: "
- "#990000; \n font-family: Verdana, Arial, "
- "Helvetica, sans-serif; \n font-size: "
- "10pt;\n font-weight: bold;\n text-decoratio"
- "n: underline;\n}\n#content a:visited "
- "{\n color: #990000; \n font-family: "
- "Verdana, Arial, Helvetica, sans-serif; "
- "\n font-size: 10pt;\n font-weight: "
- "bold;\n text-decoration: underline;\n}\n#con"
- "tent a:hover {\n color: #cc6600; \n "
- " font-family: Verdana, Arial, Helvetica, "
- "sans-serif; \n font-size: 10pt;\n "
- "font-weight: bold;\n text-decoration: "
- "underline;\n}\n\n\n#content ul li {\n "
- " list-style-type: disc;\n font-size: "
- "10pt;\n /*font-size: 7pt;*/\n padding-left: "
- "10px;\n}\n\n#content ul.nolistyle>li "
- "{\n list-style-type: none;\n}\n\n#content "
- "li.big {\n font-size: 10pt;\n}\n\n#content "
- "{\n font-family: Verdana, Arial, Helvetica, "
- "sans-serif; \n font-size: 10pt;\n "
- "padding-left: 17em;\n padding-top: "
- "5px;\n}\n\ndiv.guidelink {\n text-align: "
- "right;\n padding-right: 1em;\n}\n\ntable.wit"
- "htextareas>tbody>tr>td {\n vertical-align: "
- "top;\n}\n\np.result {\n border: 1px;\n "
- " border-style: dashed;\n border-color: "
- "#FE8A02;\n padding: 1em;\n margin-right: "
- "1em;\n background: #FFE3C9;\n}\n\n*.alignrig"
- "ht {\n font-size: 10pt;\n text-align: "
- "right;\n}\n\n">>.
-
-favicon() ->
- jlib:decode_base64(<<"AAABAAEAEBAQAAEABAAoAQAAFgAAACgAAAAQAAAAIAAAA"
- "AEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJf+cAAI"
- "PsAAGC8gAVhecAAIr8ACiR7wBBmOcAUKPsAFun8ABhqeo"
- "AgLryAJLB8ACz1PcAv9r7AMvi+gAAAAAAAgICARMhICAk"
- "JCQkQkFCQgICN2d2cSMgJCRevdvVQkICAlqYh5MgICQkX"
- "rRCQkJCMgI7kiAjICAUFF2swkFBQRQUXazCQUFBAgI7ki"
- "AgICAkJF60QkJCQgICOpiHkyAgJCRevdvlQkICAjdndnM"
- "gICQkJCRCQkJCAgICARAgICAAAAAAAAAAAAAAAAAAAAAA"
- "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
- "AAAAAAAAAAAAAAAAAAA">>).
-
-logo() ->
- jlib:decode_base64(<<"iVBORw0KGgoAAAANSUhEUgAAAVcAAAA3CAMAAACPbPnEA"
- "AAAAXNSR0IArs4c6QAAAEtQTFRFcTIA1XcE/YsA/40E/p"
- "IH/JYc/5kg/54i/KIu/6U6/apE/61H/61P/bFX/7Vh/bd"
- "a/rpq/L5s/8J2/cJ8/8qI/86Y/9aj/9mt/+bJ7EGiPwAA"
- "AZRJREFUeNrt28lug0AQhGHajrPv+/s/aVwpDlgE0gQ3t"
- "qO/DhxihMg33VJ7JmmCVKSJlVJ4bZQ93Jl/zjJv+8tzcM"
- "UVV1xxLXIlRfPAZptYrbf5YeW618PWyvG8w/g9ZwquuJ6"
- "Y6+bbdY0rrifhSmrmgUulVXbVDq3H39Zy6Cf9+8c7JNM/"
- "mXeY8+SMRmuIK6644oprkSupmQdulLhQdup1qJKmrmWmV"
- "pb5NN9LUyddu7nnLYkrrrjiimuVK6mZB+6VuFbiXJk8v/"
- "bnv0PVa+Yd5tdr/x7vCfqbgPsfV1xxxRXXKldSMw+8KPG"
- "gxJWyU7WZE538p0vOr/lOm/q7dPf+bOVKvVXiUcEVV1xx"
- "xbXMldTMA29KPCtxp7T6XpvxE6/9nm/l987mnG9l5u/8j"
- "O4Ot9uTEq8KrrjiiiuuZa6kZh74UFpli3sO61btMfyHyW"
- "Gv/RMs7wB67ne32/BdwRVXXHHFtcyV1MwDn0qrbHHvyPT"
- "/Dsarla/R/1GpQydYPhf0bqC/A7jz7YkrrrjiimuVK6nI"
- "F5dWoNvcLcs/AAAAAElFTkSuQmCC">>).
-
-logo_fill() ->
- jlib:decode_base64(<<"iVBORw0KGgoAAAANSUhEUgAAAAYAAAA3BAMAAADdxCZzA"
- "AAAAXNSR0IArs4c6QAAAB5QTFRF1nYO/ooC/o4O/pIS/p"
- "4q/q5K/rpq/sqM/tam/ubGzn/S/AAAAEFJREFUCNdlw0s"
- "RwCAQBUE+gSRHLGABC1jAAhbWAhZwC+88XdXOXb4UlFAr"
- "SmwN5ekdJY2BkudEec1QvrVQ/r3xOlK9HsTvertmAAAAA"
- "ElFTkSuQmCC">>).
-
-%%%==================================
-%%%% process_admin
-
-process_admin(global,
- #request{path = [], auth = {_, _, AJID},
- lang = Lang}) ->
- make_xhtml((?H1GL((?T(<<"Administration">>)), <<"toc">>,
- <<"Contents">>))
- ++
- [?XE(<<"ul">>,
- [?LI([?ACT(MIU, MIN)])
- || {MIU, MIN}
- <- get_menu_items(global, cluster, Lang, AJID)])],
- global, Lang, AJID);
-process_admin(Host,
- #request{path = [], auth = {_, _Auth, AJID},
- lang = Lang}) ->
- make_xhtml([?XCT(<<"h1">>, <<"Administration">>),
- ?XE(<<"ul">>,
- [?LI([?ACT(MIU, MIN)])
- || {MIU, MIN}
- <- get_menu_items(Host, cluster, Lang, AJID)])],
- Host, Lang, AJID);
-process_admin(Host,
- #request{path = [<<"style.css">>]}) ->
- {200,
- [{<<"Content-Type">>, <<"text/css">>}, last_modified(),
- cache_control_public()],
- css(Host)};
-process_admin(_Host,
- #request{path = [<<"favicon.ico">>]}) ->
- {200,
- [{<<"Content-Type">>, <<"image/x-icon">>},
- last_modified(), cache_control_public()],
- favicon()};
-process_admin(_Host,
- #request{path = [<<"logo.png">>]}) ->
- {200,
- [{<<"Content-Type">>, <<"image/png">>}, last_modified(),
- cache_control_public()],
- logo()};
-process_admin(_Host,
- #request{path = [<<"logo-fill.png">>]}) ->
- {200,
- [{<<"Content-Type">>, <<"image/png">>}, last_modified(),
- cache_control_public()],
- logo_fill()};
-process_admin(_Host,
- #request{path = [<<"additions.js">>]}) ->
- {200,
- [{<<"Content-Type">>, <<"text/javascript">>},
- last_modified(), cache_control_public()],
- additions_js()};
-process_admin(Host,
- #request{path = [<<"acls-raw">>], q = Query,
- auth = {_, _Auth, AJID}, lang = Lang}) ->
- Res = case lists:keysearch(<<"acls">>, 1, Query) of
- {value, {_, String}} ->
- case erl_scan:string(binary_to_list(String)) of
- {ok, Tokens, _} ->
- case erl_parse:parse_term(Tokens) of
- {ok, NewACLs} ->
- case acl:add_list(Host, NewACLs, true) of
- ok -> ok;
- _ -> error
- end;
- _ -> error
- end;
- _ -> error
- end;
- _ -> nothing
- end,
- ACLs = lists:keysort(2,
- ets:select(acl,
- [{{acl, {'$1', Host}, '$2'}, [],
- [{{acl, '$1', '$2'}}]}])),
- {NumLines, ACLsP} = term_to_paragraph(ACLs, 80),
- make_xhtml((?H1GL((?T(<<"Access Control Lists">>)),
- <<"ACLDefinition">>, <<"ACL Definition">>))
- ++
- case Res of
- ok -> [?XREST(<<"Submitted">>)];
- error -> [?XREST(<<"Bad format">>)];
- nothing -> []
- end
- ++
- [?XAE(<<"form">>,
- [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
- [?TEXTAREA(<<"acls">>,
- (iolist_to_binary(integer_to_list(lists:max([16,
- NumLines])))),
- <<"80">>, <<(iolist_to_binary(ACLsP))/binary, ".">>),
- ?BR,
- ?INPUTT(<<"submit">>, <<"submit">>, <<"Submit">>)])],
- Host, Lang, AJID);
-process_admin(Host,
- #request{method = Method, path = [<<"acls">>],
- auth = {_, _Auth, AJID}, q = Query, lang = Lang}) ->
- ?DEBUG("query: ~p", [Query]),
- Res = case Method of
- 'POST' ->
- case catch acl_parse_query(Host, Query) of
- {'EXIT', _} -> error;
- NewACLs ->
- ?INFO_MSG("NewACLs at ~s: ~p", [Host, NewACLs]),
- case acl:add_list(Host, NewACLs, true) of
- ok -> ?INFO_MSG("NewACLs: ok", []), ok;
- _ -> error
- end
- end;
- _ -> nothing
- end,
- ACLs = lists:keysort(2,
- ets:select(acl,
- [{{acl, {'$1', Host}, '$2'}, [],
- [{{acl, '$1', '$2'}}]}])),
- make_xhtml((?H1GL((?T(<<"Access Control Lists">>)),
- <<"ACLDefinition">>, <<"ACL Definition">>))
- ++
- case Res of
- ok -> [?XREST(<<"Submitted">>)];
- error -> [?XREST(<<"Bad format">>)];
- nothing -> []
- end
- ++
- [?XE(<<"p">>, [?ACT(<<"../acls-raw/">>, <<"Raw">>)])] ++
- [?XAE(<<"form">>,
- [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
- [acls_to_xhtml(ACLs), ?BR,
- ?INPUTT(<<"submit">>, <<"delete">>,
- <<"Delete Selected">>),
- ?C(<<" ">>),
- ?INPUTT(<<"submit">>, <<"submit">>,
- <<"Submit">>)])],
- Host, Lang, AJID);
-process_admin(Host,
- #request{path = [<<"access-raw">>],
- auth = {_, _Auth, AJID}, q = Query, lang = Lang}) ->
- SetAccess = fun (Rs) ->
- mnesia:transaction(fun () ->
- Os = mnesia:select(config,
- [{{config,
- {access,
- '$1',
- Host},
- '$2'},
- [],
- ['$_']}]),
- lists:foreach(fun (O) ->
- mnesia:delete_object(O)
- end,
- Os),
- lists:foreach(fun ({access,
- Name,
- Rules}) ->
- mnesia:write({config,
- {access,
- Name,
- Host},
- Rules})
- end,
- Rs)
- end)
- end,
- Res = case lists:keysearch(<<"access">>, 1, Query) of
- {value, {_, String}} ->
- case erl_scan:string(binary_to_list(String)) of
- {ok, Tokens, _} ->
- case erl_parse:parse_term(Tokens) of
- {ok, Rs} ->
- case SetAccess(Rs) of
- {atomic, _} -> ok;
- _ -> error
- end;
- _ -> error
- end;
- _ -> error
- end;
- _ -> nothing
- end,
- Access = ets:select(config,
- [{{config, {access, '$1', Host}, '$2'}, [],
- [{{access, '$1', '$2'}}]}]),
- {NumLines, AccessP} = term_to_paragraph(lists:keysort(2,Access), 80),
- make_xhtml((?H1GL((?T(<<"Access Rules">>)),
- <<"AccessRights">>, <<"Access Rights">>))
- ++
- case Res of
- ok -> [?XREST(<<"Submitted">>)];
- error -> [?XREST(<<"Bad format">>)];
- nothing -> []
- end
- ++
- [?XAE(<<"form">>,
- [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
- [?TEXTAREA(<<"access">>,
- (iolist_to_binary(integer_to_list(lists:max([16,
- NumLines])))),
- <<"80">>, <<(iolist_to_binary(AccessP))/binary, ".">>),
- ?BR,
- ?INPUTT(<<"submit">>, <<"submit">>, <<"Submit">>)])],
- Host, Lang, AJID);
-process_admin(Host,
- #request{method = Method, path = [<<"access">>],
- q = Query, auth = {_, _Auth, AJID}, lang = Lang}) ->
- ?DEBUG("query: ~p", [Query]),
- Res = case Method of
- 'POST' ->
- case catch access_parse_query(Host, Query) of
- {'EXIT', _} -> error;
- ok -> ok
- end;
- _ -> nothing
- end,
- AccessRules = ets:select(config,
- [{{config, {access, '$1', Host}, '$2'}, [],
- [{{access, '$1', '$2'}}]}]),
- make_xhtml((?H1GL((?T(<<"Access Rules">>)),
- <<"AccessRights">>, <<"Access Rights">>))
- ++
- case Res of
- ok -> [?XREST(<<"Submitted">>)];
- error -> [?XREST(<<"Bad format">>)];
- nothing -> []
- end
- ++
- [?XE(<<"p">>, [?ACT(<<"../access-raw/">>, <<"Raw">>)])]
- ++
- [?XAE(<<"form">>,
- [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
- [access_rules_to_xhtml(AccessRules, Lang), ?BR,
- ?INPUTT(<<"submit">>, <<"delete">>,
- <<"Delete Selected">>)])],
- Host, Lang, AJID);
-process_admin(Host,
- #request{path = [<<"access">>, SName], q = Query,
- auth = {_, _Auth, AJID}, lang = Lang}) ->
- ?DEBUG("query: ~p", [Query]),
- Name = jlib:binary_to_atom(SName),
- Res = case lists:keysearch(<<"rules">>, 1, Query) of
- {value, {_, String}} ->
- case parse_access_rule(String) of
- {ok, Rs} ->
- ejabberd_config:add_global_option({access, Name, Host},
- Rs),
- ok;
- _ -> error
- end;
- _ -> nothing
- end,
- Rules = case ejabberd_config:get_global_option(
- {access, Name, Host}, fun(V) -> V end)
- of
- undefined -> [];
- Rs1 -> Rs1
- end,
- make_xhtml([?XC(<<"h1">>,
- list_to_binary(io_lib:format(
- ?T(<<"~s access rule configuration">>),
- [SName])))]
- ++
- case Res of
- ok -> [?XREST(<<"Submitted">>)];
- error -> [?XREST(<<"Bad format">>)];
- nothing -> []
- end
- ++
- [?XAE(<<"form">>,
- [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
- [access_rule_to_xhtml(Rules), ?BR,
- ?INPUTT(<<"submit">>, <<"submit">>, <<"Submit">>)])],
- Host, Lang, AJID);
-process_admin(global,
- #request{path = [<<"vhosts">>], auth = {_, _Auth, AJID},
- lang = Lang}) ->
- Res = list_vhosts(Lang, AJID),
- make_xhtml((?H1GL((?T(<<"Virtual Hosts">>)),
- <<"virtualhost">>, <<"Virtual Hosting">>))
- ++ Res,
- global, Lang, AJID);
-process_admin(Host,
- #request{path = [<<"users">>], q = Query,
- auth = {_, _Auth, AJID}, lang = Lang})
- when is_binary(Host) ->
- Res = list_users(Host, Query, Lang, fun url_func/1),
- make_xhtml([?XCT(<<"h1">>, <<"Users">>)] ++ Res, Host,
- Lang, AJID);
-process_admin(Host,
- #request{path = [<<"users">>, Diap],
- auth = {_, _Auth, AJID}, lang = Lang})
- when is_binary(Host) ->
- Res = list_users_in_diapason(Host, Diap, Lang,
- fun url_func/1),
- make_xhtml([?XCT(<<"h1">>, <<"Users">>)] ++ Res, Host,
- Lang, AJID);
-process_admin(Host,
- #request{path = [<<"online-users">>],
- auth = {_, _Auth, AJID}, lang = Lang})
- when is_binary(Host) ->
- Res = list_online_users(Host, Lang),
- make_xhtml([?XCT(<<"h1">>, <<"Online Users">>)] ++ Res,
- Host, Lang, AJID);
-process_admin(Host,
- #request{path = [<<"last-activity">>],
- auth = {_, _Auth, AJID}, q = Query, lang = Lang})
- when is_binary(Host) ->
- ?DEBUG("query: ~p", [Query]),
- Month = case lists:keysearch(<<"period">>, 1, Query) of
- {value, {_, Val}} -> Val;
- _ -> <<"month">>
- end,
- Res = case lists:keysearch(<<"ordinary">>, 1, Query) of
- {value, {_, _}} ->
- list_last_activity(Host, Lang, false, Month);
- _ -> list_last_activity(Host, Lang, true, Month)
- end,
- make_xhtml([?XCT(<<"h1">>, <<"Users Last Activity">>)]
- ++
- [?XAE(<<"form">>,
- [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
- [?CT(<<"Period: ">>),
- ?XAE(<<"select">>, [{<<"name">>, <<"period">>}],
- (lists:map(fun ({O, V}) ->
- Sel = if O == Month ->
- [{<<"selected">>,
- <<"selected">>}];
- true -> []
- end,
- ?XAC(<<"option">>,
- (Sel ++
- [{<<"value">>, O}]),
- V)
- end,
- [{<<"month">>, ?T(<<"Last month">>)},
- {<<"year">>, ?T(<<"Last year">>)},
- {<<"all">>,
- ?T(<<"All activity">>)}]))),
- ?C(<<" ">>),
- ?INPUTT(<<"submit">>, <<"ordinary">>,
- <<"Show Ordinary Table">>),
- ?C(<<" ">>),
- ?INPUTT(<<"submit">>, <<"integral">>,
- <<"Show Integral Table">>)])]
- ++ Res,
- Host, Lang, AJID);
-process_admin(Host,
- #request{path = [<<"stats">>], auth = {_, _Auth, AJID},
- lang = Lang}) ->
- Res = get_stats(Host, Lang),
- make_xhtml([?XCT(<<"h1">>, <<"Statistics">>)] ++ Res,
- Host, Lang, AJID);
-process_admin(Host,
- #request{path = [<<"user">>, U],
- auth = {_, _Auth, AJID}, q = Query, lang = Lang}) ->
- case ejabberd_auth:is_user_exists(U, Host) of
- true ->
- Res = user_info(U, Host, Query, Lang),
- make_xhtml(Res, Host, Lang, AJID);
- false ->
- make_xhtml([?XCT(<<"h1">>, <<"Not Found">>)], Host,
- Lang, AJID)
- end;
-process_admin(Host,
- #request{path = [<<"nodes">>], auth = {_, _Auth, AJID},
- lang = Lang}) ->
- Res = get_nodes(Lang),
- make_xhtml(Res, Host, Lang, AJID);
-process_admin(Host,
- #request{path = [<<"node">>, SNode | NPath],
- auth = {_, _Auth, AJID}, q = Query, lang = Lang}) ->
- case search_running_node(SNode) of
- false ->
- make_xhtml([?XCT(<<"h1">>, <<"Node not found">>)], Host,
- Lang, AJID);
- Node ->
- Res = get_node(Host, Node, NPath, Query, Lang),
- make_xhtml(Res, Host, Node, Lang, AJID)
- end;
-%%%==================================
-%%%% process_admin default case
-process_admin(Host,
- #request{lang = Lang, auth = {_, _Auth, AJID}} =
- Request) ->
- {Hook, Opts} = case Host of
- global -> {webadmin_page_main, [Request]};
- Host -> {webadmin_page_host, [Host, Request]}
- end,
- case ejabberd_hooks:run_fold(Hook, Host, [], Opts) of
- [] ->
- setelement(1,
- make_xhtml([?XC(<<"h1">>, <<"Not Found">>)], Host, Lang,
- AJID),
- 404);
- Res -> make_xhtml(Res, Host, Lang, AJID)
- end.
-
-%%%==================================
-%%%% acl
-
-acls_to_xhtml(ACLs) ->
- ?XAE(<<"table">>, [],
- [?XE(<<"tbody">>,
- (lists:map(fun ({acl, Name, Spec} = ACL) ->
- SName = iolist_to_binary(atom_to_list(Name)),
- ID = term_to_id(ACL),
- ?XE(<<"tr">>,
- ([?XE(<<"td">>,
- [?INPUT(<<"checkbox">>,
- <<"selected">>, ID)]),
- ?XC(<<"td">>, SName)]
- ++ acl_spec_to_xhtml(ID, Spec)))
- end,
- ACLs)
- ++
- [?XE(<<"tr">>,
- ([?X(<<"td">>),
- ?XE(<<"td">>,
- [?INPUT(<<"text">>, <<"namenew">>, <<"">>)])]
- ++ acl_spec_to_xhtml(<<"new">>, {user, <<"">>})))]))]).
-
-acl_spec_to_text({user, U}) -> {user, U};
-acl_spec_to_text({server, S}) -> {server, S};
-acl_spec_to_text({user, U, S}) ->
- {user, <<U/binary, "@", S/binary>>};
-acl_spec_to_text({user_regexp, RU}) ->
- {user_regexp, RU};
-acl_spec_to_text({user_regexp, RU, S}) ->
- {user_regexp, <<RU/binary, "@", S/binary>>};
-acl_spec_to_text({server_regexp, RS}) ->
- {server_regexp, RS};
-acl_spec_to_text({node_regexp, RU, RS}) ->
- {node_regexp, <<RU/binary, "@", RS/binary>>};
-acl_spec_to_text({user_glob, RU}) -> {user_glob, RU};
-acl_spec_to_text({user_glob, RU, S}) ->
- {user_glob, <<RU/binary, "@", S/binary>>};
-acl_spec_to_text({server_glob, RS}) ->
- {server_glob, RS};
-acl_spec_to_text({node_glob, RU, RS}) ->
- {node_glob, <<RU/binary, "@", RS/binary>>};
-acl_spec_to_text(all) -> {all, <<"">>};
-acl_spec_to_text(Spec) -> {raw, term_to_string(Spec)}.
-
-acl_spec_to_xhtml(ID, Spec) ->
- {Type, Str} = acl_spec_to_text(Spec),
- [acl_spec_select(ID, Type), ?ACLINPUT(Str)].
-
-acl_spec_select(ID, Opt) ->
- ?XE(<<"td">>,
- [?XAE(<<"select">>,
- [{<<"name">>, <<"type", ID/binary>>}],
- (lists:map(fun (O) ->
- Sel = if O == Opt ->
- [{<<"selected">>,
- <<"selected">>}];
- true -> []
- end,
- ?XAC(<<"option">>,
- (Sel ++
- [{<<"value">>,
- iolist_to_binary(atom_to_list(O))}]),
- (iolist_to_binary(atom_to_list(O))))
- end,
- [user, server, user_regexp, server_regexp, node_regexp,
- user_glob, server_glob, node_glob, all, raw])))]).
-
-%% @spec (T::any()) -> StringLine::string()
-term_to_string(T) ->
- StringParagraph =
- iolist_to_binary(io_lib:format("~1000000p", [T])),
- ejabberd_regexp:greplace(StringParagraph, <<"\\n ">>,
- <<"">>).
-
-%% @spec (T::any(), Cols::integer()) -> {NumLines::integer(), Paragraph::string()}
-term_to_paragraph(T, Cols) ->
- Paragraph = list_to_binary(erl_prettypr:format(erl_syntax:abstract(T),
- [{paper, Cols}])),
- FieldList = ejabberd_regexp:split(Paragraph, <<"\n">>),
- NumLines = length(FieldList),
- {NumLines, Paragraph}.
-
-term_to_id(T) -> jlib:encode_base64((term_to_binary(T))).
-
-acl_parse_query(Host, Query) ->
- ACLs = ets:select(acl,
- [{{acl, {'$1', Host}, '$2'}, [],
- [{{acl, '$1', '$2'}}]}]),
- case lists:keysearch(<<"submit">>, 1, Query) of
- {value, _} -> acl_parse_submit(ACLs, Query);
- _ ->
- case lists:keysearch(<<"delete">>, 1, Query) of
- {value, _} -> acl_parse_delete(ACLs, Query)
- end
- end.
-
-acl_parse_submit(ACLs, Query) ->
- NewACLs = lists:map(fun ({acl, Name, Spec} = ACL) ->
- ID = term_to_id(ACL),
- case {lists:keysearch(<<"type", ID/binary>>, 1,
- Query),
- lists:keysearch(<<"value", ID/binary>>, 1,
- Query)}
- of
- {{value, {_, T}}, {value, {_, V}}} ->
- {Type, Str} = acl_spec_to_text(Spec),
- case
- {iolist_to_binary(atom_to_list(Type)),
- Str}
- of
- {T, V} -> ACL;
- _ ->
- NewSpec = string_to_spec(T, V),
- {acl, Name, NewSpec}
- end;
- _ -> ACL
- end
- end,
- ACLs),
- NewACL = case {lists:keysearch(<<"namenew">>, 1, Query),
- lists:keysearch(<<"typenew">>, 1, Query),
- lists:keysearch(<<"valuenew">>, 1, Query)}
- of
- {{value, {_, <<"">>}}, _, _} -> [];
- {{value, {_, N}}, {value, {_, T}}, {value, {_, V}}} ->
- NewName = jlib:binary_to_atom(N),
- NewSpec = string_to_spec(T, V),
- [{acl, NewName, NewSpec}];
- _ -> []
- end,
- NewACLs ++ NewACL.
-
-string_to_spec(<<"user">>, Val) ->
- string_to_spec2(user, Val);
-string_to_spec(<<"server">>, Val) -> {server, Val};
-string_to_spec(<<"user_regexp">>, Val) ->
- string_to_spec2(user_regexp, Val);
-string_to_spec(<<"server_regexp">>, Val) ->
- {server_regexp, Val};
-string_to_spec(<<"node_regexp">>, Val) ->
- #jid{luser = U, lserver = S, resource = <<"">>} =
- jlib:string_to_jid(Val),
- {node_regexp, U, S};
-string_to_spec(<<"user_glob">>, Val) ->
- string_to_spec2(user_glob, Val);
-string_to_spec(<<"server_glob">>, Val) ->
- {server_glob, Val};
-string_to_spec(<<"node_glob">>, Val) ->
- #jid{luser = U, lserver = S, resource = <<"">>} =
- jlib:string_to_jid(Val),
- {node_glob, U, S};
-string_to_spec(<<"all">>, _) -> all;
-string_to_spec(<<"raw">>, Val) ->
- {ok, Tokens, _} = erl_scan:string(binary_to_list(<<Val/binary, ".">>)),
- {ok, NewSpec} = erl_parse:parse_term(Tokens),
- NewSpec.
-
-string_to_spec2(ACLName, Val) ->
- #jid{luser = U, lserver = S, resource = <<"">>} =
- jlib:string_to_jid(Val),
- case U of
- <<"">> -> {ACLName, S};
- _ -> {ACLName, U, S}
- end.
-
-acl_parse_delete(ACLs, Query) ->
- NewACLs = lists:filter(fun ({acl, _Name, _Spec} =
- ACL) ->
- ID = term_to_id(ACL),
- not lists:member({<<"selected">>, ID}, Query)
- end,
- ACLs),
- NewACLs.
-
-access_rules_to_xhtml(AccessRules, Lang) ->
- ?XAE(<<"table">>, [],
- [?XE(<<"tbody">>,
- (lists:map(fun ({access, Name, Rules} = Access) ->
- SName = iolist_to_binary(atom_to_list(Name)),
- ID = term_to_id(Access),
- ?XE(<<"tr">>,
- [?XE(<<"td">>,
- [?INPUT(<<"checkbox">>,
- <<"selected">>, ID)]),
- ?XE(<<"td">>,
- [?AC(<<SName/binary, "/">>, SName)]),
- ?XC(<<"td">>, (term_to_string(Rules)))])
- end,
- lists:keysort(2,AccessRules))
- ++
- [?XE(<<"tr">>,
- [?X(<<"td">>),
- ?XE(<<"td">>,
- [?INPUT(<<"text">>, <<"namenew">>, <<"">>)]),
- ?XE(<<"td">>,
- [?INPUTT(<<"submit">>, <<"addnew">>,
- <<"Add New">>)])])]))]).
-
-access_parse_query(Host, Query) ->
- AccessRules = ets:select(config,
- [{{config, {access, '$1', Host}, '$2'}, [],
- [{{access, '$1', '$2'}}]}]),
- case lists:keysearch(<<"addnew">>, 1, Query) of
- {value, _} ->
- access_parse_addnew(AccessRules, Host, Query);
- _ ->
- case lists:keysearch(<<"delete">>, 1, Query) of
- {value, _} ->
- access_parse_delete(AccessRules, Host, Query)
- end
- end.
-
-access_parse_addnew(_AccessRules, Host, Query) ->
- case lists:keysearch(<<"namenew">>, 1, Query) of
- {value, {_, String}} when String /= <<"">> ->
- Name = jlib:binary_to_atom(String),
- ejabberd_config:add_global_option({access, Name, Host},
- []),
- ok
- end.
-
-access_parse_delete(AccessRules, Host, Query) ->
- lists:foreach(fun ({access, Name, _Rules} =
- AccessRule) ->
- ID = term_to_id(AccessRule),
- case lists:member({<<"selected">>, ID}, Query) of
- true ->
- mnesia:transaction(fun () ->
- mnesia:delete({config,
- {access,
- Name,
- Host}})
- end);
- _ -> ok
- end
- end,
- AccessRules),
- ok.
-
-access_rule_to_xhtml(Rules) ->
- Text = lists:flatmap(fun ({Access, ACL} = _Rule) ->
- SAccess = element_to_list(Access),
- SACL = atom_to_list(ACL),
- [SAccess, " \t", SACL, "\n"]
- end,
- Rules),
- ?XAC(<<"textarea">>,
- [{<<"name">>, <<"rules">>}, {<<"rows">>, <<"16">>},
- {<<"cols">>, <<"80">>}],
- list_to_binary(Text)).
-
-parse_access_rule(Text) ->
- Strings = str:tokens(Text, <<"\r\n">>),
- case catch lists:flatmap(fun (String) ->
- case str:tokens(String, <<" \t">>) of
- [Access, ACL] ->
- [{list_to_element(Access),
- jlib:binary_to_atom(ACL)}];
- [] -> []
- end
- end,
- Strings)
- of
- {'EXIT', _Reason} -> error;
- Rs -> {ok, Rs}
- end.
-
-%%%==================================
-%%%% list_vhosts
-
-list_vhosts(Lang, JID) ->
- Hosts = (?MYHOSTS),
- HostsAllowed = lists:filter(fun (Host) ->
- is_acl_match(Host,
- [configure, webadmin_view],
- JID)
- end,
- Hosts),
- list_vhosts2(Lang, HostsAllowed).
-
-list_vhosts2(Lang, Hosts) ->
- SHosts = lists:sort(Hosts),
- [?XE(<<"table">>,
- [?XE(<<"thead">>,
- [?XE(<<"tr">>,
- [?XCT(<<"td">>, <<"Host">>),
- ?XCT(<<"td">>, <<"Registered Users">>),
- ?XCT(<<"td">>, <<"Online Users">>)])]),
- ?XE(<<"tbody">>,
- (lists:map(fun (Host) ->
- OnlineUsers =
- length(ejabberd_sm:get_vh_session_list(Host)),
- RegisteredUsers =
- ejabberd_auth:get_vh_registered_users_number(Host),
- ?XE(<<"tr">>,
- [?XE(<<"td">>,
- [?AC(<<"../server/", Host/binary,
- "/">>,
- Host)]),
- ?XC(<<"td">>,
- (pretty_string_int(RegisteredUsers))),
- ?XC(<<"td">>,
- (pretty_string_int(OnlineUsers)))])
- end,
- SHosts)))])].
-
-%%%==================================
-%%%% list_users
-
-list_users(Host, Query, Lang, URLFunc) ->
- Res = list_users_parse_query(Query, Host),
- Users = ejabberd_auth:get_vh_registered_users(Host),
- SUsers = lists:sort([{S, U} || {U, S} <- Users]),
- FUsers = case length(SUsers) of
- N when N =< 100 ->
- [list_given_users(Host, SUsers, <<"../">>, Lang,
- URLFunc)];
- N ->
- NParts = trunc(math:sqrt(N * 6.17999999999999993783e-1))
- + 1,
- M = trunc(N / NParts) + 1,
- lists:flatmap(fun (K) ->
- L = K + M - 1,
- Last = if L < N ->
- su_to_list(lists:nth(L,
- SUsers));
- true ->
- su_to_list(lists:last(SUsers))
- end,
- Name = <<(su_to_list(lists:nth(K,
- SUsers)))/binary,
- $\s, 226, 128, 148, $\s,
- Last/binary>>,
- [?AC((URLFunc({user_diapason, K, L})),
- Name),
- ?BR]
- end,
- lists:seq(1, N, M))
- end,
- case Res of
-%% Parse user creation query and try register:
- ok -> [?XREST(<<"Submitted">>)];
- error -> [?XREST(<<"Bad format">>)];
- nothing -> []
- end
- ++
- [?XAE(<<"form">>,
- [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
- ([?XE(<<"table">>,
- [?XE(<<"tr">>,
- [?XC(<<"td">>, <<(?T(<<"User">>))/binary, ":">>),
- ?XE(<<"td">>,
- [?INPUT(<<"text">>, <<"newusername">>, <<"">>)]),
- ?XE(<<"td">>, [?C(<<" @ ", Host/binary>>)])]),
- ?XE(<<"tr">>,
- [?XC(<<"td">>, <<(?T(<<"Password">>))/binary, ":">>),
- ?XE(<<"td">>,
- [?INPUT(<<"password">>, <<"newuserpassword">>,
- <<"">>)]),
- ?X(<<"td">>)]),
- ?XE(<<"tr">>,
- [?X(<<"td">>),
- ?XAE(<<"td">>, [{<<"class">>, <<"alignright">>}],
- [?INPUTT(<<"submit">>, <<"addnewuser">>,
- <<"Add User">>)]),
- ?X(<<"td">>)])]),
- ?P]
- ++ FUsers))].
-
-list_users_parse_query(Query, Host) ->
- case lists:keysearch(<<"addnewuser">>, 1, Query) of
- {value, _} ->
- {value, {_, Username}} =
- lists:keysearch(<<"newusername">>, 1, Query),
- {value, {_, Password}} =
- lists:keysearch(<<"newuserpassword">>, 1, Query),
- case jlib:string_to_jid(<<Username/binary, "@",
- Host/binary>>)
- of
- error -> error;
- #jid{user = User, server = Server} ->
- case ejabberd_auth:try_register(User, Server, Password)
- of
- {error, _Reason} -> error;
- _ -> ok
- end
- end;
- false -> nothing
- end.
-
-list_users_in_diapason(Host, Diap, Lang, URLFunc) ->
- Users = ejabberd_auth:get_vh_registered_users(Host),
- SUsers = lists:sort([{S, U} || {U, S} <- Users]),
- [S1, S2] = ejabberd_regexp:split(Diap, <<"-">>),
- N1 = jlib:binary_to_integer(S1),
- N2 = jlib:binary_to_integer(S2),
- Sub = lists:sublist(SUsers, N1, N2 - N1 + 1),
- [list_given_users(Host, Sub, <<"../../">>, Lang,
- URLFunc)].
-
-list_given_users(Host, Users, Prefix, Lang, URLFunc) ->
- ModOffline = get_offlinemsg_module(Host),
- ?XE(<<"table">>,
- [?XE(<<"thead">>,
- [?XE(<<"tr">>,
- [?XCT(<<"td">>, <<"User">>),
- ?XCT(<<"td">>, <<"Offline Messages">>),
- ?XCT(<<"td">>, <<"Last Activity">>)])]),
- ?XE(<<"tbody">>,
- (lists:map(fun (_SU = {Server, User}) ->
- US = {User, Server},
- QueueLenStr = get_offlinemsg_length(ModOffline,
- User,
- Server),
- FQueueLen = [?AC((URLFunc({users_queue, Prefix,
- User, Server})),
- QueueLenStr)],
- FLast = case
- ejabberd_sm:get_user_resources(User,
- Server)
- of
- [] ->
- case mod_last:get_last_info(User,
- Server)
- of
- not_found -> ?T(<<"Never">>);
- {ok, Shift, _Status} ->
- TimeStamp = {Shift div
- 1000000,
- Shift rem
- 1000000,
- 0},
- {{Year, Month, Day},
- {Hour, Minute, Second}} =
- calendar:now_to_local_time(TimeStamp),
- iolist_to_binary(io_lib:format("~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w",
- [Year,
- Month,
- Day,
- Hour,
- Minute,
- Second]))
- end;
- _ -> ?T(<<"Online">>)
- end,
- ?XE(<<"tr">>,
- [?XE(<<"td">>,
- [?AC((URLFunc({user, Prefix,
- ejabberd_http:url_encode(User),
- Server})),
- (us_to_list(US)))]),
- ?XE(<<"td">>, FQueueLen),
- ?XC(<<"td">>, FLast)])
- end,
- Users)))]).
-
-get_offlinemsg_length(ModOffline, User, Server) ->
- case ModOffline of
- none -> <<"disabled">>;
- _ ->
- pretty_string_int(ModOffline:get_queue_length(User,
- Server))
- end.
-
-get_offlinemsg_module(Server) ->
- case gen_mod:is_loaded(Server, mod_offline) of
- true -> mod_offline;
- false -> none
- end.
-
-get_lastactivity_menuitem_list(Server) ->
- case gen_mod:db_type(Server, mod_last) of
- mnesia -> [{<<"last-activity">>, <<"Last Activity">>}];
- _ -> []
- end.
-
-us_to_list({User, Server}) ->
- jlib:jid_to_string({User, Server, <<"">>}).
-
-su_to_list({Server, User}) ->
- jlib:jid_to_string({User, Server, <<"">>}).
-
-%%%==================================
-%%%% get_stats
-
-get_stats(global, Lang) ->
- OnlineUsers = mnesia:table_info(session, size),
- RegisteredUsers = lists:foldl(fun (Host, Total) ->
- ejabberd_auth:get_vh_registered_users_number(Host)
- + Total
- end,
- 0, ?MYHOSTS),
- S2SConns = ejabberd_s2s:dirty_get_connections(),
- S2SConnections = length(S2SConns),
- S2SServers = length(lists:usort([element(2, C)
- || C <- S2SConns])),
- [?XAE(<<"table">>, [],
- [?XE(<<"tbody">>,
- [?XE(<<"tr">>,
- [?XCT(<<"td">>, <<"Registered Users:">>),
- ?XC(<<"td">>, (pretty_string_int(RegisteredUsers)))]),
- ?XE(<<"tr">>,
- [?XCT(<<"td">>, <<"Online Users:">>),
- ?XC(<<"td">>, (pretty_string_int(OnlineUsers)))]),
- ?XE(<<"tr">>,
- [?XCT(<<"td">>, <<"Outgoing s2s Connections:">>),
- ?XC(<<"td">>, (pretty_string_int(S2SConnections)))]),
- ?XE(<<"tr">>,
- [?XCT(<<"td">>, <<"Outgoing s2s Servers:">>),
- ?XC(<<"td">>, (pretty_string_int(S2SServers)))])])])];
-get_stats(Host, Lang) ->
- OnlineUsers =
- length(ejabberd_sm:get_vh_session_list(Host)),
- RegisteredUsers =
- ejabberd_auth:get_vh_registered_users_number(Host),
- [?XAE(<<"table">>, [],
- [?XE(<<"tbody">>,
- [?XE(<<"tr">>,
- [?XCT(<<"td">>, <<"Registered Users:">>),
- ?XC(<<"td">>, (pretty_string_int(RegisteredUsers)))]),
- ?XE(<<"tr">>,
- [?XCT(<<"td">>, <<"Online Users:">>),
- ?XC(<<"td">>, (pretty_string_int(OnlineUsers)))])])])].
-
-list_online_users(Host, _Lang) ->
- Users = [{S, U}
- || {U, S, _R} <- ejabberd_sm:get_vh_session_list(Host)],
- SUsers = lists:usort(Users),
- lists:flatmap(fun ({_S, U} = SU) ->
- [?AC(<<"../user/",
- (ejabberd_http:url_encode(U))/binary, "/">>,
- (su_to_list(SU))),
- ?BR]
- end,
- SUsers).
-
-user_info(User, Server, Query, Lang) ->
- LServer = jlib:nameprep(Server),
- US = {jlib:nodeprep(User), LServer},
- Res = user_parse_query(User, Server, Query),
- Resources = ejabberd_sm:get_user_resources(User,
- Server),
- FResources =
- case Resources of
- [] -> [?CT(<<"None">>)];
- _ ->
- [?XE(<<"ul">>,
- (lists:map(
- fun (R) ->
- FIP = case
- ejabberd_sm:get_user_info(User,
- Server,
- R)
- of
- offline -> <<"">>;
- Info
- when
- is_list(Info) ->
- Node =
- proplists:get_value(node,
- Info),
- Conn =
- proplists:get_value(conn,
- Info),
- {IP, Port} =
- proplists:get_value(ip,
- Info),
- ConnS = case Conn of
- c2s ->
- <<"plain">>;
- c2s_tls ->
- <<"tls">>;
- c2s_compressed ->
- <<"zlib">>;
- c2s_compressed_tls ->
- <<"tls+zlib">>;
- http_bind ->
- <<"http-bind">>;
- http_poll ->
- <<"http-poll">>
- end,
- <<" (", ConnS/binary,
- "://",
- (jlib:ip_to_list(IP))/binary,
- ":",
- (jlib:integer_to_binary(Port))/binary,
- "#",
- (jlib:atom_to_binary(Node))/binary,
- ")">>
- end,
- ?LI([?C((<<R/binary, FIP/binary>>))])
- end,
- lists:sort(Resources))))]
- end,
- Password = ejabberd_auth:get_password_s(User, Server),
- FPassword = [?INPUT(<<"password">>, <<"password">>,
- Password),
- ?C(<<" ">>),
- ?INPUTT(<<"submit">>, <<"chpassword">>,
- <<"Change Password">>)],
- UserItems = ejabberd_hooks:run_fold(webadmin_user,
- LServer, [], [User, Server, Lang]),
- LastActivity = case ejabberd_sm:get_user_resources(User,
- Server)
- of
- [] ->
- case mod_last:get_last_info(User, Server) of
- not_found -> ?T(<<"Never">>);
- {ok, Shift, _Status} ->
- TimeStamp = {Shift div 1000000,
- Shift rem 1000000, 0},
- {{Year, Month, Day}, {Hour, Minute, Second}} =
- calendar:now_to_local_time(TimeStamp),
- iolist_to_binary(io_lib:format("~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w",
- [Year, Month, Day,
- Hour, Minute,
- Second]))
- end;
- _ -> ?T(<<"Online">>)
- end,
- [?XC(<<"h1">>, list_to_binary(io_lib:format(?T(<<"User ~s">>),
- [us_to_list(US)])))]
- ++
- case Res of
- ok -> [?XREST(<<"Submitted">>)];
- error -> [?XREST(<<"Bad format">>)];
- nothing -> []
- end
- ++
- [?XAE(<<"form">>,
- [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
- ([?XCT(<<"h3">>, <<"Connected Resources:">>)] ++
- FResources ++
- [?XCT(<<"h3">>, <<"Password:">>)] ++
- FPassword ++
- [?XCT(<<"h3">>, <<"Last Activity">>)] ++
- [?C(LastActivity)] ++
- UserItems ++
- [?P,
- ?INPUTT(<<"submit">>, <<"removeuser">>,
- <<"Remove User">>)]))].
-
-user_parse_query(User, Server, Query) ->
- lists:foldl(fun ({Action, _Value}, Acc)
- when Acc == nothing ->
- user_parse_query1(Action, User, Server, Query);
- ({_Action, _Value}, Acc) -> Acc
- end,
- nothing, Query).
-
-user_parse_query1(<<"password">>, _User, _Server,
- _Query) ->
- nothing;
-user_parse_query1(<<"chpassword">>, User, Server,
- Query) ->
- case lists:keysearch(<<"password">>, 1, Query) of
- {value, {_, Password}} ->
- ejabberd_auth:set_password(User, Server, Password), ok;
- _ -> error
- end;
-user_parse_query1(<<"removeuser">>, User, Server,
- _Query) ->
- ejabberd_auth:remove_user(User, Server), ok;
-user_parse_query1(Action, User, Server, Query) ->
- case ejabberd_hooks:run_fold(webadmin_user_parse_query,
- Server, [], [Action, User, Server, Query])
- of
- [] -> nothing;
- Res -> Res
- end.
-
-list_last_activity(Host, Lang, Integral, Period) ->
- {MegaSecs, Secs, _MicroSecs} = now(),
- TimeStamp = MegaSecs * 1000000 + Secs,
- case Period of
- <<"all">> -> TS = 0, Days = infinity;
- <<"year">> -> TS = TimeStamp - 366 * 86400, Days = 366;
- _ -> TS = TimeStamp - 31 * 86400, Days = 31
- end,
- case catch mnesia:dirty_select(last_activity,
- [{{last_activity, {'_', Host}, '$1', '_'},
- [{'>', '$1', TS}],
- [{trunc,
- {'/', {'-', TimeStamp, '$1'}, 86400}}]}])
- of
- {'EXIT', _Reason} -> [];
- Vals ->
- Hist = histogram(Vals, Integral),
- if Hist == [] -> [?CT(<<"No Data">>)];
- true ->
- Left = if Days == infinity -> 0;
- true -> Days - length(Hist)
- end,
- Tail = if Integral ->
- lists:duplicate(Left, lists:last(Hist));
- true -> lists:duplicate(Left, 0)
- end,
- Max = lists:max(Hist),
- [?XAE(<<"ol">>,
- [{<<"id">>, <<"lastactivity">>},
- {<<"start">>, <<"0">>}],
- [?XAE(<<"li">>,
- [{<<"style">>,
- <<"width:",
- (iolist_to_binary(integer_to_list(trunc(90 * V
- /
- Max))))/binary,
- "%;">>}],
- [{xmlcdata, pretty_string_int(V)}])
- || V <- Hist ++ Tail])]
- end
- end.
-
-histogram(Values, Integral) ->
- histogram(lists:sort(Values), Integral, 0, 0, []).
-
-histogram([H | T], Integral, Current, Count, Hist)
- when Current == H ->
- histogram(T, Integral, Current, Count + 1, Hist);
-histogram([H | _] = Values, Integral, Current, Count,
- Hist)
- when Current < H ->
- if Integral ->
- histogram(Values, Integral, Current + 1, Count,
- [Count | Hist]);
- true ->
- histogram(Values, Integral, Current + 1, 0,
- [Count | Hist])
- end;
-histogram([], _Integral, _Current, Count, Hist) ->
- if Count > 0 -> lists:reverse([Count | Hist]);
- true -> lists:reverse(Hist)
- end.
-
-%%%==================================
-%%%% get_nodes
-
-get_nodes(Lang) ->
- RunningNodes = mnesia:system_info(running_db_nodes),
- StoppedNodes = lists:usort(mnesia:system_info(db_nodes)
- ++ mnesia:system_info(extra_db_nodes))
- -- RunningNodes,
- FRN = if RunningNodes == [] -> ?CT(<<"None">>);
- true ->
- ?XE(<<"ul">>,
- (lists:map(fun (N) ->
- S = iolist_to_binary(atom_to_list(N)),
- ?LI([?AC(<<"../node/", S/binary, "/">>,
- S)])
- end,
- lists:sort(RunningNodes))))
- end,
- FSN = if StoppedNodes == [] -> ?CT(<<"None">>);
- true ->
- ?XE(<<"ul">>,
- (lists:map(fun (N) ->
- S = iolist_to_binary(atom_to_list(N)),
- ?LI([?C(S)])
- end,
- lists:sort(StoppedNodes))))
- end,
- [?XCT(<<"h1">>, <<"Nodes">>),
- ?XCT(<<"h3">>, <<"Running Nodes">>), FRN,
- ?XCT(<<"h3">>, <<"Stopped Nodes">>), FSN].
-
-search_running_node(SNode) ->
- search_running_node(SNode,
- mnesia:system_info(running_db_nodes)).
-
-search_running_node(_, []) -> false;
-search_running_node(SNode, [Node | Nodes]) ->
- case iolist_to_binary(atom_to_list(Node)) of
- SNode -> Node;
- _ -> search_running_node(SNode, Nodes)
- end.
-
-get_node(global, Node, [], Query, Lang) ->
- Res = node_parse_query(Node, Query),
- Base = get_base_path(global, Node),
- MenuItems2 = make_menu_items(global, Node, Base, Lang),
- [?XC(<<"h1">>,
- list_to_binary(io_lib:format(?T(<<"Node ~p">>), [Node])))]
- ++
- case Res of
- ok -> [?XREST(<<"Submitted">>)];
- error -> [?XREST(<<"Bad format">>)];
- nothing -> []
- end
- ++
- [?XE(<<"ul">>,
- ([?LI([?ACT(<<Base/binary, "db/">>, <<"Database">>)]),
- ?LI([?ACT(<<Base/binary, "backup/">>, <<"Backup">>)]),
- ?LI([?ACT(<<Base/binary, "ports/">>,
- <<"Listened Ports">>)]),
- ?LI([?ACT(<<Base/binary, "stats/">>,
- <<"Statistics">>)]),
- ?LI([?ACT(<<Base/binary, "update/">>, <<"Update">>)])]
- ++ MenuItems2)),
- ?XAE(<<"form">>,
- [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
- [?INPUTT(<<"submit">>, <<"restart">>, <<"Restart">>),
- ?C(<<" ">>),
- ?INPUTT(<<"submit">>, <<"stop">>, <<"Stop">>)])];
-get_node(Host, Node, [], _Query, Lang) ->
- Base = get_base_path(Host, Node),
- MenuItems2 = make_menu_items(Host, Node, Base, Lang),
- [?XC(<<"h1">>, list_to_binary(io_lib:format(?T(<<"Node ~p">>), [Node]))),
- ?XE(<<"ul">>,
- ([?LI([?ACT(<<Base/binary, "modules/">>,
- <<"Modules">>)])]
- ++ MenuItems2))];
-get_node(global, Node, [<<"db">>], Query, Lang) ->
- case rpc:call(Node, mnesia, system_info, [tables]) of
- {badrpc, _Reason} ->
- [?XCT(<<"h1">>, <<"RPC Call Error">>)];
- Tables ->
- ResS = case node_db_parse_query(Node, Tables, Query) of
- nothing -> [];
- ok -> [?XREST(<<"Submitted">>)]
- end,
- STables = lists:sort(Tables),
- Rows = lists:map(fun (Table) ->
- STable =
- iolist_to_binary(atom_to_list(Table)),
- TInfo = case rpc:call(Node, mnesia,
- table_info,
- [Table, all])
- of
- {badrpc, _} -> [];
- I -> I
- end,
- {Type, Size, Memory} = case
- {lists:keysearch(storage_type,
- 1,
- TInfo),
- lists:keysearch(size,
- 1,
- TInfo),
- lists:keysearch(memory,
- 1,
- TInfo)}
- of
- {{value,
- {storage_type,
- T}},
- {value, {size, S}},
- {value,
- {memory, M}}} ->
- {T, S, M};
- _ -> {unknown, 0, 0}
- end,
- ?XE(<<"tr">>,
- [?XC(<<"td">>, STable),
- ?XE(<<"td">>,
- [db_storage_select(STable, Type,
- Lang)]),
- ?XAC(<<"td">>,
- [{<<"class">>, <<"alignright">>}],
- (pretty_string_int(Size))),
- ?XAC(<<"td">>,
- [{<<"class">>, <<"alignright">>}],
- (pretty_string_int(Memory)))])
- end,
- STables),
- [?XC(<<"h1">>,
- list_to_binary(io_lib:format(?T(<<"Database Tables at ~p">>),
- [Node]))
- )]
- ++
- ResS ++
- [?XAE(<<"form">>,
- [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
- [?XAE(<<"table">>, [],
- [?XE(<<"thead">>,
- [?XE(<<"tr">>,
- [?XCT(<<"td">>, <<"Name">>),
- ?XCT(<<"td">>, <<"Storage Type">>),
- ?XCT(<<"td">>, <<"Elements">>),
- ?XCT(<<"td">>, <<"Memory">>)])]),
- ?XE(<<"tbody">>,
- (Rows ++
- [?XE(<<"tr">>,
- [?XAE(<<"td">>,
- [{<<"colspan">>, <<"4">>},
- {<<"class">>, <<"alignright">>}],
- [?INPUTT(<<"submit">>,
- <<"submit">>,
- <<"Submit">>)])])]))])])]
- end;
-get_node(global, Node, [<<"backup">>], Query, Lang) ->
- HomeDirRaw = case {os:getenv("HOME"), os:type()} of
- {EnvHome, _} when is_list(EnvHome) -> list_to_binary(EnvHome);
- {false, {win32, _Osname}} -> <<"C:/">>;
- {false, _} -> <<"/tmp/">>
- end,
- HomeDir = filename:nativename(HomeDirRaw),
- ResS = case node_backup_parse_query(Node, Query) of
- nothing -> [];
- ok -> [?XREST(<<"Submitted">>)];
- {error, Error} ->
- [?XRES(<<(?T(<<"Error">>))/binary, ": ",
- (list_to_binary(io_lib:format("~p", [Error])))/binary>>)]
- end,
- (?H1GL(list_to_binary(io_lib:format(?T(<<"Backup of ~p">>), [Node])),
- <<"list-eja-commands">>,
- <<"List of ejabberd Commands">>))
- ++
- ResS ++
- [?XCT(<<"p">>,
- <<"Please note that these options will "
- "only backup the builtin Mnesia database. "
- "If you are using the ODBC module, you "
- "also need to backup your SQL database "
- "separately.">>),
- ?XAE(<<"form">>,
- [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
- [?XAE(<<"table">>, [],
- [?XE(<<"tbody">>,
- [?XE(<<"tr">>,
- [?XCT(<<"td">>, <<"Store binary backup:">>),
- ?XE(<<"td">>,
- [?INPUT(<<"text">>, <<"storepath">>,
- (filename:join(HomeDir,
- "ejabberd.backup")))]),
- ?XE(<<"td">>,
- [?INPUTT(<<"submit">>, <<"store">>,
- <<"OK">>)])]),
- ?XE(<<"tr">>,
- [?XCT(<<"td">>,
- <<"Restore binary backup immediately:">>),
- ?XE(<<"td">>,
- [?INPUT(<<"text">>, <<"restorepath">>,
- (filename:join(HomeDir,
- "ejabberd.backup")))]),
- ?XE(<<"td">>,
- [?INPUTT(<<"submit">>, <<"restore">>,
- <<"OK">>)])]),
- ?XE(<<"tr">>,
- [?XCT(<<"td">>,
- <<"Restore binary backup after next ejabberd "
- "restart (requires less memory):">>),
- ?XE(<<"td">>,
- [?INPUT(<<"text">>, <<"fallbackpath">>,
- (filename:join(HomeDir,
- "ejabberd.backup")))]),
- ?XE(<<"td">>,
- [?INPUTT(<<"submit">>, <<"fallback">>,
- <<"OK">>)])]),
- ?XE(<<"tr">>,
- [?XCT(<<"td">>, <<"Store plain text backup:">>),
- ?XE(<<"td">>,
- [?INPUT(<<"text">>, <<"dumppath">>,
- (filename:join(HomeDir,
- "ejabberd.dump")))]),
- ?XE(<<"td">>,
- [?INPUTT(<<"submit">>, <<"dump">>,
- <<"OK">>)])]),
- ?XE(<<"tr">>,
- [?XCT(<<"td">>,
- <<"Restore plain text backup immediately:">>),
- ?XE(<<"td">>,
- [?INPUT(<<"text">>, <<"loadpath">>,
- (filename:join(HomeDir,
- "ejabberd.dump")))]),
- ?XE(<<"td">>,
- [?INPUTT(<<"submit">>, <<"load">>,
- <<"OK">>)])]),
- ?XE(<<"tr">>,
- [?XCT(<<"td">>,
- <<"Import users data from a PIEFXIS file "
- "(XEP-0227):">>),
- ?XE(<<"td">>,
- [?INPUT(<<"text">>,
- <<"import_piefxis_filepath">>,
- (filename:join(HomeDir,
- "users.xml")))]),
- ?XE(<<"td">>,
- [?INPUTT(<<"submit">>,
- <<"import_piefxis_file">>,
- <<"OK">>)])]),
- ?XE(<<"tr">>,
- [?XCT(<<"td">>,
- <<"Export data of all users in the server "
- "to PIEFXIS files (XEP-0227):">>),
- ?XE(<<"td">>,
- [?INPUT(<<"text">>,
- <<"export_piefxis_dirpath">>,
- HomeDir)]),
- ?XE(<<"td">>,
- [?INPUTT(<<"submit">>,
- <<"export_piefxis_dir">>,
- <<"OK">>)])]),
- ?XE(<<"tr">>,
- [?XE(<<"td">>,
- [?CT(<<"Export data of users in a host to PIEFXIS "
- "files (XEP-0227):">>),
- ?C(<<" ">>),
- ?INPUT(<<"text">>,
- <<"export_piefxis_host_dirhost">>,
- (?MYNAME))]),
- ?XE(<<"td">>,
- [?INPUT(<<"text">>,
- <<"export_piefxis_host_dirpath">>,
- HomeDir)]),
- ?XE(<<"td">>,
- [?INPUTT(<<"submit">>,
- <<"export_piefxis_host_dir">>,
- <<"OK">>)])]),
- ?XE(<<"tr">>,
- [?XE(<<"td">>,
- [?CT(<<"Export all tables as SQL queries "
- "to a file:">>),
- ?C(<<" ">>),
- ?INPUT(<<"text">>,
- <<"export_sql_filehost">>,
- (?MYNAME))]),
- ?XE(<<"td">>,
- [?INPUT(<<"text">>,
- <<"export_sql_filepath">>,
- (filename:join(HomeDir,
- "db.sql")))]),
- ?XE(<<"td">>,
- [?INPUTT(<<"submit">>, <<"export_sql_file">>,
- <<"OK">>)])]),
- ?XE(<<"tr">>,
- [?XCT(<<"td">>,
- <<"Import user data from jabberd14 spool "
- "file:">>),
- ?XE(<<"td">>,
- [?INPUT(<<"text">>, <<"import_filepath">>,
- (filename:join(HomeDir,
- "user1.xml")))]),
- ?XE(<<"td">>,
- [?INPUTT(<<"submit">>, <<"import_file">>,
- <<"OK">>)])]),
- ?XE(<<"tr">>,
- [?XCT(<<"td">>,
- <<"Import users data from jabberd14 spool "
- "directory:">>),
- ?XE(<<"td">>,
- [?INPUT(<<"text">>, <<"import_dirpath">>,
- <<"/var/spool/jabber/">>)]),
- ?XE(<<"td">>,
- [?INPUTT(<<"submit">>, <<"import_dir">>,
- <<"OK">>)])])])])])];
-get_node(global, Node, [<<"ports">>], Query, Lang) ->
- Ports = rpc:call(Node, ejabberd_config,
- get_local_option, [listen,
- {ejabberd_listener, validate_cfg},
- []]),
- Res = case catch node_ports_parse_query(Node, Ports,
- Query)
- of
- submitted -> ok;
- {'EXIT', _Reason} -> error;
- {is_added, ok} -> ok;
- {is_added, {error, Reason}} ->
- {error, iolist_to_binary(io_lib:format("~p", [Reason]))};
- _ -> nothing
- end,
- NewPorts = lists:sort(rpc:call(Node, ejabberd_config,
- get_local_option,
- [listen,
- {ejabberd_listener, validate_cfg},
- []])),
- H1String = <<(?T(<<"Listened Ports at ">>))/binary,
- (iolist_to_binary(atom_to_list(Node)))/binary>>,
- (?H1GL(H1String, <<"listened">>, <<"Listening Ports">>))
- ++
- case Res of
- ok -> [?XREST(<<"Submitted">>)];
- error -> [?XREST(<<"Bad format">>)];
- {error, ReasonT} ->
- [?XRES(<<(?T(<<"Error">>))/binary, ": ",
- ReasonT/binary>>)];
- nothing -> []
- end
- ++
- [?XAE(<<"form">>,
- [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
- [node_ports_to_xhtml(NewPorts, Lang)])];
-get_node(Host, Node, [<<"modules">>], Query, Lang)
- when is_binary(Host) ->
- Modules = rpc:call(Node, gen_mod,
- loaded_modules_with_opts, [Host]),
- Res = case catch node_modules_parse_query(Host, Node,
- Modules, Query)
- of
- submitted -> ok;
- {'EXIT', Reason} -> ?INFO_MSG("~p~n", [Reason]), error;
- _ -> nothing
- end,
- NewModules = lists:sort(rpc:call(Node, gen_mod,
- loaded_modules_with_opts, [Host])),
- H1String = list_to_binary(io_lib:format(?T(<<"Modules at ~p">>), [Node])),
- (?H1GL(H1String, <<"modoverview">>,
- <<"Modules Overview">>))
- ++
- case Res of
- ok -> [?XREST(<<"Submitted">>)];
- error -> [?XREST(<<"Bad format">>)];
- nothing -> []
- end
- ++
- [?XAE(<<"form">>,
- [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
- [node_modules_to_xhtml(NewModules, Lang)])];
-get_node(global, Node, [<<"stats">>], _Query, Lang) ->
- UpTime = rpc:call(Node, erlang, statistics,
- [wall_clock]),
- UpTimeS = list_to_binary(io_lib:format("~.3f",
- [element(1, UpTime) / 1000])),
- CPUTime = rpc:call(Node, erlang, statistics, [runtime]),
- CPUTimeS = list_to_binary(io_lib:format("~.3f",
- [element(1, CPUTime) / 1000])),
- OnlineUsers = mnesia:table_info(session, size),
- TransactionsCommitted = rpc:call(Node, mnesia,
- system_info, [transaction_commits]),
- TransactionsAborted = rpc:call(Node, mnesia,
- system_info, [transaction_failures]),
- TransactionsRestarted = rpc:call(Node, mnesia,
- system_info, [transaction_restarts]),
- TransactionsLogged = rpc:call(Node, mnesia, system_info,
- [transaction_log_writes]),
- [?XC(<<"h1">>,
- list_to_binary(io_lib:format(?T(<<"Statistics of ~p">>), [Node]))),
- ?XAE(<<"table">>, [],
- [?XE(<<"tbody">>,
- [?XE(<<"tr">>,
- [?XCT(<<"td">>, <<"Uptime:">>),
- ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}],
- UpTimeS)]),
- ?XE(<<"tr">>,
- [?XCT(<<"td">>, <<"CPU Time:">>),
- ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}],
- CPUTimeS)]),
- ?XE(<<"tr">>,
- [?XCT(<<"td">>, <<"Online Users:">>),
- ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}],
- (pretty_string_int(OnlineUsers)))]),
- ?XE(<<"tr">>,
- [?XCT(<<"td">>, <<"Transactions Committed:">>),
- ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}],
- (pretty_string_int(TransactionsCommitted)))]),
- ?XE(<<"tr">>,
- [?XCT(<<"td">>, <<"Transactions Aborted:">>),
- ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}],
- (pretty_string_int(TransactionsAborted)))]),
- ?XE(<<"tr">>,
- [?XCT(<<"td">>, <<"Transactions Restarted:">>),
- ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}],
- (pretty_string_int(TransactionsRestarted)))]),
- ?XE(<<"tr">>,
- [?XCT(<<"td">>, <<"Transactions Logged:">>),
- ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}],
- (pretty_string_int(TransactionsLogged)))])])])];
-get_node(global, Node, [<<"update">>], Query, Lang) ->
- rpc:call(Node, code, purge, [ejabberd_update]),
- Res = node_update_parse_query(Node, Query),
- rpc:call(Node, code, load_file, [ejabberd_update]),
- {ok, _Dir, UpdatedBeams, Script, LowLevelScript,
- Check} =
- rpc:call(Node, ejabberd_update, update_info, []),
- Mods = case UpdatedBeams of
- [] -> ?CT(<<"None">>);
- _ ->
- BeamsLis = lists:map(fun (Beam) ->
- BeamString =
- iolist_to_binary(atom_to_list(Beam)),
- ?LI([?INPUT(<<"checkbox">>,
- <<"selected">>,
- BeamString),
- ?C(BeamString)])
- end,
- UpdatedBeams),
- SelectButtons = [?BR,
- ?INPUTATTRS(<<"button">>, <<"selectall">>,
- <<"Select All">>,
- [{<<"onClick">>,
- <<"selectAll()">>}]),
- ?C(<<" ">>),
- ?INPUTATTRS(<<"button">>, <<"unselectall">>,
- <<"Unselect All">>,
- [{<<"onClick">>,
- <<"unSelectAll()">>}])],
- ?XAE(<<"ul">>, [{<<"class">>, <<"nolistyle">>}],
- (BeamsLis ++ SelectButtons))
- end,
- FmtScript = (?XC(<<"pre">>,
- list_to_binary(io_lib:format("~p", [Script])))),
- FmtLowLevelScript = (?XC(<<"pre">>,
- list_to_binary(io_lib:format("~p", [LowLevelScript])))),
- [?XC(<<"h1">>,
- list_to_binary(io_lib:format(?T(<<"Update ~p">>), [Node])))]
- ++
- case Res of
- ok -> [?XREST(<<"Submitted">>)];
- {error, ErrorText} ->
- [?XREST(<<"Error: ", ErrorText/binary>>)];
- nothing -> []
- end
- ++
- [?XAE(<<"form">>,
- [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
- [?XCT(<<"h2">>, <<"Update plan">>),
- ?XCT(<<"h3">>, <<"Modified modules">>), Mods,
- ?XCT(<<"h3">>, <<"Update script">>), FmtScript,
- ?XCT(<<"h3">>, <<"Low level update script">>),
- FmtLowLevelScript, ?XCT(<<"h3">>, <<"Script check">>),
- ?XC(<<"pre">>, (iolist_to_binary(Check))),
- ?BR,
- ?INPUTT(<<"submit">>, <<"update">>, <<"Update">>)])];
-get_node(Host, Node, NPath, Query, Lang) ->
- {Hook, Opts} = case Host of
- global ->
- {webadmin_page_node, [Node, NPath, Query, Lang]};
- Host ->
- {webadmin_page_hostnode,
- [Host, Node, NPath, Query, Lang]}
- end,
- case ejabberd_hooks:run_fold(Hook, Host, [], Opts) of
- [] -> [?XC(<<"h1">>, <<"Not Found">>)];
- Res -> Res
- end.
-
-%%%==================================
-%%%% node parse
-
-node_parse_query(Node, Query) ->
- case lists:keysearch(<<"restart">>, 1, Query) of
- {value, _} ->
- case rpc:call(Node, init, restart, []) of
- {badrpc, _Reason} -> error;
- _ -> ok
- end;
- _ ->
- case lists:keysearch(<<"stop">>, 1, Query) of
- {value, _} ->
- case rpc:call(Node, init, stop, []) of
- {badrpc, _Reason} -> error;
- _ -> ok
- end;
- _ -> nothing
- end
- end.
-
-db_storage_select(ID, Opt, Lang) ->
- ?XAE(<<"select">>,
- [{<<"name">>, <<"table", ID/binary>>}],
- (lists:map(fun ({O, Desc}) ->
- Sel = if O == Opt ->
- [{<<"selected">>, <<"selected">>}];
- true -> []
- end,
- ?XACT(<<"option">>,
- (Sel ++
- [{<<"value">>,
- iolist_to_binary(atom_to_list(O))}]),
- Desc)
- end,
- [{ram_copies, <<"RAM copy">>},
- {disc_copies, <<"RAM and disc copy">>},
- {disc_only_copies, <<"Disc only copy">>},
- {unknown, <<"Remote copy">>},
- {delete_content, <<"Delete content">>},
- {delete_table, <<"Delete table">>}]))).
-
-node_db_parse_query(_Node, _Tables, [{nokey, <<>>}]) ->
- nothing;
-node_db_parse_query(Node, Tables, Query) ->
- lists:foreach(fun (Table) ->
- STable = iolist_to_binary(atom_to_list(Table)),
- case lists:keysearch(<<"table", STable/binary>>, 1,
- Query)
- of
- {value, {_, SType}} ->
- Type = case SType of
- <<"unknown">> -> unknown;
- <<"ram_copies">> -> ram_copies;
- <<"disc_copies">> -> disc_copies;
- <<"disc_only_copies">> ->
- disc_only_copies;
- <<"delete_content">> -> delete_content;
- <<"delete_table">> -> delete_table;
- _ -> false
- end,
- if Type == false -> ok;
- Type == delete_content ->
- mnesia:clear_table(Table);
- Type == delete_table ->
- mnesia:delete_table(Table);
- Type == unknown ->
- mnesia:del_table_copy(Table, Node);
- true ->
- case mnesia:add_table_copy(Table, Node,
- Type)
- of
- {aborted, _} ->
- mnesia:change_table_copy_type(Table,
- Node,
- Type);
- _ -> ok
- end
- end;
- _ -> ok
- end
- end,
- Tables),
- ok.
-
-node_backup_parse_query(_Node, [{nokey, <<>>}]) ->
- nothing;
-node_backup_parse_query(Node, Query) ->
- lists:foldl(fun (Action, nothing) ->
- case lists:keysearch(Action, 1, Query) of
- {value, _} ->
- case lists:keysearch(<<Action/binary, "path">>, 1,
- Query)
- of
- {value, {_, Path}} ->
- Res = case Action of
- <<"store">> ->
- rpc:call(Node, mnesia, backup,
- [binary_to_list(Path)]);
- <<"restore">> ->
- rpc:call(Node, ejabberd_admin,
- restore, [Path]);
- <<"fallback">> ->
- rpc:call(Node, mnesia,
- install_fallback,
- [binary_to_list(Path)]);
- <<"dump">> ->
- rpc:call(Node, ejabberd_admin,
- dump_to_textfile,
- [Path]);
- <<"load">> ->
- rpc:call(Node, mnesia,
- load_textfile,
- [binary_to_list(Path)]);
- <<"import_piefxis_file">> ->
- rpc:call(Node, ejabberd_piefxis,
- import_file, [Path]);
- <<"export_piefxis_dir">> ->
- rpc:call(Node, ejabberd_piefxis,
- export_server, [Path]);
- <<"export_piefxis_host_dir">> ->
- {value, {_, Host}} =
- lists:keysearch(<<Action/binary,
- "host">>,
- 1, Query),
- rpc:call(Node, ejabberd_piefxis,
- export_host,
- [Path, Host]);
- <<"export_sql_file">> ->
- {value, {_, Host}} =
- lists:keysearch(<<Action/binary,
- "host">>,
- 1, Query),
- rpc:call(Node, ejd2odbc,
- export, [Host, Path]);
- <<"import_file">> ->
- rpc:call(Node, ejabberd_admin,
- import_file, [Path]);
- <<"import_dir">> ->
- rpc:call(Node, ejabberd_admin,
- import_dir, [Path])
- end,
- case Res of
- {error, Reason} -> {error, Reason};
- {badrpc, Reason} -> {badrpc, Reason};
- _ -> ok
- end;
- OtherError -> {error, OtherError}
- end;
- _ -> nothing
- end;
- (_Action, Res) -> Res
- end,
- nothing,
- [<<"store">>, <<"restore">>, <<"fallback">>, <<"dump">>,
- <<"load">>, <<"import_file">>, <<"import_dir">>,
- <<"import_piefxis_file">>, <<"export_piefxis_dir">>,
- <<"export_piefxis_host_dir">>, <<"export_sql_file">>]).
-
-node_ports_to_xhtml(Ports, Lang) ->
- ?XAE(<<"table">>, [{<<"class">>, <<"withtextareas">>}],
- [?XE(<<"thead">>,
- [?XE(<<"tr">>,
- [?XCT(<<"td">>, <<"Port">>), ?XCT(<<"td">>, <<"IP">>),
- ?XCT(<<"td">>, <<"Protocol">>),
- ?XCT(<<"td">>, <<"Module">>),
- ?XCT(<<"td">>, <<"Options">>)])]),
- ?XE(<<"tbody">>,
- (lists:map(fun ({PortIP, Module, Opts} = _E) ->
- {_Port, SPort, _TIP, SIP, SSPort, NetProt,
- OptsClean} =
- get_port_data(PortIP, Opts),
- SModule =
- iolist_to_binary(atom_to_list(Module)),
- {NumLines, SOptsClean} =
- term_to_paragraph(OptsClean, 40),
- ?XE(<<"tr">>,
- [?XAE(<<"td">>, [{<<"size">>, <<"6">>}],
- [?C(SPort)]),
- ?XAE(<<"td">>, [{<<"size">>, <<"15">>}],
- [?C(SIP)]),
- ?XAE(<<"td">>, [{<<"size">>, <<"4">>}],
- [?C((iolist_to_binary(atom_to_list(NetProt))))]),
- ?XE(<<"td">>,
- [?INPUTS(<<"text">>,
- <<"module", SSPort/binary>>,
- SModule, <<"15">>)]),
- ?XE(<<"td">>,
- [?TEXTAREA(<<"opts", SSPort/binary>>,
- (iolist_to_binary(integer_to_list(NumLines))),
- <<"35">>, SOptsClean)]),
- ?XE(<<"td">>,
- [?INPUTT(<<"submit">>,
- <<"add", SSPort/binary>>,
- <<"Update">>)]),
- ?XE(<<"td">>,
- [?INPUTT(<<"submit">>,
- <<"delete", SSPort/binary>>,
- <<"Delete">>)])])
- end,
- Ports)
- ++
- [?XE(<<"tr">>,
- [?XE(<<"td">>,
- [?INPUTS(<<"text">>, <<"portnew">>, <<"">>,
- <<"6">>)]),
- ?XE(<<"td">>,
- [?INPUTS(<<"text">>, <<"ipnew">>, <<"0.0.0.0">>,
- <<"15">>)]),
- ?XE(<<"td">>, [make_netprot_html(<<"tcp">>)]),
- ?XE(<<"td">>,
- [?INPUTS(<<"text">>, <<"modulenew">>, <<"">>,
- <<"15">>)]),
- ?XE(<<"td">>,
- [?TEXTAREA(<<"optsnew">>, <<"2">>, <<"35">>,
- <<"[]">>)]),
- ?XAE(<<"td">>, [{<<"colspan">>, <<"2">>}],
- [?INPUTT(<<"submit">>, <<"addnew">>,
- <<"Add New">>)])])]))]).
-
-make_netprot_html(NetProt) ->
- ?XAE(<<"select">>, [{<<"name">>, <<"netprotnew">>}],
- (lists:map(fun (O) ->
- Sel = if O == NetProt ->
- [{<<"selected">>, <<"selected">>}];
- true -> []
- end,
- ?XAC(<<"option">>, (Sel ++ [{<<"value">>, O}]), O)
- end,
- [<<"tcp">>, <<"udp">>]))).
-
-get_port_data(PortIP, Opts) ->
- {Port, IPT, IPS, _IPV, NetProt, OptsClean} =
- ejabberd_listener:parse_listener_portip(PortIP, Opts),
- SPort = jlib:integer_to_binary(Port),
- SSPort = list_to_binary(
- lists:map(fun (N) ->
- io_lib:format("~.16b", [N])
- end,
- binary_to_list(
- crypto:md5(
- [SPort, IPS, atom_to_list(NetProt)])))),
- {Port, SPort, IPT, IPS, SSPort, NetProt, OptsClean}.
-
-node_ports_parse_query(Node, Ports, Query) ->
- lists:foreach(fun ({PortIpNetp, Module1, Opts1}) ->
- {Port, _SPort, TIP, _SIP, SSPort, NetProt,
- _OptsClean} =
- get_port_data(PortIpNetp, Opts1),
- case lists:keysearch(<<"add", SSPort/binary>>, 1,
- Query)
- of
- {value, _} ->
- PortIpNetp2 = {Port, TIP, NetProt},
- {{value, {_, SModule}}, {value, {_, SOpts}}} =
- {lists:keysearch(<<"module",
- SSPort/binary>>,
- 1, Query),
- lists:keysearch(<<"opts", SSPort/binary>>,
- 1, Query)},
- Module = jlib:binary_to_atom(SModule),
- {ok, Tokens, _} =
- erl_scan:string(binary_to_list(SOpts) ++ "."),
- {ok, Opts} = erl_parse:parse_term(Tokens),
- rpc:call(Node, ejabberd_listener,
- delete_listener,
- [PortIpNetp2, Module1]),
- R = rpc:call(Node, ejabberd_listener,
- add_listener,
- [PortIpNetp2, Module, Opts]),
- throw({is_added, R});
- _ ->
- case lists:keysearch(<<"delete",
- SSPort/binary>>,
- 1, Query)
- of
- {value, _} ->
- rpc:call(Node, ejabberd_listener,
- delete_listener,
- [PortIpNetp, Module1]),
- throw(submitted);
- _ -> ok
- end
- end
- end,
- Ports),
- case lists:keysearch(<<"addnew">>, 1, Query) of
- {value, _} ->
- {{value, {_, SPort}}, {value, {_, STIP}},
- {value, {_, SNetProt}}, {value, {_, SModule}},
- {value, {_, SOpts}}} =
- {lists:keysearch(<<"portnew">>, 1, Query),
- lists:keysearch(<<"ipnew">>, 1, Query),
- lists:keysearch(<<"netprotnew">>, 1, Query),
- lists:keysearch(<<"modulenew">>, 1, Query),
- lists:keysearch(<<"optsnew">>, 1, Query)},
- {ok, Toks, _} = erl_scan:string(binary_to_list(<<SPort/binary, ".">>)),
- {ok, Port2} = erl_parse:parse_term(Toks),
- {ok, ToksIP, _} = erl_scan:string(binary_to_list(<<STIP/binary, ".">>)),
- STIP2 = case erl_parse:parse_term(ToksIP) of
- {ok, IPTParsed} -> IPTParsed;
- {error, _} -> STIP
- end,
- Module = jlib:binary_to_atom(SModule),
- NetProt2 = jlib:binary_to_atom(SNetProt),
- {ok, Tokens, _} = erl_scan:string(binary_to_list(<<SOpts/binary, ".">>)),
- {ok, Opts} = erl_parse:parse_term(Tokens),
- {Port2, _SPort, IP2, _SIP, _SSPort, NetProt2,
- OptsClean} =
- get_port_data({Port2, STIP2, NetProt2}, Opts),
- R = rpc:call(Node, ejabberd_listener, add_listener,
- [{Port2, IP2, NetProt2}, Module, OptsClean]),
- throw({is_added, R});
- _ -> ok
- end.
-
-node_modules_to_xhtml(Modules, Lang) ->
- ?XAE(<<"table">>, [{<<"class">>, <<"withtextareas">>}],
- [?XE(<<"thead">>,
- [?XE(<<"tr">>,
- [?XCT(<<"td">>, <<"Module">>),
- ?XCT(<<"td">>, <<"Options">>)])]),
- ?XE(<<"tbody">>,
- (lists:map(fun ({Module, Opts} = _E) ->
- SModule =
- iolist_to_binary(atom_to_list(Module)),
- {NumLines, SOpts} = term_to_paragraph(Opts,
- 40),
- ?XE(<<"tr">>,
- [?XC(<<"td">>, SModule),
- ?XE(<<"td">>,
- [?TEXTAREA(<<"opts", SModule/binary>>,
- (iolist_to_binary(integer_to_list(NumLines))),
- <<"40">>, SOpts)]),
- ?XE(<<"td">>,
- [?INPUTT(<<"submit">>,
- <<"restart",
- SModule/binary>>,
- <<"Restart">>)]),
- ?XE(<<"td">>,
- [?INPUTT(<<"submit">>,
- <<"stop", SModule/binary>>,
- <<"Stop">>)])])
- end,
- Modules)
- ++
- [?XE(<<"tr">>,
- [?XE(<<"td">>,
- [?INPUT(<<"text">>, <<"modulenew">>, <<"">>)]),
- ?XE(<<"td">>,
- [?TEXTAREA(<<"optsnew">>, <<"2">>, <<"40">>,
- <<"[]">>)]),
- ?XAE(<<"td">>, [{<<"colspan">>, <<"2">>}],
- [?INPUTT(<<"submit">>, <<"start">>,
- <<"Start">>)])])]))]).
-
-node_modules_parse_query(Host, Node, Modules, Query) ->
- lists:foreach(fun ({Module, _Opts1}) ->
- SModule = iolist_to_binary(atom_to_list(Module)),
- case lists:keysearch(<<"restart", SModule/binary>>, 1,
- Query)
- of
- {value, _} ->
- {value, {_, SOpts}} = lists:keysearch(<<"opts",
- SModule/binary>>,
- 1, Query),
- {ok, Tokens, _} =
- erl_scan:string(binary_to_list(<<SOpts/binary, ".">>)),
- {ok, Opts} = erl_parse:parse_term(Tokens),
- rpc:call(Node, gen_mod, stop_module,
- [Host, Module]),
- rpc:call(Node, gen_mod, start_module,
- [Host, Module, Opts]),
- throw(submitted);
- _ ->
- case lists:keysearch(<<"stop", SModule/binary>>,
- 1, Query)
- of
- {value, _} ->
- rpc:call(Node, gen_mod, stop_module,
- [Host, Module]),
- throw(submitted);
- _ -> ok
- end
- end
- end,
- Modules),
- case lists:keysearch(<<"start">>, 1, Query) of
- {value, _} ->
- {{value, {_, SModule}}, {value, {_, SOpts}}} =
- {lists:keysearch(<<"modulenew">>, 1, Query),
- lists:keysearch(<<"optsnew">>, 1, Query)},
- Module = jlib:binary_to_atom(SModule),
- {ok, Tokens, _} = erl_scan:string(binary_to_list(<<SOpts/binary, ".">>)),
- {ok, Opts} = erl_parse:parse_term(Tokens),
- rpc:call(Node, gen_mod, start_module,
- [Host, Module, Opts]),
- throw(submitted);
- _ -> ok
- end.
-
-node_update_parse_query(Node, Query) ->
- case lists:keysearch(<<"update">>, 1, Query) of
- {value, _} ->
- ModulesToUpdateStrings =
- proplists:get_all_values(<<"selected">>, Query),
- ModulesToUpdate = [jlib:binary_to_atom(M)
- || M <- ModulesToUpdateStrings],
- case rpc:call(Node, ejabberd_update, update,
- [ModulesToUpdate])
- of
- {ok, _} -> ok;
- {error, Error} ->
- ?ERROR_MSG("~p~n", [Error]),
- {error, iolist_to_binary(io_lib:format("~p", [Error]))};
- {badrpc, Error} ->
- ?ERROR_MSG("Bad RPC: ~p~n", [Error]),
- {error,
- <<"Bad RPC: ", (iolist_to_binary(io_lib:format("~p", [Error])))/binary>>}
- end;
- _ -> nothing
- end.
-
-pretty_print_xml(El) ->
- list_to_binary(pretty_print_xml(El, <<"">>)).
-
-pretty_print_xml({xmlcdata, CData}, Prefix) ->
- IsBlankCData = lists:all(
- fun($\f) -> true;
- ($\r) -> true;
- ($\n) -> true;
- ($\t) -> true;
- ($\v) -> true;
- ($ ) -> true;
- (_) -> false
- end, binary_to_list(CData)),
- if IsBlankCData ->
- [];
- true ->
- [Prefix, CData, $\n]
- end;
-pretty_print_xml(#xmlel{name = Name, attrs = Attrs,
- children = Els},
- Prefix) ->
- [Prefix, $<, Name,
- case Attrs of
- [] -> [];
- [{Attr, Val} | RestAttrs] ->
- AttrPrefix = [Prefix,
- str:copies(<<" ">>, byte_size(Name) + 2)],
- [$\s, Attr, $=, $', xml:crypt(Val) | [$',
- lists:map(fun ({Attr1,
- Val1}) ->
- [$\n,
- AttrPrefix,
- Attr1, $=,
- $',
- xml:crypt(Val1),
- $']
- end,
- RestAttrs)]]
- end,
- if Els == [] -> <<"/>\n">>;
- true ->
- OnlyCData = lists:all(fun ({xmlcdata, _}) -> true;
- (#xmlel{}) -> false
- end,
- Els),
- if OnlyCData ->
- [$>, xml:get_cdata(Els), $<, $/, Name, $>, $\n];
- true ->
- [$>, $\n,
- lists:map(fun (E) ->
- pretty_print_xml(E, [Prefix, <<" ">>])
- end,
- Els),
- Prefix, $<, $/, Name, $>, $\n]
- end
- end].
-
-element_to_list(X) when is_atom(X) ->
- iolist_to_binary(atom_to_list(X));
-element_to_list(X) when is_integer(X) ->
- iolist_to_binary(integer_to_list(X)).
-
-list_to_element(Bin) ->
- {ok, Tokens, _} = erl_scan:string(binary_to_list(Bin)),
- [{_, _, Element}] = Tokens,
- Element.
-
-url_func({user_diapason, From, To}) ->
- <<(iolist_to_binary(integer_to_list(From)))/binary, "-",
- (iolist_to_binary(integer_to_list(To)))/binary, "/">>;
-url_func({users_queue, Prefix, User, _Server}) ->
- <<Prefix/binary, "user/", User/binary, "/queue/">>;
-url_func({user, Prefix, User, _Server}) ->
- <<Prefix/binary, "user/", User/binary, "/">>.
-
-last_modified() ->
- {<<"Last-Modified">>,
- <<"Mon, 25 Feb 2008 13:23:30 GMT">>}.
-
-cache_control_public() ->
- {<<"Cache-Control">>, <<"public">>}.
-
-%% Transform 1234567890 into "1,234,567,890"
-pretty_string_int(Integer) when is_integer(Integer) ->
- pretty_string_int(iolist_to_binary(integer_to_list(Integer)));
-pretty_string_int(String) when is_binary(String) ->
- {_, Result} = lists:foldl(fun (NewNumber, {3, Result}) ->
- {1, <<NewNumber, $,, Result/binary>>};
- (NewNumber, {CountAcc, Result}) ->
- {CountAcc + 1, <<NewNumber, Result/binary>>}
- end,
- {0, <<"">>}, lists:reverse(binary_to_list(String))),
- Result.
-
-%%%==================================
-%%%% navigation menu
-
-%% @spec (Host, Node, Lang, JID::jid()) -> [LI]
-make_navigation(Host, Node, Lang, JID) ->
- Menu = make_navigation_menu(Host, Node, Lang, JID),
- make_menu_items(Lang, Menu).
-
-%% @spec (Host, Node, Lang, JID::jid()) -> Menu
-%% where Host = global | string()
-%% Node = cluster | string()
-%% Lang = string()
-%% Menu = {URL, Title} | {URL, Title, [Menu]}
-%% URL = string()
-%% Title = string()
-make_navigation_menu(Host, Node, Lang, JID) ->
- HostNodeMenu = make_host_node_menu(Host, Node, Lang,
- JID),
- HostMenu = make_host_menu(Host, HostNodeMenu, Lang,
- JID),
- NodeMenu = make_node_menu(Host, Node, Lang),
- make_server_menu(HostMenu, NodeMenu, Lang, JID).
-
-%% @spec (Host, Node, Base, Lang) -> [LI]
-make_menu_items(global, cluster, Base, Lang) ->
- HookItems = get_menu_items_hook(server, Lang),
- make_menu_items(Lang, {Base, <<"">>, HookItems});
-make_menu_items(global, Node, Base, Lang) ->
- HookItems = get_menu_items_hook({node, Node}, Lang),
- make_menu_items(Lang, {Base, <<"">>, HookItems});
-make_menu_items(Host, cluster, Base, Lang) ->
- HookItems = get_menu_items_hook({host, Host}, Lang),
- make_menu_items(Lang, {Base, <<"">>, HookItems});
-make_menu_items(Host, Node, Base, Lang) ->
- HookItems = get_menu_items_hook({hostnode, Host, Node},
- Lang),
- make_menu_items(Lang, {Base, <<"">>, HookItems}).
-
-make_host_node_menu(global, _, _Lang, _JID) ->
- {<<"">>, <<"">>, []};
-make_host_node_menu(_, cluster, _Lang, _JID) ->
- {<<"">>, <<"">>, []};
-make_host_node_menu(Host, Node, Lang, JID) ->
- HostNodeBase = get_base_path(Host, Node),
- HostNodeFixed = [{<<"modules/">>, <<"Modules">>}] ++
- get_menu_items_hook({hostnode, Host, Node}, Lang),
- HostNodeBasePath = url_to_path(HostNodeBase),
- HostNodeFixed2 = [Tuple
- || Tuple <- HostNodeFixed,
- is_allowed_path(HostNodeBasePath, Tuple, JID)],
- {HostNodeBase, iolist_to_binary(atom_to_list(Node)),
- HostNodeFixed2}.
-
-make_host_menu(global, _HostNodeMenu, _Lang, _JID) ->
- {<<"">>, <<"">>, []};
-make_host_menu(Host, HostNodeMenu, Lang, JID) ->
- HostBase = get_base_path(Host, cluster),
- HostFixed = [{<<"acls">>, <<"Access Control Lists">>},
- {<<"access">>, <<"Access Rules">>},
- {<<"users">>, <<"Users">>},
- {<<"online-users">>, <<"Online Users">>}]
- ++
- get_lastactivity_menuitem_list(Host) ++
- [{<<"nodes">>, <<"Nodes">>, HostNodeMenu},
- {<<"stats">>, <<"Statistics">>}]
- ++ get_menu_items_hook({host, Host}, Lang),
- HostBasePath = url_to_path(HostBase),
- HostFixed2 = [Tuple
- || Tuple <- HostFixed,
- is_allowed_path(HostBasePath, Tuple, JID)],
- {HostBase, Host, HostFixed2}.
-
-make_node_menu(_Host, cluster, _Lang) ->
- {<<"">>, <<"">>, []};
-make_node_menu(global, Node, Lang) ->
- NodeBase = get_base_path(global, Node),
- NodeFixed = [{<<"db/">>, <<"Database">>},
- {<<"backup/">>, <<"Backup">>},
- {<<"ports/">>, <<"Listened Ports">>},
- {<<"stats/">>, <<"Statistics">>},
- {<<"update/">>, <<"Update">>}]
- ++ get_menu_items_hook({node, Node}, Lang),
- {NodeBase, iolist_to_binary(atom_to_list(Node)),
- NodeFixed};
-make_node_menu(_Host, _Node, _Lang) ->
- {<<"">>, <<"">>, []}.
-
-make_server_menu(HostMenu, NodeMenu, Lang, JID) ->
- Base = get_base_path(global, cluster),
- Fixed = [{<<"acls">>, <<"Access Control Lists">>},
- {<<"access">>, <<"Access Rules">>},
- {<<"vhosts">>, <<"Virtual Hosts">>, HostMenu},
- {<<"nodes">>, <<"Nodes">>, NodeMenu},
- {<<"stats">>, <<"Statistics">>}]
- ++ get_menu_items_hook(server, Lang),
- BasePath = url_to_path(Base),
- Fixed2 = [Tuple
- || Tuple <- Fixed,
- is_allowed_path(BasePath, Tuple, JID)],
- {Base, <<"ejabberd">>, Fixed2}.
-
-get_menu_items_hook({hostnode, Host, Node}, Lang) ->
- ejabberd_hooks:run_fold(webadmin_menu_hostnode, Host,
- [], [Host, Node, Lang]);
-get_menu_items_hook({host, Host}, Lang) ->
- ejabberd_hooks:run_fold(webadmin_menu_host, Host, [],
- [Host, Lang]);
-get_menu_items_hook({node, Node}, Lang) ->
- ejabberd_hooks:run_fold(webadmin_menu_node, [],
- [Node, Lang]);
-get_menu_items_hook(server, Lang) ->
- ejabberd_hooks:run_fold(webadmin_menu_main, [], [Lang]).
-
-%% @spec (Lang::string(), Menu) -> [LI]
-%% where Menu = {MURI::string(), MName::string(), Items::[Item]}
-%% Item = {IURI::string(), IName::string()} | {IURI::string(), IName::string(), Menu}
-make_menu_items(Lang, Menu) ->
- lists:reverse(make_menu_items2(Lang, 1, Menu)).
-
-make_menu_items2(Lang, Deep, {MURI, MName, _} = Menu) ->
- Res = case MName of
- <<"">> -> [];
- _ -> [make_menu_item(header, Deep, MURI, MName, Lang)]
- end,
- make_menu_items2(Lang, Deep, Menu, Res).
-
-make_menu_items2(_, _Deep, {_, _, []}, Res) -> Res;
-make_menu_items2(Lang, Deep,
- {MURI, MName, [Item | Items]}, Res) ->
- Res2 = case Item of
- {IURI, IName} ->
- [make_menu_item(item, Deep,
- <<MURI/binary, IURI/binary, "/">>, IName, Lang)
- | Res];
- {IURI, IName, SubMenu} ->
- ResTemp = [make_menu_item(item, Deep,
- <<MURI/binary, IURI/binary, "/">>,
- IName, Lang)
- | Res],
- ResSubMenu = make_menu_items2(Lang, Deep + 1, SubMenu),
- ResSubMenu ++ ResTemp
- end,
- make_menu_items2(Lang, Deep, {MURI, MName, Items},
- Res2).
-
-make_menu_item(header, 1, URI, Name, _Lang) ->
- ?LI([?XAE(<<"div">>, [{<<"id">>, <<"navhead">>}],
- [?AC(URI, Name)])]);
-make_menu_item(header, 2, URI, Name, _Lang) ->
- ?LI([?XAE(<<"div">>, [{<<"id">>, <<"navheadsub">>}],
- [?AC(URI, Name)])]);
-make_menu_item(header, 3, URI, Name, _Lang) ->
- ?LI([?XAE(<<"div">>, [{<<"id">>, <<"navheadsubsub">>}],
- [?AC(URI, Name)])]);
-make_menu_item(item, 1, URI, Name, Lang) ->
- ?LI([?XAE(<<"div">>, [{<<"id">>, <<"navitem">>}],
- [?ACT(URI, Name)])]);
-make_menu_item(item, 2, URI, Name, Lang) ->
- ?LI([?XAE(<<"div">>, [{<<"id">>, <<"navitemsub">>}],
- [?ACT(URI, Name)])]);
-make_menu_item(item, 3, URI, Name, Lang) ->
- ?LI([?XAE(<<"div">>, [{<<"id">>, <<"navitemsubsub">>}],
- [?ACT(URI, Name)])]).
-
-%%%==================================
-
-%%% vim: set foldmethod=marker foldmarker=%%%%,%%%=:
-
diff --git a/src/web/ejabberd_web_admin.hrl b/src/web/ejabberd_web_admin.hrl
deleted file mode 100644
index 6c939f9e9..000000000
--- a/src/web/ejabberd_web_admin.hrl
+++ /dev/null
@@ -1,105 +0,0 @@
-%%%----------------------------------------------------------------------
-%%%
-%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
--define(X(Name),
- #xmlel{name = Name, attrs = [], children = []}).
-
--define(XA(Name, Attrs),
- #xmlel{name = Name, attrs = Attrs, children = []}).
-
--define(XE(Name, Els),
- #xmlel{name = Name, attrs = [], children = Els}).
-
--define(XAE(Name, Attrs, Els),
- #xmlel{name = Name, attrs = Attrs, children = Els}).
-
--define(C(Text), {xmlcdata, Text}).
-
--define(XC(Name, Text), ?XE(Name, [?C(Text)])).
-
--define(XAC(Name, Attrs, Text),
- ?XAE(Name, Attrs, [?C(Text)])).
-
--define(T(Text), translate:translate(Lang, Text)).
-
--define(CT(Text), ?C((?T(Text)))).
-
--define(XCT(Name, Text), ?XC(Name, (?T(Text)))).
-
--define(XACT(Name, Attrs, Text),
- ?XAC(Name, Attrs, (?T(Text)))).
-
--define(LI(Els), ?XE(<<"li">>, Els)).
-
--define(A(URL, Els),
- ?XAE(<<"a">>, [{<<"href">>, URL}], Els)).
-
--define(AC(URL, Text), ?A(URL, [?C(Text)])).
-
--define(ACT(URL, Text), ?AC(URL, (?T(Text)))).
-
--define(P, ?X(<<"p">>)).
-
--define(BR, ?X(<<"br">>)).
-
--define(INPUT(Type, Name, Value),
- ?XA(<<"input">>,
- [{<<"type">>, Type}, {<<"name">>, Name},
- {<<"value">>, Value}])).
-
--define(INPUTT(Type, Name, Value),
- ?INPUT(Type, Name, (?T(Value)))).
-
--define(INPUTS(Type, Name, Value, Size),
- ?XA(<<"input">>,
- [{<<"type">>, Type}, {<<"name">>, Name},
- {<<"value">>, Value}, {<<"size">>, Size}])).
-
--define(INPUTST(Type, Name, Value, Size),
- ?INPUT(Type, Name, (?T(Value)), Size)).
-
--define(ACLINPUT(Text),
- ?XE(<<"td">>,
- [?INPUT(<<"text">>, <<"value", ID/binary>>, Text)])).
-
--define(TEXTAREA(Name, Rows, Cols, Value),
- ?XAC(<<"textarea">>,
- [{<<"name">>, Name}, {<<"rows">>, Rows},
- {<<"cols">>, Cols}],
- Value)).
-
-%% Build an xmlelement for result
--define(XRES(Text),
- ?XAC(<<"p">>, [{<<"class">>, <<"result">>}], Text)).
-
-%% Guide Link
--define(XREST(Text), ?XRES((?T(Text)))).
-
--define(GL(Ref, Title),
- ?XAE(<<"div">>, [{<<"class">>, <<"guidelink">>}],
- [?XAE(<<"a">>,
- [{<<"href">>, <<"/admin/doc/guide.html#", Ref/binary>>},
- {<<"target">>, <<"_blank">>}],
- [?C(<<"[Guide: ", Title/binary, "]">>)])])).
-
-%% h1 with a Guide Link
--define(H1GL(Name, Ref, Title),
- [?XC(<<"h1">>, Name), ?GL(Ref, Title)]).
diff --git a/src/web/http_bind.hrl b/src/web/http_bind.hrl
deleted file mode 100644
index 2eac91e9d..000000000
--- a/src/web/http_bind.hrl
+++ /dev/null
@@ -1,47 +0,0 @@
-%%%----------------------------------------------------------------------
-%%%
-%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
--define(CT_XML,
- {<<"Content-Type">>, <<"text/xml; charset=utf-8">>}).
-
--define(CT_PLAIN,
- {<<"Content-Type">>, <<"text/plain">>}).
-
--define(AC_ALLOW_ORIGIN,
- {<<"Access-Control-Allow-Origin">>, <<"*">>}).
-
--define(AC_ALLOW_METHODS,
- {<<"Access-Control-Allow-Methods">>,
- <<"GET, POST, OPTIONS">>}).
-
--define(AC_ALLOW_HEADERS,
- {<<"Access-Control-Allow-Headers">>,
- <<"Content-Type">>}).
-
--define(AC_MAX_AGE,
- {<<"Access-Control-Max-Age">>, <<"86400">>}).
-
--define(OPTIONS_HEADER,
- [?CT_PLAIN, ?AC_ALLOW_ORIGIN, ?AC_ALLOW_METHODS,
- ?AC_ALLOW_HEADERS, ?AC_MAX_AGE]).
-
--define(HEADER,
- [?CT_XML, ?AC_ALLOW_ORIGIN, ?AC_ALLOW_HEADERS]).
diff --git a/src/web/mod_http_bind.erl b/src/web/mod_http_bind.erl
deleted file mode 100644
index 164c07d33..000000000
--- a/src/web/mod_http_bind.erl
+++ /dev/null
@@ -1,144 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File : mod_http_bind.erl
-%%% Author : Stefan Strigler <steve@zeank.in-berlin.de>
-%%% Purpose : Implementation of XMPP over BOSH (XEP-0206)
-%%% Created : Tue Feb 20 13:15:52 CET 2007
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
-%%%----------------------------------------------------------------------
-%%% This module acts as a bridge to ejabberd_http_bind which implements
-%%% the real stuff, this is to handle the new pluggable architecture for
-%%% extending ejabberd's http service.
-%%%----------------------------------------------------------------------
-
--module(mod_http_bind).
-
--author('steve@zeank.in-berlin.de').
-
-%%-define(ejabberd_debug, true).
-
--behaviour(gen_mod).
-
--export([start/2, stop/1, process/2]).
-
--include("ejabberd.hrl").
-
--include("jlib.hrl").
-
--include("ejabberd_http.hrl").
-
--include("http_bind.hrl").
-
--define(PROCNAME_MHB, ejabberd_mod_http_bind).
-
-%% Duplicated from ejabberd_http_bind.
-%% TODO: move to hrl file.
--record(http_bind,
- {id, pid, to, hold, wait, process_delay, version}).
-
-%%%----------------------------------------------------------------------
-%%% API
-%%%----------------------------------------------------------------------
-
-process([], #request{method = 'POST', data = <<>>}) ->
- ?DEBUG("Bad Request: no data", []),
- {400, ?HEADER,
- #xmlel{name = <<"h1">>, children = [{xmlcdata, <<"400 Bad Request">>}]}};
-process([],
- #request{method = 'POST', data = Data, ip = IP}) ->
- ?DEBUG("Incoming data: ~s", [Data]),
- ejabberd_http_bind:process_request(Data, IP);
-process([], #request{method = 'GET', data = <<>>}) ->
- {200, ?HEADER, get_human_html_xmlel()};
-process([], #request{method = 'OPTIONS', data = <<>>}) ->
- {200, ?OPTIONS_HEADER, <<>>};
-process([], #request{method = 'HEAD'}) ->
- {200, ?HEADER, <<>>};
-process(_Path, _Request) ->
- ?DEBUG("Bad Request: ~p", [_Request]),
- {400, ?HEADER,
- #xmlel{name = <<"h1">>, children = [{xmlcdata, <<"400 Bad Request">>}]}}.
-
-get_human_html_xmlel() ->
- Heading = <<"ejabberd ",
- (iolist_to_binary(atom_to_list(?MODULE)))/binary>>,
- #xmlel{name = <<"html">>,
- attrs =
- [{<<"xmlns">>, <<"http://www.w3.org/1999/xhtml">>}],
- children =
- [#xmlel{name = <<"head">>,
- children =
- [#xmlel{name = <<"title">>,
- children = [{xmlcdata, Heading}]}]},
- #xmlel{name = <<"body">>,
- children =
- [#xmlel{name = <<"h1">>,
- children = [{xmlcdata, Heading}]},
- #xmlel{name = <<"p">>,
- children =
- [{xmlcdata, <<"An implementation of ">>},
- #xmlel{name = <<"a">>,
- attrs =
- [{<<"href">>,
- <<"http://xmpp.org/extensions/xep-0206.html">>}],
- children =
- [{xmlcdata,
- <<"XMPP over BOSH (XEP-0206)">>}]}]},
- #xmlel{name = <<"p">>,
- children =
- [{xmlcdata,
- <<"This web page is only informative. To "
- "use HTTP-Bind you need a Jabber/XMPP "
- "client that supports it.">>}]}]}]}.
-
-%%%----------------------------------------------------------------------
-%%% BEHAVIOUR CALLBACKS
-%%%----------------------------------------------------------------------
-start(Host, _Opts) ->
- setup_database(),
- Proc = gen_mod:get_module_proc(Host, ?PROCNAME_MHB),
- ChildSpec = {Proc,
- {ejabberd_tmp_sup, start_link,
- [Proc, ejabberd_http_bind]},
- permanent, infinity, supervisor, [ejabberd_tmp_sup]},
- supervisor:start_child(ejabberd_sup, ChildSpec).
-
-stop(Host) ->
- Proc = gen_mod:get_module_proc(Host, ?PROCNAME_MHB),
- supervisor:terminate_child(ejabberd_sup, Proc),
- supervisor:delete_child(ejabberd_sup, Proc).
-
-setup_database() ->
- migrate_database(),
- mnesia:create_table(http_bind,
- [{ram_copies, [node()]},
- {attributes, record_info(fields, http_bind)}]).
-
-migrate_database() ->
- case catch mnesia:table_info(http_bind, attributes) of
- [id, pid, to, hold, wait, process_delay, version] ->
- ok;
- _ ->
- %% Since the stored information is not important, instead
- %% of actually migrating data, let's just destroy the table
- mnesia:delete_table(http_bind)
- end.
diff --git a/src/web/mod_http_fileserver.erl b/src/web/mod_http_fileserver.erl
deleted file mode 100644
index 98206a5f0..000000000
--- a/src/web/mod_http_fileserver.erl
+++ /dev/null
@@ -1,454 +0,0 @@
-%%%-------------------------------------------------------------------
-%%% File : mod_http_fileserver.erl
-%%% Author : Massimiliano Mirra <mmirra [at] process-one [dot] net>
-%%% Purpose : Simple file server plugin for embedded ejabberd web server
-%%% Created :
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
--module(mod_http_fileserver).
-
--author('mmirra@process-one.net').
-
--behaviour(gen_mod).
--behaviour(gen_server).
-
-%% gen_mod callbacks
--export([start/2, stop/1]).
-
-%% API
--export([start_link/2]).
-
-%% gen_server callbacks
--export([init/1, handle_call/3, handle_cast/2, handle_info/2,
- terminate/2, code_change/3]).
-
-%% request_handlers callbacks
--export([process/2]).
-
-%% ejabberd_hooks callbacks
--export([reopen_log/1]).
-
--include("ejabberd.hrl").
-
--include("jlib.hrl").
-
--include_lib("kernel/include/file.hrl").
-
-%%-include("ejabberd_http.hrl").
-%% TODO: When ejabberd-modules SVN gets the new ejabberd_http.hrl, delete this code:
--record(request,
- {method, path, q = [], us, auth, lang = <<"">>,
- data = <<"">>, ip, host, port, tp, headers}).
-
--record(state,
- {host, docroot, accesslog, accesslogfd,
- directory_indices, custom_headers, default_content_type,
- content_types = []}).
-
--define(PROCNAME, ejabberd_mod_http_fileserver).
-
-%% Response is {DataSize, Code, [{HeaderKey, HeaderValue}], Data}
--define(HTTP_ERR_FILE_NOT_FOUND,
- {-1, 404, [], <<"Not found">>}).
-
--define(HTTP_ERR_FORBIDDEN,
- {-1, 403, [], <<"Forbidden">>}).
-
--define(DEFAULT_CONTENT_TYPE,
- <<"application/octet-stream">>).
-
--define(DEFAULT_CONTENT_TYPES,
- [{<<".css">>, <<"text/css">>},
- {<<".gif">>, <<"image/gif">>},
- {<<".html">>, <<"text/html">>},
- {<<".jar">>, <<"application/java-archive">>},
- {<<".jpeg">>, <<"image/jpeg">>},
- {<<".jpg">>, <<"image/jpeg">>},
- {<<".js">>, <<"text/javascript">>},
- {<<".png">>, <<"image/png">>},
- {<<".svg">>, <<"image/svg+xml">>},
- {<<".txt">>, <<"text/plain">>},
- {<<".xml">>, <<"application/xml">>},
- {<<".xpi">>, <<"application/x-xpinstall">>},
- {<<".xul">>, <<"application/vnd.mozilla.xul+xml">>}]).
-
--compile(export_all).
-
-%%====================================================================
-%% gen_mod callbacks
-%%====================================================================
-
-start(Host, Opts) ->
- Proc = get_proc_name(Host),
- ChildSpec =
- {Proc,
- {?MODULE, start_link, [Host, Opts]},
- transient, % if process crashes abruptly, it gets restarted
- 1000,
- worker,
- [?MODULE]},
- supervisor:start_child(ejabberd_sup, ChildSpec).
-
-stop(Host) ->
- Proc = get_proc_name(Host),
- gen_server:call(Proc, stop),
- supervisor:terminate_child(ejabberd_sup, Proc),
- supervisor:delete_child(ejabberd_sup, Proc).
-
-%%====================================================================
-%% API
-%%====================================================================
-%%--------------------------------------------------------------------
-%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
-%% Description: Starts the server
-%%--------------------------------------------------------------------
-start_link(Host, Opts) ->
- Proc = get_proc_name(Host),
- gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
-
-%%====================================================================
-%% gen_server callbacks
-%%====================================================================
-%%--------------------------------------------------------------------
-%% Function: init(Args) -> {ok, State} |
-%% {ok, State, Timeout} |
-%% ignore |
-%% {stop, Reason}
-%% Description: Initiates the server
-%%--------------------------------------------------------------------
-init([Host, Opts]) ->
- try initialize(Host, Opts) of
- {DocRoot, AccessLog, AccessLogFD, DirectoryIndices,
- CustomHeaders, DefaultContentType, ContentTypes} ->
- {ok, #state{host = Host,
- accesslog = AccessLog,
- accesslogfd = AccessLogFD,
- docroot = DocRoot,
- directory_indices = DirectoryIndices,
- custom_headers = CustomHeaders,
- default_content_type = DefaultContentType,
- content_types = ContentTypes}}
- catch
- throw:Reason ->
- {stop, Reason}
- end.
-
-initialize(Host, Opts) ->
- DocRoot = gen_mod:get_opt(docroot, Opts, fun(A) -> A end, undefined),
- check_docroot_defined(DocRoot, Host),
- DRInfo = check_docroot_exists(DocRoot),
- check_docroot_is_dir(DRInfo, DocRoot),
- check_docroot_is_readable(DRInfo, DocRoot),
- AccessLog = gen_mod:get_opt(accesslog, Opts,
- fun iolist_to_binary/1,
- undefined),
- AccessLogFD = try_open_log(AccessLog, Host),
- DirectoryIndices = gen_mod:get_opt(directory_indices, Opts,
- fun(L) when is_list(L) -> L end,
- []),
- CustomHeaders = gen_mod:get_opt(custom_headers, Opts,
- fun(L) when is_list(L) -> L end,
- []),
- DefaultContentType = gen_mod:get_opt(default_content_type, Opts,
- fun iolist_to_binary/1,
- ?DEFAULT_CONTENT_TYPE),
- ContentTypes = build_list_content_types(
- gen_mod:get_opt(content_types, Opts,
- fun(L) when is_list(L) -> L end,
- []),
- ?DEFAULT_CONTENT_TYPES),
- ?INFO_MSG("initialize: ~n ~p", [ContentTypes]),%+++
- {DocRoot, AccessLog, AccessLogFD, DirectoryIndices,
- CustomHeaders, DefaultContentType, ContentTypes}.
-
-
-%% @spec (AdminCTs::[CT], Default::[CT]) -> [CT]
-%% where CT = {Extension::string(), Value}
-%% Value = string() | undefined
-%% @doc Return a unified list without duplicates.
-%% Elements of AdminCTs have more priority.
-%% If a CT is declared as 'undefined', then it is not included in the result.
-build_list_content_types(AdminCTsUnsorted, DefaultCTsUnsorted) ->
- AdminCTs = lists:ukeysort(1, AdminCTsUnsorted),
- DefaultCTs = lists:ukeysort(1, DefaultCTsUnsorted),
- CTsUnfiltered = lists:ukeymerge(1, AdminCTs,
- DefaultCTs),
- [{Extension, Value}
- || {Extension, Value} <- CTsUnfiltered,
- Value /= undefined].
-
-check_docroot_defined(DocRoot, Host) ->
- case DocRoot of
- undefined -> throw({undefined_docroot_option, Host});
- _ -> ok
- end.
-
-check_docroot_exists(DocRoot) ->
- case file:read_file_info(DocRoot) of
- {error, Reason} ->
- throw({error_access_docroot, DocRoot, Reason});
- {ok, FI} -> FI
- end.
-
-check_docroot_is_dir(DRInfo, DocRoot) ->
- case DRInfo#file_info.type of
- directory -> ok;
- _ -> throw({docroot_not_directory, DocRoot})
- end.
-
-check_docroot_is_readable(DRInfo, DocRoot) ->
- case DRInfo#file_info.access of
- read -> ok;
- read_write -> ok;
- _ -> throw({docroot_not_readable, DocRoot})
- end.
-
-try_open_log(undefined, _Host) ->
- undefined;
-try_open_log(FN, Host) ->
- FD = try open_log(FN) of
- FD1 -> FD1
- catch
- throw:{cannot_open_accesslog, FN, Reason} ->
- ?ERROR_MSG("Cannot open access log file: ~p~nReason: ~p", [FN, Reason]),
- undefined
- end,
- ejabberd_hooks:add(reopen_log_hook, Host, ?MODULE, reopen_log, 50),
- FD.
-
-%%--------------------------------------------------------------------
-%% Function: handle_call(Request, From, State) -> {reply, Reply, State} |
-%% {reply, Reply, State, Timeout} |
-%% {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, Reply, State} |
-%% {stop, Reason, State}
-%% Description: Handling call messages
-%%--------------------------------------------------------------------
-handle_call({serve, LocalPath}, _From, State) ->
- Reply = serve(LocalPath, State#state.docroot, State#state.directory_indices,
- State#state.custom_headers,
- State#state.default_content_type, State#state.content_types),
- {reply, Reply, State};
-handle_call(_Request, _From, State) ->
- {reply, ok, State}.
-
-%%--------------------------------------------------------------------
-%% Function: handle_cast(Msg, State) -> {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, State}
-%% Description: Handling cast messages
-%%--------------------------------------------------------------------
-handle_cast({add_to_log, FileSize, Code, Request}, State) ->
- add_to_log(State#state.accesslogfd, FileSize, Code, Request),
- {noreply, State};
-handle_cast(reopen_log, State) ->
- FD2 = reopen_log(State#state.accesslog, State#state.accesslogfd),
- {noreply, State#state{accesslogfd = FD2}};
-handle_cast(_Msg, State) ->
- {noreply, State}.
-
-%%--------------------------------------------------------------------
-%% Function: handle_info(Info, State) -> {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, State}
-%% Description: Handling all non call/cast messages
-%%--------------------------------------------------------------------
-handle_info(_Info, State) ->
- {noreply, State}.
-
-%%--------------------------------------------------------------------
-%% Function: terminate(Reason, State) -> void()
-%% Description: This function is called by a gen_server when it is about to
-%% terminate. It should be the opposite of Module:init/1 and do any necessary
-%% cleaning up. When it returns, the gen_server terminates with Reason.
-%% The return value is ignored.
-%%--------------------------------------------------------------------
-terminate(_Reason, State) ->
- close_log(State#state.accesslogfd),
- ejabberd_hooks:delete(reopen_log_hook, State#state.host, ?MODULE, reopen_log, 50),
- ok.
-
-%%--------------------------------------------------------------------
-%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
-%% Description: Convert process state when code is changed
-%%--------------------------------------------------------------------
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
-%%====================================================================
-%% request_handlers callbacks
-%%====================================================================
-
-%% @spec (LocalPath, Request) -> {HTTPCode::integer(), [Header], Page::string()}
-%% @doc Handle an HTTP request.
-%% LocalPath is the part of the requested URL path that is "local to the module".
-%% Returns the page to be sent back to the client and/or HTTP status code.
-process(LocalPath, Request) ->
- ?DEBUG("Requested ~p", [LocalPath]),
- try gen_server:call(get_proc_name(Request#request.host), {serve, LocalPath}) of
- {FileSize, Code, Headers, Contents} ->
- add_to_log(FileSize, Code, Request),
- {Code, Headers, Contents}
- catch
- exit:{noproc, _} ->
- ?ERROR_MSG("Received an HTTP request with Host ~p, but couldn't find the related "
- "ejabberd virtual host", [Request#request.host]),
- ejabberd_web:error(not_found)
- end.
-
-serve(LocalPath, DocRoot, DirectoryIndices, CustomHeaders, DefaultContentType, ContentTypes) ->
- FileName = filename:join(filename:split(DocRoot) ++ LocalPath),
- case file:read_file_info(FileName) of
- {error, enoent} -> ?HTTP_ERR_FILE_NOT_FOUND;
- {error, eacces} -> ?HTTP_ERR_FORBIDDEN;
- {ok, #file_info{type = directory}} -> serve_index(FileName,
- DirectoryIndices,
- CustomHeaders,
- DefaultContentType,
- ContentTypes);
- {ok, FileInfo} -> serve_file(FileInfo, FileName,
- CustomHeaders,
- DefaultContentType,
- ContentTypes)
- end.
-
-%% Troll through the directory indices attempting to find one which
-%% works, if none can be found, return a 404.
-serve_index(_FileName, [], _CH, _DefaultContentType, _ContentTypes) ->
- ?HTTP_ERR_FILE_NOT_FOUND;
-serve_index(FileName, [Index | T], CH, DefaultContentType, ContentTypes) ->
- IndexFileName = filename:join([FileName] ++ [Index]),
- case file:read_file_info(IndexFileName) of
- {error, _Error} -> serve_index(FileName, T, CH, DefaultContentType, ContentTypes);
- {ok, #file_info{type = directory}} -> serve_index(FileName, T, CH, DefaultContentType, ContentTypes);
- {ok, FileInfo} -> serve_file(FileInfo, IndexFileName, CH, DefaultContentType, ContentTypes)
- end.
-
-%% Assume the file exists if we got this far and attempt to read it in
-%% and serve it up.
-serve_file(FileInfo, FileName, CustomHeaders, DefaultContentType, ContentTypes) ->
- ?DEBUG("Delivering: ~s", [FileName]),
- ContentType = content_type(FileName, DefaultContentType,
- ContentTypes),
- {ok, FileContents} = file:read_file(FileName),
- {FileInfo#file_info.size, 200,
- [{<<"Server">>, <<"ejabberd">>},
- {<<"Last-Modified">>, last_modified(FileInfo)},
- {<<"Content-Type">>, ContentType}
- | CustomHeaders],
- FileContents}.
-
-%%----------------------------------------------------------------------
-%% Log file
-%%----------------------------------------------------------------------
-
-open_log(FN) ->
- case file:open(FN, [append]) of
- {ok, FD} ->
- FD;
- {error, Reason} ->
- throw({cannot_open_accesslog, FN, Reason})
- end.
-
-close_log(FD) ->
- file:close(FD).
-
-reopen_log(undefined, undefined) ->
- ok;
-reopen_log(FN, FD) ->
- close_log(FD),
- open_log(FN).
-
-reopen_log(Host) ->
- gen_server:cast(get_proc_name(Host), reopen_log).
-
-add_to_log(FileSize, Code, Request) ->
- gen_server:cast(get_proc_name(Request#request.host),
- {add_to_log, FileSize, Code, Request}).
-
-add_to_log(undefined, _FileSize, _Code, _Request) ->
- ok;
-add_to_log(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), "=", binary_to_list(element(2, E))]) end,
- Request#request.q), "&") of
- [] ->
- "";
- String ->
- [$? | String]
- 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]).
-
-find_header(Header, Headers, Default) ->
- case lists:keysearch(Header, 1, Headers) of
- {value, {_, Value}} -> Value;
- false -> Default
- end.
-
-%%----------------------------------------------------------------------
-%% Utilities
-%%----------------------------------------------------------------------
-
-get_proc_name(Host) -> gen_mod:get_module_proc(Host, ?PROCNAME).
-
-join([], _) ->
- <<"">>;
-join([E], _) ->
- E;
-join([H | T], Separator) ->
- [H2 | T2] = case is_binary(H) of true -> [binary_to_list(I)||I<-[H|T]]; false -> [H | T] end,
- Res=lists:foldl(fun(E, Acc) -> lists:concat([Acc, Separator, E]) end, H2, T2),
- case is_binary(H) of true -> list_to_binary(Res); false -> Res end.
-
-content_type(Filename, DefaultContentType, ContentTypes) ->
- Extension = str:to_lower(filename:extension(Filename)),
- case lists:keysearch(Extension, 1, ContentTypes) of
- {value, {_, ContentType}} -> ContentType;
- false -> DefaultContentType
- end.
-
-last_modified(FileInfo) ->
- Then = FileInfo#file_info.mtime,
- httpd_util:rfc1123_date(Then).
-
-%% 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, ":"))).
diff --git a/src/web/mod_register_web.erl b/src/web/mod_register_web.erl
deleted file mode 100644
index 0ccba9945..000000000
--- a/src/web/mod_register_web.erl
+++ /dev/null
@@ -1,565 +0,0 @@
-%%%-------------------------------------------------------------------
-%%% File : mod_register_web.erl
-%%% Author : Badlop <badlop@process-one.net>
-%%% Purpose : Web page to register account and related tasks
-%%% Created : 4 May 2008 by Badlop <badlop@process-one.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
-%%% IDEAS:
-%%%
-%%% * Implement those options, already present in mod_register:
-%%% + access
-%%% + captcha_protected
-%%% + password_strength
-%%% + welcome_message
-%%% + registration_timeout
-%%%
-%%% * Improve this module to allow each virtual host to have different
-%%% options. See http://support.process-one.net/browse/EJAB-561
-%%%
-%%% * Check that all the text is translatable.
-%%%
-%%% * Add option to use a custom CSS file, or custom CSS lines.
-%%%
-%%% * Don't hardcode the "register" path in URL.
-%%%
-%%% * Allow private email during register, and store in custom table.
-%%% * Optionally require private email to register.
-%%% * Optionally require email confirmation to register.
-%%% * Allow to set a private email address anytime.
-%%% * Allow to recover password using private email to confirm (mod_passrecover)
-%%% * Optionally require invitation
-%%% * Optionally register request is forwarded to admin, no account created.
-
--module(mod_register_web).
-
--author('badlop@process-one.net').
-
--behaviour(gen_mod).
-
--export([start/2, stop/1, process/2]).
-
--include("ejabberd.hrl").
-
--include("jlib.hrl").
-
--include("ejabberd_http.hrl").
-
--include("ejabberd_web_admin.hrl").
-
-%%%----------------------------------------------------------------------
-%%% gen_mod callbacks
-%%%----------------------------------------------------------------------
-
-start(_Host, _Opts) ->
- %% case gen_mod:get_opt(docroot, Opts, fun(A) -> A end, undefined) of
- ok.
-
-stop(_Host) -> ok.
-
-%%%----------------------------------------------------------------------
-%%% HTTP handlers
-%%%----------------------------------------------------------------------
-
-process([], #request{method = 'GET', lang = Lang}) ->
- index_page(Lang);
-process([<<"register.css">>],
- #request{method = 'GET'}) ->
- serve_css();
-process([<<"new">>],
- #request{method = 'GET', lang = Lang, host = Host,
- ip = IP}) ->
- {Addr, _Port} = IP, form_new_get(Host, Lang, Addr);
-process([<<"delete">>],
- #request{method = 'GET', lang = Lang, host = Host}) ->
- form_del_get(Host, Lang);
-process([<<"change_password">>],
- #request{method = 'GET', lang = Lang, host = Host}) ->
- form_changepass_get(Host, Lang);
-process([<<"new">>],
- #request{method = 'POST', q = Q, ip = {Ip, _Port},
- lang = Lang, host = Host}) ->
- case form_new_post(Q, Host) of
- {success, ok, {Username, Host, _Password}} ->
- Jid = jlib:make_jid(Username, Host, <<"">>),
- mod_register:send_registration_notifications(?MODULE, Jid, Ip),
- Text = (?T(<<"Your Jabber account was successfully "
- "created.">>)),
- {200, [], Text};
- Error ->
- ErrorText =
- list_to_binary([?T(<<"There was an error creating the account: ">>),
- ?T(get_error_text(Error))]),
- {404, [], ErrorText}
- end;
-process([<<"delete">>],
- #request{method = 'POST', q = Q, lang = Lang,
- host = Host}) ->
- case form_del_post(Q, Host) of
- {atomic, ok} ->
- Text = (?T(<<"Your Jabber account was successfully "
- "deleted.">>)),
- {200, [], Text};
- Error ->
- ErrorText =
- list_to_binary([?T(<<"There was an error deleting the account: ">>),
- ?T(get_error_text(Error))]),
- {404, [], ErrorText}
- end;
-%% TODO: Currently only the first vhost is usable. The web request record
-%% should include the host where the POST was sent.
-process([<<"change_password">>],
- #request{method = 'POST', q = Q, lang = Lang,
- host = Host}) ->
- case form_changepass_post(Q, Host) of
- {atomic, ok} ->
- Text = (?T(<<"The password of your Jabber account "
- "was successfully changed.">>)),
- {200, [], Text};
- Error ->
- ErrorText =
- list_to_binary([?T(<<"There was an error changing the password: ">>),
- ?T(get_error_text(Error))]),
- {404, [], ErrorText}
- end.
-
-%%%----------------------------------------------------------------------
-%%% CSS
-%%%----------------------------------------------------------------------
-
-serve_css() ->
- {200,
- [{<<"Content-Type">>, <<"text/css">>}, last_modified(),
- cache_control_public()],
- css()}.
-
-last_modified() ->
- {<<"Last-Modified">>,
- <<"Mon, 25 Feb 2008 13:23:30 GMT">>}.
-
-cache_control_public() ->
- {<<"Cache-Control">>, <<"public">>}.
-
-css() ->
- <<"html,body {\nbackground: white;\nmargin: "
- "0;\npadding: 0;\nheight: 100%;\n}">>.
-
-%%%----------------------------------------------------------------------
-%%% Index page
-%%%----------------------------------------------------------------------
-
-index_page(Lang) ->
- HeadEls = [?XCT(<<"title">>,
- <<"Jabber Account Registration">>),
- ?XA(<<"link">>,
- [{<<"href">>, <<"/register/register.css">>},
- {<<"type">>, <<"text/css">>},
- {<<"rel">>, <<"stylesheet">>}])],
- Els = [?XACT(<<"h1">>,
- [{<<"class">>, <<"title">>},
- {<<"style">>, <<"text-align:center;">>}],
- <<"Jabber Account Registration">>),
- ?XE(<<"ul">>,
- [?XE(<<"li">>,
- [?ACT(<<"new">>, <<"Register a Jabber account">>)]),
- ?XE(<<"li">>,
- [?ACT(<<"change_password">>, <<"Change Password">>)]),
- ?XE(<<"li">>,
- [?ACT(<<"delete">>,
- <<"Unregister a Jabber account">>)])])],
- {200,
- [{<<"Server">>, <<"ejabberd">>},
- {<<"Content-Type">>, <<"text/html">>}],
- ejabberd_web:make_xhtml(HeadEls, Els)}.
-
-%%%----------------------------------------------------------------------
-%%% Formulary new account GET
-%%%----------------------------------------------------------------------
-
-form_new_get(Host, Lang, IP) ->
- CaptchaEls = build_captcha_li_list(Lang, IP),
- HeadEls = [?XCT(<<"title">>,
- <<"Register a Jabber account">>),
- ?XA(<<"link">>,
- [{<<"href">>, <<"/register/register.css">>},
- {<<"type">>, <<"text/css">>},
- {<<"rel">>, <<"stylesheet">>}])],
- Els = [?XACT(<<"h1">>,
- [{<<"class">>, <<"title">>},
- {<<"style">>, <<"text-align:center;">>}],
- <<"Register a Jabber account">>),
- ?XCT(<<"p">>,
- <<"This page allows to create a Jabber "
- "account in this Jabber server. Your "
- "JID (Jabber IDentifier) will be of the "
- "form: username@server. Please read carefully "
- "the instructions to fill correctly the "
- "fields.">>),
- ?XAE(<<"form">>,
- [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
- [?XE(<<"ol">>,
- ([?XE(<<"li">>,
- [?CT(<<"Username:">>), ?C(<<" ">>),
- ?INPUTS(<<"text">>, <<"username">>, <<"">>,
- <<"20">>),
- ?BR,
- ?XE(<<"ul">>,
- [?XCT(<<"li">>,
- <<"This is case insensitive: macbeth is "
- "the same that MacBeth and Macbeth.">>),
- ?XC(<<"li">>,
- <<(?T(<<"Characters not allowed:">>))/binary,
- " \" & ' / : < > @ ">>)])]),
- ?XE(<<"li">>,
- [?CT(<<"Server:">>), ?C(<<" ">>), ?C(Host)]),
- ?XE(<<"li">>,
- [?CT(<<"Password:">>), ?C(<<" ">>),
- ?INPUTS(<<"password">>, <<"password">>, <<"">>,
- <<"20">>),
- ?BR,
- ?XE(<<"ul">>,
- [?XCT(<<"li">>,
- <<"Don't tell your password to anybody, "
- "not even the administrators of the Jabber "
- "server.">>),
- ?XCT(<<"li">>,
- <<"You can later change your password using "
- "a Jabber client.">>),
- ?XCT(<<"li">>,
- <<"Some Jabber clients can store your password "
- "in your computer. Use that feature only "
- "if you trust your computer is safe.">>),
- ?XCT(<<"li">>,
- <<"Memorize your password, or write it "
- "in a paper placed in a safe place. In "
- "Jabber there isn't an automated way "
- "to recover your password if you forget "
- "it.">>)])]),
- ?XE(<<"li">>,
- [?CT(<<"Password Verification:">>), ?C(<<" ">>),
- ?INPUTS(<<"password">>, <<"password2">>, <<"">>,
- <<"20">>)])]
- ++
- CaptchaEls ++
- [?XE(<<"li">>,
- [?INPUTT(<<"submit">>, <<"register">>,
- <<"Register">>)])]))])],
- {200,
- [{<<"Server">>, <<"ejabberd">>},
- {<<"Content-Type">>, <<"text/html">>}],
- ejabberd_web:make_xhtml(HeadEls, Els)}.
-
-%% Copied from mod_register.erl
-%% Function copied from ejabberd_logger_h.erl and customized
-%%%----------------------------------------------------------------------
-%%% Formulary new POST
-%%%----------------------------------------------------------------------
-
-form_new_post(Q, Host) ->
- case catch get_register_parameters(Q) of
- [Username, Password, Password, Id, Key] ->
- form_new_post(Username, Host, Password, {Id, Key});
- [_Username, _Password, _Password2, false, false] ->
- {error, passwords_not_identical};
- [_Username, _Password, _Password2, Id, Key] ->
- ejabberd_captcha:check_captcha(Id, Key),
- {error, passwords_not_identical};
- _ -> {error, wrong_parameters}
- end.
-
-get_register_parameters(Q) ->
- lists:map(fun (Key) ->
- case lists:keysearch(Key, 1, Q) of
- {value, {_Key, Value}} -> Value;
- false -> false
- end
- end,
- [<<"username">>, <<"password">>, <<"password2">>,
- <<"id">>, <<"key">>]).
-
-form_new_post(Username, Host, Password,
- {false, false}) ->
- register_account(Username, Host, Password);
-form_new_post(Username, Host, Password, {Id, Key}) ->
- case ejabberd_captcha:check_captcha(Id, Key) of
- captcha_valid ->
- register_account(Username, Host, Password);
- captcha_non_valid -> {error, captcha_non_valid};
- captcha_not_found -> {error, captcha_non_valid}
- end.
-
-%%%----------------------------------------------------------------------
-%%% Formulary Captcha support for new GET/POST
-%%%----------------------------------------------------------------------
-
-build_captcha_li_list(Lang, IP) ->
- case ejabberd_captcha:is_feature_available() of
- true -> build_captcha_li_list2(Lang, IP);
- false -> []
- end.
-
-build_captcha_li_list2(Lang, IP) ->
- SID = <<"">>,
- From = #jid{user = <<"">>, server = <<"test">>,
- resource = <<"">>},
- To = #jid{user = <<"">>, server = <<"test">>,
- resource = <<"">>},
- Args = [],
- case ejabberd_captcha:create_captcha(SID, From, To,
- Lang, IP, Args)
- of
- {ok, Id, _} ->
- {_, {CImg, CText, CId, CKey}} =
- ejabberd_captcha:build_captcha_html(Id, Lang),
- [?XE(<<"li">>,
- [CText, ?C(<<" ">>), CId, CKey, ?BR, CImg])];
- _ -> []
- end.
-
-%%%----------------------------------------------------------------------
-%%% Formulary change password GET
-%%%----------------------------------------------------------------------
-
-form_changepass_get(Host, Lang) ->
- HeadEls = [?XCT(<<"title">>, <<"Change Password">>),
- ?XA(<<"link">>,
- [{<<"href">>, <<"/register/register.css">>},
- {<<"type">>, <<"text/css">>},
- {<<"rel">>, <<"stylesheet">>}])],
- Els = [?XACT(<<"h1">>,
- [{<<"class">>, <<"title">>},
- {<<"style">>, <<"text-align:center;">>}],
- <<"Change Password">>),
- ?XAE(<<"form">>,
- [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
- [?XE(<<"ol">>,
- [?XE(<<"li">>,
- [?CT(<<"Username:">>), ?C(<<" ">>),
- ?INPUTS(<<"text">>, <<"username">>, <<"">>,
- <<"20">>)]),
- ?XE(<<"li">>,
- [?CT(<<"Server:">>), ?C(<<" ">>), ?C(Host)]),
- ?XE(<<"li">>,
- [?CT(<<"Old Password:">>), ?C(<<" ">>),
- ?INPUTS(<<"password">>, <<"passwordold">>, <<"">>,
- <<"20">>)]),
- ?XE(<<"li">>,
- [?CT(<<"New Password:">>), ?C(<<" ">>),
- ?INPUTS(<<"password">>, <<"password">>, <<"">>,
- <<"20">>)]),
- ?XE(<<"li">>,
- [?CT(<<"Password Verification:">>), ?C(<<" ">>),
- ?INPUTS(<<"password">>, <<"password2">>, <<"">>,
- <<"20">>)]),
- ?XE(<<"li">>,
- [?INPUTT(<<"submit">>, <<"changepass">>,
- <<"Change Password">>)])])])],
- {200,
- [{<<"Server">>, <<"ejabberd">>},
- {<<"Content-Type">>, <<"text/html">>}],
- ejabberd_web:make_xhtml(HeadEls, Els)}.
-
-%%%----------------------------------------------------------------------
-%%% Formulary change password POST
-%%%----------------------------------------------------------------------
-
-form_changepass_post(Q, Host) ->
- case catch get_changepass_parameters(Q) of
- [Username, PasswordOld, Password, Password] ->
- try_change_password(Username, Host, PasswordOld,
- Password);
- [_Username, _PasswordOld, _Password, _Password2] ->
- {error, passwords_not_identical};
- _ -> {error, wrong_parameters}
- end.
-
-get_changepass_parameters(Q) ->
-%% @spec(Username,Host,PasswordOld,Password) -> {atomic, ok} |
-%% {error, account_doesnt_exist} |
-%% {error, password_not_changed} |
-%% {error, password_incorrect}
- lists:map(fun (Key) ->
- {value, {_Key, Value}} = lists:keysearch(Key, 1, Q),
- Value
- end,
- [<<"username">>, <<"passwordold">>, <<"password">>,
- <<"password2">>]).
-
-try_change_password(Username, Host, PasswordOld,
- Password) ->
- try change_password(Username, Host, PasswordOld,
- Password)
- of
- {atomic, ok} -> {atomic, ok}
- catch
- error:{badmatch, Error} -> {error, Error}
- end.
-
-change_password(Username, Host, PasswordOld,
- Password) ->
- account_exists = check_account_exists(Username, Host),
- password_correct = check_password(Username, Host,
- PasswordOld),
- ok = ejabberd_auth:set_password(Username, Host,
- Password),
- case check_password(Username, Host, Password) of
- password_correct -> {atomic, ok};
- password_incorrect -> {error, password_not_changed}
- end.
-
-check_account_exists(Username, Host) ->
- case ejabberd_auth:is_user_exists(Username, Host) of
- true -> account_exists;
- false -> account_doesnt_exist
- end.
-
-check_password(Username, Host, Password) ->
- case ejabberd_auth:check_password(Username, Host,
- Password)
- of
- true -> password_correct;
- false -> password_incorrect
- end.
-
-%%%----------------------------------------------------------------------
-%%% Formulary delete account GET
-%%%----------------------------------------------------------------------
-
-form_del_get(Host, Lang) ->
- HeadEls = [?XCT(<<"title">>,
- <<"Unregister a Jabber account">>),
- ?XA(<<"link">>,
- [{<<"href">>, <<"/register/register.css">>},
- {<<"type">>, <<"text/css">>},
- {<<"rel">>, <<"stylesheet">>}])],
- Els = [?XACT(<<"h1">>,
- [{<<"class">>, <<"title">>},
- {<<"style">>, <<"text-align:center;">>}],
- <<"Unregister a Jabber account">>),
- ?XCT(<<"p">>,
- <<"This page allows to unregister a Jabber "
- "account in this Jabber server.">>),
- ?XAE(<<"form">>,
- [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
- [?XE(<<"ol">>,
- [?XE(<<"li">>,
- [?CT(<<"Username:">>), ?C(<<" ">>),
- ?INPUTS(<<"text">>, <<"username">>, <<"">>,
- <<"20">>)]),
- ?XE(<<"li">>,
- [?CT(<<"Server:">>), ?C(<<" ">>), ?C(Host)]),
- ?XE(<<"li">>,
- [?CT(<<"Password:">>), ?C(<<" ">>),
- ?INPUTS(<<"password">>, <<"password">>, <<"">>,
- <<"20">>)]),
- ?XE(<<"li">>,
- [?INPUTT(<<"submit">>, <<"unregister">>,
- <<"Unregister">>)])])])],
- {200,
- [{<<"Server">>, <<"ejabberd">>},
- {<<"Content-Type">>, <<"text/html">>}],
- ejabberd_web:make_xhtml(HeadEls, Els)}.
-
-%% @spec(Username, Host, Password) -> {success, ok, {Username, Host, Password} |
-%% {success, exists, {Username, Host, Password}} |
-%% {error, not_allowed} |
-%% {error, invalid_jid}
-register_account(Username, Host, Password) ->
- case jlib:make_jid(Username, Host, <<"">>) of
- error -> {error, invalid_jid};
- _ -> register_account2(Username, Host, Password)
- end.
-
-register_account2(Username, Host, Password) ->
- case ejabberd_auth:try_register(Username, Host,
- Password)
- of
- {atomic, Res} ->
- {success, Res, {Username, Host, Password}};
- Other -> Other
- end.
-
-%%%----------------------------------------------------------------------
-%%% Formulary delete POST
-%%%----------------------------------------------------------------------
-
-form_del_post(Q, Host) ->
- case catch get_unregister_parameters(Q) of
- [Username, Password] ->
- try_unregister_account(Username, Host, Password);
- _ -> {error, wrong_parameters}
- end.
-
-get_unregister_parameters(Q) ->
-%% @spec(Username, Host, Password) -> {atomic, ok} |
-%% {error, account_doesnt_exist} |
-%% {error, account_exists} |
-%% {error, password_incorrect}
- lists:map(fun (Key) ->
- {value, {_Key, Value}} = lists:keysearch(Key, 1, Q),
- Value
- end,
- [<<"username">>, <<"password">>]).
-
-try_unregister_account(Username, Host, Password) ->
- try unregister_account(Username, Host, Password) of
- {atomic, ok} -> {atomic, ok}
- catch
- error:{badmatch, Error} -> {error, Error}
- end.
-
-unregister_account(Username, Host, Password) ->
- account_exists = check_account_exists(Username, Host),
- password_correct = check_password(Username, Host,
- Password),
- ok = ejabberd_auth:remove_user(Username, Host,
- Password),
- account_doesnt_exist = check_account_exists(Username,
- Host),
- {atomic, ok}.
-
-%%%----------------------------------------------------------------------
-%%% Error texts
-%%%----------------------------------------------------------------------
-
-get_error_text({error, captcha_non_valid}) ->
- <<"The captcha you entered is wrong">>;
-get_error_text({success, exists, _}) ->
- get_error_text({atomic, exists});
-get_error_text({atomic, exists}) ->
- <<"The account already exists">>;
-get_error_text({error, password_incorrect}) ->
- <<"Incorrect password">>;
-get_error_text({error, invalid_jid}) ->
- <<"The username is not valid">>;
-get_error_text({error, not_allowed}) ->
- <<"Not allowed">>;
-get_error_text({error, account_doesnt_exist}) ->
- <<"Account doesn't exist">>;
-get_error_text({error, account_exists}) ->
- <<"The account was not deleted">>;
-get_error_text({error, password_not_changed}) ->
- <<"The password was not changed">>;
-get_error_text({error, passwords_not_identical}) ->
- <<"The passwords are different">>;
-get_error_text({error, wrong_parameters}) ->
- <<"Wrong parameters in the web formulary">>.