mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-02 15:27:09 +01:00
* 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>
|
||||
|
||||
* src/msgs/ru.msg: Updated (thanks to Sergei Golovan)
|
||||
|
@ -16,7 +16,8 @@
|
||||
-export([start/2,
|
||||
start_link/2,
|
||||
send_text/2,
|
||||
send_element/2]).
|
||||
send_element/2,
|
||||
get_presence/1]).
|
||||
|
||||
%% gen_fsm callbacks
|
||||
-export([init/1,
|
||||
@ -90,6 +91,10 @@ start(SockData, Opts) ->
|
||||
start_link(SockData, Opts) ->
|
||||
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
|
||||
%%%----------------------------------------------------------------------
|
||||
@ -661,6 +666,22 @@ handle_event(_Event, StateName, StateData) ->
|
||||
%% {stop, Reason, 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) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, StateName, StateData}.
|
||||
|
@ -87,6 +87,14 @@ init([]) ->
|
||||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_tmp_sup]},
|
||||
HTTPSupervisor =
|
||||
{ejabberd_http_sup,
|
||||
{ejabberd_tmp_sup, start_link,
|
||||
[ejabberd_http_sup, ejabberd_http]},
|
||||
permanent,
|
||||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_tmp_sup]},
|
||||
IQSupervisor =
|
||||
{ejabberd_iq_sup,
|
||||
{ejabberd_tmp_sup, start_link,
|
||||
@ -102,6 +110,7 @@ init([]) ->
|
||||
S2SInSupervisor,
|
||||
S2SOutSupervisor,
|
||||
ServiceSupervisor,
|
||||
HTTPSupervisor,
|
||||
IQSupervisor,
|
||||
Listener]}}.
|
||||
|
||||
|
50
src/jlib.hrl
50
src/jlib.hrl
@ -97,6 +97,56 @@
|
||||
%-define(ERR_,
|
||||
% ?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
|
||||
% Application-specific stanza errors
|
||||
-define(AUTH_STANZA_ERROR(Condition),
|
||||
|
@ -79,7 +79,7 @@ do_route(Host, From, To, Packet) ->
|
||||
lang = Lang} = IQ ->
|
||||
Res = IQ#iq{type = result,
|
||||
sub_el =
|
||||
[{xmlelement, "query",
|
||||
[{xmlelement, "vCard",
|
||||
[{"xmlns", XMLNS}],
|
||||
iq_get_vcard(Lang)}]},
|
||||
ejabberd_router:route(To,
|
||||
|
@ -113,8 +113,9 @@ do_route(Host, From, To, Packet) ->
|
||||
jlib:iq_to_xml(Res));
|
||||
#iq{type = set,
|
||||
xmlns = ?NS_REGISTER = XMLNS,
|
||||
lang = Lang,
|
||||
sub_el = SubEl} = IQ ->
|
||||
case process_iq_register_set(From, SubEl) of
|
||||
case process_iq_register_set(From, SubEl, Lang) of
|
||||
{result, IQRes} ->
|
||||
Res = IQ#iq{type = result,
|
||||
sub_el =
|
||||
@ -135,7 +136,7 @@ do_route(Host, From, To, Packet) ->
|
||||
sub_el = SubEl} = IQ ->
|
||||
Res = IQ#iq{type = result,
|
||||
sub_el =
|
||||
[{xmlelement, "query",
|
||||
[{xmlelement, "vCard",
|
||||
[{"xmlns", XMLNS}],
|
||||
iq_get_vcard(Lang)}]},
|
||||
ejabberd_router:route(To,
|
||||
@ -161,9 +162,12 @@ do_route(Host, From, To, Packet) ->
|
||||
[{elem, "body"}, cdata]),
|
||||
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(
|
||||
Packet,
|
||||
?ERR_NOT_ALLOWED),
|
||||
?ERRT_FORBIDDEN(Lang, ErrText)),
|
||||
ejabberd_router:route(
|
||||
To, From, Err)
|
||||
end
|
||||
@ -198,8 +202,10 @@ do_route(Host, From, To, Packet) ->
|
||||
mod_muc_room:route(Pid, From, Nick, Packet),
|
||||
ok;
|
||||
_ ->
|
||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||
ErrText = "Conference room does not exist",
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_SERVICE_UNAVAILABLE),
|
||||
Packet, ?ERRT_ITEM_NOT_FOUND(Lang, ErrText)),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end;
|
||||
[R] ->
|
||||
@ -332,12 +338,13 @@ iq_get_register_info(From, Host, Lang) ->
|
||||
Lang, "Enter nickname you want to register")}]},
|
||||
?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),
|
||||
LUS = {LUser, LServer},
|
||||
case lists:keysearch("nick", 1, XData) of
|
||||
false ->
|
||||
{error, ?ERR_BAD_REQUEST};
|
||||
ErrText = "You must fill in field \"nick\" in the form",
|
||||
{error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)};
|
||||
{value, {_, [Nick]}} ->
|
||||
F = fun() ->
|
||||
case Nick of
|
||||
@ -369,13 +376,14 @@ iq_set_register_info(From, XData) ->
|
||||
{atomic, ok} ->
|
||||
{result, []};
|
||||
{atomic, false} ->
|
||||
{error, ?ERR_CONFLICT};
|
||||
ErrText = "Specified nickname is already registered",
|
||||
{error, ?ERRT_CONFLICT(Lang, ErrText)};
|
||||
_ ->
|
||||
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
||||
end
|
||||
end.
|
||||
|
||||
process_iq_register_set(From, SubEl) ->
|
||||
process_iq_register_set(From, SubEl, Lang) ->
|
||||
{xmlelement, Name, Attrs, Els} = SubEl,
|
||||
case xml:remove_cdata(Els) of
|
||||
[{xmlelement, "x", Attrs1, Els1} = XEl] ->
|
||||
@ -389,7 +397,7 @@ process_iq_register_set(From, SubEl) ->
|
||||
invalid ->
|
||||
{error, ?ERR_BAD_REQUEST};
|
||||
_ ->
|
||||
iq_set_register_info(From, XData)
|
||||
iq_set_register_info(From, XData, Lang)
|
||||
end;
|
||||
_ ->
|
||||
{error, ?ERR_BAD_REQUEST}
|
||||
|
@ -119,6 +119,7 @@ init([Host, Room, Opts]) ->
|
||||
normal_state({route, From, "",
|
||||
{xmlelement, "message", Attrs, Els} = Packet},
|
||||
StateData) ->
|
||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||
case is_user_online(From, StateData) of
|
||||
true ->
|
||||
case xml:get_attr_s("type", Attrs) of
|
||||
@ -172,16 +173,26 @@ normal_state({route, From, "",
|
||||
NewStateData1),
|
||||
{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(
|
||||
Packet, ?ERR_FORBIDDEN),
|
||||
Packet, ?ERRT_FORBIDDEN(Lang, ErrText)),
|
||||
ejabberd_router:route(
|
||||
StateData#state.jid,
|
||||
From, Err),
|
||||
{next_state, normal_state, StateData}
|
||||
end;
|
||||
true ->
|
||||
ErrText = "Visitors are not allowed to send messages to all occupants",
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_FORBIDDEN),
|
||||
Packet, ?ERRT_FORBIDDEN(Lang, ErrText)),
|
||||
ejabberd_router:route(
|
||||
StateData#state.jid,
|
||||
From, Err),
|
||||
@ -202,11 +213,20 @@ normal_state({route, From, "",
|
||||
_ ->
|
||||
{next_state, normal_state, StateData}
|
||||
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") ->
|
||||
case check_invitation(From, Els, StateData) of
|
||||
error ->
|
||||
ErrText = "It is not allowed to send normal messages to the conference",
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_NOT_ALLOWED),
|
||||
Packet, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)),
|
||||
ejabberd_router:route(
|
||||
StateData#state.jid,
|
||||
From, Err),
|
||||
@ -231,8 +251,9 @@ normal_state({route, From, "",
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
ErrText = "Improper message type",
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_NOT_ALLOWED),
|
||||
Packet, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)),
|
||||
ejabberd_router:route(
|
||||
StateData#state.jid,
|
||||
From, Err),
|
||||
@ -243,8 +264,9 @@ normal_state({route, From, "",
|
||||
"error" ->
|
||||
ok;
|
||||
_ ->
|
||||
ErrText = "Only occupants are allowed to send messages to the conference",
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_NOT_ACCEPTABLE),
|
||||
Packet, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)),
|
||||
ejabberd_router:route(StateData#state.jid, From, Err)
|
||||
end,
|
||||
{next_state, normal_state, StateData}
|
||||
@ -305,6 +327,7 @@ normal_state({route, From, Nick,
|
||||
{xmlelement, "presence", Attrs, Els} = Packet},
|
||||
StateData) ->
|
||||
Type = xml:get_attr_s("type", Attrs),
|
||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||
StateData1 =
|
||||
case Type of
|
||||
"unavailable" ->
|
||||
@ -336,17 +359,32 @@ normal_state({route, From, Nick,
|
||||
true ->
|
||||
case is_nick_change(From, Nick, StateData) of
|
||||
true ->
|
||||
case is_nick_exists(Nick, StateData) of
|
||||
true ->
|
||||
case {is_nick_exists(Nick, StateData),
|
||||
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(
|
||||
Packet,
|
||||
?ERR_CONFLICT),
|
||||
?ERRT_CONFLICT(Lang, ErrText)),
|
||||
ejabberd_router:route(
|
||||
jlib:jid_replace_resource(
|
||||
StateData#state.jid,
|
||||
Nick), % TODO: s/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(
|
||||
% TODO: s/Nick/""/
|
||||
jlib:jid_replace_resource(
|
||||
StateData#state.jid,
|
||||
Nick),
|
||||
From, Err),
|
||||
StateData;
|
||||
_ ->
|
||||
change_nick(From, Nick, StateData)
|
||||
end;
|
||||
@ -376,6 +414,7 @@ normal_state({route, From, ToNick,
|
||||
{xmlelement, "message", Attrs, Els} = Packet},
|
||||
StateData) ->
|
||||
Type = xml:get_attr_s("type", Attrs),
|
||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||
case Type of
|
||||
"error" ->
|
||||
case is_user_online(From, StateData) of
|
||||
@ -398,8 +437,10 @@ normal_state({route, From, ToNick,
|
||||
true ->
|
||||
case Type of
|
||||
"groupchat" ->
|
||||
ErrText = "It is not allowed to send private "
|
||||
"messages of type \"groupchat\"",
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_BAD_REQUEST),
|
||||
Packet, ?ERRT_BAD_REQUEST(Lang, ErrText)),
|
||||
ejabberd_router:route(
|
||||
jlib:jid_replace_resource(
|
||||
StateData#state.jid,
|
||||
@ -408,8 +449,9 @@ normal_state({route, From, ToNick,
|
||||
_ ->
|
||||
case find_jid_by_nick(ToNick, StateData) of
|
||||
false ->
|
||||
ErrText = "Recipient is not in the conference room",
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_ITEM_NOT_FOUND),
|
||||
Packet, ?ERRT_ITEM_NOT_FOUND(Lang, ErrText)),
|
||||
ejabberd_router:route(
|
||||
jlib:jid_replace_resource(
|
||||
StateData#state.jid,
|
||||
@ -427,8 +469,9 @@ normal_state({route, From, ToNick,
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
ErrText = "Only occupants are allowed to send messages to the conference",
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_NOT_ALLOWED),
|
||||
Packet, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)),
|
||||
ejabberd_router:route(
|
||||
jlib:jid_replace_resource(
|
||||
StateData#state.jid,
|
||||
@ -441,17 +484,19 @@ normal_state({route, From, ToNick,
|
||||
normal_state({route, From, ToNick,
|
||||
{xmlelement, "iq", Attrs, Els} = Packet},
|
||||
StateData) ->
|
||||
case (StateData#state.config)#config.allow_query_users
|
||||
andalso is_user_online(From, StateData) of
|
||||
true ->
|
||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||
case {(StateData#state.config)#config.allow_query_users,
|
||||
is_user_online(From, StateData)} of
|
||||
{true, true} ->
|
||||
case find_jid_by_nick(ToNick, StateData) of
|
||||
false ->
|
||||
case jlib:iq_query_info(Packet) of
|
||||
reply ->
|
||||
ok;
|
||||
_ ->
|
||||
ErrText = "Recipient is not in the conference room",
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_ITEM_NOT_FOUND),
|
||||
Packet, ?ERRT_ITEM_NOT_FOUND(Lang, ErrText)),
|
||||
ejabberd_router:route(
|
||||
jlib:jid_replace_resource(
|
||||
StateData#state.jid, ToNick),
|
||||
@ -465,13 +510,26 @@ normal_state({route, From, ToNick,
|
||||
jlib:jid_replace_resource(StateData#state.jid, FromNick),
|
||||
ToJID, Packet)
|
||||
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
|
||||
reply ->
|
||||
ok;
|
||||
_ ->
|
||||
ErrText = "Queries to the conference members are not allowed in this room",
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_NOT_ALLOWED),
|
||||
Packet, ?ERRT_NOT_ALLOWED(Lang, ErrText)),
|
||||
ejabberd_router:route(
|
||||
jlib:jid_replace_resource(StateData#state.jid, ToNick),
|
||||
From, Err)
|
||||
@ -798,10 +856,20 @@ is_nick_change(JID, Nick, StateData) ->
|
||||
end.
|
||||
|
||||
add_new_user(From, Nick, {xmlelement, _, Attrs, Els} = Packet, StateData) ->
|
||||
case is_nick_exists(Nick, StateData) or
|
||||
not mod_muc:can_use_nick(From, Nick) of
|
||||
true ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_CONFLICT),
|
||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||
case {is_nick_exists(Nick, StateData),
|
||||
mod_muc:can_use_nick(From, Nick)} of
|
||||
{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(
|
||||
% TODO: s/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(
|
||||
Packet,
|
||||
case Affiliation of
|
||||
outcast -> ?ERR_FORBIDDEN;
|
||||
_ -> ?ERR_REGISTRATION_REQUIRED
|
||||
outcast ->
|
||||
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),
|
||||
ejabberd_router:route( % TODO: s/Nick/""/
|
||||
jlib:jid_replace_resource(StateData#state.jid, Nick),
|
||||
@ -836,13 +908,22 @@ add_new_user(From, Nick, {xmlelement, _, Attrs, Els} = Packet, StateData) ->
|
||||
true ->
|
||||
ok;
|
||||
_ ->
|
||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||
send_subject(From, Lang, StateData)
|
||||
end,
|
||||
NewState;
|
||||
_ ->
|
||||
nopass ->
|
||||
ErrText = "Password required to enter this room",
|
||||
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/""/
|
||||
jlib:jid_replace_resource(
|
||||
StateData#state.jid, Nick),
|
||||
@ -860,20 +941,30 @@ check_password(_Affiliation, Els, StateData) ->
|
||||
true;
|
||||
true ->
|
||||
Pass = extract_password(Els),
|
||||
case (StateData#state.config)#config.password of
|
||||
Pass ->
|
||||
true;
|
||||
case Pass of
|
||||
false ->
|
||||
nopass;
|
||||
_ ->
|
||||
false
|
||||
case (StateData#state.config)#config.password of
|
||||
Pass ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
extract_password([]) ->
|
||||
"";
|
||||
extract_password([{xmlelement, Name, Attrs, SubEls} = El | Els]) ->
|
||||
false;
|
||||
extract_password([{xmlelement, _Name, Attrs, _SubEls} = El | Els]) ->
|
||||
case xml:get_attr_s("xmlns", Attrs) of
|
||||
?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)
|
||||
end;
|
||||
@ -913,6 +1004,7 @@ count_stanza_shift(Nick, Els, StateData) ->
|
||||
_ ->
|
||||
count_maxchars_shift(Nick, MaxChars, HL)
|
||||
end,
|
||||
|
||||
lists:max([Shift0, Shift1, Shift2, Shift3]).
|
||||
|
||||
count_seconds_shift(Seconds, HistoryList) ->
|
||||
@ -968,7 +1060,12 @@ extract_history([{xmlelement, Name, Attrs, SubEls} = El | Els], Type) ->
|
||||
[{elem, "history"}, {attr, Type}]),
|
||||
case Type of
|
||||
"since" ->
|
||||
parse_datetime(AttrVal);
|
||||
case catch parse_datetime(AttrVal) of
|
||||
{'EXIT', Err} ->
|
||||
false;
|
||||
Res ->
|
||||
Res
|
||||
end;
|
||||
_ ->
|
||||
case catch list_to_integer(AttrVal) of
|
||||
{'EXIT', _} ->
|
||||
@ -991,50 +1088,20 @@ extract_history([_ | Els], Type) ->
|
||||
% JEP-0082
|
||||
% yyyy-mm-ddThh:mm:ss[.sss]{Z|{+|-}hh:mm} -> {{yyyy, mm, dd}, {hh, mm, ss}} (UTC)
|
||||
parse_datetime(TimeStr) ->
|
||||
DateTime = string:tokens(TimeStr, "T"),
|
||||
case DateTime of
|
||||
[Date, Time] ->
|
||||
case parse_date(Date) of
|
||||
false ->
|
||||
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.
|
||||
[Date, Time] = string:tokens(TimeStr, "T"),
|
||||
D = parse_date(Date),
|
||||
{T, TZH, TZM} = parse_time(Time),
|
||||
S = calendar:datetime_to_gregorian_seconds({D, T}),
|
||||
calendar:gregorian_seconds_to_datetime(S - TZH * 60 * 60 - TZM * 60).
|
||||
|
||||
% yyyy-mm-dd
|
||||
parse_date(Date) ->
|
||||
YearMonthDay = string:tokens(Date, "-"),
|
||||
case length(YearMonthDay) of
|
||||
3 ->
|
||||
[Y, M, D] = lists:map(
|
||||
fun(L)->
|
||||
case catch list_to_integer(L) of
|
||||
{'EXIT', _} ->
|
||||
false;
|
||||
Int ->
|
||||
Int
|
||||
end
|
||||
end, YearMonthDay),
|
||||
case catch calendar:valid_date(Y, M, D) of
|
||||
true ->
|
||||
{Y, M, D};
|
||||
_ ->
|
||||
false
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
[Y, M, D] = lists:map(
|
||||
fun(L)->
|
||||
list_to_integer(L)
|
||||
end, YearMonthDay),
|
||||
{Y, M, D}.
|
||||
|
||||
% hh:mm:ss[.sss]TZD
|
||||
parse_time(Time) ->
|
||||
@ -1043,12 +1110,7 @@ parse_time(Time) ->
|
||||
parse_time_with_timezone(Time);
|
||||
_ ->
|
||||
[T | _] = string:tokens(Time, "Z"),
|
||||
case parse_time1(T) of
|
||||
false ->
|
||||
false;
|
||||
TT ->
|
||||
{TT, 0, 0}
|
||||
end
|
||||
{parse_time1(T), 0, 0}
|
||||
end.
|
||||
|
||||
parse_time_with_timezone(Time) ->
|
||||
@ -1065,71 +1127,35 @@ parse_time_with_timezone(Time) ->
|
||||
end.
|
||||
|
||||
parse_time_with_timezone(Time, Delim) ->
|
||||
TTZ = string:tokens(Time, Delim),
|
||||
case TTZ of
|
||||
[T, TZ] ->
|
||||
case parse_timezone(TZ) of
|
||||
false ->
|
||||
false;
|
||||
{TZH, TZM} ->
|
||||
case parse_time1(T) of
|
||||
false ->
|
||||
false;
|
||||
TT ->
|
||||
case Delim of
|
||||
"-" ->
|
||||
{TT, -TZH, -TZM};
|
||||
"+" ->
|
||||
{TT, TZH, TZM};
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
[T, TZ] = string:tokens(Time, Delim),
|
||||
{TZH, TZM} = parse_timezone(TZ),
|
||||
TT = parse_time1(T),
|
||||
case Delim of
|
||||
"-" ->
|
||||
{TT, -TZH, -TZM};
|
||||
"+" ->
|
||||
{TT, TZH, TZM}
|
||||
end.
|
||||
|
||||
parse_timezone(TZ) ->
|
||||
case string:tokens(TZ, ":") of
|
||||
[H, M] ->
|
||||
case check_list([{H, 12}, {M, 60}]) of
|
||||
{[H, M], true} ->
|
||||
{H, M};
|
||||
_ ->
|
||||
false
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
[H, M] = string:tokens(TZ, ":"),
|
||||
{[H1, M1], true} = check_list([{H, 12}, {M, 60}]),
|
||||
{H1, M1}.
|
||||
|
||||
parse_time1(Time) ->
|
||||
case string:tokens(Time, ".") of
|
||||
[HMS | _] ->
|
||||
case string:tokens(HMS, ":") of
|
||||
[H, M, S] ->
|
||||
case check_list([{H, 24}, {M, 60}, {S, 60}]) of
|
||||
{[H1, M1, S1], true} ->
|
||||
{H1, M1, S1};
|
||||
_ ->
|
||||
false
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
[HMS | _] = string:tokens(Time, "."),
|
||||
[H, M, S] = string:tokens(HMS, ":"),
|
||||
{[H1, M1, S1], true} = check_list([{H, 24}, {M, 60}, {S, 60}]),
|
||||
{H1, M1, S1}.
|
||||
|
||||
check_list(List) ->
|
||||
lists:mapfoldl(
|
||||
fun({L, N}, B)->
|
||||
case catch list_to_integer(L) of
|
||||
{'EXIT', _} ->
|
||||
{false, false};
|
||||
Int when (Int >= 0) and (Int =< N) ->
|
||||
{Int, B};
|
||||
_ ->
|
||||
V = list_to_integer(L),
|
||||
if
|
||||
(V >= 0) and (V =< N) ->
|
||||
{V, B};
|
||||
true ->
|
||||
{false, false}
|
||||
end
|
||||
end, true, List).
|
||||
@ -1405,7 +1431,7 @@ can_change_subject(Role, StateData) ->
|
||||
|
||||
process_iq_admin(From, set, Lang, SubEl, StateData) ->
|
||||
{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) ->
|
||||
case xml:get_subtag(SubEl, "item") of
|
||||
@ -1431,7 +1457,8 @@ process_iq_admin(From, get, Lang, SubEl, StateData) ->
|
||||
SAffiliation, StateData),
|
||||
{result, Items, StateData};
|
||||
true ->
|
||||
{error, ?ERR_NOT_ALLOWED}
|
||||
ErrText = "Administrator privileges required",
|
||||
{error, ?ERRT_FORBIDDEN(Lang, ErrText)}
|
||||
end
|
||||
end
|
||||
end;
|
||||
@ -1445,7 +1472,8 @@ process_iq_admin(From, get, Lang, SubEl, StateData) ->
|
||||
Items = items_with_role(SRole, StateData),
|
||||
{result, Items, StateData};
|
||||
true ->
|
||||
{error, ?ERR_NOT_ALLOWED}
|
||||
ErrText = "Moderator privileges required",
|
||||
{error, ?ERRT_FORBIDDEN(Lang, ErrText)}
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1492,10 +1520,10 @@ search_affiliation(Affiliation, StateData) ->
|
||||
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),
|
||||
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} ->
|
||||
NSD =
|
||||
lists:foldl(
|
||||
@ -1555,19 +1583,23 @@ process_admin_items_set(UJID, Items, StateData) ->
|
||||
end.
|
||||
|
||||
|
||||
find_changed_items(UJID, UAffiliation, URole, [], StateData, Res) ->
|
||||
find_changed_items(UJID, UAffiliation, URole, [], Lang, StateData, Res) ->
|
||||
{result, Res};
|
||||
find_changed_items(UJID, UAffiliation, URole, [{xmlcdata, _} | Items],
|
||||
StateData, Res) ->
|
||||
find_changed_items(UJID, UAffiliation, URole, Items, StateData, Res);
|
||||
Lang, StateData, Res) ->
|
||||
find_changed_items(UJID, UAffiliation, URole, Items, Lang, StateData, Res);
|
||||
find_changed_items(UJID, UAffiliation, URole,
|
||||
[{xmlelement, "item", Attrs, Els} = Item | Items],
|
||||
StateData, Res) ->
|
||||
Lang, StateData, Res) ->
|
||||
TJID = case xml:get_attr("jid", Attrs) of
|
||||
{value, S} ->
|
||||
case jlib:string_to_jid(S) of
|
||||
error ->
|
||||
{error, ?ERR_BAD_REQUEST};
|
||||
ErrText = io_lib:format(
|
||||
translate:translate(
|
||||
Lang,
|
||||
"JID ~s is invalid"), [S]),
|
||||
{error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)};
|
||||
J ->
|
||||
{value, J}
|
||||
end;
|
||||
@ -1576,7 +1608,13 @@ find_changed_items(UJID, UAffiliation, URole,
|
||||
{value, N} ->
|
||||
case find_jid_by_nick(N, StateData) of
|
||||
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 ->
|
||||
{value, J}
|
||||
end;
|
||||
@ -1596,7 +1634,13 @@ find_changed_items(UJID, UAffiliation, URole,
|
||||
{value, StrAffiliation} ->
|
||||
case catch list_to_affiliation(StrAffiliation) of
|
||||
{'EXIT', _} ->
|
||||
{error, ?ERR_BAD_REQUEST};
|
||||
ErrText1 =
|
||||
io_lib:format(
|
||||
translate:translate(
|
||||
Lang,
|
||||
"Invalid affiliation: ~s"),
|
||||
[StrAffiliation]),
|
||||
{error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText1)};
|
||||
SAffiliation ->
|
||||
case can_change_ra(
|
||||
UAffiliation, URole,
|
||||
@ -1606,13 +1650,13 @@ find_changed_items(UJID, UAffiliation, URole,
|
||||
find_changed_items(
|
||||
UJID,
|
||||
UAffiliation, URole,
|
||||
Items, StateData,
|
||||
Items, Lang, StateData,
|
||||
Res);
|
||||
true ->
|
||||
find_changed_items(
|
||||
UJID,
|
||||
UAffiliation, URole,
|
||||
Items, StateData,
|
||||
Items, Lang, StateData,
|
||||
[{jlib:jid_remove_resource(JID),
|
||||
affiliation,
|
||||
SAffiliation,
|
||||
@ -1627,7 +1671,13 @@ find_changed_items(UJID, UAffiliation, URole,
|
||||
{value, StrRole} ->
|
||||
case catch list_to_role(StrRole) of
|
||||
{'EXIT', _} ->
|
||||
{error, ?ERR_BAD_REQUEST};
|
||||
ErrText1 =
|
||||
io_lib:format(
|
||||
translate:translate(
|
||||
Lang,
|
||||
"Invalid role: ~s"),
|
||||
[StrRole]),
|
||||
{error, ?ERRT_BAD_REQUEST(Lang, ErrText1)};
|
||||
SRole ->
|
||||
case can_change_ra(
|
||||
UAffiliation, URole,
|
||||
@ -1637,13 +1687,13 @@ find_changed_items(UJID, UAffiliation, URole,
|
||||
find_changed_items(
|
||||
UJID,
|
||||
UAffiliation, URole,
|
||||
Items, StateData,
|
||||
Items, Lang, StateData,
|
||||
Res);
|
||||
true ->
|
||||
find_changed_items(
|
||||
UJID,
|
||||
UAffiliation, URole,
|
||||
Items, StateData,
|
||||
Items, Lang, StateData,
|
||||
[{JID, role, SRole,
|
||||
xml:get_path_s(
|
||||
Item, [{elem, "reason"},
|
||||
@ -1656,7 +1706,7 @@ find_changed_items(UJID, UAffiliation, URole,
|
||||
Err ->
|
||||
Err
|
||||
end;
|
||||
find_changed_items(UJID, UAffiliation, URole, Items, StateData, Res) ->
|
||||
find_changed_items(UJID, UAffiliation, URole, Items, Lang, StateData, Res) ->
|
||||
{error, ?ERR_BAD_REQUEST}.
|
||||
|
||||
|
||||
@ -1851,10 +1901,11 @@ process_iq_owner(From, set, Lang, SubEl, StateData) ->
|
||||
[{xmlelement, "destroy", Attrs1, Els1}] ->
|
||||
destroy_room(Els1, StateData);
|
||||
Items ->
|
||||
process_admin_items_set(From, Items, StateData)
|
||||
process_admin_items_set(From, Items, Lang, StateData)
|
||||
end;
|
||||
_ ->
|
||||
{error, ?ERR_FORBIDDEN}
|
||||
ErrText = "Owner privileges required",
|
||||
{error, ?ERRT_FORBIDDEN(Lang, ErrText)}
|
||||
end;
|
||||
|
||||
process_iq_owner(From, get, Lang, SubEl, StateData) ->
|
||||
@ -1872,7 +1923,13 @@ process_iq_owner(From, get, Lang, SubEl, StateData) ->
|
||||
{value, StrAffiliation} ->
|
||||
case catch list_to_affiliation(StrAffiliation) of
|
||||
{'EXIT', _} ->
|
||||
{error, ?ERR_BAD_REQUEST};
|
||||
ErrText =
|
||||
io_lib:format(
|
||||
translate:translate(
|
||||
Lang,
|
||||
"Invalid affiliation: ~s"),
|
||||
[StrAffiliation]),
|
||||
{error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)};
|
||||
SAffiliation ->
|
||||
Items = items_with_affiliation(
|
||||
SAffiliation, StateData),
|
||||
@ -1883,7 +1940,8 @@ process_iq_owner(From, get, Lang, SubEl, StateData) ->
|
||||
{error, ?ERR_FEATURE_NOT_IMPLEMENTED}
|
||||
end;
|
||||
_ ->
|
||||
{error, ?ERR_NOT_ALLOWED}
|
||||
ErrText = "Owner privileges required",
|
||||
{error, ?ERRT_FORBIDDEN(Lang, ErrText)}
|
||||
end.
|
||||
|
||||
|
||||
@ -2191,7 +2249,7 @@ process_iq_disco_items(From, get, Lang, StateData) ->
|
||||
?DICT:to_list(StateData#state.users)),
|
||||
{result, UList, StateData};
|
||||
_ ->
|
||||
{error, ?ERR_NOT_ALLOWED}
|
||||
{error, ?ERR_FORBIDDEN}
|
||||
end.
|
||||
|
||||
get_title(StateData) ->
|
||||
|
@ -162,7 +162,7 @@ do_route(Host, From, To, Packet) ->
|
||||
#iq{type = get, xmlns = ?NS_VCARD = XMLNS,
|
||||
lang = Lang, sub_el = SubEl} = IQ ->
|
||||
Res = IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
sub_el = [{xmlelement, "vCard",
|
||||
[{"xmlns", XMLNS}],
|
||||
iq_get_vcard(Lang)}]},
|
||||
ejabberd_router:route(To,
|
||||
|
@ -336,7 +336,7 @@ do_route(From, To, Packet) ->
|
||||
ResIQ =
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement,
|
||||
"query",
|
||||
"vCard",
|
||||
[{"xmlns", ?NS_VCARD}],
|
||||
iq_get_vcard(Lang)}]},
|
||||
ejabberd_router:route(To,
|
||||
@ -356,8 +356,10 @@ iq_get_vcard(Lang) ->
|
||||
[{xmlcdata,
|
||||
"http://ejabberd.jabberstudio.org/"}]},
|
||||
{xmlelement, "DESC", [],
|
||||
[{xmlcdata, translate:translate(Lang, "ejabberd vCard module\n"
|
||||
"Copyright (c) 2003-2004 Alexey Shchepin")}]}].
|
||||
[{xmlcdata, translate:translate(
|
||||
Lang,
|
||||
"ejabberd vCard module\n"
|
||||
"Copyright (c) 2003-2004 Alexey Shchepin")}]}].
|
||||
|
||||
find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
|
||||
find_xdata_el1(SubEls).
|
||||
|
@ -102,6 +102,12 @@
|
||||
{"Enter nickname you want to register", "Введите псевдоним, который Вы хотели бы зарегистрировать"}.
|
||||
{"ejabberd MUC module\nCopyright (c) 2003-2004 Alexey Shchepin",
|
||||
"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
|
||||
{" has set the subject to: ", " установил(а) тему: "}.
|
||||
@ -128,6 +134,39 @@
|
||||
{"Password", "Пароль"}.
|
||||
{"Make room anonymous?", "Сделать комнату анонимной?"}.
|
||||
{"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
|
||||
{"ejabberd IRC module\nCopyright (c) 2003-2004 Alexey Shchepin",
|
||||
|
33
src/web/Makefile.in
Normal file
33
src/web/Makefile.in
Normal file
@ -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
|
||||
|
401
src/web/ejabberd_http.erl
Normal file
401
src/web/ejabberd_http.erl
Normal file
@ -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(< |