* src/web/: Small HTTP server and admin web-interface to ejabberd
(not completed yet) * src/ejabberd_sup.erl: Added HTTP processes supervisor * src/ejabberd_c2s.erl: Added API to ask presence (thanks to Mickael Remond) * src/msgs/ru.msg: Updated (thanks to Sergei Golovan) * src/mod_muc/mod_muc_room.erl: Updated date parser (thanks to Sergei Golovan) * src/mod_muc/mod_muc.erl: Added error descriptions (thanks to Sergei Golovan) * src/mod_muc/mod_muc_room.erl: Likewise * src/mod_vcard.erl: Fixed vCard tag (thanks to Sergei Golovan) * src/mod_irc/mod_irc.erl: Likewise * src/mod_pubsub/mod_pubsub.erl: Likewise * src/jlib.hrl: Added macros for errors with <text/> (thanks to Sergei Golovan) SVN Revision: 206
This commit is contained in:
parent
21c4b65610
commit
ec182cbd60
25
ChangeLog
25
ChangeLog
|
@ -1,3 +1,28 @@
|
||||||
|
2004-03-02 Alexey Shchepin <alexey@sevcom.net>
|
||||||
|
|
||||||
|
* src/web/: Small HTTP server and admin web-interface to ejabberd
|
||||||
|
(not completed yet)
|
||||||
|
* src/ejabberd_sup.erl: Added HTTP processes supervisor
|
||||||
|
|
||||||
|
* src/ejabberd_c2s.erl: Added API to ask presence (thanks to
|
||||||
|
Mickael Remond)
|
||||||
|
|
||||||
|
* src/msgs/ru.msg: Updated (thanks to Sergei Golovan)
|
||||||
|
|
||||||
|
* src/mod_muc/mod_muc_room.erl: Updated date parser (thanks to
|
||||||
|
Sergei Golovan)
|
||||||
|
|
||||||
|
* src/mod_muc/mod_muc.erl: Added error descriptions (thanks to
|
||||||
|
Sergei Golovan)
|
||||||
|
* src/mod_muc/mod_muc_room.erl: Likewise
|
||||||
|
|
||||||
|
* src/mod_vcard.erl: Fixed vCard tag (thanks to Sergei Golovan)
|
||||||
|
* src/mod_irc/mod_irc.erl: Likewise
|
||||||
|
* src/mod_pubsub/mod_pubsub.erl: Likewise
|
||||||
|
|
||||||
|
* src/jlib.hrl: Added macros for errors with <text/> (thanks to
|
||||||
|
Sergei Golovan)
|
||||||
|
|
||||||
2004-02-26 Alexey Shchepin <alexey@sevcom.net>
|
2004-02-26 Alexey Shchepin <alexey@sevcom.net>
|
||||||
|
|
||||||
* src/msgs/ru.msg: Updated (thanks to Sergei Golovan)
|
* src/msgs/ru.msg: Updated (thanks to Sergei Golovan)
|
||||||
|
|
|
@ -16,7 +16,8 @@
|
||||||
-export([start/2,
|
-export([start/2,
|
||||||
start_link/2,
|
start_link/2,
|
||||||
send_text/2,
|
send_text/2,
|
||||||
send_element/2]).
|
send_element/2,
|
||||||
|
get_presence/1]).
|
||||||
|
|
||||||
%% gen_fsm callbacks
|
%% gen_fsm callbacks
|
||||||
-export([init/1,
|
-export([init/1,
|
||||||
|
@ -90,6 +91,10 @@ start(SockData, Opts) ->
|
||||||
start_link(SockData, Opts) ->
|
start_link(SockData, Opts) ->
|
||||||
gen_fsm:start_link(ejabberd_c2s, [SockData, Opts], ?FSMOPTS).
|
gen_fsm:start_link(ejabberd_c2s, [SockData, Opts], ?FSMOPTS).
|
||||||
|
|
||||||
|
%% Return Username, Resource and presence information
|
||||||
|
get_presence(FsmRef) ->
|
||||||
|
gen_fsm:sync_send_all_state_event(FsmRef, {get_presence}, 1000).
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
%%% Callback functions from gen_fsm
|
%%% Callback functions from gen_fsm
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
@ -661,6 +666,22 @@ handle_event(_Event, StateName, StateData) ->
|
||||||
%% {stop, Reason, NewStateData} |
|
%% {stop, Reason, NewStateData} |
|
||||||
%% {stop, Reason, Reply, NewStateData}
|
%% {stop, Reason, Reply, NewStateData}
|
||||||
%%----------------------------------------------------------------------
|
%%----------------------------------------------------------------------
|
||||||
|
handle_sync_event({get_presence}, _From, StateName, StateData) ->
|
||||||
|
User = StateData#state.user,
|
||||||
|
Status =
|
||||||
|
case StateData#state.pres_last of
|
||||||
|
undefined ->
|
||||||
|
"unavailable";
|
||||||
|
PresLast ->
|
||||||
|
case xml:get_path_s(PresLast, [{elem, "status"}, cdata]) of
|
||||||
|
"" ->
|
||||||
|
"available";
|
||||||
|
Stat ->
|
||||||
|
Stat
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
Reply = {User, Status},
|
||||||
|
{reply, Reply, StateName, StateData};
|
||||||
handle_sync_event(_Event, _From, StateName, StateData) ->
|
handle_sync_event(_Event, _From, StateName, StateData) ->
|
||||||
Reply = ok,
|
Reply = ok,
|
||||||
{reply, Reply, StateName, StateData}.
|
{reply, Reply, StateName, StateData}.
|
||||||
|
|
|
@ -87,6 +87,14 @@ init([]) ->
|
||||||
infinity,
|
infinity,
|
||||||
supervisor,
|
supervisor,
|
||||||
[ejabberd_tmp_sup]},
|
[ejabberd_tmp_sup]},
|
||||||
|
HTTPSupervisor =
|
||||||
|
{ejabberd_http_sup,
|
||||||
|
{ejabberd_tmp_sup, start_link,
|
||||||
|
[ejabberd_http_sup, ejabberd_http]},
|
||||||
|
permanent,
|
||||||
|
infinity,
|
||||||
|
supervisor,
|
||||||
|
[ejabberd_tmp_sup]},
|
||||||
IQSupervisor =
|
IQSupervisor =
|
||||||
{ejabberd_iq_sup,
|
{ejabberd_iq_sup,
|
||||||
{ejabberd_tmp_sup, start_link,
|
{ejabberd_tmp_sup, start_link,
|
||||||
|
@ -102,6 +110,7 @@ init([]) ->
|
||||||
S2SInSupervisor,
|
S2SInSupervisor,
|
||||||
S2SOutSupervisor,
|
S2SOutSupervisor,
|
||||||
ServiceSupervisor,
|
ServiceSupervisor,
|
||||||
|
HTTPSupervisor,
|
||||||
IQSupervisor,
|
IQSupervisor,
|
||||||
Listener]}}.
|
Listener]}}.
|
||||||
|
|
||||||
|
|
50
src/jlib.hrl
50
src/jlib.hrl
|
@ -97,6 +97,56 @@
|
||||||
%-define(ERR_,
|
%-define(ERR_,
|
||||||
% ?STANZA_ERROR("", "", "")).
|
% ?STANZA_ERROR("", "", "")).
|
||||||
|
|
||||||
|
-define(STANZA_ERRORT(Code, Type, Condition, Lang, Text),
|
||||||
|
{xmlelement, "error",
|
||||||
|
[{"code", Code}, {"type", Type}],
|
||||||
|
[{xmlelement, Condition, [{"xmlns", ?NS_STANZAS}], []},
|
||||||
|
{xmlelement, "text", [{"xmlns", ?NS_STANZAS}],
|
||||||
|
[{xmlcdata, translate:translate(Lang, Text)}]}]}).
|
||||||
|
|
||||||
|
-define(ERRT_BAD_REQUEST(Lang, Text),
|
||||||
|
?STANZA_ERRORT("400", "modify", "bad-request", Lang, Text)).
|
||||||
|
-define(ERRT_CONFLICT(Lang, Text),
|
||||||
|
?STANZA_ERRORT("409", "cancel", "conflict", Lang, Text)).
|
||||||
|
-define(ERRT_FEATURE_NOT_IMPLEMENTED(Lang, Text),
|
||||||
|
?STANZA_ERRORT("501", "cancel", "feature-not-implemented", Lang, Text)).
|
||||||
|
-define(ERRT_FORBIDDEN(Lang, Text),
|
||||||
|
?STANZA_ERRORT("403", "auth", "forbidden", Lang, Text)).
|
||||||
|
-define(ERRT_GONE(Lang, Text),
|
||||||
|
?STANZA_ERRORT("302", "modify", "gone", Lang, Text)).
|
||||||
|
-define(ERRT_INTERNAL_SERVER_ERROR(Lang, Text),
|
||||||
|
?STANZA_ERRORT("500", "wait", "internal-server-error", Lang, Text)).
|
||||||
|
-define(ERRT_ITEM_NOT_FOUND(Lang, Text),
|
||||||
|
?STANZA_ERRORT("404", "cancel", "item-not-found", Lang, Text)).
|
||||||
|
-define(ERRT_JID_MALFORMED(Lang, Text),
|
||||||
|
?STANZA_ERRORT("400", "modify", "jid-malformed", Lang, Text)).
|
||||||
|
-define(ERRT_NOT_ACCEPTABLE(Lang, Text),
|
||||||
|
?STANZA_ERRORT("406", "modify", "not-acceptable", Lang, Text)).
|
||||||
|
-define(ERRT_NOT_ALLOWED(Lang, Text),
|
||||||
|
?STANZA_ERRORT("405", "cancel", "not-allowed", Lang, Text)).
|
||||||
|
-define(ERRT_NOT_AUTHORIZED(Lang, Text),
|
||||||
|
?STANZA_ERRORT("401", "auth", "not-authorized", Lang, Text)).
|
||||||
|
-define(ERRT_PAYMENT_REQUIRED(Lang, Text),
|
||||||
|
?STANZA_ERRORT("402", "auth", "payment-required", Lang, Text)).
|
||||||
|
-define(ERRT_RECIPIENT_UNAVAILABLE(Lang, Text),
|
||||||
|
?STANZA_ERRORT("404", "wait", "recipient-unavailable", Lang, Text)).
|
||||||
|
-define(ERRT_REDIRECT(Lang, Text),
|
||||||
|
?STANZA_ERRORT("302", "modify", "redirect", Lang, Text)).
|
||||||
|
-define(ERRT_REGISTRATION_REQUIRED(Lang, Text),
|
||||||
|
?STANZA_ERRORT("407", "auth", "registration-required", Lang, Text)).
|
||||||
|
-define(ERRT_REMOTE_SERVER_NOT_FOUND(Lang, Text),
|
||||||
|
?STANZA_ERRORT("404", "cancel", "remote-server-not-found", Lang, Text)).
|
||||||
|
-define(ERRT_REMOTE_SERVER_TIMEOUT(Lang, Text),
|
||||||
|
?STANZA_ERRORT("504", "wait", "remote-server-timeout", Lang, Text)).
|
||||||
|
-define(ERRT_RESOURCE_CONSTRAINT(Lang, Text),
|
||||||
|
?STANZA_ERRORT("500", "wait", "resource-constraint", Lang, Text)).
|
||||||
|
-define(ERRT_SERVICE_UNAVAILABLE(Lang, Text),
|
||||||
|
?STANZA_ERRORT("503", "cancel", "service-unavailable", Lang, Text)).
|
||||||
|
-define(ERRT_SUBSCRIPTION_REQUIRED(Lang, Text),
|
||||||
|
?STANZA_ERRORT("407", "auth", "subscription-required", Lang, Text)).
|
||||||
|
-define(ERRT_UNEXPECTED_REQUEST(Lang, Text),
|
||||||
|
?STANZA_ERRORT("400", "wait", "unexpected-request", Lang, Text)).
|
||||||
|
|
||||||
% TODO: update to new-style
|
% TODO: update to new-style
|
||||||
% Application-specific stanza errors
|
% Application-specific stanza errors
|
||||||
-define(AUTH_STANZA_ERROR(Condition),
|
-define(AUTH_STANZA_ERROR(Condition),
|
||||||
|
|
|
@ -79,7 +79,7 @@ do_route(Host, From, To, Packet) ->
|
||||||
lang = Lang} = IQ ->
|
lang = Lang} = IQ ->
|
||||||
Res = IQ#iq{type = result,
|
Res = IQ#iq{type = result,
|
||||||
sub_el =
|
sub_el =
|
||||||
[{xmlelement, "query",
|
[{xmlelement, "vCard",
|
||||||
[{"xmlns", XMLNS}],
|
[{"xmlns", XMLNS}],
|
||||||
iq_get_vcard(Lang)}]},
|
iq_get_vcard(Lang)}]},
|
||||||
ejabberd_router:route(To,
|
ejabberd_router:route(To,
|
||||||
|
|
|
@ -113,8 +113,9 @@ do_route(Host, From, To, Packet) ->
|
||||||
jlib:iq_to_xml(Res));
|
jlib:iq_to_xml(Res));
|
||||||
#iq{type = set,
|
#iq{type = set,
|
||||||
xmlns = ?NS_REGISTER = XMLNS,
|
xmlns = ?NS_REGISTER = XMLNS,
|
||||||
|
lang = Lang,
|
||||||
sub_el = SubEl} = IQ ->
|
sub_el = SubEl} = IQ ->
|
||||||
case process_iq_register_set(From, SubEl) of
|
case process_iq_register_set(From, SubEl, Lang) of
|
||||||
{result, IQRes} ->
|
{result, IQRes} ->
|
||||||
Res = IQ#iq{type = result,
|
Res = IQ#iq{type = result,
|
||||||
sub_el =
|
sub_el =
|
||||||
|
@ -135,7 +136,7 @@ do_route(Host, From, To, Packet) ->
|
||||||
sub_el = SubEl} = IQ ->
|
sub_el = SubEl} = IQ ->
|
||||||
Res = IQ#iq{type = result,
|
Res = IQ#iq{type = result,
|
||||||
sub_el =
|
sub_el =
|
||||||
[{xmlelement, "query",
|
[{xmlelement, "vCard",
|
||||||
[{"xmlns", XMLNS}],
|
[{"xmlns", XMLNS}],
|
||||||
iq_get_vcard(Lang)}]},
|
iq_get_vcard(Lang)}]},
|
||||||
ejabberd_router:route(To,
|
ejabberd_router:route(To,
|
||||||
|
@ -161,9 +162,12 @@ do_route(Host, From, To, Packet) ->
|
||||||
[{elem, "body"}, cdata]),
|
[{elem, "body"}, cdata]),
|
||||||
broadcast_service_message(Msg);
|
broadcast_service_message(Msg);
|
||||||
_ ->
|
_ ->
|
||||||
|
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||||
|
ErrText = "Only service administrators "
|
||||||
|
"are allowed to send service messages",
|
||||||
Err = jlib:make_error_reply(
|
Err = jlib:make_error_reply(
|
||||||
Packet,
|
Packet,
|
||||||
?ERR_NOT_ALLOWED),
|
?ERRT_FORBIDDEN(Lang, ErrText)),
|
||||||
ejabberd_router:route(
|
ejabberd_router:route(
|
||||||
To, From, Err)
|
To, From, Err)
|
||||||
end
|
end
|
||||||
|
@ -198,8 +202,10 @@ do_route(Host, From, To, Packet) ->
|
||||||
mod_muc_room:route(Pid, From, Nick, Packet),
|
mod_muc_room:route(Pid, From, Nick, Packet),
|
||||||
ok;
|
ok;
|
||||||
_ ->
|
_ ->
|
||||||
|
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||||
|
ErrText = "Conference room does not exist",
|
||||||
Err = jlib:make_error_reply(
|
Err = jlib:make_error_reply(
|
||||||
Packet, ?ERR_SERVICE_UNAVAILABLE),
|
Packet, ?ERRT_ITEM_NOT_FOUND(Lang, ErrText)),
|
||||||
ejabberd_router:route(To, From, Err)
|
ejabberd_router:route(To, From, Err)
|
||||||
end;
|
end;
|
||||||
[R] ->
|
[R] ->
|
||||||
|
@ -332,12 +338,13 @@ iq_get_register_info(From, Host, Lang) ->
|
||||||
Lang, "Enter nickname you want to register")}]},
|
Lang, "Enter nickname you want to register")}]},
|
||||||
?XFIELD("text-single", "Nickname", "nick", Nick)]}].
|
?XFIELD("text-single", "Nickname", "nick", Nick)]}].
|
||||||
|
|
||||||
iq_set_register_info(From, XData) ->
|
iq_set_register_info(From, XData, Lang) ->
|
||||||
{LUser, LServer, _} = jlib:jid_tolower(From),
|
{LUser, LServer, _} = jlib:jid_tolower(From),
|
||||||
LUS = {LUser, LServer},
|
LUS = {LUser, LServer},
|
||||||
case lists:keysearch("nick", 1, XData) of
|
case lists:keysearch("nick", 1, XData) of
|
||||||
false ->
|
false ->
|
||||||
{error, ?ERR_BAD_REQUEST};
|
ErrText = "You must fill in field \"nick\" in the form",
|
||||||
|
{error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)};
|
||||||
{value, {_, [Nick]}} ->
|
{value, {_, [Nick]}} ->
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
case Nick of
|
case Nick of
|
||||||
|
@ -369,13 +376,14 @@ iq_set_register_info(From, XData) ->
|
||||||
{atomic, ok} ->
|
{atomic, ok} ->
|
||||||
{result, []};
|
{result, []};
|
||||||
{atomic, false} ->
|
{atomic, false} ->
|
||||||
{error, ?ERR_CONFLICT};
|
ErrText = "Specified nickname is already registered",
|
||||||
|
{error, ?ERRT_CONFLICT(Lang, ErrText)};
|
||||||
_ ->
|
_ ->
|
||||||
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
process_iq_register_set(From, SubEl) ->
|
process_iq_register_set(From, SubEl, Lang) ->
|
||||||
{xmlelement, Name, Attrs, Els} = SubEl,
|
{xmlelement, Name, Attrs, Els} = SubEl,
|
||||||
case xml:remove_cdata(Els) of
|
case xml:remove_cdata(Els) of
|
||||||
[{xmlelement, "x", Attrs1, Els1} = XEl] ->
|
[{xmlelement, "x", Attrs1, Els1} = XEl] ->
|
||||||
|
@ -389,7 +397,7 @@ process_iq_register_set(From, SubEl) ->
|
||||||
invalid ->
|
invalid ->
|
||||||
{error, ?ERR_BAD_REQUEST};
|
{error, ?ERR_BAD_REQUEST};
|
||||||
_ ->
|
_ ->
|
||||||
iq_set_register_info(From, XData)
|
iq_set_register_info(From, XData, Lang)
|
||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
{error, ?ERR_BAD_REQUEST}
|
{error, ?ERR_BAD_REQUEST}
|
||||||
|
|
|
@ -119,6 +119,7 @@ init([Host, Room, Opts]) ->
|
||||||
normal_state({route, From, "",
|
normal_state({route, From, "",
|
||||||
{xmlelement, "message", Attrs, Els} = Packet},
|
{xmlelement, "message", Attrs, Els} = Packet},
|
||||||
StateData) ->
|
StateData) ->
|
||||||
|
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||||
case is_user_online(From, StateData) of
|
case is_user_online(From, StateData) of
|
||||||
true ->
|
true ->
|
||||||
case xml:get_attr_s("type", Attrs) of
|
case xml:get_attr_s("type", Attrs) of
|
||||||
|
@ -172,16 +173,26 @@ normal_state({route, From, "",
|
||||||
NewStateData1),
|
NewStateData1),
|
||||||
{next_state, normal_state, NewStateData2};
|
{next_state, normal_state, NewStateData2};
|
||||||
_ ->
|
_ ->
|
||||||
|
ErrText =
|
||||||
|
case (StateData#state.config)#config.allow_change_subj of
|
||||||
|
true ->
|
||||||
|
"Only moderators and participants "
|
||||||
|
"are allowed to change subject in this room";
|
||||||
|
_ ->
|
||||||
|
"Only moderators "
|
||||||
|
"are allowed to change subject in this room"
|
||||||
|
end,
|
||||||
Err = jlib:make_error_reply(
|
Err = jlib:make_error_reply(
|
||||||
Packet, ?ERR_FORBIDDEN),
|
Packet, ?ERRT_FORBIDDEN(Lang, ErrText)),
|
||||||
ejabberd_router:route(
|
ejabberd_router:route(
|
||||||
StateData#state.jid,
|
StateData#state.jid,
|
||||||
From, Err),
|
From, Err),
|
||||||
{next_state, normal_state, StateData}
|
{next_state, normal_state, StateData}
|
||||||
end;
|
end;
|
||||||
true ->
|
true ->
|
||||||
|
ErrText = "Visitors are not allowed to send messages to all occupants",
|
||||||
Err = jlib:make_error_reply(
|
Err = jlib:make_error_reply(
|
||||||
Packet, ?ERR_FORBIDDEN),
|
Packet, ?ERRT_FORBIDDEN(Lang, ErrText)),
|
||||||
ejabberd_router:route(
|
ejabberd_router:route(
|
||||||
StateData#state.jid,
|
StateData#state.jid,
|
||||||
From, Err),
|
From, Err),
|
||||||
|
@ -202,11 +213,20 @@ normal_state({route, From, "",
|
||||||
_ ->
|
_ ->
|
||||||
{next_state, normal_state, StateData}
|
{next_state, normal_state, StateData}
|
||||||
end;
|
end;
|
||||||
|
"chat" ->
|
||||||
|
ErrText = "It is not allowed to send private messages to the conference",
|
||||||
|
Err = jlib:make_error_reply(
|
||||||
|
Packet, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)),
|
||||||
|
ejabberd_router:route(
|
||||||
|
StateData#state.jid,
|
||||||
|
From, Err),
|
||||||
|
{next_state, normal_state, StateData};
|
||||||
Type when (Type == "") or (Type == "normal") ->
|
Type when (Type == "") or (Type == "normal") ->
|
||||||
case check_invitation(From, Els, StateData) of
|
case check_invitation(From, Els, StateData) of
|
||||||
error ->
|
error ->
|
||||||
|
ErrText = "It is not allowed to send normal messages to the conference",
|
||||||
Err = jlib:make_error_reply(
|
Err = jlib:make_error_reply(
|
||||||
Packet, ?ERR_NOT_ALLOWED),
|
Packet, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)),
|
||||||
ejabberd_router:route(
|
ejabberd_router:route(
|
||||||
StateData#state.jid,
|
StateData#state.jid,
|
||||||
From, Err),
|
From, Err),
|
||||||
|
@ -231,8 +251,9 @@ normal_state({route, From, "",
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
|
ErrText = "Improper message type",
|
||||||
Err = jlib:make_error_reply(
|
Err = jlib:make_error_reply(
|
||||||
Packet, ?ERR_NOT_ALLOWED),
|
Packet, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)),
|
||||||
ejabberd_router:route(
|
ejabberd_router:route(
|
||||||
StateData#state.jid,
|
StateData#state.jid,
|
||||||
From, Err),
|
From, Err),
|
||||||
|
@ -243,8 +264,9 @@ normal_state({route, From, "",
|
||||||
"error" ->
|
"error" ->
|
||||||
ok;
|
ok;
|
||||||
_ ->
|
_ ->
|
||||||
|
ErrText = "Only occupants are allowed to send messages to the conference",
|
||||||
Err = jlib:make_error_reply(
|
Err = jlib:make_error_reply(
|
||||||
Packet, ?ERR_NOT_ACCEPTABLE),
|
Packet, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)),
|
||||||
ejabberd_router:route(StateData#state.jid, From, Err)
|
ejabberd_router:route(StateData#state.jid, From, Err)
|
||||||
end,
|
end,
|
||||||
{next_state, normal_state, StateData}
|
{next_state, normal_state, StateData}
|
||||||
|
@ -305,6 +327,7 @@ normal_state({route, From, Nick,
|
||||||
{xmlelement, "presence", Attrs, Els} = Packet},
|
{xmlelement, "presence", Attrs, Els} = Packet},
|
||||||
StateData) ->
|
StateData) ->
|
||||||
Type = xml:get_attr_s("type", Attrs),
|
Type = xml:get_attr_s("type", Attrs),
|
||||||
|
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||||
StateData1 =
|
StateData1 =
|
||||||
case Type of
|
case Type of
|
||||||
"unavailable" ->
|
"unavailable" ->
|
||||||
|
@ -336,17 +359,32 @@ normal_state({route, From, Nick,
|
||||||
true ->
|
true ->
|
||||||
case is_nick_change(From, Nick, StateData) of
|
case is_nick_change(From, Nick, StateData) of
|
||||||
true ->
|
true ->
|
||||||
case is_nick_exists(Nick, StateData) of
|
case {is_nick_exists(Nick, StateData),
|
||||||
true ->
|
mod_muc:can_use_nick(From, Nick)} of
|
||||||
|
{true, _} ->
|
||||||
|
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||||
|
ErrText = "Nickname is already in use by another occupant",
|
||||||
Err = jlib:make_error_reply(
|
Err = jlib:make_error_reply(
|
||||||
Packet,
|
Packet,
|
||||||
?ERR_CONFLICT),
|
?ERRT_CONFLICT(Lang, ErrText)),
|
||||||
ejabberd_router:route(
|
ejabberd_router:route(
|
||||||
jlib:jid_replace_resource(
|
jlib:jid_replace_resource(
|
||||||
StateData#state.jid,
|
StateData#state.jid,
|
||||||
Nick), % TODO: s/Nick/""/
|
Nick), % TODO: s/Nick/""/
|
||||||
From, Err),
|
From, Err),
|
||||||
StateData;
|
StateData;
|
||||||
|
{_, false} ->
|
||||||
|
ErrText = "Nickname is registered by another person",
|
||||||
|
Err = jlib:make_error_reply(
|
||||||
|
Packet,
|
||||||
|
?ERRT_CONFLICT(Lang, ErrText)),
|
||||||
|
ejabberd_router:route(
|
||||||
|
% TODO: s/Nick/""/
|
||||||
|
jlib:jid_replace_resource(
|
||||||
|
StateData#state.jid,
|
||||||
|
Nick),
|
||||||
|
From, Err),
|
||||||
|
StateData;
|
||||||
_ ->
|
_ ->
|
||||||
change_nick(From, Nick, StateData)
|
change_nick(From, Nick, StateData)
|
||||||
end;
|
end;
|
||||||
|
@ -376,6 +414,7 @@ normal_state({route, From, ToNick,
|
||||||
{xmlelement, "message", Attrs, Els} = Packet},
|
{xmlelement, "message", Attrs, Els} = Packet},
|
||||||
StateData) ->
|
StateData) ->
|
||||||
Type = xml:get_attr_s("type", Attrs),
|
Type = xml:get_attr_s("type", Attrs),
|
||||||
|
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||||
case Type of
|
case Type of
|
||||||
"error" ->
|
"error" ->
|
||||||
case is_user_online(From, StateData) of
|
case is_user_online(From, StateData) of
|
||||||
|
@ -398,8 +437,10 @@ normal_state({route, From, ToNick,
|
||||||
true ->
|
true ->
|
||||||
case Type of
|
case Type of
|
||||||
"groupchat" ->
|
"groupchat" ->
|
||||||
|
ErrText = "It is not allowed to send private "
|
||||||
|
"messages of type \"groupchat\"",
|
||||||
Err = jlib:make_error_reply(
|
Err = jlib:make_error_reply(
|
||||||
Packet, ?ERR_BAD_REQUEST),
|
Packet, ?ERRT_BAD_REQUEST(Lang, ErrText)),
|
||||||
ejabberd_router:route(
|
ejabberd_router:route(
|
||||||
jlib:jid_replace_resource(
|
jlib:jid_replace_resource(
|
||||||
StateData#state.jid,
|
StateData#state.jid,
|
||||||
|
@ -408,8 +449,9 @@ normal_state({route, From, ToNick,
|
||||||
_ ->
|
_ ->
|
||||||
case find_jid_by_nick(ToNick, StateData) of
|
case find_jid_by_nick(ToNick, StateData) of
|
||||||
false ->
|
false ->
|
||||||
|
ErrText = "Recipient is not in the conference room",
|
||||||
Err = jlib:make_error_reply(
|
Err = jlib:make_error_reply(
|
||||||
Packet, ?ERR_ITEM_NOT_FOUND),
|
Packet, ?ERRT_ITEM_NOT_FOUND(Lang, ErrText)),
|
||||||
ejabberd_router:route(
|
ejabberd_router:route(
|
||||||
jlib:jid_replace_resource(
|
jlib:jid_replace_resource(
|
||||||
StateData#state.jid,
|
StateData#state.jid,
|
||||||
|
@ -427,8 +469,9 @@ normal_state({route, From, ToNick,
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
|
ErrText = "Only occupants are allowed to send messages to the conference",
|
||||||
Err = jlib:make_error_reply(
|
Err = jlib:make_error_reply(
|
||||||
Packet, ?ERR_NOT_ALLOWED),
|
Packet, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)),
|
||||||
ejabberd_router:route(
|
ejabberd_router:route(
|
||||||
jlib:jid_replace_resource(
|
jlib:jid_replace_resource(
|
||||||
StateData#state.jid,
|
StateData#state.jid,
|
||||||
|
@ -441,17 +484,19 @@ normal_state({route, From, ToNick,
|
||||||
normal_state({route, From, ToNick,
|
normal_state({route, From, ToNick,
|
||||||
{xmlelement, "iq", Attrs, Els} = Packet},
|
{xmlelement, "iq", Attrs, Els} = Packet},
|
||||||
StateData) ->
|
StateData) ->
|
||||||
case (StateData#state.config)#config.allow_query_users
|
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||||
andalso is_user_online(From, StateData) of
|
case {(StateData#state.config)#config.allow_query_users,
|
||||||
true ->
|
is_user_online(From, StateData)} of
|
||||||
|
{true, true} ->
|
||||||
case find_jid_by_nick(ToNick, StateData) of
|
case find_jid_by_nick(ToNick, StateData) of
|
||||||
false ->
|
false ->
|
||||||
case jlib:iq_query_info(Packet) of
|
case jlib:iq_query_info(Packet) of
|
||||||
reply ->
|
reply ->
|
||||||
ok;
|
ok;
|
||||||
_ ->
|
_ ->
|
||||||
|
ErrText = "Recipient is not in the conference room",
|
||||||
Err = jlib:make_error_reply(
|
Err = jlib:make_error_reply(
|
||||||
Packet, ?ERR_ITEM_NOT_FOUND),
|
Packet, ?ERRT_ITEM_NOT_FOUND(Lang, ErrText)),
|
||||||
ejabberd_router:route(
|
ejabberd_router:route(
|
||||||
jlib:jid_replace_resource(
|
jlib:jid_replace_resource(
|
||||||
StateData#state.jid, ToNick),
|
StateData#state.jid, ToNick),
|
||||||
|
@ -465,13 +510,26 @@ normal_state({route, From, ToNick,
|
||||||
jlib:jid_replace_resource(StateData#state.jid, FromNick),
|
jlib:jid_replace_resource(StateData#state.jid, FromNick),
|
||||||
ToJID, Packet)
|
ToJID, Packet)
|
||||||
end;
|
end;
|
||||||
|
{_, false} ->
|
||||||
|
case jlib:iq_query_info(Packet) of
|
||||||
|
reply ->
|
||||||
|
ok;
|
||||||
|
_ ->
|
||||||
|
ErrText = "Only occupants are allowed to send queries to the conference",
|
||||||
|
Err = jlib:make_error_reply(
|
||||||
|
Packet, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)),
|
||||||
|
ejabberd_router:route(
|
||||||
|
jlib:jid_replace_resource(StateData#state.jid, ToNick),
|
||||||
|
From, Err)
|
||||||
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
case jlib:iq_query_info(Packet) of
|
case jlib:iq_query_info(Packet) of
|
||||||
reply ->
|
reply ->
|
||||||
ok;
|
ok;
|
||||||
_ ->
|
_ ->
|
||||||
|
ErrText = "Queries to the conference members are not allowed in this room",
|
||||||
Err = jlib:make_error_reply(
|
Err = jlib:make_error_reply(
|
||||||
Packet, ?ERR_NOT_ALLOWED),
|
Packet, ?ERRT_NOT_ALLOWED(Lang, ErrText)),
|
||||||
ejabberd_router:route(
|
ejabberd_router:route(
|
||||||
jlib:jid_replace_resource(StateData#state.jid, ToNick),
|
jlib:jid_replace_resource(StateData#state.jid, ToNick),
|
||||||
From, Err)
|
From, Err)
|
||||||
|
@ -798,10 +856,20 @@ is_nick_change(JID, Nick, StateData) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
add_new_user(From, Nick, {xmlelement, _, Attrs, Els} = Packet, StateData) ->
|
add_new_user(From, Nick, {xmlelement, _, Attrs, Els} = Packet, StateData) ->
|
||||||
case is_nick_exists(Nick, StateData) or
|
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||||
not mod_muc:can_use_nick(From, Nick) of
|
case {is_nick_exists(Nick, StateData),
|
||||||
true ->
|
mod_muc:can_use_nick(From, Nick)} of
|
||||||
Err = jlib:make_error_reply(Packet, ?ERR_CONFLICT),
|
{true, _} ->
|
||||||
|
ErrText = "Nickname is already in use by another occupant",
|
||||||
|
Err = jlib:make_error_reply(Packet, ?ERRT_CONFLICT(Lang, ErrText)),
|
||||||
|
ejabberd_router:route(
|
||||||
|
% TODO: s/Nick/""/
|
||||||
|
jlib:jid_replace_resource(StateData#state.jid, Nick),
|
||||||
|
From, Err),
|
||||||
|
StateData;
|
||||||
|
{_, false} ->
|
||||||
|
ErrText = "Nickname is registered by another person",
|
||||||
|
Err = jlib:make_error_reply(Packet, ?ERRT_CONFLICT(Lang, ErrText)),
|
||||||
ejabberd_router:route(
|
ejabberd_router:route(
|
||||||
% TODO: s/Nick/""/
|
% TODO: s/Nick/""/
|
||||||
jlib:jid_replace_resource(StateData#state.jid, Nick),
|
jlib:jid_replace_resource(StateData#state.jid, Nick),
|
||||||
|
@ -815,8 +883,12 @@ add_new_user(From, Nick, {xmlelement, _, Attrs, Els} = Packet, StateData) ->
|
||||||
Err = jlib:make_error_reply(
|
Err = jlib:make_error_reply(
|
||||||
Packet,
|
Packet,
|
||||||
case Affiliation of
|
case Affiliation of
|
||||||
outcast -> ?ERR_FORBIDDEN;
|
outcast ->
|
||||||
_ -> ?ERR_REGISTRATION_REQUIRED
|
ErrText = "You have been banned from this room",
|
||||||
|
?ERRT_FORBIDDEN(Lang, ErrText);
|
||||||
|
_ ->
|
||||||
|
ErrText = "Membership required to enter this room",
|
||||||
|
?ERRT_REGISTRATION_REQUIRED(Lang, ErrText)
|
||||||
end),
|
end),
|
||||||
ejabberd_router:route( % TODO: s/Nick/""/
|
ejabberd_router:route( % TODO: s/Nick/""/
|
||||||
jlib:jid_replace_resource(StateData#state.jid, Nick),
|
jlib:jid_replace_resource(StateData#state.jid, Nick),
|
||||||
|
@ -836,13 +908,22 @@ add_new_user(From, Nick, {xmlelement, _, Attrs, Els} = Packet, StateData) ->
|
||||||
true ->
|
true ->
|
||||||
ok;
|
ok;
|
||||||
_ ->
|
_ ->
|
||||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
|
||||||
send_subject(From, Lang, StateData)
|
send_subject(From, Lang, StateData)
|
||||||
end,
|
end,
|
||||||
NewState;
|
NewState;
|
||||||
_ ->
|
nopass ->
|
||||||
|
ErrText = "Password required to enter this room",
|
||||||
Err = jlib:make_error_reply(
|
Err = jlib:make_error_reply(
|
||||||
Packet, ?ERR_NOT_AUTHORIZED),
|
Packet, ?ERRT_NOT_AUTHORIZED(Lang, ErrText)),
|
||||||
|
ejabberd_router:route( % TODO: s/Nick/""/
|
||||||
|
jlib:jid_replace_resource(
|
||||||
|
StateData#state.jid, Nick),
|
||||||
|
From, Err),
|
||||||
|
StateData;
|
||||||
|
_ ->
|
||||||
|
ErrText = "Incorrect password",
|
||||||
|
Err = jlib:make_error_reply(
|
||||||
|
Packet, ?ERRT_NOT_AUTHORIZED(Lang, ErrText)),
|
||||||
ejabberd_router:route( % TODO: s/Nick/""/
|
ejabberd_router:route( % TODO: s/Nick/""/
|
||||||
jlib:jid_replace_resource(
|
jlib:jid_replace_resource(
|
||||||
StateData#state.jid, Nick),
|
StateData#state.jid, Nick),
|
||||||
|
@ -860,20 +941,30 @@ check_password(_Affiliation, Els, StateData) ->
|
||||||
true;
|
true;
|
||||||
true ->
|
true ->
|
||||||
Pass = extract_password(Els),
|
Pass = extract_password(Els),
|
||||||
case (StateData#state.config)#config.password of
|
case Pass of
|
||||||
Pass ->
|
false ->
|
||||||
true;
|
nopass;
|
||||||
_ ->
|
_ ->
|
||||||
false
|
case (StateData#state.config)#config.password of
|
||||||
|
Pass ->
|
||||||
|
true;
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
extract_password([]) ->
|
extract_password([]) ->
|
||||||
"";
|
false;
|
||||||
extract_password([{xmlelement, Name, Attrs, SubEls} = El | Els]) ->
|
extract_password([{xmlelement, _Name, Attrs, _SubEls} = El | Els]) ->
|
||||||
case xml:get_attr_s("xmlns", Attrs) of
|
case xml:get_attr_s("xmlns", Attrs) of
|
||||||
?NS_MUC ->
|
?NS_MUC ->
|
||||||
xml:get_path_s(El, [{elem, "password"}, cdata]);
|
case xml:get_subtag(El, "password") of
|
||||||
|
false ->
|
||||||
|
false;
|
||||||
|
SubEl ->
|
||||||
|
xml:get_tag_cdata(SubEl)
|
||||||
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
extract_password(Els)
|
extract_password(Els)
|
||||||
end;
|
end;
|
||||||
|
@ -913,6 +1004,7 @@ count_stanza_shift(Nick, Els, StateData) ->
|
||||||
_ ->
|
_ ->
|
||||||
count_maxchars_shift(Nick, MaxChars, HL)
|
count_maxchars_shift(Nick, MaxChars, HL)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
lists:max([Shift0, Shift1, Shift2, Shift3]).
|
lists:max([Shift0, Shift1, Shift2, Shift3]).
|
||||||
|
|
||||||
count_seconds_shift(Seconds, HistoryList) ->
|
count_seconds_shift(Seconds, HistoryList) ->
|
||||||
|
@ -968,7 +1060,12 @@ extract_history([{xmlelement, Name, Attrs, SubEls} = El | Els], Type) ->
|
||||||
[{elem, "history"}, {attr, Type}]),
|
[{elem, "history"}, {attr, Type}]),
|
||||||
case Type of
|
case Type of
|
||||||
"since" ->
|
"since" ->
|
||||||
parse_datetime(AttrVal);
|
case catch parse_datetime(AttrVal) of
|
||||||
|
{'EXIT', Err} ->
|
||||||
|
false;
|
||||||
|
Res ->
|
||||||
|
Res
|
||||||
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
case catch list_to_integer(AttrVal) of
|
case catch list_to_integer(AttrVal) of
|
||||||
{'EXIT', _} ->
|
{'EXIT', _} ->
|
||||||
|
@ -991,50 +1088,20 @@ extract_history([_ | Els], Type) ->
|
||||||
% JEP-0082
|
% JEP-0082
|
||||||
% yyyy-mm-ddThh:mm:ss[.sss]{Z|{+|-}hh:mm} -> {{yyyy, mm, dd}, {hh, mm, ss}} (UTC)
|
% yyyy-mm-ddThh:mm:ss[.sss]{Z|{+|-}hh:mm} -> {{yyyy, mm, dd}, {hh, mm, ss}} (UTC)
|
||||||
parse_datetime(TimeStr) ->
|
parse_datetime(TimeStr) ->
|
||||||
DateTime = string:tokens(TimeStr, "T"),
|
[Date, Time] = string:tokens(TimeStr, "T"),
|
||||||
case DateTime of
|
D = parse_date(Date),
|
||||||
[Date, Time] ->
|
{T, TZH, TZM} = parse_time(Time),
|
||||||
case parse_date(Date) of
|
S = calendar:datetime_to_gregorian_seconds({D, T}),
|
||||||
false ->
|
calendar:gregorian_seconds_to_datetime(S - TZH * 60 * 60 - TZM * 60).
|
||||||
false;
|
|
||||||
D ->
|
|
||||||
case parse_time(Time) of
|
|
||||||
false ->
|
|
||||||
false;
|
|
||||||
{T, TZH, TZM} ->
|
|
||||||
S = calendar:datetime_to_gregorian_seconds(
|
|
||||||
{D, T}),
|
|
||||||
calendar:gregorian_seconds_to_datetime(
|
|
||||||
S - TZH * 60 * 60 - TZM * 60 * 30)
|
|
||||||
end
|
|
||||||
end;
|
|
||||||
_ ->
|
|
||||||
false
|
|
||||||
end.
|
|
||||||
|
|
||||||
% yyyy-mm-dd
|
% yyyy-mm-dd
|
||||||
parse_date(Date) ->
|
parse_date(Date) ->
|
||||||
YearMonthDay = string:tokens(Date, "-"),
|
YearMonthDay = string:tokens(Date, "-"),
|
||||||
case length(YearMonthDay) of
|
[Y, M, D] = lists:map(
|
||||||
3 ->
|
fun(L)->
|
||||||
[Y, M, D] = lists:map(
|
list_to_integer(L)
|
||||||
fun(L)->
|
end, YearMonthDay),
|
||||||
case catch list_to_integer(L) of
|
{Y, M, D}.
|
||||||
{'EXIT', _} ->
|
|
||||||
false;
|
|
||||||
Int ->
|
|
||||||
Int
|
|
||||||
end
|
|
||||||
end, YearMonthDay),
|
|
||||||
case catch calendar:valid_date(Y, M, D) of
|
|
||||||
true ->
|
|
||||||
{Y, M, D};
|
|
||||||
_ ->
|
|
||||||
false
|
|
||||||
end;
|
|
||||||
_ ->
|
|
||||||
false
|
|
||||||
end.
|
|
||||||
|
|
||||||
% hh:mm:ss[.sss]TZD
|
% hh:mm:ss[.sss]TZD
|
||||||
parse_time(Time) ->
|
parse_time(Time) ->
|
||||||
|
@ -1043,12 +1110,7 @@ parse_time(Time) ->
|
||||||
parse_time_with_timezone(Time);
|
parse_time_with_timezone(Time);
|
||||||
_ ->
|
_ ->
|
||||||
[T | _] = string:tokens(Time, "Z"),
|
[T | _] = string:tokens(Time, "Z"),
|
||||||
case parse_time1(T) of
|
{parse_time1(T), 0, 0}
|
||||||
false ->
|
|
||||||
false;
|
|
||||||
TT ->
|
|
||||||
{TT, 0, 0}
|
|
||||||
end
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
parse_time_with_timezone(Time) ->
|
parse_time_with_timezone(Time) ->
|
||||||
|
@ -1065,71 +1127,35 @@ parse_time_with_timezone(Time) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
parse_time_with_timezone(Time, Delim) ->
|
parse_time_with_timezone(Time, Delim) ->
|
||||||
TTZ = string:tokens(Time, Delim),
|
[T, TZ] = string:tokens(Time, Delim),
|
||||||
case TTZ of
|
{TZH, TZM} = parse_timezone(TZ),
|
||||||
[T, TZ] ->
|
TT = parse_time1(T),
|
||||||
case parse_timezone(TZ) of
|
case Delim of
|
||||||
false ->
|
"-" ->
|
||||||
false;
|
{TT, -TZH, -TZM};
|
||||||
{TZH, TZM} ->
|
"+" ->
|
||||||
case parse_time1(T) of
|
{TT, TZH, TZM}
|
||||||
false ->
|
|
||||||
false;
|
|
||||||
TT ->
|
|
||||||
case Delim of
|
|
||||||
"-" ->
|
|
||||||
{TT, -TZH, -TZM};
|
|
||||||
"+" ->
|
|
||||||
{TT, TZH, TZM};
|
|
||||||
_ ->
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end;
|
|
||||||
_ ->
|
|
||||||
false
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
parse_timezone(TZ) ->
|
parse_timezone(TZ) ->
|
||||||
case string:tokens(TZ, ":") of
|
[H, M] = string:tokens(TZ, ":"),
|
||||||
[H, M] ->
|
{[H1, M1], true} = check_list([{H, 12}, {M, 60}]),
|
||||||
case check_list([{H, 12}, {M, 60}]) of
|
{H1, M1}.
|
||||||
{[H, M], true} ->
|
|
||||||
{H, M};
|
|
||||||
_ ->
|
|
||||||
false
|
|
||||||
end;
|
|
||||||
_ ->
|
|
||||||
false
|
|
||||||
end.
|
|
||||||
|
|
||||||
parse_time1(Time) ->
|
parse_time1(Time) ->
|
||||||
case string:tokens(Time, ".") of
|
[HMS | _] = string:tokens(Time, "."),
|
||||||
[HMS | _] ->
|
[H, M, S] = string:tokens(HMS, ":"),
|
||||||
case string:tokens(HMS, ":") of
|
{[H1, M1, S1], true} = check_list([{H, 24}, {M, 60}, {S, 60}]),
|
||||||
[H, M, S] ->
|
{H1, M1, S1}.
|
||||||
case check_list([{H, 24}, {M, 60}, {S, 60}]) of
|
|
||||||
{[H1, M1, S1], true} ->
|
|
||||||
{H1, M1, S1};
|
|
||||||
_ ->
|
|
||||||
false
|
|
||||||
end;
|
|
||||||
_ ->
|
|
||||||
false
|
|
||||||
end;
|
|
||||||
_ ->
|
|
||||||
false
|
|
||||||
end.
|
|
||||||
|
|
||||||
check_list(List) ->
|
check_list(List) ->
|
||||||
lists:mapfoldl(
|
lists:mapfoldl(
|
||||||
fun({L, N}, B)->
|
fun({L, N}, B)->
|
||||||
case catch list_to_integer(L) of
|
V = list_to_integer(L),
|
||||||
{'EXIT', _} ->
|
if
|
||||||
{false, false};
|
(V >= 0) and (V =< N) ->
|
||||||
Int when (Int >= 0) and (Int =< N) ->
|
{V, B};
|
||||||
{Int, B};
|
true ->
|
||||||
_ ->
|
|
||||||
{false, false}
|
{false, false}
|
||||||
end
|
end
|
||||||
end, true, List).
|
end, true, List).
|
||||||
|
@ -1405,7 +1431,7 @@ can_change_subject(Role, StateData) ->
|
||||||
|
|
||||||
process_iq_admin(From, set, Lang, SubEl, StateData) ->
|
process_iq_admin(From, set, Lang, SubEl, StateData) ->
|
||||||
{xmlelement, _, _, Items} = SubEl,
|
{xmlelement, _, _, Items} = SubEl,
|
||||||
process_admin_items_set(From, Items, StateData);
|
process_admin_items_set(From, Items, Lang, StateData);
|
||||||
|
|
||||||
process_iq_admin(From, get, Lang, SubEl, StateData) ->
|
process_iq_admin(From, get, Lang, SubEl, StateData) ->
|
||||||
case xml:get_subtag(SubEl, "item") of
|
case xml:get_subtag(SubEl, "item") of
|
||||||
|
@ -1431,7 +1457,8 @@ process_iq_admin(From, get, Lang, SubEl, StateData) ->
|
||||||
SAffiliation, StateData),
|
SAffiliation, StateData),
|
||||||
{result, Items, StateData};
|
{result, Items, StateData};
|
||||||
true ->
|
true ->
|
||||||
{error, ?ERR_NOT_ALLOWED}
|
ErrText = "Administrator privileges required",
|
||||||
|
{error, ?ERRT_FORBIDDEN(Lang, ErrText)}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
|
@ -1445,7 +1472,8 @@ process_iq_admin(From, get, Lang, SubEl, StateData) ->
|
||||||
Items = items_with_role(SRole, StateData),
|
Items = items_with_role(SRole, StateData),
|
||||||
{result, Items, StateData};
|
{result, Items, StateData};
|
||||||
true ->
|
true ->
|
||||||
{error, ?ERR_NOT_ALLOWED}
|
ErrText = "Moderator privileges required",
|
||||||
|
{error, ?ERRT_FORBIDDEN(Lang, ErrText)}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1492,10 +1520,10 @@ search_affiliation(Affiliation, StateData) ->
|
||||||
end, ?DICT:to_list(StateData#state.affiliations)).
|
end, ?DICT:to_list(StateData#state.affiliations)).
|
||||||
|
|
||||||
|
|
||||||
process_admin_items_set(UJID, Items, StateData) ->
|
process_admin_items_set(UJID, Items, Lang, StateData) ->
|
||||||
UAffiliation = get_affiliation(UJID, StateData),
|
UAffiliation = get_affiliation(UJID, StateData),
|
||||||
URole = get_role(UJID, StateData),
|
URole = get_role(UJID, StateData),
|
||||||
case find_changed_items(UJID, UAffiliation, URole, Items, StateData, []) of
|
case find_changed_items(UJID, UAffiliation, URole, Items, Lang, StateData, []) of
|
||||||
{result, Res} ->
|
{result, Res} ->
|
||||||
NSD =
|
NSD =
|
||||||
lists:foldl(
|
lists:foldl(
|
||||||
|
@ -1555,19 +1583,23 @@ process_admin_items_set(UJID, Items, StateData) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
find_changed_items(UJID, UAffiliation, URole, [], StateData, Res) ->
|
find_changed_items(UJID, UAffiliation, URole, [], Lang, StateData, Res) ->
|
||||||
{result, Res};
|
{result, Res};
|
||||||
find_changed_items(UJID, UAffiliation, URole, [{xmlcdata, _} | Items],
|
find_changed_items(UJID, UAffiliation, URole, [{xmlcdata, _} | Items],
|
||||||
StateData, Res) ->
|
Lang, StateData, Res) ->
|
||||||
find_changed_items(UJID, UAffiliation, URole, Items, StateData, Res);
|
find_changed_items(UJID, UAffiliation, URole, Items, Lang, StateData, Res);
|
||||||
find_changed_items(UJID, UAffiliation, URole,
|
find_changed_items(UJID, UAffiliation, URole,
|
||||||
[{xmlelement, "item", Attrs, Els} = Item | Items],
|
[{xmlelement, "item", Attrs, Els} = Item | Items],
|
||||||
StateData, Res) ->
|
Lang, StateData, Res) ->
|
||||||
TJID = case xml:get_attr("jid", Attrs) of
|
TJID = case xml:get_attr("jid", Attrs) of
|
||||||
{value, S} ->
|
{value, S} ->
|
||||||
case jlib:string_to_jid(S) of
|
case jlib:string_to_jid(S) of
|
||||||
error ->
|
error ->
|
||||||
{error, ?ERR_BAD_REQUEST};
|
ErrText = io_lib:format(
|
||||||
|
translate:translate(
|
||||||
|
Lang,
|
||||||
|
"JID ~s is invalid"), [S]),
|
||||||
|
{error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)};
|
||||||
J ->
|
J ->
|
||||||
{value, J}
|
{value, J}
|
||||||
end;
|
end;
|
||||||
|
@ -1576,7 +1608,13 @@ find_changed_items(UJID, UAffiliation, URole,
|
||||||
{value, N} ->
|
{value, N} ->
|
||||||
case find_jid_by_nick(N, StateData) of
|
case find_jid_by_nick(N, StateData) of
|
||||||
false ->
|
false ->
|
||||||
{error, ?ERR_NOT_ALLOWED};
|
ErrText =
|
||||||
|
io_lib:format(
|
||||||
|
translate:translate(
|
||||||
|
Lang,
|
||||||
|
"Nickname ~s does not exist in the room"),
|
||||||
|
[N]),
|
||||||
|
{error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)};
|
||||||
J ->
|
J ->
|
||||||
{value, J}
|
{value, J}
|
||||||
end;
|
end;
|
||||||
|
@ -1596,7 +1634,13 @@ find_changed_items(UJID, UAffiliation, URole,
|
||||||
{value, StrAffiliation} ->
|
{value, StrAffiliation} ->
|
||||||
case catch list_to_affiliation(StrAffiliation) of
|
case catch list_to_affiliation(StrAffiliation) of
|
||||||
{'EXIT', _} ->
|
{'EXIT', _} ->
|
||||||
{error, ?ERR_BAD_REQUEST};
|
ErrText1 =
|
||||||
|
io_lib:format(
|
||||||
|
translate:translate(
|
||||||
|
Lang,
|
||||||
|
"Invalid affiliation: ~s"),
|
||||||
|
[StrAffiliation]),
|
||||||
|
{error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText1)};
|
||||||
SAffiliation ->
|
SAffiliation ->
|
||||||
case can_change_ra(
|
case can_change_ra(
|
||||||
UAffiliation, URole,
|
UAffiliation, URole,
|
||||||
|
@ -1606,13 +1650,13 @@ find_changed_items(UJID, UAffiliation, URole,
|
||||||
find_changed_items(
|
find_changed_items(
|
||||||
UJID,
|
UJID,
|
||||||
UAffiliation, URole,
|
UAffiliation, URole,
|
||||||
Items, StateData,
|
Items, Lang, StateData,
|
||||||
Res);
|
Res);
|
||||||
true ->
|
true ->
|
||||||
find_changed_items(
|
find_changed_items(
|
||||||
UJID,
|
UJID,
|
||||||
UAffiliation, URole,
|
UAffiliation, URole,
|
||||||
Items, StateData,
|
Items, Lang, StateData,
|
||||||
[{jlib:jid_remove_resource(JID),
|
[{jlib:jid_remove_resource(JID),
|
||||||
affiliation,
|
affiliation,
|
||||||
SAffiliation,
|
SAffiliation,
|
||||||
|
@ -1627,7 +1671,13 @@ find_changed_items(UJID, UAffiliation, URole,
|
||||||
{value, StrRole} ->
|
{value, StrRole} ->
|
||||||
case catch list_to_role(StrRole) of
|
case catch list_to_role(StrRole) of
|
||||||
{'EXIT', _} ->
|
{'EXIT', _} ->
|
||||||
{error, ?ERR_BAD_REQUEST};
|
ErrText1 =
|
||||||
|
io_lib:format(
|
||||||
|
translate:translate(
|
||||||
|
Lang,
|
||||||
|
"Invalid role: ~s"),
|
||||||
|
[StrRole]),
|
||||||
|
{error, ?ERRT_BAD_REQUEST(Lang, ErrText1)};
|
||||||
SRole ->
|
SRole ->
|
||||||
case can_change_ra(
|
case can_change_ra(
|
||||||
UAffiliation, URole,
|
UAffiliation, URole,
|
||||||
|
@ -1637,13 +1687,13 @@ find_changed_items(UJID, UAffiliation, URole,
|
||||||
find_changed_items(
|
find_changed_items(
|
||||||
UJID,
|
UJID,
|
||||||
UAffiliation, URole,
|
UAffiliation, URole,
|
||||||
Items, StateData,
|
Items, Lang, StateData,
|
||||||
Res);
|
Res);
|
||||||
true ->
|
true ->
|
||||||
find_changed_items(
|
find_changed_items(
|
||||||
UJID,
|
UJID,
|
||||||
UAffiliation, URole,
|
UAffiliation, URole,
|
||||||
Items, StateData,
|
Items, Lang, StateData,
|
||||||
[{JID, role, SRole,
|
[{JID, role, SRole,
|
||||||
xml:get_path_s(
|
xml:get_path_s(
|
||||||
Item, [{elem, "reason"},
|
Item, [{elem, "reason"},
|
||||||
|
@ -1656,7 +1706,7 @@ find_changed_items(UJID, UAffiliation, URole,
|
||||||
Err ->
|
Err ->
|
||||||
Err
|
Err
|
||||||
end;
|
end;
|
||||||
find_changed_items(UJID, UAffiliation, URole, Items, StateData, Res) ->
|
find_changed_items(UJID, UAffiliation, URole, Items, Lang, StateData, Res) ->
|
||||||
{error, ?ERR_BAD_REQUEST}.
|
{error, ?ERR_BAD_REQUEST}.
|
||||||
|
|
||||||
|
|
||||||
|
@ -1851,10 +1901,11 @@ process_iq_owner(From, set, Lang, SubEl, StateData) ->
|
||||||
[{xmlelement, "destroy", Attrs1, Els1}] ->
|
[{xmlelement, "destroy", Attrs1, Els1}] ->
|
||||||
destroy_room(Els1, StateData);
|
destroy_room(Els1, StateData);
|
||||||
Items ->
|
Items ->
|
||||||
process_admin_items_set(From, Items, StateData)
|
process_admin_items_set(From, Items, Lang, StateData)
|
||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
{error, ?ERR_FORBIDDEN}
|
ErrText = "Owner privileges required",
|
||||||
|
{error, ?ERRT_FORBIDDEN(Lang, ErrText)}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
process_iq_owner(From, get, Lang, SubEl, StateData) ->
|
process_iq_owner(From, get, Lang, SubEl, StateData) ->
|
||||||
|
@ -1872,7 +1923,13 @@ process_iq_owner(From, get, Lang, SubEl, StateData) ->
|
||||||
{value, StrAffiliation} ->
|
{value, StrAffiliation} ->
|
||||||
case catch list_to_affiliation(StrAffiliation) of
|
case catch list_to_affiliation(StrAffiliation) of
|
||||||
{'EXIT', _} ->
|
{'EXIT', _} ->
|
||||||
{error, ?ERR_BAD_REQUEST};
|
ErrText =
|
||||||
|
io_lib:format(
|
||||||
|
translate:translate(
|
||||||
|
Lang,
|
||||||
|
"Invalid affiliation: ~s"),
|
||||||
|
[StrAffiliation]),
|
||||||
|
{error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)};
|
||||||
SAffiliation ->
|
SAffiliation ->
|
||||||
Items = items_with_affiliation(
|
Items = items_with_affiliation(
|
||||||
SAffiliation, StateData),
|
SAffiliation, StateData),
|
||||||
|
@ -1883,7 +1940,8 @@ process_iq_owner(From, get, Lang, SubEl, StateData) ->
|
||||||
{error, ?ERR_FEATURE_NOT_IMPLEMENTED}
|
{error, ?ERR_FEATURE_NOT_IMPLEMENTED}
|
||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
{error, ?ERR_NOT_ALLOWED}
|
ErrText = "Owner privileges required",
|
||||||
|
{error, ?ERRT_FORBIDDEN(Lang, ErrText)}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
@ -2191,7 +2249,7 @@ process_iq_disco_items(From, get, Lang, StateData) ->
|
||||||
?DICT:to_list(StateData#state.users)),
|
?DICT:to_list(StateData#state.users)),
|
||||||
{result, UList, StateData};
|
{result, UList, StateData};
|
||||||
_ ->
|
_ ->
|
||||||
{error, ?ERR_NOT_ALLOWED}
|
{error, ?ERR_FORBIDDEN}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_title(StateData) ->
|
get_title(StateData) ->
|
||||||
|
|
|
@ -162,7 +162,7 @@ do_route(Host, From, To, Packet) ->
|
||||||
#iq{type = get, xmlns = ?NS_VCARD = XMLNS,
|
#iq{type = get, xmlns = ?NS_VCARD = XMLNS,
|
||||||
lang = Lang, sub_el = SubEl} = IQ ->
|
lang = Lang, sub_el = SubEl} = IQ ->
|
||||||
Res = IQ#iq{type = result,
|
Res = IQ#iq{type = result,
|
||||||
sub_el = [{xmlelement, "query",
|
sub_el = [{xmlelement, "vCard",
|
||||||
[{"xmlns", XMLNS}],
|
[{"xmlns", XMLNS}],
|
||||||
iq_get_vcard(Lang)}]},
|
iq_get_vcard(Lang)}]},
|
||||||
ejabberd_router:route(To,
|
ejabberd_router:route(To,
|
||||||
|
|
|
@ -336,7 +336,7 @@ do_route(From, To, Packet) ->
|
||||||
ResIQ =
|
ResIQ =
|
||||||
IQ#iq{type = result,
|
IQ#iq{type = result,
|
||||||
sub_el = [{xmlelement,
|
sub_el = [{xmlelement,
|
||||||
"query",
|
"vCard",
|
||||||
[{"xmlns", ?NS_VCARD}],
|
[{"xmlns", ?NS_VCARD}],
|
||||||
iq_get_vcard(Lang)}]},
|
iq_get_vcard(Lang)}]},
|
||||||
ejabberd_router:route(To,
|
ejabberd_router:route(To,
|
||||||
|
@ -356,8 +356,10 @@ iq_get_vcard(Lang) ->
|
||||||
[{xmlcdata,
|
[{xmlcdata,
|
||||||
"http://ejabberd.jabberstudio.org/"}]},
|
"http://ejabberd.jabberstudio.org/"}]},
|
||||||
{xmlelement, "DESC", [],
|
{xmlelement, "DESC", [],
|
||||||
[{xmlcdata, translate:translate(Lang, "ejabberd vCard module\n"
|
[{xmlcdata, translate:translate(
|
||||||
"Copyright (c) 2003-2004 Alexey Shchepin")}]}].
|
Lang,
|
||||||
|
"ejabberd vCard module\n"
|
||||||
|
"Copyright (c) 2003-2004 Alexey Shchepin")}]}].
|
||||||
|
|
||||||
find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
|
find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
|
||||||
find_xdata_el1(SubEls).
|
find_xdata_el1(SubEls).
|
||||||
|
|
|
@ -102,6 +102,12 @@
|
||||||
{"Enter nickname you want to register", "Введите псевдоним, который Вы хотели бы зарегистрировать"}.
|
{"Enter nickname you want to register", "Введите псевдоним, который Вы хотели бы зарегистрировать"}.
|
||||||
{"ejabberd MUC module\nCopyright (c) 2003-2004 Alexey Shchepin",
|
{"ejabberd MUC module\nCopyright (c) 2003-2004 Alexey Shchepin",
|
||||||
"ejabberd MUC модуль\nCopyright (c) 2003-2004 Алексей Щепин"}.
|
"ejabberd MUC модуль\nCopyright (c) 2003-2004 Алексей Щепин"}.
|
||||||
|
{"Only service administrators are allowed to send service messages",
|
||||||
|
"Только администратор службы может посылать служебные сообщения"}.
|
||||||
|
{"Conference room does not exist", "Конференция не существует"}.
|
||||||
|
{"You must fill in field \"nick\" in the form",
|
||||||
|
"Вы должны заполнить поле \"nick\" в форме"}.
|
||||||
|
{"Specified nickname is already registered", "Указанный псевдоним уже зарегистрирован"}.
|
||||||
|
|
||||||
% mod_muc/mod_muc_room.erl
|
% mod_muc/mod_muc_room.erl
|
||||||
{" has set the subject to: ", " установил(а) тему: "}.
|
{" has set the subject to: ", " установил(а) тему: "}.
|
||||||
|
@ -128,6 +134,39 @@
|
||||||
{"Password", "Пароль"}.
|
{"Password", "Пароль"}.
|
||||||
{"Make room anonymous?", "Сделать комнату анонимной?"}.
|
{"Make room anonymous?", "Сделать комнату анонимной?"}.
|
||||||
{"Enable logging?", "Включить журналирование?"}.
|
{"Enable logging?", "Включить журналирование?"}.
|
||||||
|
{"Only moderators and participants are allowed to change subject in this room",
|
||||||
|
"Только модераторы и участники могут изменять тему в этой комнате"}.
|
||||||
|
{"Only moderators are allowed to change subject in this room",
|
||||||
|
"Только модераторы могут изменять тему в этой комнате"}.
|
||||||
|
{"Visitors are not allowed to send messages to all occupants",
|
||||||
|
"Посетителям не разрешается посылать сообщения всем присутствующим"}.
|
||||||
|
{"Only occupants are allowed to send messages to the conference",
|
||||||
|
"Только присутствующим разрешается посылать сообщения в конференцию"}.
|
||||||
|
{"It is not allowed to send normal messages to the conference",
|
||||||
|
"Нельзя посылать обычные сообщения в конференцию"}.
|
||||||
|
{"It is not allowed to send private messages to the conference",
|
||||||
|
"Не разрешается посылать частные сообщения прямо в конференцию"}.
|
||||||
|
{"Improper message type", "Неправильный тип сообщения"}.
|
||||||
|
{"Nickname is already in use by another occupant", "Псевдоним занят кем-то из присутствующих"}.
|
||||||
|
{"Nickname is registered by another person", "Псевдоним зарегистирован кем-то другим"}.
|
||||||
|
{"It is not allowed to send private messages of type \"groupchat\"",
|
||||||
|
"Нельзя посылать частные сообщения типа \"groupchat\""}.
|
||||||
|
{"Recipient is not in the conference room", "Адресата нет в конференции"}.
|
||||||
|
{"Only occupants are allowed to send queries to the conference",
|
||||||
|
"Только присутствующим разрешается посылать запросы в конференцию"}.
|
||||||
|
{"Queries to the conference members are not allowed in this room",
|
||||||
|
"Запросы к пользователям в этой конференции запрещены"}.
|
||||||
|
{"You have been banned from this room", "Вам запрещено входить в эту конференцию"}.
|
||||||
|
{"Membership required to enter this room", "В эту конференцию могут входить только её члены"}.
|
||||||
|
{"Password required to enter this room", "Чтобы войти в эту конференцию, нужен пароль"}.
|
||||||
|
{"Incorrect password", "Неправильный пароль"}.
|
||||||
|
{"Administrator privileges required", "Требуются права администратора"}.
|
||||||
|
{"Moderator privileges required", "Требуются права модератора"}.
|
||||||
|
{"JID ~s is invalid", "JID ~s недопустимый"}.
|
||||||
|
{"Nickname ~s does not exist in the room", "Псевдоним ~s в комнате отсутствует"}.
|
||||||
|
{"Invalid affiliation: ~s", "Недопустимый ранг: ~s"}.
|
||||||
|
{"Invalid role: ~s", "Недопустимая роль: ~s"}.
|
||||||
|
{"Owner privileges required", "Требуются права владельца"}.
|
||||||
|
|
||||||
% mod_irc/mod_irc.erl
|
% mod_irc/mod_irc.erl
|
||||||
{"ejabberd IRC module\nCopyright (c) 2003-2004 Alexey Shchepin",
|
{"ejabberd IRC module\nCopyright (c) 2003-2004 Alexey Shchepin",
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
# $Id$
|
||||||
|
|
||||||
|
CC = @CC@
|
||||||
|
CFLAGS = @CFLAGS@
|
||||||
|
CPPFLAGS = @CPPFLAGS@
|
||||||
|
LDFLAGS = @LDFLAGS@
|
||||||
|
LIBS = @LIBS@
|
||||||
|
|
||||||
|
INCLUDES = @ERLANG_CFLAGS@
|
||||||
|
|
||||||
|
LIBDIRS = @ERLANG_LIBS@
|
||||||
|
|
||||||
|
SUBDIRS =
|
||||||
|
|
||||||
|
|
||||||
|
OUTDIR = ..
|
||||||
|
EFLAGS = -I .. -pz ..
|
||||||
|
OBJS = \
|
||||||
|
$(OUTDIR)/ejabberd_http.beam \
|
||||||
|
$(OUTDIR)/ejabberd_web.beam
|
||||||
|
|
||||||
|
all: $(OBJS)
|
||||||
|
|
||||||
|
$(OUTDIR)/%.beam: %.erl
|
||||||
|
erlc -W $(EFLAGS) -o $(OUTDIR) $<
|
||||||
|
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(OBJS)
|
||||||
|
|
||||||
|
TAGS:
|
||||||
|
etags *.erl
|
||||||
|
|
|
@ -0,0 +1,401 @@
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
%%% File : ejabberd_http.erl
|
||||||
|
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||||
|
%%% Purpose :
|
||||||
|
%%% Created : 27 Feb 2004 by Alexey Shchepin <alexey@sevcom.net>
|
||||||
|
%%% Id : $Id$
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(ejabberd_http).
|
||||||
|
-author('alexey@sevcom.net').
|
||||||
|
-vsn('$Revision$ ').
|
||||||
|
|
||||||
|
%% External exports
|
||||||
|
-export([start/2,
|
||||||
|
start_link/2,
|
||||||
|
receive_headers/1]).
|
||||||
|
|
||||||
|
-include("ejabberd.hrl").
|
||||||
|
-include("jlib.hrl").
|
||||||
|
|
||||||
|
-record(state, {sockmod,
|
||||||
|
socket,
|
||||||
|
request_method,
|
||||||
|
request_path,
|
||||||
|
request_auth,
|
||||||
|
request_content_length
|
||||||
|
}).
|
||||||
|
|
||||||
|
|
||||||
|
-define(XHTML_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({SockMod, Socket}, Opts) ->
|
||||||
|
?INFO_MSG("started: ~p", [{SockMod, Socket}]),
|
||||||
|
case SockMod of
|
||||||
|
gen_tcp ->
|
||||||
|
inet:setopts(Socket, [{packet, http}]);
|
||||||
|
ssl ->
|
||||||
|
ssl:setopts(Socket, [{packet, http}])
|
||||||
|
end,
|
||||||
|
{ok, proc_lib:spawn_link(ejabberd_http,
|
||||||
|
receive_headers,
|
||||||
|
[#state{sockmod = SockMod, socket = Socket}])}.
|
||||||
|
|
||||||
|
|
||||||
|
send_text(State, Text) ->
|
||||||
|
(State#state.sockmod):send(State#state.socket, Text).
|
||||||
|
|
||||||
|
|
||||||
|
receive_headers(State) ->
|
||||||
|
Data = (State#state.sockmod):recv(State#state.socket, 0, 300000),
|
||||||
|
?INFO_MSG("recv: ~p~n", [Data]),
|
||||||
|
case Data of
|
||||||
|
{ok, {http_request, Method, Path, _Version}} ->
|
||||||
|
receive_headers(State#state{request_method = Method,
|
||||||
|
request_path = Path});
|
||||||
|
{ok, {http_header, _, 'Authorization', _, Auth}} ->
|
||||||
|
receive_headers(State#state{request_auth = parse_auth(Auth)});
|
||||||
|
{ok, {http_header, _, 'Content-Length', _, SLen}} ->
|
||||||
|
case catch list_to_integer(SLen) of
|
||||||
|
Len when is_integer(Len) ->
|
||||||
|
receive_headers(State#state{request_content_length = Len});
|
||||||
|
_ ->
|
||||||
|
receive_headers(State)
|
||||||
|
end;
|
||||||
|
{ok, {http_header, _, _, _, _}} ->
|
||||||
|
receive_headers(State);
|
||||||
|
{ok, http_eoh} ->
|
||||||
|
Out = process_request(State),
|
||||||
|
send_text(State, Out),
|
||||||
|
ok;
|
||||||
|
{error, _Reason} ->
|
||||||
|
ok;
|
||||||
|
_ ->
|
||||||
|
ok
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
process_request(#state{request_method = 'GET',
|
||||||
|
request_path = {abs_path, Path},
|
||||||
|
request_auth = undefined}) ->
|
||||||
|
make_xhtml_output(
|
||||||
|
401,
|
||||||
|
[{"WWW-Authenticate", "basic realm=\"ejabberd\""}],
|
||||||
|
ejabberd_web:make_xhtml([{xmlelement, "h1", [],
|
||||||
|
[{xmlcdata, "401 Unauthorized"}]}]));
|
||||||
|
|
||||||
|
process_request(#state{request_method = 'GET',
|
||||||
|
request_path = {abs_path, Path},
|
||||||
|
request_auth = {User, Pass}}) ->
|
||||||
|
case ejabberd_auth:check_password(User, Pass) of
|
||||||
|
true ->
|
||||||
|
case (catch url_decode_q_split(Path)) of
|
||||||
|
{'EXIT', _} ->
|
||||||
|
process_request(false);
|
||||||
|
{NPath, Query} ->
|
||||||
|
LQuery = parse_urlencoded(Query),
|
||||||
|
?INFO_MSG("path: ~p, query: ~p~n", [NPath, LQuery]),
|
||||||
|
LPath = string:tokens(NPath, "/"),
|
||||||
|
case ejabberd_web:process_get(User, LPath, LQuery) of
|
||||||
|
El when element(1, El) == xmlelement ->
|
||||||
|
make_xhtml_output(200, [], El);
|
||||||
|
{Status, Headers, El} ->
|
||||||
|
make_xhtml_output(Status, Headers, El)
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
make_xhtml_output(
|
||||||
|
401,
|
||||||
|
[{"WWW-Authenticate", "basic realm=\"ejabberd\""}],
|
||||||
|
ejabberd_web:make_xhtml([{xmlelement, "h1", [],
|
||||||
|
[{xmlcdata, "401 Unauthorized"}]}]))
|
||||||
|
end;
|
||||||
|
|
||||||
|
process_request(#state{request_method = 'POST',
|
||||||
|
request_path = {abs_path, Path},
|
||||||
|
request_auth = {User, Pass},
|
||||||
|
request_content_length = Len,
|
||||||
|
sockmod = SockMod,
|
||||||
|
socket = Socket} = State) when is_integer(Len) ->
|
||||||
|
case ejabberd_auth:check_password(User, Pass) of
|
||||||
|
true ->
|
||||||
|
case SockMod of
|
||||||
|
gen_tcp ->
|
||||||
|
inet:setopts(Socket, [{packet, 0}]);
|
||||||
|
ssl ->
|
||||||
|
ssl:setopts(Socket, [{packet, 0}])
|
||||||
|
end,
|
||||||
|
Data = recv_data(State, Len),
|
||||||
|
?INFO_MSG("client data: ~p~n", [Data]),
|
||||||
|
case (catch url_decode_q_split(Path)) of
|
||||||
|
{'EXIT', _} ->
|
||||||
|
process_request(false);
|
||||||
|
{NPath, Query} ->
|
||||||
|
?INFO_MSG("path: ~p, query: ~p~n", [NPath, Query]),
|
||||||
|
LPath = string:tokens(NPath, "/"),
|
||||||
|
LQuery = parse_urlencoded(Data),
|
||||||
|
?INFO_MSG("client query: ~p~n", [LQuery]),
|
||||||
|
case ejabberd_web:process_get(User, LPath, LQuery) of
|
||||||
|
El when element(1, El) == xmlelement ->
|
||||||
|
make_xhtml_output(200, [], El);
|
||||||
|
{Status, Headers, El} ->
|
||||||
|
make_xhtml_output(Status, Headers, El)
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
make_xhtml_output(
|
||||||
|
401,
|
||||||
|
[{"WWW-Authenticate", "basic realm=\"ejabberd\""}],
|
||||||
|
ejabberd_web:make_xhtml([{xmlelement, "h1", [],
|
||||||
|
[{xmlcdata, "401 Unauthorized"}]}]))
|
||||||
|
end;
|
||||||
|
|
||||||
|
process_request(State) ->
|
||||||
|
make_xhtml_output(
|
||||||
|
400,
|
||||||
|
[],
|
||||||
|
ejabberd_web:make_xhtml([{xmlelement, "h1", [],
|
||||||
|
[{xmlcdata, "400 Bad Request"}]}])).
|
||||||
|
|
||||||
|
|
||||||
|
recv_data(State, Len) ->
|
||||||
|
recv_data(State, Len, []).
|
||||||
|
|
||||||
|
recv_data(State, 0, Acc) ->
|
||||||
|
binary_to_list(list_to_binary(Acc));
|
||||||
|
recv_data(State, Len, Acc) ->
|
||||||
|
case (State#state.sockmod):recv(State#state.socket, Len, 300000) of
|
||||||
|
{ok, Data} ->
|
||||||
|
recv_data(State, Len - size(Data), [Acc | Data]);
|
||||||
|
_ ->
|
||||||
|
""
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
make_xhtml_output(Status, Headers, XHTML) ->
|
||||||
|
Data = list_to_binary([?XHTML_DOCTYPE, xml:element_to_string(XHTML)]),
|
||||||
|
Headers1 = [{"Content-Type", "text/html; charset=utf-8"},
|
||||||
|
{"Content-Length", integer_to_list(size(Data))} | Headers],
|
||||||
|
H = lists:map(fun({Attr, Val}) ->
|
||||||
|
[Attr, ": ", Val, "\r\n"]
|
||||||
|
end, Headers1),
|
||||||
|
SL = ["HTTP/1.1 ", integer_to_list(Status), " ",
|
||||||
|
code_to_phrase(Status), "\r\n"],
|
||||||
|
[SL, H, "\r\n", Data].
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
% 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.
|
||||||
|
|
||||||
|
|
||||||
|
%% url decode the path and return {Path, QueryPart}
|
||||||
|
|
||||||
|
url_decode_q_split(Path) ->
|
||||||
|
url_decode_q_split(Path, []).
|
||||||
|
|
||||||
|
url_decode_q_split([$%, $C, $2, $%, Hi, Lo | Tail], Ack) ->
|
||||||
|
Hex = hex_to_integer([Hi, Lo]),
|
||||||
|
url_decode_q_split(Tail, [Hex|Ack]);
|
||||||
|
url_decode_q_split([$%, $C, $3, $%, Hi, Lo | Tail], Ack) when Hi > $9 ->
|
||||||
|
Hex = hex_to_integer([Hi+4, Lo]),
|
||||||
|
url_decode_q_split(Tail, [Hex|Ack]);
|
||||||
|
url_decode_q_split([$%, $C, $3, $%, Hi, Lo | Tail], Ack) when Hi < $A ->
|
||||||
|
Hex = hex_to_integer([Hi+4+7, Lo]),
|
||||||
|
url_decode_q_split(Tail, [Hex|Ack]);
|
||||||
|
url_decode_q_split([$%, Hi, Lo | Tail], Ack) ->
|
||||||
|
Hex = hex_to_integer([Hi, Lo]),
|
||||||
|
url_decode_q_split(Tail, [Hex|Ack]);
|
||||||
|
url_decode_q_split([$?|T], Ack) ->
|
||||||
|
%% Don't decode the query string here, that is parsed separately.
|
||||||
|
{path_norm_reverse(Ack), T};
|
||||||
|
url_decode_q_split([H|T], Ack) ->
|
||||||
|
url_decode_q_split(T, [H|Ack]);
|
||||||
|
url_decode_q_split([], Ack) ->
|
||||||
|
{path_norm_reverse(Ack), []}.
|
||||||
|
|
||||||
|
path_norm_reverse("/" ++ T) -> 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 ) -> start_dir(N , Path, T);
|
||||||
|
start_dir(N, Path, "./" ++ T ) -> start_dir(N , Path, T);
|
||||||
|
start_dir(N, Path, "../" ++ T ) -> 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 ] ) -> start_dir(0 , [ $/ | Path ], T);
|
||||||
|
rest_dir (N, Path, [ $/ | T ] ) -> start_dir(N - 1, Path , T);
|
||||||
|
rest_dir (0, Path, [ H | T ] ) -> rest_dir (0 , [ H | Path ], T);
|
||||||
|
rest_dir (N, Path, [ _H | T ] ) -> rest_dir (N , Path , T).
|
||||||
|
|
||||||
|
|
||||||
|
%% hex_to_integer
|
||||||
|
|
||||||
|
|
||||||
|
hex_to_integer(Hex) ->
|
||||||
|
case catch erlang: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(Orig = "Basic " ++ Auth64) ->
|
||||||
|
case decode_base64(Auth64) of
|
||||||
|
{error, _Err} ->
|
||||||
|
undefined;
|
||||||
|
Auth ->
|
||||||
|
case string:tokens(Auth, ":") of
|
||||||
|
[User, Pass] ->
|
||||||
|
{User, Pass};
|
||||||
|
_ ->
|
||||||
|
undefined
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
parse_auth(_) ->
|
||||||
|
undefined.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
decode_base64([]) ->
|
||||||
|
[];
|
||||||
|
decode_base64([Sextet1,Sextet2,$=,$=|Rest]) ->
|
||||||
|
Bits2x6=
|
||||||
|
(d(Sextet1) bsl 18) bor
|
||||||
|
(d(Sextet2) bsl 12),
|
||||||
|
Octet1=Bits2x6 bsr 16,
|
||||||
|
[Octet1|decode_base64(Rest)];
|
||||||
|
decode_base64([Sextet1,Sextet2,Sextet3,$=|Rest]) ->
|
||||||
|
Bits3x6=
|
||||||
|
(d(Sextet1) bsl 18) bor
|
||||||
|
(d(Sextet2) bsl 12) bor
|
||||||
|
(d(Sextet3) bsl 6),
|
||||||
|
Octet1=Bits3x6 bsr 16,
|
||||||
|
Octet2=(Bits3x6 bsr 8) band 16#ff,
|
||||||
|
[Octet1,Octet2|decode_base64(Rest)];
|
||||||
|
decode_base64([Sextet1,Sextet2,Sextet3,Sextet4|Rest]) ->
|
||||||
|
Bits4x6=
|
||||||
|
(d(Sextet1) bsl 18) bor
|
||||||
|
(d(Sextet2) bsl 12) bor
|
||||||
|
(d(Sextet3) bsl 6) bor
|
||||||
|
d(Sextet4),
|
||||||
|
Octet1=Bits4x6 bsr 16,
|
||||||
|
Octet2=(Bits4x6 bsr 8) band 16#ff,
|
||||||
|
Octet3=Bits4x6 band 16#ff,
|
||||||
|
[Octet1,Octet2,Octet3|decode_base64(Rest)];
|
||||||
|
decode_base64(_CatchAll) ->
|
||||||
|
{error, bad_base64}.
|
||||||
|
|
||||||
|
d(X) when X >= $A, X =<$Z ->
|
||||||
|
X-65;
|
||||||
|
d(X) when X >= $a, X =<$z ->
|
||||||
|
X-71;
|
||||||
|
d(X) when X >= $0, X =<$9 ->
|
||||||
|
X+4;
|
||||||
|
d($+) -> 62;
|
||||||
|
d($/) -> 63;
|
||||||
|
d(_) -> 63.
|
||||||
|
|
||||||
|
|
||||||
|
parse_urlencoded(S) ->
|
||||||
|
parse_urlencoded(S, nokey, [], key).
|
||||||
|
|
||||||
|
parse_urlencoded([$%, Hi, Lo | Tail], Last, Cur, State) ->
|
||||||
|
Hex = hex_to_integer([Hi, Lo]),
|
||||||
|
parse_urlencoded(Tail, Last, [Hex | Cur], State);
|
||||||
|
|
||||||
|
parse_urlencoded([$& | Tail], _Last, Cur, key) ->
|
||||||
|
[{lists:reverse(Cur), ""} |
|
||||||
|
parse_urlencoded(Tail, nokey, [], key)]; %% cont keymode
|
||||||
|
|
||||||
|
parse_urlencoded([$& | Tail], Last, Cur, value) ->
|
||||||
|
V = {Last, lists:reverse(Cur)},
|
||||||
|
[V | parse_urlencoded(Tail, nokey, [], key)];
|
||||||
|
|
||||||
|
parse_urlencoded([$+ | Tail], Last, Cur, State) ->
|
||||||
|
parse_urlencoded(Tail, Last, [$\s | Cur], State);
|
||||||
|
|
||||||
|
parse_urlencoded([$= | Tail], _Last, Cur, key) ->
|
||||||
|
parse_urlencoded(Tail, lists:reverse(Cur), [], value); %% change mode
|
||||||
|
|
||||||
|
parse_urlencoded([H | Tail], Last, Cur, State) ->
|
||||||
|
parse_urlencoded(Tail, Last, [H|Cur], State);
|
||||||
|
|
||||||
|
parse_urlencoded([], Last, Cur, _State) ->
|
||||||
|
[{Last, lists:reverse(Cur)}];
|
||||||
|
|
||||||
|
parse_urlencoded(undefined, _, _, _) ->
|
||||||
|
[].
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
%%% File : ejabberd_web.erl
|
||||||
|
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||||
|
%%% Purpose :
|
||||||
|
%%% Created : 28 Feb 2004 by Alexey Shchepin <alexey@sevcom.net>
|
||||||
|
%%% Id : $Id$
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(ejabberd_web).
|
||||||
|
-author('alexey@sevcom.net').
|
||||||
|
-vsn('$Revision$ ').
|
||||||
|
|
||||||
|
%% External exports
|
||||||
|
-export([make_xhtml/1,
|
||||||
|
process_get/3]).
|
||||||
|
|
||||||
|
-include("ejabberd.hrl").
|
||||||
|
-include("jlib.hrl").
|
||||||
|
|
||||||
|
|
||||||
|
make_xhtml(Els) ->
|
||||||
|
{xmlelement, "html", [{"xmlns", "http://www.w3.org/1999/xhtml"},
|
||||||
|
{"xml:lang", "en"},
|
||||||
|
{"lang", "en"}],
|
||||||
|
[{xmlelement, "head", [],
|
||||||
|
[{xmlelement, "meta", [{"http-equiv", "Content-Type"},
|
||||||
|
{"content", "text/html; charset=utf-8"}], []}]},
|
||||||
|
{xmlelement, "body", [], Els}
|
||||||
|
]}.
|
||||||
|
|
||||||
|
|
||||||
|
-define(X(Name), {xmlelement, Name, [], []}).
|
||||||
|
-define(XA(Name, Attrs), {xmlelement, Name, Attrs, []}).
|
||||||
|
-define(XE(Name, Els), {xmlelement, Name, [], Els}).
|
||||||
|
-define(XAE(Name, Attrs, Els), {xmlelement, Name, Attrs, 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")).
|
||||||
|
|
||||||
|
process_get(User, [], Query) ->
|
||||||
|
make_xhtml([?XC("h1", "ejabberd configuration"),
|
||||||
|
?XE("ul",
|
||||||
|
[?LI([?AC("acls/", "Access Control Lists")]),
|
||||||
|
?LI([?AC("access/", "Access Rules")]),
|
||||||
|
?LI([?AC("users/", "Users")]),
|
||||||
|
?LI([?AC("nodes/", "Nodes")])
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
|
||||||
|
process_get(User, ["acls"], Query) ->
|
||||||
|
case acl:match_rule(configure, jlib:make_jid(User, ?MYNAME, "")) of
|
||||||
|
deny ->
|
||||||
|
{401, [], make_xhtml([?XC("h1", "Not Allowed")])};
|
||||||
|
allow ->
|
||||||
|
Res = case lists:keysearch("acls", 1, Query) of
|
||||||
|
{value, {_, String}} ->
|
||||||
|
case erl_scan:string(String) of
|
||||||
|
{ok, Tokens, _} ->
|
||||||
|
case erl_parse:parse_term(Tokens) of
|
||||||
|
{ok, NewACLs} ->
|
||||||
|
case acl:add_list(NewACLs, true) of
|
||||||
|
ok ->
|
||||||
|
ok;
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
nothing
|
||||||
|
end,
|
||||||
|
ACLs = lists:flatten(io_lib:format("~p.", [ets:tab2list(acl)])),
|
||||||
|
make_xhtml([?XC("h1", "ejabberd ACLs configuration")] ++
|
||||||
|
case Res of
|
||||||
|
ok -> [?C("submited"), ?P];
|
||||||
|
error -> [?C("bad format"), ?P];
|
||||||
|
nothing -> []
|
||||||
|
end ++
|
||||||
|
[?XAE("form", [{"method", "post"}],
|
||||||
|
[?XAC("textarea", [{"name", "acls"},
|
||||||
|
{"rows", "16"},
|
||||||
|
{"cols", "80"}],
|
||||||
|
ACLs),
|
||||||
|
?BR,
|
||||||
|
?XA("input", [{"type", "submit"}])
|
||||||
|
])
|
||||||
|
])
|
||||||
|
end;
|
||||||
|
|
||||||
|
process_get(User, Path, Query) ->
|
||||||
|
{404, [], make_xhtml([?XC("h1", "Not found")])}.
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue