mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-26 16:26:24 +01:00
Merge from trunk (r1734 to r1752).
Note: this merge doesn't include the following revisions because it was made by previous commits: r1737, r1740, r1745, r1747, r1748. Additionnaly, any deprecated content was removed from jlib.hrl. This leaves only the new RSM records. Warning: Ejabberd may be broken until the merge is completly finished. PR: EJABP-1 SVN Revision: 1824
This commit is contained in:
parent
04f6a2be37
commit
2cc2039ad8
122
ChangeLog
122
ChangeLog
@ -1,3 +1,14 @@
|
||||
2009-01-16 Jean-Sébastien Pédron <js.pedron@meetic-corp.com>
|
||||
|
||||
Merge from trunk (r1734 to r1752).
|
||||
|
||||
Note: this merge doesn't include the following revisions because it
|
||||
was made by previous commits:
|
||||
r1737, r1740, r1745, r1747, r1748.
|
||||
|
||||
* src/jlib.hrl: Any deprecated content was removed from jlib.hrl. This
|
||||
leaves only the new RSM records.
|
||||
|
||||
2009-01-15 Pablo Polvorin <pablo.polvorin@process-one.net>
|
||||
|
||||
* src/mod_muc/mod_muc.erl, src/mod_muc/mod_muc_room.erl:
|
||||
@ -28,12 +39,13 @@
|
||||
* src/mod_vcard_ldap.erl, src/mod_vcard.erl, src/mod_configure.erl,
|
||||
src/ejabberd_sm.erl, src/mod_privacy_odbc.erl, src/ejabberd_c2s.erl,
|
||||
src/ejabberd_local.erl, src/mod_privacy.erl, src/mod_adhoc.erl,
|
||||
src/mod_pubsub/mod_pubsub.erl, src/mod_vcard_odbc.erl, src/mod_stats.erl,
|
||||
src/mod_last.erl, src/mod_private.erl, src/mod_roster.erl,
|
||||
src/mod_disco.erl, src/mod_private_odbc.erl, src/mod_configure2.erl,
|
||||
src/mod_roster_odbc.erl, src/mod_register.erl, src/mod_version.erl,
|
||||
src/mod_caps.erl, src/mod_last_odbc.erl, src/mod_time.erl: Update
|
||||
gen_iq_handler API, require the 'Host' argument to be in binary() format.
|
||||
src/mod_pubsub/mod_pubsub.erl, src/mod_vcard_odbc.erl,
|
||||
src/mod_stats.erl, src/mod_last.erl, src/mod_private.erl,
|
||||
src/mod_roster.erl, src/mod_disco.erl, src/mod_private_odbc.erl,
|
||||
src/mod_configure2.erl, src/mod_roster_odbc.erl, src/mod_register.erl,
|
||||
src/mod_version.erl, src/mod_caps.erl, src/mod_last_odbc.erl,
|
||||
src/mod_time.erl: Update gen_iq_handler API, require the 'Host'
|
||||
argument to be in binary() format.
|
||||
|
||||
2009-01-10 Christophe Romain <christophe.romain@process-one.net>
|
||||
|
||||
@ -45,18 +57,17 @@
|
||||
* src/mod_muc/mod_muc_room.erl, src/mod_muc/mod_muc.erl
|
||||
src/mod_offline_odbc.erl, src/mod_irc/mod_irc_connection.erl,
|
||||
src/mod_irc/mod_irc.erl, src/ejabberd_c2s.erl, src/ejabberd_local.erl,
|
||||
src/mod_pubsub/mod_pubsub.erl, src/ejabberd_s2s.erl, src/mod_roster.erl,
|
||||
src/mod_roster_odbc.erl, src/ejabberd_s2s_out.erl, src/mod_offline.erl,
|
||||
src/translate.erl: Adapt to new exmpp API where get_id/1, get_lang/1,
|
||||
get_initiating_entity/1, get_receiving_entity/1 and get_type/1
|
||||
returns binary().
|
||||
src/mod_pubsub/mod_pubsub.erl, src/ejabberd_s2s.erl,
|
||||
src/mod_roster.erl, src/mod_roster_odbc.erl, src/ejabberd_s2s_out.erl,
|
||||
src/mod_offline.erl, src/translate.erl: Adapt to new exmpp API where
|
||||
get_id/1, get_lang/1, get_initiating_entity/1, get_receiving_entity/1
|
||||
and get_type/1 return binary().
|
||||
|
||||
* src/mod_pubsub/node_default.erl: Fix typo in variable name.
|
||||
|
||||
* src/ejabberd_c2s.erl: Fix bug in handle_info/3 when dealing with
|
||||
VCARD requests: convert to IQ struct before invoking gen_iq_handler.
|
||||
|
||||
|
||||
2009-01-08 Christophe Romain <christophe.romain@process-one.net>
|
||||
|
||||
* src/mod_pubsub/mod_pubsub.erl: completely support subscription using
|
||||
@ -78,6 +89,7 @@
|
||||
* src/mod_pubsub/node_club.erl: Likewise
|
||||
|
||||
2009-01-08 Pablo Polvorin <pablo.polvorin@process-one.net>
|
||||
|
||||
* src/mod_vcard_ldap.erl, src/mod_muc/mod_muc.erl, src/mod_roster.hrl,
|
||||
src/mod_offline_odbc.erl, src/ejabberd_s2s_in.erl, src/adhoc.erl,
|
||||
src/mod_configure.erl, src/mod_irc/mod_irc_connection.erl,
|
||||
@ -86,26 +98,29 @@
|
||||
src/mod_privacy.erl, src/mod_adhoc.erl, src/mod_pubsub/mod_pubsub.erl,
|
||||
src/mod_vcard_odbc.erl, src/mod_stats.erl, src/mod_last.erl,
|
||||
src/mod_roster.erl, src/ejabberd_service.erl, src/mod_disco.erl,
|
||||
src/mod_configure2.erl, src/mod_roster_odbc.erl, src/ejabberd_s2s_out.erl,
|
||||
src/mod_last_odbc.erl: XML attributes as binary(). Change Node argument
|
||||
to binary in the following hooks: disco_local_items, disco_local_features,
|
||||
disco_local_identity, disco_sm_items and disco_sm_identity.
|
||||
|
||||
src/mod_configure2.erl, src/mod_roster_odbc.erl,
|
||||
src/ejabberd_s2s_out.erl, src/mod_last_odbc.erl: XML attributes as
|
||||
binary(). Change Node argument to binary in the following hooks:
|
||||
disco_local_items, disco_local_features, disco_local_identity,
|
||||
disco_sm_items and disco_sm_identity.
|
||||
|
||||
2009-01-05 Pablo Polvorin <pablo.polvorin@process-one.net>
|
||||
|
||||
* src/mod_roster.erl: Fix typo.
|
||||
|
||||
2009-01-03 Pablo Polvorin <pablo.polvorin@process-one.net>
|
||||
* src/mod_pubsub_node_default.erl: Fix typo
|
||||
|
||||
* src/mod_vcard.erl, src/mod_vcard_ldap.erl,src/ ejabberd_hooks.erl,
|
||||
mod_muc/mod_muc_room.erl, src/mod_muc/mod_muc.erl,
|
||||
* src/mod_pubsub_node_default.erl: Fix typo.
|
||||
|
||||
* src/mod_vcard.erl, src/mod_vcard_ldap.erl, src/ejabberd_hooks.erl,
|
||||
src/mod_muc/mod_muc_room.erl, src/mod_muc/mod_muc.erl,
|
||||
src/mod_muc/mod_muc_log.erl, src/mod_shared_roster.erl,
|
||||
src/ejabberd_auth_odbc.erl, src/mod_offline_odbc.erl,
|
||||
src/ejabberd_system_monitor.erl, src/ejabberd_s2s_in.erl,
|
||||
src/mod_configure.erl, src/ejabberd_receiver.erl, src/mod_irc/mod_irc.erl,
|
||||
src/ejabberd_sm.erl, src/mod_privacy_odbc.erl, src/ejabberd_c2s.erl,
|
||||
src/mod_announce.erl, src/ejabberd_local.erl, src/mod_privacy.erl,
|
||||
src/mod_configure.erl, src/ejabberd_receiver.erl,
|
||||
src/mod_irc/mod_irc.erl, src/ejabberd_sm.erl,
|
||||
src/mod_privacy_odbc.erl, src/ejabberd_c2s.erl, src/mod_announce.erl,
|
||||
src/ejabberd_local.erl, src/mod_privacy.erl,
|
||||
src/ejabberd_auth_internal.erl, src/mod_adhoc.erl, src/mod_echo.erl,
|
||||
src/jlib.erl, src/mod_vcard_odbc.erl, src/ejabberd_s2s.erl,
|
||||
src/mod_stats.erl, src/ejabberd_router.erl, src/mod_last.erl,
|
||||
@ -113,13 +128,11 @@
|
||||
src/mod_disco.erl, src/mod_private_odbc.erl, src/mod_service_log.erl,
|
||||
src/mod_configure2.erl, src/mod_roster_odbc.erl, src/mod_offline.erl,
|
||||
src/mod_register.erl, src/mod_version.erl, src/mod_caps.erl,
|
||||
src/mod_last_odbc.erl: Use exmpp API to access JID fields. Keep
|
||||
#jid fields in binary format when possible. Change all 'user' and
|
||||
'server' arguments in all hooks to binary. Change internal tables of
|
||||
ejabberd_sm, ejabberd_router, ejabberd_hooks, mod_last, mod_roster
|
||||
to use binary() storage.
|
||||
|
||||
|
||||
src/mod_last_odbc.erl: Use exmpp API to access JID fields. Keep #jid
|
||||
fields in binary format when possible. Change all 'user' and 'server'
|
||||
arguments in all hooks to binary. Change internal tables of
|
||||
ejabberd_sm, ejabberd_router, ejabberd_hooks, mod_last, mod_roster to
|
||||
use binary() storage.
|
||||
|
||||
2009-01-03 Christophe Romain <christophe.romain@process-one.net>
|
||||
|
||||
@ -142,12 +155,61 @@
|
||||
* src/mod_pubsub/mod_pubsub.erl: Added "access-whitelist" and
|
||||
"member-affiliation" features (thanks to Andy Skelton)(EJAB-780)
|
||||
|
||||
2008-12-23 Badlop <badlop@process-one.net>
|
||||
|
||||
* src/acl.erl: New ACL: shared_group (thanks to Maxim Ryazanov)
|
||||
* doc/guide.tex: Likewise
|
||||
|
||||
* src/mod_shared_roster.erl: Push new group members when
|
||||
registered or manually added to group: EJAB-730 EJAB-731 EJAB-732
|
||||
EJAB-767 EJAB-794. When user is added to group, push it to other
|
||||
members, and other members to it. When user is removed from group,
|
||||
push deletion to other members, and other members to it. When user
|
||||
is registered, push him to members of group @all@. When user is
|
||||
deleted, push deletion to members of group @all@. Document several
|
||||
functions in mod_shared_roster.
|
||||
|
||||
* src/ejabberd_auth.erl: Rename hook user_registered to
|
||||
register_user, for name consistency with the widely used hook
|
||||
remove_user. Run hook register_user in ejabberd_auth, so it's run
|
||||
when account is created with any method. Run hook remove_user in
|
||||
ejabberd_auth, so it's run when account is deleted with any
|
||||
method.
|
||||
* src/ejabberd_auth_internal.erl: Likewise
|
||||
* src/ejabberd_auth_ldap.erl: Likewise
|
||||
* src/ejabberd_auth_odbc.erl: Likewise
|
||||
* src/ejabberd_auth_pam.erl: Likewise
|
||||
* src/mod_register.erl: Likewise
|
||||
|
||||
* src/jlib.erl: Implementation of XEP-0059 Result Set
|
||||
Management (thanks to Eric Cestari)(EJAB-807)
|
||||
* src/jlib.hrl: Likewise
|
||||
* src/mod_muc/mod_muc.erl: Likewise
|
||||
|
||||
2008-12-23 Christophe Romain <christophe.romain@process-one.net>
|
||||
|
||||
* src/mod_pubsub/mod_pubsub.erl: Improve handling of PEP sent to
|
||||
external contacts (EJAB-825)
|
||||
* src/mod_caps.erl: Likewise
|
||||
|
||||
2008-12-23 Badlop <badlop@process-one.net>
|
||||
|
||||
* src/mod_last.erl: Implement workaround for uptime statistic in
|
||||
32 bit machines, so it can show uptime greater than 50
|
||||
days (EJAB-610)
|
||||
* src/mod_last_odbc.erl: Likewise
|
||||
* src/ejabberd_config.erl: Store start time in local_config table
|
||||
|
||||
* src/cyrsasl_digest.erl: Check digest-uri in SASL digest
|
||||
authentication (thanks to Paul Guyot)(EJAB-569)
|
||||
|
||||
* src/odbc/odbc_queries.erl: Fix removal of private_storage of an
|
||||
account when the account is removed
|
||||
|
||||
* src/mod_privacy.erl: Remove privacy lists of an account when the
|
||||
account is removed (EJAB-720)
|
||||
* src/mod_privacy_odbc.erl: Likewise
|
||||
|
||||
2008-12-19 Christophe Romain <christophe.romain@process-one.net>
|
||||
|
||||
* src/mod_pubsub/mod_pubsub.erl: Fix send_last_published_item issue
|
||||
|
@ -1239,6 +1239,14 @@ declarations of ACLs in the configuration file have the following syntax:
|
||||
\begin{verbatim}
|
||||
{acl, mucklres, {resource, "muckl"}}.
|
||||
\end{verbatim}
|
||||
\titem{\{shared\_group, <groupname>\}} Matches any member of a Shared Roster Group with name \term{<groupname>} in the virtual host. Example:
|
||||
\begin{verbatim}
|
||||
{acl, techgroupmembers, {shared_group, "techteam"}}.
|
||||
\end{verbatim}
|
||||
\titem{\{shared\_group, <groupname>, <server>\}} Matches any member of a Shared Roster Group with name \term{<groupname>} in the virtual host \term{<server>}. Example:
|
||||
\begin{verbatim}
|
||||
{acl, techgroupmembers, {shared_group, "techteam", "example.org"}}.
|
||||
\end{verbatim}
|
||||
\titem{\{user\_regexp, <regexp>\}} Matches any local user with a name that
|
||||
matches \term{<regexp>} on local virtual hosts. Example:
|
||||
\begin{verbatim}
|
||||
|
@ -180,6 +180,10 @@ match_acl(ACL, JID, Host) ->
|
||||
((Host == global) andalso
|
||||
lists:member(Server, ?MYHOSTS)))
|
||||
andalso is_regexp_match(User, UR);
|
||||
{shared_group, G} ->
|
||||
mod_shared_roster:is_user_in_group({User, Server}, G, Host);
|
||||
{shared_group, G, H} ->
|
||||
mod_shared_roster:is_user_in_group({User, Server}, G, H);
|
||||
{user_regexp, UR, S} ->
|
||||
(S == Server) andalso
|
||||
is_regexp_match(User, UR);
|
||||
|
@ -18,7 +18,8 @@
|
||||
|
||||
-behaviour(cyrsasl).
|
||||
|
||||
-record(state, {step, nonce, username, authzid, get_password, auth_module}).
|
||||
-record(state, {step, nonce, username, authzid, get_password, auth_module,
|
||||
host}).
|
||||
|
||||
start(_Opts) ->
|
||||
cyrsasl:register_mechanism("DIGEST-MD5", ?MODULE, true).
|
||||
@ -26,9 +27,10 @@ start(_Opts) ->
|
||||
stop() ->
|
||||
ok.
|
||||
|
||||
mech_new(_Host, GetPassword, _CheckPassword) ->
|
||||
mech_new(Host, GetPassword, _CheckPassword) ->
|
||||
{ok, #state{step = 1,
|
||||
nonce = randoms:get_string(),
|
||||
host = Host,
|
||||
get_password = GetPassword}}.
|
||||
|
||||
mech_step(#state{step = 1, nonce = Nonce} = State, _) ->
|
||||
@ -41,7 +43,14 @@ mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
|
||||
bad ->
|
||||
{error, 'bad-protocol'};
|
||||
KeyVals ->
|
||||
DigestURI = xml:get_attr_s("digest-uri", KeyVals),
|
||||
UserName = xml:get_attr_s("username", KeyVals),
|
||||
case is_digesturi_valid(DigestURI, State#state.host) of
|
||||
false ->
|
||||
?DEBUG("User login not authorized because digest-uri "
|
||||
"seems invalid: ~p", [DigestURI]),
|
||||
{error, 'not-authorized', UserName};
|
||||
true ->
|
||||
AuthzId = xml:get_attr_s("authzid", KeyVals),
|
||||
case (State#state.get_password)(UserName) of
|
||||
{false, _} ->
|
||||
@ -64,6 +73,7 @@ mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
|
||||
{error, 'not-authorized', UserName}
|
||||
end
|
||||
end
|
||||
end
|
||||
end;
|
||||
mech_step(#state{step = 5,
|
||||
auth_module = AuthModule,
|
||||
@ -75,7 +85,6 @@ mech_step(A, B) ->
|
||||
?DEBUG("SASL DIGEST: A ~p B ~p", [A,B]),
|
||||
{error, 'bad-protocol'}.
|
||||
|
||||
|
||||
parse(S) ->
|
||||
parse1(S, "", []).
|
||||
|
||||
@ -118,6 +127,23 @@ parse4([], Key, Val, Ts) ->
|
||||
parse1([], "", [{Key, lists:reverse(Val)} | Ts]).
|
||||
|
||||
|
||||
%% @doc Check if the digest-uri is valid.
|
||||
%% RFC-2831 allows to provide the IP address in Host,
|
||||
%% however ejabberd doesn't allow that.
|
||||
%% If the service (for example jabber.example.org)
|
||||
%% is provided by several hosts (being one of them server3.example.org),
|
||||
%% then digest-uri can be like xmpp/server3.example.org/jabber.example.org
|
||||
%% In that case, ejabberd only checks the service name, not the host.
|
||||
is_digesturi_valid(DigestURICase, JabberHost) ->
|
||||
DigestURI = stringprep:tolower(DigestURICase),
|
||||
case catch string:tokens(DigestURI, "/") of
|
||||
["xmpp", Host] when Host == JabberHost ->
|
||||
true;
|
||||
["xmpp", _Host, ServName] when ServName == JabberHost ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
|
||||
|
||||
|
@ -139,6 +139,7 @@ set_password(User, Server, Password) ->
|
||||
Res
|
||||
end, {error, not_allowed}, auth_modules(Server)).
|
||||
|
||||
%% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, not_allowed}
|
||||
try_register(_User, _Server, "") ->
|
||||
%% We do not allow empty password
|
||||
{error, not_allowed};
|
||||
@ -149,12 +150,19 @@ try_register(User, Server, Password) ->
|
||||
false ->
|
||||
case lists:member(exmpp_stringprep:nameprep(Server), ?MYHOSTS) of
|
||||
true ->
|
||||
lists:foldl(
|
||||
Res = lists:foldl(
|
||||
fun(_M, {atomic, ok} = Res) ->
|
||||
Res;
|
||||
(M, _) ->
|
||||
M:try_register(User, Server, Password)
|
||||
end, {error, not_allowed}, auth_modules(Server));
|
||||
end, {error, not_allowed}, auth_modules(Server)),
|
||||
case Res of
|
||||
{atomic, ok} ->
|
||||
ejabberd_hooks:run(register_user, Server,
|
||||
[User, Server]),
|
||||
{atomic, ok};
|
||||
_ -> Res
|
||||
end;
|
||||
false ->
|
||||
{error, not_allowed}
|
||||
end
|
||||
@ -251,17 +259,37 @@ is_user_exists_in_other_modules(Module, User, Server) ->
|
||||
M:is_user_exists(User, Server)
|
||||
end, auth_modules(Server)--[Module]).
|
||||
|
||||
%% @spec (User, Server) -> ok | error | {error, not_allowed}
|
||||
%% Remove user.
|
||||
%% Note: it may return ok even if there was some problem removing the user.
|
||||
remove_user(User, Server) ->
|
||||
lists:foreach(
|
||||
R = lists:foreach(
|
||||
fun(M) ->
|
||||
M:remove_user(User, Server)
|
||||
end, auth_modules(Server)).
|
||||
end, auth_modules(Server)),
|
||||
case R of
|
||||
ok -> ejabberd_hooks:run(remove_user, jlib:nameprep(Server), [User, Server]);
|
||||
_ -> none
|
||||
end,
|
||||
R.
|
||||
|
||||
%% @spec (User, Server, Password) -> ok | not_exists | not_allowed | bad_request | error
|
||||
%% Try to remove user if the provided password is correct.
|
||||
%% The removal is attempted in each auth method provided:
|
||||
%% when one returns 'ok' the loop stops;
|
||||
%% if no method returns 'ok' then it returns the error message indicated by the last method attempted.
|
||||
remove_user(User, Server, Password) ->
|
||||
lists:foreach(
|
||||
fun(M) ->
|
||||
R = lists:foldl(
|
||||
fun(_M, ok = Res) ->
|
||||
Res;
|
||||
(M, _) ->
|
||||
M:remove_user(User, Server, Password)
|
||||
end, auth_modules(Server)).
|
||||
end, error, auth_modules(Server)),
|
||||
case R of
|
||||
ok -> ejabberd_hooks:run(remove_user, jlib:nameprep(Server), [User, Server]);
|
||||
_ -> none
|
||||
end,
|
||||
R.
|
||||
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
|
@ -112,6 +112,7 @@ set_password(User, Server, Password) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
%% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, invalid_jid} | {aborted, Reason}
|
||||
try_register(User, Server, Password) ->
|
||||
LUser = exmpp_stringprep:nodeprep(User),
|
||||
LServer = exmpp_stringprep:nameprep(Server),
|
||||
@ -252,6 +253,9 @@ is_user_exists(User, Server) ->
|
||||
false
|
||||
end.
|
||||
|
||||
%% @spec (User, Server) -> ok
|
||||
%% Remove user.
|
||||
%% Note: it returns ok even if there was some problem removing the user.
|
||||
remove_user(User, Server) ->
|
||||
try
|
||||
LUser = exmpp_stringprep:nodeprep(User),
|
||||
@ -261,14 +265,14 @@ remove_user(User, Server) ->
|
||||
mnesia:delete({passwd, US})
|
||||
end,
|
||||
mnesia:transaction(F),
|
||||
ejabberd_hooks:run(remove_user,
|
||||
list_to_binary(LServer),
|
||||
[list_to_binary(User), list_to_binary(Server)])
|
||||
ok
|
||||
catch
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
%% @spec (User, Server, Password) -> ok | not_exists | not_allowed | bad_request
|
||||
%% Remove user if the provided password is correct.
|
||||
remove_user(User, Server, Password) ->
|
||||
try
|
||||
LUser = exmpp_stringprep:nodeprep(User),
|
||||
@ -287,9 +291,6 @@ remove_user(User, Server, Password) ->
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, ok} ->
|
||||
ejabberd_hooks:run(remove_user,
|
||||
list_to_binary(LServer),
|
||||
[list_to_binary(User), list_to_binary(Server)]),
|
||||
ok;
|
||||
{atomic, Res} ->
|
||||
Res;
|
||||
|
@ -153,6 +153,7 @@ check_password(User, Server, Password, _StreamID, _Digest) ->
|
||||
set_password(_User, _Server, _Password) ->
|
||||
{error, not_allowed}.
|
||||
|
||||
%% @spec (User, Server, Password) -> {error, not_allowed}
|
||||
try_register(_User, _Server, _Password) ->
|
||||
{error, not_allowed}.
|
||||
|
||||
|
@ -117,6 +117,7 @@ set_password(User, Server, Password) ->
|
||||
end.
|
||||
|
||||
|
||||
%% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, invalid_jid}
|
||||
try_register(User, Server, Password) ->
|
||||
try
|
||||
LUser = exmpp_stringprep:nodeprep(User),
|
||||
@ -225,19 +226,23 @@ is_user_exists(User, Server) ->
|
||||
false
|
||||
end.
|
||||
|
||||
%% @spec (User, Server) -> ok | error
|
||||
%% Remove user.
|
||||
%% Note: it may return ok even if there was some problem removing the user.
|
||||
remove_user(User, Server) ->
|
||||
try
|
||||
LUser = exmpp_stringprep:nodeprep(User),
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
LServer = exmpp_stringprep:nameprep(Server),
|
||||
catch odbc_queries:del_user(LServer, Username),
|
||||
ejabberd_hooks:run(remove_user, list_to_binary(LServer),
|
||||
[list_to_binary(User), list_to_binary(Server)])
|
||||
ok
|
||||
catch
|
||||
_ ->
|
||||
error
|
||||
end.
|
||||
|
||||
%% @spec (User, Server, Password) -> ok | error | not_exists | not_allowed
|
||||
%% Remove user if the provided password is correct.
|
||||
remove_user(User, Server, Password) ->
|
||||
try
|
||||
LUser = exmpp_stringprep:nodeprep(User),
|
||||
@ -249,8 +254,6 @@ remove_user(User, Server, Password) ->
|
||||
LServer, Username, Pass),
|
||||
case Result of
|
||||
{selected, ["password"], [{Password}]} ->
|
||||
ejabberd_hooks:run(remove_user, list_to_binary(LServer),
|
||||
[list_to_binary(User), list_to_binary(Server)]),
|
||||
ok;
|
||||
{selected, ["password"], []} ->
|
||||
not_exists;
|
||||
|
@ -91,7 +91,7 @@ remove_user(_User, _Server) ->
|
||||
{error, not_allowed}.
|
||||
|
||||
remove_user(_User, _Server, _Password) ->
|
||||
{error, not_allowed}.
|
||||
not_allowed.
|
||||
|
||||
plain_password_required() ->
|
||||
true.
|
||||
|
@ -55,7 +55,10 @@ start() ->
|
||||
{attributes, record_info(fields, local_config)}]),
|
||||
mnesia:add_table_copy(local_config, node(), ram_copies),
|
||||
Config = get_ejabberd_config_path(),
|
||||
load_file(Config).
|
||||
load_file(Config),
|
||||
%% This start time is used by mod_last:
|
||||
add_local_option(node_start, now()),
|
||||
ok.
|
||||
|
||||
%% @doc Get the filename of the ejabberd configuration file.
|
||||
%% The filename can be specified with: erl -config "/path/to/ejabberd.cfg".
|
||||
@ -76,7 +79,7 @@ get_ejabberd_config_path() ->
|
||||
|
||||
%% @doc Load the ejabberd configuration file.
|
||||
%% It also includes additional configuration files and replaces macros.
|
||||
%% @spec (File::string()) -> [term()]
|
||||
%% @spec (File::string()) -> ok
|
||||
load_file(File) ->
|
||||
Terms = get_plain_terms_file(File),
|
||||
State = lists:foldl(fun search_hosts/2, #state{}, Terms),
|
||||
|
69
src/jlib.erl
69
src/jlib.erl
@ -36,6 +36,9 @@
|
||||
decode_base64/1,
|
||||
encode_base64/1,
|
||||
ip_to_list/1,
|
||||
rsm_encode/1,
|
||||
rsm_encode/2,
|
||||
rsm_decode/1,
|
||||
from_old_jid/1,
|
||||
short_jid/1,
|
||||
short_bare_jid/1,
|
||||
@ -44,6 +47,8 @@
|
||||
|
||||
-include_lib("exmpp/include/exmpp.hrl").
|
||||
|
||||
-include("jlib.hrl").
|
||||
|
||||
|
||||
parse_xdata_submit(#xmlel{attrs = Attrs, children = Els}) ->
|
||||
case exmpp_xml:get_attribute_from_list(Attrs, 'type', "") of
|
||||
@ -75,6 +80,70 @@ parse_xdata_values([#xmlel{name = 'value', children = SubEls} | Els], Res) ->
|
||||
parse_xdata_values([_ | Els], Res) ->
|
||||
parse_xdata_values(Els, Res).
|
||||
|
||||
rsm_decode(#iq{payload=SubEl})->
|
||||
rsm_decode(SubEl);
|
||||
rsm_decode(#xmlel{}=SubEl)->
|
||||
case exmpp_xml:get_element(SubEl, 'set') of
|
||||
undefined ->
|
||||
none;
|
||||
#xmlelement{name = 'set', children = SubEls}->
|
||||
lists:foldl(fun rsm_parse_element/2, #rsm_in{}, SubEls)
|
||||
end.
|
||||
|
||||
rsm_parse_element(#xmlel{name = 'max'}=Elem, RsmIn)->
|
||||
CountStr = exmpp_xml:get_cdata_as_list(Elem),
|
||||
{Count, _} = string:to_integer(CountStr),
|
||||
RsmIn#rsm_in{max=Count};
|
||||
|
||||
rsm_parse_element(#xmlel{name = 'before'}=Elem, RsmIn)->
|
||||
UID = exmpp_xml:get_cdata_as_list(Elem),
|
||||
RsmIn#rsm_in{direction=before, id=UID};
|
||||
|
||||
rsm_parse_element(#xmlel{name = 'after'}=Elem, RsmIn)->
|
||||
UID = exmpp_xml:get_cdata_as_list(Elem),
|
||||
RsmIn#rsm_in{direction=aft, id=UID};
|
||||
|
||||
rsm_parse_element(#xmlel{name = 'index'}=Elem, RsmIn)->
|
||||
IndexStr = exmpp_xml:get_cdata_as_list(Elem),
|
||||
{Index, _} = string:to_integer(IndexStr),
|
||||
RsmIn#rsm_in{index=Index};
|
||||
|
||||
|
||||
rsm_parse_element(_, RsmIn)->
|
||||
RsmIn.
|
||||
|
||||
rsm_encode(#iq{payload=SubEl}=IQ_Rec,RsmOut)->
|
||||
Set = #xmlel{ns = ?NS_RSM, name = 'set', children =
|
||||
lists:reverse(rsm_encode_out(RsmOut))},
|
||||
New = exmpp_xml:prepend_child(SubEl, Set),
|
||||
IQ_Rec#iq{payload=New}.
|
||||
|
||||
rsm_encode(none)->
|
||||
[];
|
||||
rsm_encode(RsmOut)->
|
||||
[#xmlel{ns = ?NS_RSM, name = 'set', children = lists:reverse(rsm_encode_out(RsmOut))}].
|
||||
rsm_encode_out(#rsm_out{count=Count, index=Index, first=First, last=Last})->
|
||||
El = rsm_encode_first(First, Index, []),
|
||||
El2 = rsm_encode_last(Last,El),
|
||||
rsm_encode_count(Count, El2).
|
||||
|
||||
rsm_encode_first(undefined, undefined, Arr) ->
|
||||
Arr;
|
||||
rsm_encode_first(First, undefined, Arr) ->
|
||||
[#xmlel{ns = ?NS_RSM, name = 'first', children = [#xmlcdata{cdata = list_to_binary(First)}]}|Arr];
|
||||
rsm_encode_first(First, Index, Arr) ->
|
||||
[#xmlel{ns = ?NS_RSM, name = 'first', attrs = [#xmlattr{name = 'index', value = i2b(Index)}], children = [#xmlcdata{cdata = list_to_binary(First)}]}|Arr].
|
||||
|
||||
rsm_encode_last(undefined, Arr) -> Arr;
|
||||
rsm_encode_last(Last, Arr) ->
|
||||
[#xmlel{ns = ?NS_RSM, name = 'last', children = [#xmlcdata{cdata = list_to_binary(Last)}]}|Arr].
|
||||
|
||||
rsm_encode_count(undefined, Arr)-> Arr;
|
||||
rsm_encode_count(Count, Arr)->
|
||||
[#xmlel{ns = ?NS_RSM, name = 'count', children = [#xmlcdata{cdata = i2b(Count)}]} | Arr].
|
||||
|
||||
i2b(I) when is_integer(I) -> list_to_binary(integer_to_list(I));
|
||||
i2b(L) when is_list(L) -> list_to_binary(L).
|
||||
|
||||
timestamp_to_iso({{Year, Month, Day}, {Hour, Minute, Second}}) ->
|
||||
lists:flatten(
|
||||
|
289
src/jlib.hrl
289
src/jlib.hrl
@ -19,290 +19,5 @@
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-define(NS_DISCO_ITEMS, "http://jabber.org/protocol/disco#items").
|
||||
-define(NS_DISCO_INFO, "http://jabber.org/protocol/disco#info").
|
||||
-define(NS_VCARD, "vcard-temp").
|
||||
-define(NS_AUTH, "jabber:iq:auth").
|
||||
-define(NS_AUTH_ERROR, "jabber:iq:auth:error").
|
||||
-define(NS_REGISTER, "jabber:iq:register").
|
||||
-define(NS_SEARCH, "jabber:iq:search").
|
||||
-define(NS_ROSTER, "jabber:iq:roster").
|
||||
-define(NS_PRIVACY, "jabber:iq:privacy").
|
||||
-define(NS_PRIVATE, "jabber:iq:private").
|
||||
-define(NS_VERSION, "jabber:iq:version").
|
||||
-define(NS_TIME, "jabber:iq:time").
|
||||
-define(NS_LAST, "jabber:iq:last").
|
||||
-define(NS_XDATA, "jabber:x:data").
|
||||
-define(NS_IQDATA, "jabber:iq:data").
|
||||
-define(NS_DELAY, "jabber:x:delay").
|
||||
-define(NS_EXPIRE, "jabber:x:expire").
|
||||
-define(NS_EVENT, "jabber:x:event").
|
||||
-define(NS_XCONFERENCE, "jabber:x:conference").
|
||||
-define(NS_STATS, "http://jabber.org/protocol/stats").
|
||||
-define(NS_MUC, "http://jabber.org/protocol/muc").
|
||||
-define(NS_MUC_USER, "http://jabber.org/protocol/muc#user").
|
||||
-define(NS_MUC_ADMIN, "http://jabber.org/protocol/muc#admin").
|
||||
-define(NS_MUC_OWNER, "http://jabber.org/protocol/muc#owner").
|
||||
-define(NS_PUBSUB, "http://jabber.org/protocol/pubsub").
|
||||
-define(NS_PUBSUB_EVENT, "http://jabber.org/protocol/pubsub#event").
|
||||
-define(NS_PUBSUB_OWNER, "http://jabber.org/protocol/pubsub#owner").
|
||||
-define(NS_PUBSUB_NMI, "http://jabber.org/protocol/pubsub#node-meta-info").
|
||||
-define(NS_PUBSUB_ERRORS,"http://jabber.org/protocol/pubsub#errors").
|
||||
-define(NS_PUBSUB_NODE_CONFIG, "http://jabber.org/protocol/pubsub#node_config").
|
||||
-define(NS_PUBSUB_SUB_AUTH, "http://jabber.org/protocol/pubsub#subscribe_authorization").
|
||||
-define(NS_COMMANDS, "http://jabber.org/protocol/commands").
|
||||
-define(NS_BYTESTREAMS, "http://jabber.org/protocol/bytestreams").
|
||||
-define(NS_ADMIN, "http://jabber.org/protocol/admin").
|
||||
|
||||
-define(NS_EJABBERD_CONFIG, "ejabberd:config").
|
||||
|
||||
-define(NS_STREAM, "http://etherx.jabber.org/streams").
|
||||
|
||||
-define(NS_STANZAS, "urn:ietf:params:xml:ns:xmpp-stanzas").
|
||||
-define(NS_STREAMS, "urn:ietf:params:xml:ns:xmpp-streams").
|
||||
|
||||
-define(NS_TLS, "urn:ietf:params:xml:ns:xmpp-tls").
|
||||
-define(NS_SASL, "urn:ietf:params:xml:ns:xmpp-sasl").
|
||||
-define(NS_SESSION, "urn:ietf:params:xml:ns:xmpp-session").
|
||||
-define(NS_BIND, "urn:ietf:params:xml:ns:xmpp-bind").
|
||||
|
||||
-define(NS_FEATURE_IQAUTH, "http://jabber.org/features/iq-auth").
|
||||
-define(NS_FEATURE_IQREGISTER, "http://jabber.org/features/iq-register").
|
||||
-define(NS_FEATURE_COMPRESS, "http://jabber.org/features/compress").
|
||||
|
||||
-define(NS_COMPRESS, "http://jabber.org/protocol/compress").
|
||||
|
||||
-define(NS_CAPS, "http://jabber.org/protocol/caps").
|
||||
|
||||
% TODO: remove "code" attribute (currently it used for backward-compatibility)
|
||||
-define(STANZA_ERROR(Code, Type, Condition),
|
||||
{xmlelement, "error",
|
||||
[{"code", Code}, {"type", Type}],
|
||||
[{xmlelement, Condition, [{"xmlns", ?NS_STANZAS}], []}]}).
|
||||
|
||||
-define(ERR_BAD_REQUEST,
|
||||
?STANZA_ERROR("400", "modify", "bad-request")).
|
||||
-define(ERR_CONFLICT,
|
||||
?STANZA_ERROR("409", "cancel", "conflict")).
|
||||
-define(ERR_FEATURE_NOT_IMPLEMENTED,
|
||||
?STANZA_ERROR("501", "cancel", "feature-not-implemented")).
|
||||
-define(ERR_FORBIDDEN,
|
||||
?STANZA_ERROR("403", "auth", "forbidden")).
|
||||
-define(ERR_GONE,
|
||||
?STANZA_ERROR("302", "modify", "gone")).
|
||||
-define(ERR_INTERNAL_SERVER_ERROR,
|
||||
?STANZA_ERROR("500", "wait", "internal-server-error")).
|
||||
-define(ERR_ITEM_NOT_FOUND,
|
||||
?STANZA_ERROR("404", "cancel", "item-not-found")).
|
||||
-define(ERR_JID_MALFORMED,
|
||||
?STANZA_ERROR("400", "modify", "jid-malformed")).
|
||||
-define(ERR_NOT_ACCEPTABLE,
|
||||
?STANZA_ERROR("406", "modify", "not-acceptable")).
|
||||
-define(ERR_NOT_ALLOWED,
|
||||
?STANZA_ERROR("405", "cancel", "not-allowed")).
|
||||
-define(ERR_NOT_AUTHORIZED,
|
||||
?STANZA_ERROR("401", "auth", "not-authorized")).
|
||||
-define(ERR_PAYMENT_REQUIRED,
|
||||
?STANZA_ERROR("402", "auth", "payment-required")).
|
||||
-define(ERR_RECIPIENT_UNAVAILABLE,
|
||||
?STANZA_ERROR("404", "wait", "recipient-unavailable")).
|
||||
-define(ERR_REDIRECT,
|
||||
?STANZA_ERROR("302", "modify", "redirect")).
|
||||
-define(ERR_REGISTRATION_REQUIRED,
|
||||
?STANZA_ERROR("407", "auth", "registration-required")).
|
||||
-define(ERR_REMOTE_SERVER_NOT_FOUND,
|
||||
?STANZA_ERROR("404", "cancel", "remote-server-not-found")).
|
||||
-define(ERR_REMOTE_SERVER_TIMEOUT,
|
||||
?STANZA_ERROR("504", "wait", "remote-server-timeout")).
|
||||
-define(ERR_RESOURCE_CONSTRAINT,
|
||||
?STANZA_ERROR("500", "wait", "resource-constraint")).
|
||||
-define(ERR_SERVICE_UNAVAILABLE,
|
||||
?STANZA_ERROR("503", "cancel", "service-unavailable")).
|
||||
-define(ERR_SUBSCRIPTION_REQUIRED,
|
||||
?STANZA_ERROR("407", "auth", "subscription-required")).
|
||||
-define(ERR_UNEXPECTED_REQUEST,
|
||||
?STANZA_ERROR("400", "wait", "unexpected-request")).
|
||||
%-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)).
|
||||
|
||||
% Auth stanza errors
|
||||
-define(ERR_AUTH_NO_RESOURCE_PROVIDED(Lang),
|
||||
?ERRT_NOT_ACCEPTABLE(Lang, "No resource provided")).
|
||||
-define(ERR_AUTH_BAD_RESOURCE_FORMAT(Lang),
|
||||
?ERRT_NOT_ACCEPTABLE(Lang, "Illegal resource format")).
|
||||
-define(ERR_AUTH_RESOURCE_CONFLICT(Lang),
|
||||
?ERRT_CONFLICT(Lang, "Resource conflict")).
|
||||
|
||||
|
||||
-define(STREAM_ERROR(Condition),
|
||||
{xmlelement, "stream:error",
|
||||
[],
|
||||
[{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}], []}]}).
|
||||
|
||||
-define(SERR_BAD_FORMAT,
|
||||
?STREAM_ERROR("bad-format")).
|
||||
-define(SERR_BAD_NAMESPACE_PREFIX,
|
||||
?STREAM_ERROR("bad-namespace-prefix")).
|
||||
-define(SERR_CONFLICT,
|
||||
?STREAM_ERROR("conflict")).
|
||||
-define(SERR_CONNECTION_TIMEOUT,
|
||||
?STREAM_ERROR("connection-timeout")).
|
||||
-define(SERR_HOST_GONE,
|
||||
?STREAM_ERROR("host-gone")).
|
||||
-define(SERR_HOST_UNKNOWN,
|
||||
?STREAM_ERROR("host-unknown")).
|
||||
-define(SERR_IMPROPER_ADDRESSING,
|
||||
?STREAM_ERROR("improper-addressing")).
|
||||
-define(SERR_INTERNAL_SERVER_ERROR,
|
||||
?STREAM_ERROR("internal-server-error")).
|
||||
-define(SERR_INVALID_FROM,
|
||||
?STREAM_ERROR("invalid-from")).
|
||||
-define(SERR_INVALID_ID,
|
||||
?STREAM_ERROR("invalid-id")).
|
||||
-define(SERR_INVALID_NAMESPACE,
|
||||
?STREAM_ERROR("invalid-namespace")).
|
||||
-define(SERR_INVALID_XML,
|
||||
?STREAM_ERROR("invalid-xml")).
|
||||
-define(SERR_NOT_AUTHORIZED,
|
||||
?STREAM_ERROR("not-authorized")).
|
||||
-define(SERR_POLICY_VIOLATION,
|
||||
?STREAM_ERROR("policy-violation")).
|
||||
-define(SERR_REMOTE_CONNECTION_FAILED,
|
||||
?STREAM_ERROR("remote-connection-failed")).
|
||||
-define(SERR_RESOURSE_CONSTRAINT,
|
||||
?STREAM_ERROR("resource-constraint")).
|
||||
-define(SERR_RESTRICTED_XML,
|
||||
?STREAM_ERROR("restricted-xml")).
|
||||
% TODO: include hostname or IP
|
||||
-define(SERR_SEE_OTHER_HOST,
|
||||
?STREAM_ERROR("see-other-host")).
|
||||
-define(SERR_SYSTEM_SHUTDOWN,
|
||||
?STREAM_ERROR("system-shutdown")).
|
||||
-define(SERR_UNSUPPORTED_ENCODING,
|
||||
?STREAM_ERROR("unsupported-encoding")).
|
||||
-define(SERR_UNSUPPORTED_STANZA_TYPE,
|
||||
?STREAM_ERROR("unsupported-stanza-type")).
|
||||
-define(SERR_UNSUPPORTED_VERSION,
|
||||
?STREAM_ERROR("unsupported-version")).
|
||||
-define(SERR_XML_NOT_WELL_FORMED,
|
||||
?STREAM_ERROR("xml-not-well-formed")).
|
||||
%-define(SERR_,
|
||||
% ?STREAM_ERROR("")).
|
||||
|
||||
-define(STREAM_ERRORT(Condition, Lang, Text),
|
||||
{xmlelement, "stream:error",
|
||||
[],
|
||||
[{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}], []},
|
||||
{xmlelement, "text", [{"xml:lang", Lang}, {"xmlns", ?NS_STREAMS}],
|
||||
[{xmlcdata, translate:translate(Lang, Text)}]}]}).
|
||||
|
||||
-define(SERRT_BAD_FORMAT(Lang, Text),
|
||||
?STREAM_ERRORT("bad-format", Lang, Text)).
|
||||
-define(SERRT_BAD_NAMESPACE_PREFIX(Lang, Text),
|
||||
?STREAM_ERRORT("bad-namespace-prefix", Lang, Text)).
|
||||
-define(SERRT_CONFLICT(Lang, Text),
|
||||
?STREAM_ERRORT("conflict", Lang, Text)).
|
||||
-define(SERRT_CONNECTION_TIMEOUT(Lang, Text),
|
||||
?STREAM_ERRORT("connection-timeout", Lang, Text)).
|
||||
-define(SERRT_HOST_GONE(Lang, Text),
|
||||
?STREAM_ERRORT("host-gone", Lang, Text)).
|
||||
-define(SERRT_HOST_UNKNOWN(Lang, Text),
|
||||
?STREAM_ERRORT("host-unknown", Lang, Text)).
|
||||
-define(SERRT_IMPROPER_ADDRESSING(Lang, Text),
|
||||
?STREAM_ERRORT("improper-addressing", Lang, Text)).
|
||||
-define(SERRT_INTERNAL_SERVER_ERROR(Lang, Text),
|
||||
?STREAM_ERRORT("internal-server-error", Lang, Text)).
|
||||
-define(SERRT_INVALID_FROM(Lang, Text),
|
||||
?STREAM_ERRORT("invalid-from", Lang, Text)).
|
||||
-define(SERRT_INVALID_ID(Lang, Text),
|
||||
?STREAM_ERRORT("invalid-id", Lang, Text)).
|
||||
-define(SERRT_INVALID_NAMESPACE(Lang, Text),
|
||||
?STREAM_ERRORT("invalid-namespace", Lang, Text)).
|
||||
-define(SERRT_INVALID_XML(Lang, Text),
|
||||
?STREAM_ERRORT("invalid-xml", Lang, Text)).
|
||||
-define(SERRT_NOT_AUTHORIZED(Lang, Text),
|
||||
?STREAM_ERRORT("not-authorized", Lang, Text)).
|
||||
-define(SERRT_POLICY_VIOLATION(Lang, Text),
|
||||
?STREAM_ERRORT("policy-violation", Lang, Text)).
|
||||
-define(SERRT_REMOTE_CONNECTION_FAILED(Lang, Text),
|
||||
?STREAM_ERRORT("remote-connection-failed", Lang, Text)).
|
||||
-define(SERRT_RESOURSE_CONSTRAINT(Lang, Text),
|
||||
?STREAM_ERRORT("resource-constraint", Lang, Text)).
|
||||
-define(SERRT_RESTRICTED_XML(Lang, Text),
|
||||
?STREAM_ERRORT("restricted-xml", Lang, Text)).
|
||||
% TODO: include hostname or IP
|
||||
-define(SERRT_SEE_OTHER_HOST(Lang, Text),
|
||||
?STREAM_ERRORT("see-other-host", Lang, Text)).
|
||||
-define(SERRT_SYSTEM_SHUTDOWN(Lang, Text),
|
||||
?STREAM_ERRORT("system-shutdown", Lang, Text)).
|
||||
-define(SERRT_UNSUPPORTED_ENCODING(Lang, Text),
|
||||
?STREAM_ERRORT("unsupported-encoding", Lang, Text)).
|
||||
-define(SERRT_UNSUPPORTED_STANZA_TYPE(Lang, Text),
|
||||
?STREAM_ERRORT("unsupported-stanza-type", Lang, Text)).
|
||||
-define(SERRT_UNSUPPORTED_VERSION(Lang, Text),
|
||||
?STREAM_ERRORT("unsupported-version", Lang, Text)).
|
||||
-define(SERRT_XML_NOT_WELL_FORMED(Lang, Text),
|
||||
?STREAM_ERRORT("xml-not-well-formed", Lang, Text)).
|
||||
%-define(SERRT_(Lang, Text),
|
||||
% ?STREAM_ERRORT("", Lang, Text)).
|
||||
|
||||
|
||||
-record(jid, {user, server, resource,
|
||||
luser, lserver, lresource}).
|
||||
|
||||
-record(iq, {id = "",
|
||||
type,
|
||||
xmlns = "",
|
||||
lang = "",
|
||||
sub_el}).
|
||||
|
||||
-record(rsm_in, {max, direction, id, index}).
|
||||
-record(rsm_out, {count, index, first, last}).
|
||||
|
@ -71,14 +71,36 @@ stop(Host) ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, HostB, ?NS_LAST_ACTIVITY),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, HostB, ?NS_LAST_ACTIVITY).
|
||||
|
||||
%%%
|
||||
%%% Uptime of ejabberd node
|
||||
%%%
|
||||
|
||||
process_local_iq(_From, _To, #iq{type = get} = IQ_Rec) ->
|
||||
Sec = trunc(element(1, erlang:statistics(wall_clock))/1000),
|
||||
Sec = get_node_uptime(),
|
||||
Response = #xmlel{ns = ?NS_LAST_ACTIVITY, name = 'query', attrs =
|
||||
[#xmlattr{name = 'seconds', value = list_to_binary(integer_to_list(Sec))}]},
|
||||
exmpp_iq:result(IQ_Rec, Response);
|
||||
process_local_iq(_From, _To, #iq{type = set} = IQ_Rec) ->
|
||||
exmpp_iq:error(IQ_Rec, 'not-allowed').
|
||||
|
||||
%% @spec () -> integer()
|
||||
%% @doc Get the uptime of the ejabberd node, expressed in seconds.
|
||||
%% When ejabberd is starting, ejabberd_config:start/0 stores the datetime.
|
||||
get_node_uptime() ->
|
||||
case ejabberd_config:get_local_option(node_start) of
|
||||
{_, _, _} = StartNow ->
|
||||
now_to_seconds(now()) - now_to_seconds(StartNow);
|
||||
_undefined ->
|
||||
trunc(element(1, erlang:statistics(wall_clock))/1000)
|
||||
end.
|
||||
|
||||
now_to_seconds({MegaSecs, Secs, _MicroSecs}) ->
|
||||
MegaSecs * 1000000 + Secs.
|
||||
|
||||
|
||||
%%%
|
||||
%%% Serve queries about user last online
|
||||
%%%
|
||||
|
||||
process_sm_iq(From, To, #iq{type = get} = IQ_Rec) ->
|
||||
{Subscription, _Groups} =
|
||||
@ -117,8 +139,7 @@ get_last(IQ_Rec, LUser, LServer) ->
|
||||
[] ->
|
||||
exmpp_iq:error(IQ_Rec, 'service-unavailable');
|
||||
[#last_activity{timestamp = TimeStamp, status = Status}] ->
|
||||
{MegaSecs, Secs, _MicroSecs} = now(),
|
||||
TimeStamp2 = MegaSecs * 1000000 + Secs,
|
||||
TimeStamp2 = now_to_seconds(now()),
|
||||
Sec = TimeStamp2 - TimeStamp,
|
||||
Response = #xmlel{ns = ?NS_LAST_ACTIVITY, name = 'query',
|
||||
attrs = [#xmlattr{name = 'seconds', value = list_to_binary(integer_to_list(Sec))}],
|
||||
@ -129,8 +150,7 @@ get_last(IQ_Rec, LUser, LServer) ->
|
||||
|
||||
|
||||
on_presence_update(User, Server, _Resource, Status) ->
|
||||
{MegaSecs, Secs, _MicroSecs} = now(),
|
||||
TimeStamp = MegaSecs * 1000000 + Secs,
|
||||
TimeStamp = now_to_seconds(now()),
|
||||
store_last_info(User, Server, TimeStamp, Status).
|
||||
|
||||
store_last_info(User, Server, TimeStamp, Status)
|
||||
@ -148,7 +168,8 @@ store_last_info(User, Server, TimeStamp, Status)
|
||||
ok
|
||||
end.
|
||||
|
||||
%% Returns: {ok, Timestamp, Status} | not_found
|
||||
%% @spec (LUser::string(), LServer::string() ->
|
||||
%% {ok, Timestamp::integer(), Status::string()} | not_found
|
||||
get_last_info(LUser, LServer) when is_binary(LUser), is_binary(LServer) ->
|
||||
case catch mnesia:dirty_read(last_activity, {LUser, LServer}) of
|
||||
{'EXIT', _Reason} ->
|
||||
|
@ -64,14 +64,36 @@ stop(Host) ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, HostB, ?NS_LAST_ACTIVITY),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, HostB, ?NS_LAST_ACTIVITY).
|
||||
|
||||
%%%
|
||||
%%% Uptime of ejabberd node
|
||||
%%%
|
||||
|
||||
process_local_iq(_From, _To, #iq{type = get} = IQ_Rec) ->
|
||||
Sec = trunc(element(1, erlang:statistics(wall_clock))/1000),
|
||||
Sec = get_node_uptime(),
|
||||
Response = #xmlel{ns = ?NS_LAST_ACTIVITY, name = 'query', attrs =
|
||||
[#xmlattr{name = 'seconds', value = list_to_binary(integer_to_list(Sec))}]},
|
||||
exmpp_iq:result(IQ_Rec, Response);
|
||||
process_local_iq(_From, _To, #iq{type = set} = IQ_Rec) ->
|
||||
exmpp_iq:error(IQ_Rec, 'not-allowed').
|
||||
|
||||
%% @spec () -> integer()
|
||||
%% @doc Get the uptime of the ejabberd node, expressed in seconds.
|
||||
%% When ejabberd is starting, ejabberd_config:start/0 stores the datetime.
|
||||
get_node_uptime() ->
|
||||
case ejabberd_config:get_local_option(node_start) of
|
||||
{_, _, _} = StartNow ->
|
||||
now_to_seconds(now()) - now_to_seconds(StartNow);
|
||||
_undefined ->
|
||||
trunc(element(1, erlang:statistics(wall_clock))/1000)
|
||||
end.
|
||||
|
||||
now_to_seconds({MegaSecs, Secs, _MicroSecs}) ->
|
||||
MegaSecs * 1000000 + Secs.
|
||||
|
||||
|
||||
%%%
|
||||
%%% Serve queries about user last online
|
||||
%%%
|
||||
process_sm_iq(From, To, #iq{type = get} = IQ_Rec) ->
|
||||
User = exmpp_jid:lnode_as_list(To),
|
||||
Server = exmpp_jid:ldomain_as_list(To),
|
||||
@ -112,8 +134,7 @@ get_last(IQ_Rec, LUser, LServer) ->
|
||||
{selected, ["seconds","state"], [{STimeStamp, Status}]} ->
|
||||
case catch list_to_integer(STimeStamp) of
|
||||
TimeStamp when is_integer(TimeStamp) ->
|
||||
{MegaSecs, Secs, _MicroSecs} = now(),
|
||||
TimeStamp2 = MegaSecs * 1000000 + Secs,
|
||||
TimeStamp2 = now_to_seconds(now()),
|
||||
Sec = TimeStamp2 - TimeStamp,
|
||||
Response = #xmlel{ns = ?NS_LAST_ACTIVITY, name = 'query',
|
||||
attrs = [#xmlattr{name = 'seconds', value = list_to_binary(integer_to_list(Sec))}],
|
||||
@ -127,8 +148,7 @@ get_last(IQ_Rec, LUser, LServer) ->
|
||||
end.
|
||||
|
||||
on_presence_update(User, Server, _Resource, Status) ->
|
||||
{MegaSecs, Secs, _MicroSecs} = now(),
|
||||
TimeStamp = MegaSecs * 1000000 + Secs,
|
||||
TimeStamp = now_to_seconds(now()),
|
||||
store_last_info(User, Server, TimeStamp, Status).
|
||||
|
||||
store_last_info(User, Server, TimeStamp, Status)
|
||||
@ -148,7 +168,8 @@ store_last_info(User, Server, TimeStamp, Status)
|
||||
ok
|
||||
end.
|
||||
|
||||
%% Returns: {ok, Timestamp, Status} | not_found
|
||||
%% @spec (LUser::string(), LServer::string() ->
|
||||
%% {ok, Timestamp::integer(), Status::string()} | not_found
|
||||
get_last_info(LUser, LServer) ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
case catch odbc_queries:get_last(LServer, Username) of
|
||||
|
@ -48,6 +48,7 @@
|
||||
-include_lib("exmpp/include/exmpp.hrl").
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
|
||||
-record(muc_room, {name_host, opts}).
|
||||
@ -125,11 +126,11 @@ forget_room(Host, Name) when is_binary(Host), is_binary(Name) ->
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
process_iq_disco_items(Host, From, To, #iq{} = IQ) when is_binary(Host) ->
|
||||
Lang = exmpp_stanza:get_lang(IQ),
|
||||
process_iq_disco_items(Host, From, To, #iq{lang = Lang} = IQ) when is_binary(Host) ->
|
||||
Rsm = jlib:rsm_decode(IQ),
|
||||
Res = exmpp_iq:result(IQ, #xmlel{ns = ?NS_DISCO_ITEMS,
|
||||
name = 'query',
|
||||
children = iq_disco_items(Host, From, Lang)}),
|
||||
children = iq_disco_items(Host, From, Lang, Rsm)}),
|
||||
ejabberd_router:route(To,
|
||||
From,
|
||||
exmpp_iq:iq_to_xmlel(Res)).
|
||||
@ -513,13 +514,15 @@ iq_disco_info(Lang) ->
|
||||
#xmlel{ns = ?NS_DISCO_INFO, name = 'feature', attrs =
|
||||
[#xmlattr{name = 'var',
|
||||
value = ?NS_INBAND_REGISTER_s}]},
|
||||
#xmlel{ns = ?NS_DISCO_INFO, name = 'feature', attrs =
|
||||
[#xmlattr{name = 'var',
|
||||
value = ?NS_RSM_s}]},
|
||||
#xmlel{ns = ?NS_DISCO_INFO, name = 'feature', attrs =
|
||||
[#xmlattr{name = 'var',
|
||||
value = ?NS_VCARD_s}]}].
|
||||
|
||||
|
||||
|
||||
iq_disco_items(Host, From, Lang) when is_binary(Host) ->
|
||||
iq_disco_items(Host, From, Lang, none) when is_binary(Host) ->
|
||||
lists:zf(fun(#muc_online_room{name_host = {Name, _Host}, pid = Pid}) ->
|
||||
case catch gen_fsm:sync_send_all_state_event(
|
||||
Pid, {get_disco_item, From, Lang}, 100) of
|
||||
@ -535,7 +538,72 @@ iq_disco_items(Host, From, Lang) when is_binary(Host) ->
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end, get_vh_rooms(Host)).
|
||||
end, get_vh_rooms(Host));
|
||||
|
||||
iq_disco_items(Host, From, Lang, Rsm) ->
|
||||
{Rooms, RsmO} = get_vh_rooms(Host, Rsm),
|
||||
RsmOut = jlib:rsm_encode(RsmO),
|
||||
lists:zf(fun(#muc_online_room{name_host = {Name, _Host}, pid = Pid}) ->
|
||||
case catch gen_fsm:sync_send_all_state_event(
|
||||
Pid, {get_disco_item, From, Lang}, 100) of
|
||||
{item, Desc} ->
|
||||
flush(),
|
||||
{true,
|
||||
{xmlelement, "item",
|
||||
[{"jid", jlib:jid_to_string({Name, Host, ""})},
|
||||
{"name", Desc}], []}};
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end, Rooms) ++ RsmOut.
|
||||
|
||||
get_vh_rooms(Host, #rsm_in{max=M, direction=Direction, id=I, index=Index})->
|
||||
AllRooms = lists:sort(get_vh_rooms(Host)),
|
||||
Count = erlang:length(AllRooms),
|
||||
Guard = case Direction of
|
||||
_ when Index =/= undefined -> [{'==', {element, 2, '$1'}, Host}];
|
||||
aft -> [{'==', {element, 2, '$1'}, Host}, {'>=',{element, 1, '$1'} ,I}];
|
||||
before when I =/= []-> [{'==', {element, 2, '$1'}, Host}, {'=<',{element, 1, '$1'} ,I}];
|
||||
_ -> [{'==', {element, 2, '$1'}, Host}]
|
||||
end,
|
||||
L = lists:sort(
|
||||
mnesia:dirty_select(muc_online_room,
|
||||
[{#muc_online_room{name_host = '$1', _ = '_'},
|
||||
Guard,
|
||||
['$_']}])),
|
||||
L2 = if
|
||||
Index == undefined andalso Direction == before ->
|
||||
lists:reverse(lists:sublist(lists:reverse(L), 1, M));
|
||||
Index == undefined ->
|
||||
lists:sublist(L, 1, M);
|
||||
Index > Count orelse Index < 0 ->
|
||||
[];
|
||||
true ->
|
||||
lists:sublist(L, Index+1, M)
|
||||
end,
|
||||
if
|
||||
L2 == [] ->
|
||||
{L2, #rsm_out{count=Count}};
|
||||
true ->
|
||||
H = hd(L2),
|
||||
NewIndex = get_room_pos(H, AllRooms),
|
||||
T=lists:last(L2),
|
||||
{F, _}=H#muc_online_room.name_host,
|
||||
{Last, _}=T#muc_online_room.name_host,
|
||||
{L2, #rsm_out{first=F, last=Last, count=Count, index=NewIndex}}
|
||||
end.
|
||||
|
||||
%% @doc Return the position of desired room in the list of rooms.
|
||||
%% The room must exist in the list. The count starts in 0.
|
||||
%% @spec (Desired::muc_online_room(), Rooms::[muc_online_room()]) -> integer()
|
||||
get_room_pos(Desired, Rooms) ->
|
||||
get_room_pos(Desired, Rooms, 0).
|
||||
get_room_pos(Desired, [HeadRoom | _], HeadPosition)
|
||||
when (Desired#muc_online_room.name_host ==
|
||||
HeadRoom#muc_online_room.name_host) ->
|
||||
HeadPosition;
|
||||
get_room_pos(Desired, [_ | Rooms], HeadPosition) ->
|
||||
get_room_pos(Desired, Rooms, HeadPosition + 1).
|
||||
|
||||
flush() ->
|
||||
receive
|
||||
|
@ -35,6 +35,7 @@
|
||||
process_iq_get/5,
|
||||
get_user_list/3,
|
||||
check_packet/6,
|
||||
remove_user/2,
|
||||
updated_list/3]).
|
||||
|
||||
-include_lib("exmpp/include/exmpp.hrl").
|
||||
@ -59,6 +60,8 @@ start(Host, Opts) ->
|
||||
?MODULE, check_packet, 50),
|
||||
ejabberd_hooks:add(privacy_updated_list, HostB,
|
||||
?MODULE, updated_list, 50),
|
||||
ejabberd_hooks:add(remove_user, HostB,
|
||||
?MODULE, remove_user, 50),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, HostB, ?NS_PRIVACY,
|
||||
?MODULE, process_iq, IQDisc).
|
||||
|
||||
@ -74,6 +77,8 @@ stop(Host) ->
|
||||
?MODULE, check_packet, 50),
|
||||
ejabberd_hooks:delete(privacy_updated_list, HostB,
|
||||
?MODULE, updated_list, 50),
|
||||
ejabberd_hooks:delete(remove_user, HostB,
|
||||
?MODULE, remove_user, 50),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, HostB, ?NS_PRIVACY).
|
||||
|
||||
process_iq(_From, _To, IQ_Rec) ->
|
||||
@ -667,6 +672,16 @@ is_type_match(Type, Value, JID, Subscription, Groups) ->
|
||||
end.
|
||||
|
||||
|
||||
remove_user(User, Server) ->
|
||||
LUser = exmpp_stringprep:nodeprep(User),
|
||||
LServer = exmpp_stringprep:nameprep(Server),
|
||||
F = fun() ->
|
||||
mnesia:delete({privacy,
|
||||
{LUser, LServer}})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
|
||||
updated_list(_,
|
||||
#userlist{name = OldName} = Old,
|
||||
#userlist{name = NewName} = New) ->
|
||||
@ -678,7 +693,6 @@ updated_list(_,
|
||||
end.
|
||||
|
||||
|
||||
|
||||
update_table() ->
|
||||
Fields = record_info(fields, privacy),
|
||||
case mnesia:table_info(privacy, attributes) of
|
||||
|
@ -35,6 +35,7 @@
|
||||
process_iq_get/5,
|
||||
get_user_list/3,
|
||||
check_packet/6,
|
||||
remove_user/2,
|
||||
updated_list/3]).
|
||||
|
||||
-include_lib("exmpp/include/exmpp.hrl").
|
||||
@ -55,6 +56,8 @@ start(Host, Opts) ->
|
||||
?MODULE, check_packet, 50),
|
||||
ejabberd_hooks:add(privacy_updated_list, HostB,
|
||||
?MODULE, updated_list, 50),
|
||||
ejabberd_hooks:add(remove_user, HostB,
|
||||
?MODULE, remove_user, 50),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, HostB, ?NS_PRIVACY,
|
||||
?MODULE, process_iq, IQDisc).
|
||||
|
||||
@ -70,6 +73,8 @@ stop(Host) ->
|
||||
?MODULE, check_packet, 50),
|
||||
ejabberd_hooks:delete(privacy_updated_list, HostB,
|
||||
?MODULE, updated_list, 50),
|
||||
ejabberd_hooks:delete(remove_user, HostB,
|
||||
?MODULE, remove_user, 50),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, HostB, ?NS_PRIVACY).
|
||||
|
||||
process_iq(_From, _To, IQ_Rec) ->
|
||||
@ -666,6 +671,12 @@ is_type_match(Type, Value, JID, Subscription, Groups) ->
|
||||
end.
|
||||
|
||||
|
||||
remove_user(User, Server) ->
|
||||
LUser = exmpp_stringprep:nodeprep(User),
|
||||
LServer = exmpp_stringprep:nameprep(Server),
|
||||
sql_del_privacy_lists(LUser, LServer).
|
||||
|
||||
|
||||
updated_list(_,
|
||||
#userlist{name = OldName} = Old,
|
||||
#userlist{name = NewName} = New) ->
|
||||
@ -880,3 +891,16 @@ sql_set_privacy_list(ID, RItems) ->
|
||||
") "
|
||||
"values ('", ID, "', ", Items, ");"])
|
||||
end, RItems).
|
||||
|
||||
sql_del_privacy_lists(LUser, LServer) ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
Server = ejabberd_odbc:escape(LServer),
|
||||
ejabberd_odbc:sql_query(
|
||||
LServer,
|
||||
["delete from privacy_list where username='", Username, "';"]),
|
||||
ejabberd_odbc:sql_query(
|
||||
LServer,
|
||||
["delete from privacy_list_data where value='", Username++"@"++Server, "';"]),
|
||||
ejabberd_odbc:sql_query(
|
||||
LServer,
|
||||
["delete from privacy_default_list where username='", Username, "';"]).
|
||||
|
@ -26,7 +26,6 @@
|
||||
-author(__TO_BE_DEFINED__).
|
||||
|
||||
-include("pubsub.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-behaviour(gen_pubsub_node).
|
||||
|
||||
|
@ -215,8 +215,6 @@ try_register(User, Server, Password, Source, Lang) ->
|
||||
true ->
|
||||
case ejabberd_auth:try_register(User, Server, Password) of
|
||||
{atomic, ok} ->
|
||||
ejabberd_hooks:run(user_registered, exmpp_jid:domain(JID),
|
||||
[exmpp_jid:node(JID), exmpp_jid:domain(JID)]),
|
||||
send_welcome_message(JID),
|
||||
send_registration_notifications(JID),
|
||||
ok;
|
||||
|
@ -37,7 +37,8 @@
|
||||
process_item/2,
|
||||
in_subscription/6,
|
||||
out_subscription/4,
|
||||
user_registered/2,
|
||||
register_user/2,
|
||||
remove_user/2,
|
||||
list_groups/1,
|
||||
create_group/2,
|
||||
create_group/3,
|
||||
@ -46,6 +47,7 @@
|
||||
set_group_opts/3,
|
||||
get_group_users/2,
|
||||
get_group_explicit_users/2,
|
||||
is_user_in_group/3,
|
||||
add_user_to_group/3,
|
||||
remove_user_from_group/3]).
|
||||
|
||||
@ -85,8 +87,10 @@ start(Host, _Opts) ->
|
||||
?MODULE, get_jid_info, 70),
|
||||
ejabberd_hooks:add(roster_process_item, HostB,
|
||||
?MODULE, process_item, 50),
|
||||
ejabberd_hooks:add(user_registered, HostB,
|
||||
?MODULE, user_registered, 50).
|
||||
ejabberd_hooks:add(register_user, HostB,
|
||||
?MODULE, register_user, 50),
|
||||
ejabberd_hooks:add(remove_user, HostB,
|
||||
?MODULE, remove_user, 50).
|
||||
%%ejabberd_hooks:add(remove_user, HostB,
|
||||
%% ?MODULE, remove_user, 50),
|
||||
|
||||
@ -108,8 +112,10 @@ stop(Host) ->
|
||||
?MODULE, get_jid_info, 70),
|
||||
ejabberd_hooks:delete(roster_process_item, HostB,
|
||||
?MODULE, process_item, 50),
|
||||
ejabberd_hooks:delete(user_registered, HostB,
|
||||
?MODULE, user_registered, 50).
|
||||
ejabberd_hooks:delete(register_user, HostB,
|
||||
?MODULE, register_user, 50),
|
||||
ejabberd_hooks:delete(remove_user, HostB,
|
||||
?MODULE, remove_user, 50).
|
||||
%%ejabberd_hooks:delete(remove_user, HostB,
|
||||
%% ?MODULE, remove_user, 50),
|
||||
|
||||
@ -443,6 +449,7 @@ get_group_users(_User, Host, Group, GroupOpts) ->
|
||||
[]
|
||||
end ++ get_group_explicit_users(Host, Group).
|
||||
|
||||
%% @spec (Host::string(), Group::string()) -> [{User::string(), Server::string()}]
|
||||
get_group_explicit_users(Host, Group) ->
|
||||
Read = (catch mnesia:dirty_index_read(
|
||||
sr_user,
|
||||
@ -458,6 +465,7 @@ get_group_explicit_users(Host, Group) ->
|
||||
get_group_name(Host, Group) ->
|
||||
get_group_opt(Host, Group, name, Group).
|
||||
|
||||
%% Get list of names of groups that have @all@ in the memberlist
|
||||
get_special_users_groups(Host) ->
|
||||
lists:filter(
|
||||
fun(Group) ->
|
||||
@ -465,6 +473,8 @@ get_special_users_groups(Host) ->
|
||||
end,
|
||||
list_groups(Host)).
|
||||
|
||||
%% Given two lists of groupnames and their options,
|
||||
%% return the list of displayed groups to the second list
|
||||
displayed_groups(GroupsOpts, SelectedGroupsOpts) ->
|
||||
DisplayedGroups =
|
||||
lists:usort(
|
||||
@ -476,6 +486,9 @@ displayed_groups(GroupsOpts, SelectedGroupsOpts) ->
|
||||
[G || G <- DisplayedGroups,
|
||||
not lists:member(disabled, proplists:get_value(G, GroupsOpts, []))].
|
||||
|
||||
%% Given a list of group names with options,
|
||||
%% for those that have @all@ in memberlist,
|
||||
%% get the list of groups displayed
|
||||
get_special_displayed_groups(GroupsOpts) ->
|
||||
Groups = lists:filter(
|
||||
fun({_Group, Opts}) ->
|
||||
@ -483,6 +496,9 @@ get_special_displayed_groups(GroupsOpts) ->
|
||||
end, GroupsOpts),
|
||||
displayed_groups(GroupsOpts, Groups).
|
||||
|
||||
%% Given a username and server, and a list of group names with options,
|
||||
%% for the list of groups of that server that user is member
|
||||
%% get the list of groups displayed
|
||||
get_user_displayed_groups(LUser, LServer, GroupsOpts) ->
|
||||
Groups = case catch mnesia:dirty_read(sr_user, {LUser, LServer}) of
|
||||
Rs when is_list(Rs) ->
|
||||
@ -493,6 +509,7 @@ get_user_displayed_groups(LUser, LServer, GroupsOpts) ->
|
||||
end,
|
||||
displayed_groups(GroupsOpts, Groups).
|
||||
|
||||
%% @doc Get the list of groups that are displayed to this user
|
||||
get_user_displayed_groups(US) ->
|
||||
Host = element(2, US),
|
||||
DisplayedGroups1 =
|
||||
@ -515,22 +532,60 @@ is_user_in_group({_U, S} = US, Group, Host) ->
|
||||
_ -> true
|
||||
end.
|
||||
|
||||
|
||||
%% @spec (Host::string(), {User::string(), Server::string()}, Group::string()) -> {atomic, ok}
|
||||
add_user_to_group(Host, US, Group) ->
|
||||
{LUser, LServer} = US,
|
||||
%% Push this new user to members of groups where this group is displayed
|
||||
push_user_to_displayed(LUser, LServer, Group, both),
|
||||
%% Push members of groups that are displayed to this group
|
||||
push_displayed_to_user(LUser, LServer, Group, Host, both),
|
||||
R = #sr_user{us = US, group_host = {Group, Host}},
|
||||
F = fun() ->
|
||||
mnesia:write(R)
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
push_displayed_to_user(LUser, LServer, Group, Host, Subscription) ->
|
||||
GroupsOpts = groups_with_opts(LServer),
|
||||
GroupOpts = proplists:get_value(Group, GroupsOpts, []),
|
||||
DisplayedGroups = proplists:get_value(displayed_groups, GroupOpts, []),
|
||||
[push_members_to_user(LUser, LServer, DGroup, Host, Subscription) || DGroup <- DisplayedGroups].
|
||||
|
||||
remove_user_from_group(Host, US, Group) ->
|
||||
GroupHost = {Group, Host},
|
||||
R = #sr_user{us = US, group_host = GroupHost},
|
||||
F = fun() ->
|
||||
mnesia:delete_object(R)
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
Result = mnesia:transaction(F),
|
||||
{LUser, LServer} = US,
|
||||
%% Push removal of the old user to members of groups where the group that this user was members was displayed
|
||||
push_user_to_displayed(LUser, LServer, Group, remove),
|
||||
%% Push removal of members of groups that where displayed to the group which this user has left
|
||||
push_displayed_to_user(LUser, LServer, Group, Host, remove),
|
||||
Result.
|
||||
|
||||
user_registered(User, Server) ->
|
||||
push_members_to_user(LUser, LServer, Group, Host, Subscription) ->
|
||||
GroupsOpts = groups_with_opts(LServer),
|
||||
GroupOpts = proplists:get_value(Group, GroupsOpts, []),
|
||||
GroupName = proplists:get_value(name, GroupOpts, Group),
|
||||
Members = get_group_users(Host, Group),
|
||||
lists:foreach(
|
||||
fun({U, S}) ->
|
||||
push_roster_item(LUser, LServer, U, S, GroupName, Subscription)
|
||||
end, Members).
|
||||
|
||||
register_user(User, Server) ->
|
||||
%% Get list of groups where this user is member
|
||||
Groups = get_user_groups({User, Server}),
|
||||
%% Push this user to members of groups where is displayed a group which this user is member
|
||||
[push_user_to_displayed(User, Server, Group, both) || Group <- Groups].
|
||||
|
||||
remove_user(User, Server) ->
|
||||
push_user_to_members(User, Server, remove).
|
||||
|
||||
push_user_to_members(User, Server, Subscription) ->
|
||||
try
|
||||
LUser = exmpp_stringprep:nodeprep(User),
|
||||
LServer = exmpp_stringprep:nameprep(Server),
|
||||
@ -543,14 +598,7 @@ user_registered(User, Server) ->
|
||||
GroupName = proplists:get_value(name, GroupOpts, Group),
|
||||
lists:foreach(
|
||||
fun({U, S}) ->
|
||||
Item = #roster{usj = {U, S, {LUser, LServer, undefined}},
|
||||
us = {U, S},
|
||||
jid = {LUser, LServer, undefined},
|
||||
name = "",
|
||||
subscription = both,
|
||||
ask = none,
|
||||
groups = [GroupName]},
|
||||
push_item(U, S, exmpp_jid:make_bare_jid(S), Item)
|
||||
push_roster_item(U, S, LUser, LServer, GroupName, Subscription)
|
||||
end, get_group_users(LUser, LServer, Group, GroupOpts))
|
||||
end, lists:usort(SpecialGroups++UserGroups))
|
||||
catch
|
||||
@ -558,6 +606,27 @@ user_registered(User, Server) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
push_user_to_displayed(LUser, LServer, Group, Subscription) ->
|
||||
GroupsOpts = groups_with_opts(LServer),
|
||||
GroupOpts = proplists:get_value(Group, GroupsOpts, []),
|
||||
GroupName = proplists:get_value(name, GroupOpts, Group),
|
||||
DisplayedToGroupsOpts = displayed_to_groups(Group, LServer),
|
||||
[push_user_to_group(LUser, LServer, GroupD, GroupName, Subscription) || {GroupD, _Opts} <- DisplayedToGroupsOpts].
|
||||
|
||||
push_user_to_group(LUser, LServer, Group, GroupName, Subscription) ->
|
||||
lists:foreach(
|
||||
fun({U, S}) ->
|
||||
push_roster_item(U, S, LUser, LServer, GroupName, Subscription)
|
||||
end, get_group_users(LServer, Group)).
|
||||
|
||||
%% Get list of groups to which this group is displayed
|
||||
displayed_to_groups(GroupName, LServer) ->
|
||||
GroupsOpts = groups_with_opts(LServer),
|
||||
lists:filter(
|
||||
fun({_Group, Opts}) ->
|
||||
lists:member(GroupName, proplists:get_value(displayed_groups, Opts, []))
|
||||
end, GroupsOpts).
|
||||
|
||||
push_item(_User, _Server, _From, none) ->
|
||||
ok;
|
||||
push_item(User, Server, From, Item) ->
|
||||
@ -580,6 +649,16 @@ push_item(User, Server, From, Item) ->
|
||||
ejabberd_router:route(JID, JID, Stanza)
|
||||
end, ejabberd_sm:get_user_resources(list_to_binary(User), list_to_binary(Server))).
|
||||
|
||||
push_roster_item(User, Server, ContactU, ContactS, GroupName, Subscription) ->
|
||||
Item = #roster{usj = {User, Server, {ContactU, ContactS, ""}},
|
||||
us = {User, Server},
|
||||
jid = {ContactU, ContactS, ""},
|
||||
name = "",
|
||||
subscription = Subscription,
|
||||
ask = none,
|
||||
groups = [GroupName]},
|
||||
push_item(User, Server, jlib:make_jid("", Server, ""), Item).
|
||||
|
||||
item_to_xml(Item) ->
|
||||
{U, S, R} = Item#roster.jid,
|
||||
Attrs1 = exmpp_xml:set_attribute_in_list([],
|
||||
|
@ -376,7 +376,7 @@ get_private_data(LServer, Username, LXMLNS) ->
|
||||
"namespace='", LXMLNS, "';"]).
|
||||
|
||||
del_user_private_storage(LServer, Username) ->
|
||||
ejabberd_odbc:sql_transaction(
|
||||
ejabberd_odbc:sql_query(
|
||||
LServer,
|
||||
["delete from private_storage where username='", Username, "';"]).
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user