Merge branch 'master' of github.com:processone/ejabberd

This commit is contained in:
Evgeniy Khramtsov 2014-02-28 15:36:46 +04:00
commit afba5bc5f5
12 changed files with 150 additions and 26 deletions

View File

@ -890,6 +890,12 @@ The available modules, their purpose and the options allowed by each one are:
Handles incoming HTTP connections.\\
Options: \texttt{captcha}, \texttt{certfile}, \texttt{default\_host}, \texttt{http\_bind}, \texttt{http\_poll},
\texttt{request\_handlers}, \texttt{tls}, \texttt{tls\_compression}, \texttt{trusted\_proxies}, \texttt{web\_admin}\\
\titem{\texttt{ejabberd\_xmlrpc}}
Handles XML-RPC requests to execute ejabberd commands (\ref{eja-commands}).\\
Options: \texttt{access\_commands}, \texttt{maxsessions}, \texttt{timeout}.\\
You can find option explanations, example configuration in old and new format,
and example calls in several languages in the old
\footahref{https://raw.github.com/processone/ejabberd-contrib/master/ejabberd\_xmlrpc/README.txt}{ejabberd\_xmlrpc README.txt}
\end{description}
@ -4046,15 +4052,28 @@ Options:
not add/remove/modify contacts,
or subscribe/unsubscribe presence.
By default there aren't restrictions.
\titem{managers} \ind{options!managers}
List of remote entities that can manage users rosters using Remote Roster Management
(\xepref{0321}).
The protocol sections implemented are:
\term{4.2. The remote entity requests current user's roster}.
\term{4.3. The user updates roster}.
\term{4.4. The remote entity updates the user's roster}.
A remote entity cab only get or modify roster items that have the same domain as the entity.
Default value is: \term{[]}.
\end{description}
This example configuration enables Roster Versioning with storage of current id:
This example configuration enables Roster Versioning with storage of current id.
The ICQ and MSN transports can get ICQ and MSN contacts, add them, or remove them for any local account:
\begin{verbatim}
modules:
...
mod_roster:
versioning: true
store_current_id: true
managers:
- "icq.example.org"
- "msn.example.org"
...
\end{verbatim}
@ -5130,9 +5149,9 @@ with a defined number and type of calling arguments and type of result
that is registered in the \term{ejabberd\_commands} service.
Those commands can be defined in any Erlang module and executed using any valid frontend.
\ejabberd{} includes a frontend to execute \term{ejabberd commands}: the script \term{ejabberdctl}.
\ejabberd{} includes two frontends to execute \term{ejabberd commands}: the script \term{ejabberdctl} (\ref{ejabberdctl})
and the \term{ejabberd\_xmlrpc} listener (\ref{listened-module}).
Other known frontends that can be installed to execute ejabberd commands in different ways are:
\term{ejabberd\_xmlrpc} (XML-RPC service),
\term{mod\_rest} (HTTP POST service),
\term{mod\_shcommands} (ejabberd WebAdmin page).

View File

@ -599,12 +599,11 @@ modules:
##
## Enable modules with custom options in a specific virtual host
##
## host_config:
## append_host_config:
## "localhost":
## add:
## modules:
## mod_echo:
## host: "mirror.localhost"
## modules:
## mod_echo:
## host: "mirror.localhost"
### Local Variables:
### mode: yaml

View File

@ -16,7 +16,7 @@ Cfg = case file:consult("vars.config") of
Macros = lists:flatmap(
fun({roster_gateway_workaround, true}) ->
[{d, 'ROSTER_GATEWAY_WORKAROUND'}];
({transient_supervisors, true}) ->
({transient_supervisors, false}) ->
[{d, 'NO_TRANSIENT_SUPERVISORS'}];
({nif, true}) ->
[{d, 'NIF'}];

View File

@ -94,7 +94,7 @@
tls_options = [],
authenticated = false,
jid,
user = "", server = ?MYNAME, resource = <<"">>,
user = "", server = <<"">>, resource = <<"">>,
sid,
pres_t = ?SETS:new(),
pres_f = ?SETS:new(),
@ -291,7 +291,12 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
DefaultLang = ?MYLANG,
case xml:get_attr_s(<<"xmlns:stream">>, Attrs) of
?NS_STREAM ->
Server = jlib:nameprep(xml:get_attr_s(<<"to">>, Attrs)),
Server =
case StateData#state.server of
<<"">> ->
jlib:nameprep(xml:get_attr_s(<<"to">>, Attrs));
S -> S
end,
case lists:member(Server, ?MYHOSTS) of
true ->
Lang = case xml:get_attr_s(<<"xml:lang">>, Attrs) of
@ -647,6 +652,7 @@ wait_for_feature_request({xmlstreamelement, El},
StateData#state{streamid = new_id(),
authenticated = true,
auth_module = AuthModule,
sasl_state = undefined,
user = U});
{continue, ServerOut, NewSASLState} ->
send_element(StateData,
@ -804,6 +810,7 @@ wait_for_sasl_response({xmlstreamelement, El},
StateData#state{streamid = new_id(),
authenticated = true,
auth_module = AuthModule,
sasl_state = undefined,
user = U});
{ok, Props, ServerOut} ->
(StateData#state.sockmod):reset_stream(StateData#state.socket),
@ -824,6 +831,7 @@ wait_for_sasl_response({xmlstreamelement, El},
StateData#state{streamid = new_id(),
authenticated = true,
auth_module = AuthModule,
sasl_state = undefined,
user = U});
{continue, ServerOut, NewSASLState} ->
send_element(StateData,

View File

@ -484,7 +484,7 @@ abort_on_driver_error(Reply, From) ->
%% Open an ODBC database connection
odbc_connect(SQLServer) ->
ejabberd:start_app(odbc),
odbc:connect(SQLServer, [{scrollable_cursors, off}]).
odbc:connect(binary_to_list(SQLServer), [{scrollable_cursors, off}]).
%% == Native PostgreSQL code

View File

@ -88,7 +88,7 @@
-define(SUPERVISOR_START,
p1_fsm:start(ejabberd_s2s_in, [SockData, Opts],
?FSMOPTS ++ fsm_limit_opts(Opts)).
?FSMOPTS ++ fsm_limit_opts(Opts))).
-else.

View File

@ -1570,9 +1570,7 @@ user_info(User, Server, Query, Lang) ->
end,
lists:sort(Resources))))]
end,
Password = ejabberd_auth:get_password_s(User, Server),
FPassword = [?INPUT(<<"password">>, <<"password">>,
Password),
FPassword = [?INPUT(<<"text">>, <<"password">>, <<"">>),
?C(<<" ">>),
?INPUTT(<<"submit">>, <<"chpassword">>,
<<"Change Password">>)],

View File

@ -345,8 +345,10 @@ build_fault_response(Code, ParseString, ParseArgs) ->
do_command(AccessCommands, Auth, Command, AttrL, ArgsF,
ResultF) ->
ArgsFormatted = format_args(AttrL, ArgsF),
{UserT, ServerT, PasswordT} = Auth,
AuthBin = {list_to_binary(UserT), list_to_binary(ServerT), list_to_binary(PasswordT)},
Result =
ejabberd_commands:execute_command(AccessCommands, Auth,
ejabberd_commands:execute_command(AccessCommands, AuthBin,
Command, ArgsFormatted),
ResultFormatted = format_result(Result, ResultF),
{command_result, ResultFormatted}.

View File

@ -149,7 +149,7 @@ check_and_forward(JID, #xmlel{name = <<"message">>, attrs = Attrs} = Packet, Dir
<<"chat">> ->
case xml:get_subtag(Packet, <<"private">>) of
false ->
case xml:get_subtag(Packet,<<"forwarded">>) of
case xml:get_subtag(Packet,<<"received">>) of
false ->
send_copies(JID, Packet, Direction);
_ ->

View File

@ -657,7 +657,7 @@ load_permanent_rooms(Host, ServerHost, Access, HistorySize, RoomShaper) ->
start_new_room(Host, ServerHost, Access, Room,
HistorySize, RoomShaper, From,
Nick, DefRoomOpts) ->
case restore_room(ServerHost, Room, Host) of
case restore_room(ServerHost, Host, Room) of
error ->
?DEBUG("MUC: open new room '~s'~n", [Room]),
mod_muc_room:start(Host, ServerHost, Access,

View File

@ -2334,6 +2334,14 @@ send_nick_changing(JID, OldNick, StateData,
[{<<"affiliation">>, SAffiliation},
{<<"role">>, SRole}]
end,
Status110 = case JID == Info#user.jid of
true ->
[#xmlel{name = <<"status">>,
attrs = [{<<"code">>, <<"110">>}]
}];
false ->
[]
end,
Packet1 = #xmlel{name = <<"presence">>,
attrs =
[{<<"type">>,
@ -2356,7 +2364,7 @@ send_nick_changing(JID, OldNick, StateData,
[{<<"code">>,
<<"303">>}],
children =
[]}]}]},
[]}|Status110]}]},
Packet2 = xml:append_subtags(Presence,
[#xmlel{name = <<"x">>,
attrs =
@ -2371,7 +2379,7 @@ send_nick_changing(JID, OldNick, StateData,
ItemAttrs2,
children
=
[]}]}]),
[]}|Status110]}]),
if SendOldUnavailable ->
ejabberd_router:route(jlib:jid_replace_resource(StateData#state.jid,
OldNick),

View File

@ -130,6 +130,9 @@ stop(Host) ->
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host,
?NS_ROSTER).
process_iq(From, To, IQ) when ((From#jid.luser == <<"">>) andalso (From#jid.resource == <<"">>)) ->
process_iq_manager(From, To, IQ);
process_iq(From, To, IQ) ->
#iq{sub_el = SubEl} = IQ,
#jid{lserver = LServer} = From,
@ -465,15 +468,16 @@ try_process_iq_set(From, To, #iq{sub_el = SubEl} = IQ) ->
process_iq_set(From, To, IQ)
end.
process_iq_set(From, To, #iq{sub_el = SubEl} = IQ) ->
process_iq_set(From, To, #iq{sub_el = SubEl, id = Id} = IQ) ->
#xmlel{children = Els} = SubEl,
lists:foreach(fun (El) -> process_item_set(From, To, El)
Managed = is_managed_from_id(Id),
lists:foreach(fun (El) -> process_item_set(From, To, El, Managed)
end,
Els),
IQ#iq{type = result, sub_el = []}.
process_item_set(From, To,
#xmlel{attrs = Attrs, children = Els}) ->
#xmlel{attrs = Attrs, children = Els}, Managed) ->
JID1 = jlib:string_to_jid(xml:get_attr_s(<<"jid">>,
Attrs)),
#jid{user = User, luser = LUser, lserver = LServer} =
@ -484,12 +488,13 @@ process_item_set(From, To,
LJID = jlib:jid_tolower(JID1),
F = fun () ->
Item = get_roster_by_jid_t(LUser, LServer, LJID),
Item1 = process_item_attrs(Item, Attrs),
Item1 = process_item_attrs_managed(Item, Attrs, Managed),
Item2 = process_item_els(Item1, Els),
case Item2#roster.subscription of
remove -> del_roster_t(LUser, LServer, LJID);
_ -> update_roster_t(LUser, LServer, LJID, Item2)
end,
send_itemset_to_managers(From, Item2, Managed),
Item3 = ejabberd_hooks:run_fold(roster_process_item,
LServer, Item2,
[LServer]),
@ -511,7 +516,7 @@ process_item_set(From, To,
?DEBUG("ROSTER: roster item set error: ~p~n", [E]), ok
end
end;
process_item_set(_From, _To, _) -> ok.
process_item_set(_From, _To, _, _Managed) -> ok.
process_item_attrs(Item, [{Attr, Val} | Attrs]) ->
case Attr of
@ -1554,6 +1559,91 @@ webadmin_user(Acc, _User, _Server, Lang) ->
Acc ++
[?XE(<<"h3">>, [?ACT(<<"roster/">>, <<"Roster">>)])].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Implement XEP-0321 Remote Roster Management
process_iq_manager(From, To, IQ) ->
%% Check what access is allowed for From to To
MatchDomain = From#jid.lserver,
case is_domain_managed(MatchDomain, To#jid.lserver) of
true ->
process_iq_manager2(MatchDomain, To, IQ);
false ->
#iq{sub_el = SubEl} = IQ,
IQ#iq{type = error, sub_el = [SubEl, ?ERR_BAD_REQUEST]}
end.
process_iq_manager2(MatchDomain, To, IQ) ->
%% If IQ is SET, filter the input IQ
IQFiltered = maybe_filter_request(MatchDomain, IQ),
%% Call the standard function with reversed JIDs
IdInitial = IQFiltered#iq.id,
ResIQ = process_iq(To, To, IQFiltered#iq{id = <<"roster-remotely-managed">>}),
%% Filter the output IQ
filter_stanza(MatchDomain, ResIQ#iq{id = IdInitial}).
is_domain_managed(ContactHost, UserHost) ->
Managers = gen_mod:get_module_opt(UserHost, ?MODULE, managers,
fun(B) when is_list(B) -> B end,
[]),
lists:member(ContactHost, Managers).
maybe_filter_request(MatchDomain, IQ) when IQ#iq.type == set ->
filter_stanza(MatchDomain, IQ);
maybe_filter_request(_MatchDomain, IQ) ->
IQ.
filter_stanza(_MatchDomain, #iq{sub_el = []} = IQ) ->
IQ;
filter_stanza(MatchDomain, #iq{sub_el = [SubEl | _]} = IQ) ->
#iq{sub_el = SubElFiltered} = IQRes =
filter_stanza(MatchDomain, IQ#iq{sub_el = SubEl}),
IQRes#iq{sub_el = [SubElFiltered]};
filter_stanza(MatchDomain, #iq{sub_el = SubEl} = IQ) ->
#xmlel{name = Type, attrs = Attrs, children = Items} = SubEl,
ItemsFiltered = lists:filter(
fun(Item) ->
is_item_of_domain(MatchDomain, Item) end, Items),
SubElFiltered = #xmlel{name=Type, attrs = Attrs, children = ItemsFiltered},
IQ#iq{sub_el = SubElFiltered}.
is_item_of_domain(MatchDomain, #xmlel{} = El) ->
lists:any(fun(Attr) -> is_jid_of_domain(MatchDomain, Attr) end, El#xmlel.attrs);
is_item_of_domain(_MatchDomain, {xmlcdata, _}) ->
false.
is_jid_of_domain(MatchDomain, {<<"jid">>, JIDString}) ->
case jlib:string_to_jid(JIDString) of
JID when JID#jid.lserver == MatchDomain -> true;
_ -> false
end;
is_jid_of_domain(_, _) ->
false.
process_item_attrs_managed(Item, Attrs, true) ->
process_item_attrs_ws(Item, Attrs);
process_item_attrs_managed(Item, _Attrs, false) ->
process_item_attrs(Item, _Attrs).
send_itemset_to_managers(_From, _Item, true) ->
ok;
send_itemset_to_managers(From, Item, false) ->
{_, UserHost} = Item#roster.us,
{_ContactUser, ContactHost, _ContactResource} = Item#roster.jid,
%% Check if the component is an allowed manager
IsManager = is_domain_managed(ContactHost, UserHost),
case IsManager of
true -> push_item(<<"">>, ContactHost, <<"">>, From, Item);
false -> ok
end.
is_managed_from_id(<<"roster-remotely-managed">>) ->
true;
is_managed_from_id(_Id) ->
false.
export(_Server) ->
[{roster,
fun(Host, #roster{usj = {LUser, LServer, LJID}} = R)