mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-22 16:20:52 +01:00
* src/web/*: Plugin architecture for HTTP modules (thanks to
Massimiliano Mirra) SVN Revision: 713
This commit is contained in:
parent
456185d0d3
commit
00807235c4
@ -1,3 +1,8 @@
|
||||
2007-01-25 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/*: Plugin architecture for HTTP modules (thanks to
|
||||
Massimiliano Mirra)
|
||||
|
||||
2007-01-24 Mickael Remond <mickael.remond@process-one.net>
|
||||
|
||||
* doc/guide.tex: Documentation for the
|
||||
|
@ -31,8 +31,15 @@
|
||||
request_keepalive,
|
||||
request_content_length,
|
||||
request_lang = "en",
|
||||
use_http_poll = false,
|
||||
use_web_admin = false,
|
||||
%% 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 = [],
|
||||
end_of_request = false,
|
||||
trail = ""
|
||||
}).
|
||||
@ -71,16 +78,32 @@ start_link({SockMod, Socket}, Opts) ->
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
UseHTTPPoll = lists:member(http_poll, Opts),
|
||||
UseWebAdmin = lists:member(web_admin, Opts),
|
||||
?DEBUG("S: ~p~n", [{UseHTTPPoll, UseWebAdmin}]),
|
||||
|
||||
%% XXX bard: for backward compatibility: expand web_admin and
|
||||
%% http_poll in Opts respectively to {["admin"],
|
||||
%% ejabberd_web_admin} and {["http-poll"], ejabberd_http_poll}
|
||||
|
||||
RequestHandlers =
|
||||
case lists:keysearch(request_handlers, 1, Opts) of
|
||||
{value, {request_handlers, H}} -> H;
|
||||
false -> []
|
||||
end ++
|
||||
case lists:member(web_admin, Opts) of
|
||||
true -> [{["admin"], ejabberd_web_admin}];
|
||||
false -> []
|
||||
end ++
|
||||
case lists:member(http_poll, Opts) of
|
||||
true -> [{["http-poll"], ejabberd_http_poll}];
|
||||
false -> []
|
||||
end,
|
||||
?DEBUG("S: ~p~n", [RequestHandlers]),
|
||||
|
||||
?INFO_MSG("started: ~p", [{SockMod1, Socket1}]),
|
||||
{ok, proc_lib:spawn_link(ejabberd_http,
|
||||
receive_headers,
|
||||
[#state{sockmod = SockMod1,
|
||||
socket = Socket1,
|
||||
use_http_poll = UseHTTPPoll,
|
||||
use_web_admin = UseWebAdmin}])}.
|
||||
request_handlers = RequestHandlers}])}.
|
||||
|
||||
|
||||
become_controller(_Pid) ->
|
||||
@ -192,23 +215,44 @@ process_header(State, Data) ->
|
||||
end,
|
||||
#state{sockmod = SockMod,
|
||||
socket = Socket,
|
||||
use_http_poll = State#state.use_http_poll,
|
||||
use_web_admin = State#state.use_web_admin};
|
||||
request_handlers = State#state.request_handlers};
|
||||
_ ->
|
||||
#state{end_of_request = true}
|
||||
#state{end_of_request = true,
|
||||
request_handlers = State#state.request_handlers}
|
||||
end;
|
||||
{error, _Reason} ->
|
||||
#state{end_of_request = true};
|
||||
#state{end_of_request = true,
|
||||
request_handlers = State#state.request_handlers};
|
||||
_ ->
|
||||
#state{end_of_request = true}
|
||||
#state{end_of_request = true,
|
||||
request_handlers = State#state.request_handlers}
|
||||
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) ->
|
||||
[{PathPattern, HandlerModule} | HandlersLeft] = Handlers,
|
||||
|
||||
case lists:prefix(PathPattern, Request#request.path) of
|
||||
true ->
|
||||
%% LocalPath is the path "local to the handler", i.e. if
|
||||
%% the handler was registered to handle "/test/" and the
|
||||
%% requested path is "/test/foo/bar", the local path is
|
||||
%% ["foo", "bar"]
|
||||
LocalPath = lists:nthtail(length(PathPattern), Request#request.path),
|
||||
HandlerModule:process(LocalPath, Request);
|
||||
false ->
|
||||
process(HandlersLeft, Request)
|
||||
end.
|
||||
|
||||
process_request(#state{request_method = 'GET',
|
||||
request_path = {abs_path, Path},
|
||||
request_auth = Auth,
|
||||
request_lang = Lang,
|
||||
use_http_poll = UseHTTPPoll,
|
||||
use_web_admin = UseWebAdmin} = State) ->
|
||||
request_handlers = RequestHandlers} = State) ->
|
||||
case (catch url_decode_q_split(Path)) of
|
||||
{'EXIT', _} ->
|
||||
process_request(false);
|
||||
@ -225,8 +269,11 @@ process_request(#state{request_method = 'GET',
|
||||
q = LQuery,
|
||||
auth = Auth,
|
||||
lang = Lang},
|
||||
case ejabberd_web:process_get({UseHTTPPoll, UseWebAdmin},
|
||||
Request) of
|
||||
%% 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) == xmlelement ->
|
||||
make_xhtml_output(State, 200, [], El);
|
||||
{Status, Headers, El} when
|
||||
@ -247,8 +294,7 @@ process_request(#state{request_method = 'POST',
|
||||
request_lang = Lang,
|
||||
sockmod = SockMod,
|
||||
socket = Socket,
|
||||
use_http_poll = UseHTTPPoll,
|
||||
use_web_admin = UseWebAdmin} = State)
|
||||
request_handlers = RequestHandlers} = State)
|
||||
when is_integer(Len) ->
|
||||
case SockMod of
|
||||
gen_tcp ->
|
||||
@ -275,8 +321,7 @@ process_request(#state{request_method = 'POST',
|
||||
auth = Auth,
|
||||
data = Data,
|
||||
lang = Lang},
|
||||
case ejabberd_web:process_get({UseHTTPPoll, UseWebAdmin},
|
||||
Request) of
|
||||
case process(RequestHandlers, Request) of
|
||||
El when element(1, El) == xmlelement ->
|
||||
make_xhtml_output(State, 200, [], El);
|
||||
{Status, Headers, El} when
|
||||
|
@ -24,7 +24,7 @@
|
||||
setopts/2,
|
||||
controlling_process/2,
|
||||
close/1,
|
||||
process_request/1]).
|
||||
process/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
@ -83,8 +83,7 @@ close({http_poll, FsmRef}) ->
|
||||
catch gen_fsm:sync_send_all_state_event(FsmRef, close).
|
||||
|
||||
|
||||
process_request(#request{path = [],
|
||||
data = Data} = Request) ->
|
||||
process([], #request{data = Data} = Request) ->
|
||||
case catch parse_request(Data) of
|
||||
{ok, ID1, Key, NewKey, Packet} ->
|
||||
ID = if
|
||||
@ -130,7 +129,7 @@ process_request(#request{path = [],
|
||||
_ ->
|
||||
{200, [?CT, {"Set-Cookie", "ID=-2:0; expires=-1"}], ""}
|
||||
end;
|
||||
process_request(_Request) ->
|
||||
process(_, _Request) ->
|
||||
{400, [], {xmlelement, "h1", [],
|
||||
[{xmlcdata, "400 Bad Request"}]}}.
|
||||
|
||||
|
@ -12,13 +12,18 @@
|
||||
|
||||
%% External exports
|
||||
-export([make_xhtml/1,
|
||||
process_get/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) ->
|
||||
{xmlelement, "html", [{"xmlns", "http://www.w3.org/1999/xhtml"},
|
||||
{"xml:lang", "en"},
|
||||
@ -48,98 +53,7 @@ make_xhtml(Els) ->
|
||||
{"name", Name},
|
||||
{"value", Value}])).
|
||||
|
||||
|
||||
process_get({_, true},
|
||||
#request{auth = Auth,
|
||||
path = ["admin", "server", SHost | RPath],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
Host = jlib:nameprep(SHost),
|
||||
case lists:member(Host, ?MYHOSTS) of
|
||||
true ->
|
||||
US = case Auth of
|
||||
{SJID, P} ->
|
||||
case jlib:string_to_jid(SJID) of
|
||||
error ->
|
||||
unauthorized;
|
||||
#jid{user = U, server = S} ->
|
||||
case ejabberd_auth:check_password(U, S, P) of
|
||||
true ->
|
||||
{U, S};
|
||||
false ->
|
||||
unauthorized
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
unauthorized
|
||||
end,
|
||||
case US of
|
||||
{User, Server} ->
|
||||
case acl:match_rule(
|
||||
Host, configure, jlib:make_jid(User, Server, "")) of
|
||||
deny ->
|
||||
{401, [], make_xhtml([?XC("h1", "Not Allowed")])};
|
||||
allow ->
|
||||
ejabberd_web_admin:process_admin(
|
||||
Host, Request#request{path = RPath,
|
||||
us = US})
|
||||
end;
|
||||
unauthorized ->
|
||||
{401,
|
||||
[{"WWW-Authenticate", "basic realm=\"ejabberd\""}],
|
||||
ejabberd_web:make_xhtml([{xmlelement, "h1", [],
|
||||
[{xmlcdata, "401 Unauthorized"}]}])}
|
||||
end;
|
||||
false ->
|
||||
{404, [], make_xhtml([?XC("h1", "Not found")])}
|
||||
end;
|
||||
|
||||
process_get({_, true},
|
||||
#request{auth = Auth,
|
||||
path = ["admin" | RPath],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
US = case Auth of
|
||||
{SJID, P} ->
|
||||
case jlib:string_to_jid(SJID) of
|
||||
error ->
|
||||
unauthorized;
|
||||
#jid{user = U, server = S} ->
|
||||
case ejabberd_auth:check_password(U, S, P) of
|
||||
true ->
|
||||
{U, S};
|
||||
false ->
|
||||
unauthorized
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
unauthorized
|
||||
end,
|
||||
case US of
|
||||
{User, Server} ->
|
||||
case acl:match_rule(
|
||||
global, configure, jlib:make_jid(User, Server, "")) of
|
||||
deny ->
|
||||
{401, [], make_xhtml([?XC("h1", "Not Allowed")])};
|
||||
allow ->
|
||||
ejabberd_web_admin:process_admin(
|
||||
global, Request#request{path = RPath,
|
||||
us = US})
|
||||
end;
|
||||
unauthorized ->
|
||||
{401,
|
||||
[{"WWW-Authenticate", "basic realm=\"ejabberd\""}],
|
||||
ejabberd_web:make_xhtml([{xmlelement, "h1", [],
|
||||
[{xmlcdata, "401 Unauthorized"}]}])}
|
||||
end;
|
||||
|
||||
process_get({true, _},
|
||||
#request{path = ["http-poll" | RPath],
|
||||
q = _Query,
|
||||
lang = _Lang} = Request) ->
|
||||
ejabberd_http_poll:process_request(Request#request{path = RPath});
|
||||
|
||||
process_get(_, _Request) ->
|
||||
{404, [], make_xhtml([?XC("h1", "Not found")])}.
|
||||
|
||||
|
||||
error(not_found) ->
|
||||
{404, [], make_xhtml([?XC("h1", "404 Not Found")])};
|
||||
error(not_allowed) ->
|
||||
{401, [], make_xhtml([?XC("h1", "401 Unauthorized")])}.
|
||||
|
@ -14,7 +14,9 @@
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
%% External exports
|
||||
-export([process_admin/2,
|
||||
-export([process/2,
|
||||
%% XXX bard: unexported, since it is only called from process/2 now
|
||||
%% process_admin/2,
|
||||
list_users/4,
|
||||
list_users_in_diapason/4]).
|
||||
|
||||
@ -54,6 +56,75 @@
|
||||
{"size", Size}])).
|
||||
-define(INPUTST(Type, Name, Value, Size), ?INPUT(Type, Name, ?T(Value), Size)).
|
||||
|
||||
|
||||
process(["server", SHost | RPath], #request{auth = Auth,
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
Host = jlib:nameprep(SHost),
|
||||
case lists:member(Host, ?MYHOSTS) of
|
||||
true ->
|
||||
case get_auth(Auth) of
|
||||
{User, Server} ->
|
||||
case acl:match_rule(
|
||||
Host, configure, jlib:make_jid(User, Server, "")) of
|
||||
deny ->
|
||||
ejabberd_web:error(not_allowed);
|
||||
allow ->
|
||||
process_admin(
|
||||
Host, Request#request{path = RPath,
|
||||
us = {User, Server}})
|
||||
end;
|
||||
unauthorized ->
|
||||
{401,
|
||||
[{"WWW-Authenticate", "basic realm=\"ejabberd\""}],
|
||||
ejabberd_web:make_xhtml([{xmlelement, "h1", [],
|
||||
[{xmlcdata, "401 Unauthorized"}]}])}
|
||||
end;
|
||||
false ->
|
||||
ejabberd_web:error(not_found)
|
||||
end;
|
||||
|
||||
process(RPath, #request{auth = Auth,
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
case get_auth(Auth) of
|
||||
{User, Server} ->
|
||||
case acl:match_rule(
|
||||
global, configure, jlib:make_jid(User, Server, "")) of
|
||||
deny ->
|
||||
ejabberd_web:error(not_allowed);
|
||||
allow ->
|
||||
process_admin(
|
||||
global, Request#request{path = RPath,
|
||||
us = {User, Server}})
|
||||
end;
|
||||
unauthorized ->
|
||||
%% XXX bard: any reason to send this data now and not
|
||||
%% always in case of an 401? ought to check http specs...
|
||||
{401,
|
||||
[{"WWW-Authenticate", "basic realm=\"ejabberd\""}],
|
||||
ejabberd_web:make_xhtml([{xmlelement, "h1", [],
|
||||
[{xmlcdata, "401 Unauthorized"}]}])}
|
||||
end.
|
||||
|
||||
get_auth(Auth) ->
|
||||
case Auth of
|
||||
{SJID, P} ->
|
||||
case jlib:string_to_jid(SJID) of
|
||||
error ->
|
||||
unauthorized;
|
||||
#jid{user = U, server = S} ->
|
||||
case ejabberd_auth:check_password(U, S, P) of
|
||||
true ->
|
||||
{U, S};
|
||||
false ->
|
||||
unauthorized
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
unauthorized
|
||||
end.
|
||||
|
||||
make_xhtml(Els, global, Lang) ->
|
||||
{200, [html],
|
||||
{xmlelement, "html", [{"xmlns", "http://www.w3.org/1999/xhtml"},
|
||||
|
Loading…
Reference in New Issue
Block a user