mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-20 16:15:59 +01:00
Merged the Process One contributions ( Virtual Hosting )
SVN Revision: 307
This commit is contained in:
parent
13de45118d
commit
374446f847
@ -13,17 +13,19 @@
|
||||
-export([start/0,
|
||||
register_mechanism/2,
|
||||
listmech/0,
|
||||
server_new/4,
|
||||
server_new/6,
|
||||
server_start/3,
|
||||
server_step/2]).
|
||||
|
||||
-record(sasl_mechanism, {mechanism, module}).
|
||||
-record(sasl_state, {service, myname, realm, mech_mod, mech_state}).
|
||||
-record(sasl_state, {service, myname, realm,
|
||||
get_password, check_password,
|
||||
mech_mod, mech_state}).
|
||||
|
||||
-export([behaviour_info/1]).
|
||||
|
||||
behaviour_info(callbacks) ->
|
||||
[{mech_new, 0},
|
||||
[{mech_new, 2},
|
||||
{mech_step, 2}];
|
||||
behaviour_info(Other) ->
|
||||
undefined.
|
||||
@ -80,15 +82,19 @@ listmech() ->
|
||||
[{#sasl_mechanism{mechanism = '$1', _ = '_'}, [], ['$1']}]).
|
||||
|
||||
|
||||
server_new(Service, ServerFQDN, UserRealm, SecFlags) ->
|
||||
server_new(Service, ServerFQDN, UserRealm, SecFlags,
|
||||
GetPassword, CheckPassword) ->
|
||||
#sasl_state{service = Service,
|
||||
myname = ServerFQDN,
|
||||
realm = UserRealm}.
|
||||
realm = UserRealm,
|
||||
get_password = GetPassword,
|
||||
check_password = CheckPassword}.
|
||||
|
||||
server_start(State, Mech, ClientIn) ->
|
||||
case ets:lookup(sasl_mechanism, Mech) of
|
||||
[#sasl_mechanism{module = Module}] ->
|
||||
{ok, MechState} = Module:mech_new(),
|
||||
{ok, MechState} = Module:mech_new(State#sasl_state.get_password,
|
||||
State#sasl_state.check_password),
|
||||
server_step(State#sasl_state{mech_mod = Module,
|
||||
mech_state = MechState},
|
||||
ClientIn);
|
||||
|
@ -12,15 +12,14 @@
|
||||
|
||||
-export([start/1,
|
||||
stop/0,
|
||||
mech_new/0,
|
||||
mech_new/2,
|
||||
mech_step/2]).
|
||||
|
||||
-behaviour(cyrsasl).
|
||||
%-behaviour(gen_mod).
|
||||
|
||||
-record(state, {step, nonce, username, authzid}).
|
||||
-record(state, {step, nonce, username, authzid, get_password}).
|
||||
|
||||
start(Opts) ->
|
||||
start(_Opts) ->
|
||||
case ejabberd_auth:plain_password_required() of
|
||||
true ->
|
||||
ok;
|
||||
@ -32,15 +31,15 @@ start(Opts) ->
|
||||
stop() ->
|
||||
ok.
|
||||
|
||||
mech_new() ->
|
||||
mech_new(GetPassword, _CheckPassword) ->
|
||||
{ok, #state{step = 1,
|
||||
nonce = randoms:get_string()}}.
|
||||
nonce = randoms:get_string(),
|
||||
get_password = GetPassword}}.
|
||||
|
||||
mech_step(#state{step = 1, nonce = Nonce} = State, _) ->
|
||||
{continue,
|
||||
"nonce=\"" ++ Nonce ++
|
||||
"\",qop=\"auth\",charset=utf-8,algorithm=md5-sess",
|
||||
%"\",qop=\"auth,auth-int\",charset=utf-8,algorithm=md5-sess",
|
||||
State#state{step = 3}};
|
||||
mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
|
||||
case parse(ClientIn) of
|
||||
@ -49,7 +48,7 @@ mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
|
||||
KeyVals ->
|
||||
UserName = xml:get_attr_s("username", KeyVals),
|
||||
AuthzId = xml:get_attr_s("authzid", KeyVals),
|
||||
case ejabberd_auth:get_password(UserName) of
|
||||
case (State#state.get_password)(UserName) of
|
||||
false ->
|
||||
{error, "not-authorized"};
|
||||
Passwd ->
|
||||
@ -72,7 +71,7 @@ mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
|
||||
end;
|
||||
mech_step(#state{step = 5,
|
||||
username = UserName,
|
||||
authzid = AuthzId} = State, "") ->
|
||||
authzid = AuthzId}, "") ->
|
||||
{ok, [{username, UserName}, {authzid, AuthzId}]};
|
||||
mech_step(A, B) ->
|
||||
io:format("SASL DIGEST: A ~p B ~p", [A,B]),
|
||||
@ -88,7 +87,7 @@ parse1([C | Cs], S, Ts) ->
|
||||
parse1(Cs, [C | S], Ts);
|
||||
parse1([], [], T) ->
|
||||
lists:reverse(T);
|
||||
parse1([], S, T) ->
|
||||
parse1([], _S, _T) ->
|
||||
bad.
|
||||
|
||||
parse2([$" | Cs], Key, Val, Ts) ->
|
||||
@ -148,13 +147,13 @@ response(KeyVals, User, Passwd, Nonce, AuthzId, A2Prefix) ->
|
||||
crypto:md5(User ++ ":" ++ Realm ++ ":" ++ Passwd)) ++
|
||||
":" ++ Nonce ++ ":" ++ CNonce ++ ":" ++ AuthzId
|
||||
end,
|
||||
case QOP of
|
||||
"auth" ->
|
||||
A2 = A2Prefix ++ ":" ++ DigestURI;
|
||||
_ ->
|
||||
A2 = A2Prefix ++ ":" ++ DigestURI ++
|
||||
":00000000000000000000000000000000"
|
||||
end,
|
||||
A2 = case QOP of
|
||||
"auth" ->
|
||||
A2Prefix ++ ":" ++ DigestURI;
|
||||
_ ->
|
||||
A2Prefix ++ ":" ++ DigestURI ++
|
||||
":00000000000000000000000000000000"
|
||||
end,
|
||||
T = hex(binary_to_list(crypto:md5(A1))) ++ ":" ++ Nonce ++ ":" ++
|
||||
NC ++ ":" ++ CNonce ++ ":" ++ QOP ++ ":" ++
|
||||
hex(binary_to_list(crypto:md5(A2))),
|
||||
|
@ -10,25 +10,26 @@
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-export([start/1, stop/0, mech_new/0, mech_step/2, parse/1]).
|
||||
-export([start/1, stop/0, mech_new/2, mech_step/2, parse/1]).
|
||||
|
||||
-behaviour(cyrsasl).
|
||||
%-behaviour(gen_mod).
|
||||
|
||||
start(Opts) ->
|
||||
-record(state, {check_password}).
|
||||
|
||||
start(_Opts) ->
|
||||
cyrsasl:register_mechanism("PLAIN", ?MODULE),
|
||||
ok.
|
||||
|
||||
stop() ->
|
||||
ok.
|
||||
|
||||
mech_new() ->
|
||||
{ok, []}.
|
||||
mech_new(_GetPassword, CheckPassword) ->
|
||||
{ok, #state{check_password = CheckPassword}}.
|
||||
|
||||
mech_step(State, ClientIn) ->
|
||||
case parse(ClientIn) of
|
||||
[AuthzId, User, Password] ->
|
||||
case ejabberd_auth:check_password(User, Password) of
|
||||
case (State#state.check_password)(User, Password) of
|
||||
true ->
|
||||
{ok, [{username, User}, {authzid, AuthzId}]};
|
||||
_ ->
|
||||
|
@ -27,10 +27,9 @@
|
||||
[self(),?MODULE,?LINE]++Args)).
|
||||
|
||||
|
||||
%-define(MYNAME,"e.localhost").
|
||||
-define(MYNAME, ejabberd_config:get_global_option(host)).
|
||||
-define(MYHOSTS, ejabberd_config:get_global_option(hosts)).
|
||||
-define(MYNAME, hd(ejabberd_config:get_global_option(hosts))).
|
||||
-define(S2STIMEOUT, 600000).
|
||||
%-define(S2STIMEOUT, 6000).
|
||||
-define(MYLANG, ejabberd_config:get_global_option(language)).
|
||||
|
||||
-define(MSGS_DIR, "msgs").
|
||||
|
@ -12,16 +12,17 @@
|
||||
|
||||
%% External exports
|
||||
-export([start/0,
|
||||
set_password/2,
|
||||
check_password/2,
|
||||
check_password/4,
|
||||
try_register/2,
|
||||
set_password/3,
|
||||
check_password/3,
|
||||
check_password/5,
|
||||
try_register/3,
|
||||
dirty_get_registered_users/0,
|
||||
get_password/1,
|
||||
get_password_s/1,
|
||||
is_user_exists/1,
|
||||
remove_user/1,
|
||||
get_vh_registered_users/1,
|
||||
get_password/2,
|
||||
get_password_s/2,
|
||||
is_user_exists/2,
|
||||
remove_user/2,
|
||||
remove_user/3,
|
||||
plain_password_required/0
|
||||
]).
|
||||
|
||||
@ -34,35 +35,38 @@ start() ->
|
||||
plain_password_required() ->
|
||||
(auth_module()):plain_password_required().
|
||||
|
||||
check_password(User, Password) ->
|
||||
(auth_module()):check_password(User, Password).
|
||||
check_password(User, Server, Password) ->
|
||||
(auth_module()):check_password(User, Server, Password).
|
||||
|
||||
check_password(User, Password, StreamID, Digest) ->
|
||||
(auth_module()):check_password(User, Password, StreamID, Digest).
|
||||
check_password(User, Server, Password, StreamID, Digest) ->
|
||||
(auth_module()):check_password(User, Server, Password, StreamID, Digest).
|
||||
|
||||
set_password(User, Password) ->
|
||||
(auth_module()):set_password(User, Password).
|
||||
set_password(User, Server, Password) ->
|
||||
(auth_module()):set_password(User, Server, Password).
|
||||
|
||||
try_register(User, Password) ->
|
||||
(auth_module()):try_register(User, Password).
|
||||
try_register(User, Server, Password) ->
|
||||
(auth_module()):try_register(User, Server, Password).
|
||||
|
||||
dirty_get_registered_users() ->
|
||||
(auth_module()):dirty_get_registered_users().
|
||||
|
||||
get_password(User) ->
|
||||
(auth_module()):get_password(User).
|
||||
get_vh_registered_users(Server) ->
|
||||
(auth_module()):get_vh_registered_users(Server).
|
||||
|
||||
get_password_s(User) ->
|
||||
(auth_module()):get_password_s(User).
|
||||
get_password(User, Server) ->
|
||||
(auth_module()):get_password(User, Server).
|
||||
|
||||
is_user_exists(User) ->
|
||||
(auth_module()):is_user_exists(User).
|
||||
get_password_s(User, Server) ->
|
||||
(auth_module()):get_password_s(User, Server).
|
||||
|
||||
remove_user(User) ->
|
||||
(auth_module()):remove_user(User).
|
||||
is_user_exists(User, Server) ->
|
||||
(auth_module()):is_user_exists(User, Server).
|
||||
|
||||
remove_user(User, Password) ->
|
||||
(auth_module()):remove_user(User, Password).
|
||||
remove_user(User, Server) ->
|
||||
(auth_module()):remove_user(User, Server).
|
||||
|
||||
remove_user(User, Server, Password) ->
|
||||
(auth_module()):remove_user(User, Server, Password).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
|
@ -12,16 +12,17 @@
|
||||
|
||||
%% External exports
|
||||
-export([start/0,
|
||||
set_password/2,
|
||||
check_password/2,
|
||||
check_password/4,
|
||||
try_register/2,
|
||||
set_password/3,
|
||||
check_password/3,
|
||||
check_password/5,
|
||||
try_register/3,
|
||||
dirty_get_registered_users/0,
|
||||
get_password/1,
|
||||
get_password_s/1,
|
||||
is_user_exists/1,
|
||||
remove_user/1,
|
||||
get_vh_registered_users/1,
|
||||
get_password/2,
|
||||
get_password_s/2,
|
||||
is_user_exists/2,
|
||||
remove_user/2,
|
||||
remove_user/3,
|
||||
plain_password_required/0
|
||||
]).
|
||||
|
||||
@ -35,33 +36,36 @@ start() ->
|
||||
plain_password_required() ->
|
||||
true.
|
||||
|
||||
check_password(User, Password) ->
|
||||
check_password(User, _Server, Password) ->
|
||||
extauth:check_password(User, Password).
|
||||
|
||||
check_password(User, Password, _StreamID, _Digest) ->
|
||||
check_password(User, Password).
|
||||
check_password(User, Server, Password, _StreamID, _Digest) ->
|
||||
check_password(User, Server, Password).
|
||||
|
||||
set_password(User, Password) ->
|
||||
set_password(User, _Server, Password) ->
|
||||
extauth:set_password(User, Password).
|
||||
|
||||
try_register(_User, _Password) ->
|
||||
try_register(_User, _Server, _Password) ->
|
||||
{error, not_allowed}.
|
||||
|
||||
dirty_get_registered_users() ->
|
||||
[].
|
||||
|
||||
get_password(_User) ->
|
||||
get_vh_registered_users(_Server) ->
|
||||
[].
|
||||
|
||||
get_password(_User, _Server) ->
|
||||
false.
|
||||
|
||||
get_password_s(_User) ->
|
||||
get_password_s(_User, _Server) ->
|
||||
"".
|
||||
|
||||
is_user_exists(User) ->
|
||||
is_user_exists(User, _Server) ->
|
||||
extauth:is_user_exists(User).
|
||||
|
||||
remove_user(_User) ->
|
||||
remove_user(_User, _Server) ->
|
||||
{error, not_allowed}.
|
||||
|
||||
remove_user(_User, _Password) ->
|
||||
remove_user(_User, _Server, _Password) ->
|
||||
not_allowed.
|
||||
|
||||
|
@ -12,44 +12,52 @@
|
||||
|
||||
%% External exports
|
||||
-export([start/0,
|
||||
set_password/2,
|
||||
check_password/2,
|
||||
check_password/4,
|
||||
try_register/2,
|
||||
set_password/3,
|
||||
check_password/3,
|
||||
check_password/5,
|
||||
try_register/3,
|
||||
dirty_get_registered_users/0,
|
||||
get_password/1,
|
||||
get_password_s/1,
|
||||
is_user_exists/1,
|
||||
remove_user/1,
|
||||
get_vh_registered_users/1,
|
||||
get_password/2,
|
||||
get_password_s/2,
|
||||
is_user_exists/2,
|
||||
remove_user/2,
|
||||
remove_user/3,
|
||||
plain_password_required/0
|
||||
]).
|
||||
|
||||
-record(passwd, {user, password}).
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
-record(passwd, {us, password}).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start() ->
|
||||
mnesia:create_table(passwd,[{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, passwd)}]),
|
||||
mnesia:create_table(passwd, [{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, passwd)}]),
|
||||
update_table(),
|
||||
ok.
|
||||
|
||||
plain_password_required() ->
|
||||
false.
|
||||
|
||||
check_password(User, Password) ->
|
||||
check_password(User, Server, Password) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
case catch mnesia:dirty_read({passwd, LUser}) of
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read({passwd, US}) of
|
||||
[#passwd{password = Password}] ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
check_password(User, Password, StreamID, Digest) ->
|
||||
check_password(User, Server, Password, StreamID, Digest) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
case catch mnesia:dirty_read({passwd, LUser}) of
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read({passwd, US}) of
|
||||
[#passwd{password = Passwd}] ->
|
||||
DigRes = if
|
||||
Digest /= "" ->
|
||||
@ -66,26 +74,34 @@ check_password(User, Password, StreamID, Digest) ->
|
||||
false
|
||||
end.
|
||||
|
||||
set_password(User, Password) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error -> {error, invalid_jid};
|
||||
LUser ->
|
||||
set_password(User, Server, Password) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
if
|
||||
(LUser == error) or (LServer == error) ->
|
||||
{error, invalid_jid};
|
||||
true ->
|
||||
F = fun() ->
|
||||
mnesia:write(#passwd{user = LUser,
|
||||
mnesia:write(#passwd{us = US,
|
||||
password = Password})
|
||||
end,
|
||||
mnesia:transaction(F)
|
||||
end.
|
||||
|
||||
|
||||
try_register(User, Password) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error -> {error, invalid_jid};
|
||||
LUser ->
|
||||
try_register(User, Server, Password) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
if
|
||||
(LUser == error) or (LServer == error) ->
|
||||
{error, invalid_jid};
|
||||
true ->
|
||||
F = fun() ->
|
||||
case mnesia:read({passwd, LUser}) of
|
||||
case mnesia:read({passwd, US}) of
|
||||
[] ->
|
||||
mnesia:write(#passwd{user = LUser,
|
||||
mnesia:write(#passwd{us = US,
|
||||
password = Password}),
|
||||
ok;
|
||||
[_E] ->
|
||||
@ -98,27 +114,41 @@ try_register(User, Password) ->
|
||||
dirty_get_registered_users() ->
|
||||
mnesia:dirty_all_keys(passwd).
|
||||
|
||||
get_password(User) ->
|
||||
get_vh_registered_users(Server) ->
|
||||
LServer = jlib:nameprep(Server),
|
||||
mnesia:dirty_select(
|
||||
passwd,
|
||||
[{#passwd{us = '$1', _ = '_'},
|
||||
[{'==', {element, 2, '$1'}, LServer}],
|
||||
['$1']}]).
|
||||
|
||||
get_password(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
case catch mnesia:dirty_read(passwd, LUser) of
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read(passwd, US) of
|
||||
[#passwd{password = Password}] ->
|
||||
Password;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
get_password_s(User) ->
|
||||
get_password_s(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
case catch mnesia:dirty_read(passwd, LUser) of
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read(passwd, US) of
|
||||
[#passwd{password = Password}] ->
|
||||
Password;
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
|
||||
is_user_exists(User) ->
|
||||
is_user_exists(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
case catch mnesia:dirty_read({passwd, LUser}) of
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read({passwd, US}) of
|
||||
[] ->
|
||||
false;
|
||||
[_] ->
|
||||
@ -127,20 +157,24 @@ is_user_exists(User) ->
|
||||
false
|
||||
end.
|
||||
|
||||
remove_user(User) ->
|
||||
remove_user(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
mnesia:delete({passwd, LUser})
|
||||
mnesia:delete({passwd, US})
|
||||
end,
|
||||
mnesia:transaction(F),
|
||||
ejabberd_hooks:run(remove_user, [User]).
|
||||
ejabberd_hooks:run(remove_user, [User, Server]).
|
||||
|
||||
remove_user(User, Password) ->
|
||||
remove_user(User, Server, Password) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
case mnesia:read({passwd, LUser}) of
|
||||
case mnesia:read({passwd, US}) of
|
||||
[#passwd{password = Password}] ->
|
||||
mnesia:delete({passwd, LUser}),
|
||||
mnesia:delete({passwd, US}),
|
||||
ok;
|
||||
[_] ->
|
||||
not_allowed;
|
||||
@ -150,10 +184,56 @@ remove_user(User, Password) ->
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, ok} ->
|
||||
ejabberd_hooks:run(remove_user, [User]),
|
||||
ejabberd_hooks:run(remove_user, [User, Server]),
|
||||
ok;
|
||||
{atomic, Res} ->
|
||||
Res;
|
||||
_ ->
|
||||
bad_request
|
||||
end.
|
||||
|
||||
|
||||
update_table() ->
|
||||
Fields = record_info(fields, passwd),
|
||||
case mnesia:table_info(passwd, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[user, password] ->
|
||||
?INFO_MSG("Converting passwd table from "
|
||||
"{user, password} format", []),
|
||||
Host = ?MYNAME,
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
ejabberd_auth_internal_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, passwd},
|
||||
{attributes, record_info(fields, passwd)}]),
|
||||
mnesia:transform_table(passwd, ignore, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(ejabberd_auth_internal_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#passwd{us = U} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
ejabberd_auth_internal_tmp_table,
|
||||
R#passwd{us = {U, Host}})
|
||||
end, ok, passwd)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(passwd),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(passwd),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, ejabberd_auth_internal_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(ejabberd_auth_internal_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating passwd table", []),
|
||||
mnesia:transform_table(passwd, ignore, Fields)
|
||||
end.
|
||||
|
||||
|
||||
|
||||
|
@ -12,16 +12,17 @@
|
||||
|
||||
%% External exports
|
||||
-export([start/0,
|
||||
set_password/2,
|
||||
check_password/2,
|
||||
check_password/4,
|
||||
try_register/2,
|
||||
set_password/3,
|
||||
check_password/3,
|
||||
check_password/5,
|
||||
try_register/3,
|
||||
dirty_get_registered_users/0,
|
||||
get_password/1,
|
||||
get_password_s/1,
|
||||
is_user_exists/1,
|
||||
remove_user/1,
|
||||
get_vh_registered_users/1,
|
||||
get_password/2,
|
||||
get_password_s/2,
|
||||
is_user_exists/2,
|
||||
remove_user/2,
|
||||
remove_user/3,
|
||||
plain_password_required/0
|
||||
]).
|
||||
|
||||
@ -41,7 +42,7 @@ start() ->
|
||||
plain_password_required() ->
|
||||
true.
|
||||
|
||||
check_password(User, Password) ->
|
||||
check_password(User, _Server, Password) ->
|
||||
case find_user_dn(User) of
|
||||
false ->
|
||||
false;
|
||||
@ -54,25 +55,28 @@ check_password(User, Password) ->
|
||||
end
|
||||
end.
|
||||
|
||||
check_password(User, Password, _StreamID, _Digest) ->
|
||||
check_password(User, Password).
|
||||
check_password(User, Server, Password, _StreamID, _Digest) ->
|
||||
check_password(User, Server, Password).
|
||||
|
||||
set_password(_User, _Password) ->
|
||||
set_password(_User, _Server, _Password) ->
|
||||
{error, not_allowed}.
|
||||
|
||||
try_register(_User, _Password) ->
|
||||
try_register(_User, _Server, _Password) ->
|
||||
{error, not_allowed}.
|
||||
|
||||
dirty_get_registered_users() ->
|
||||
[].
|
||||
|
||||
get_password(_User) ->
|
||||
get_vh_registered_users(_Server) ->
|
||||
[].
|
||||
|
||||
get_password(_User, _Server) ->
|
||||
false.
|
||||
|
||||
get_password_s(_User) ->
|
||||
get_password_s(_User, _Server) ->
|
||||
"".
|
||||
|
||||
is_user_exists(User) ->
|
||||
is_user_exists(User, _Server) ->
|
||||
case find_user_dn(User) of
|
||||
false ->
|
||||
false;
|
||||
@ -80,10 +84,10 @@ is_user_exists(User) ->
|
||||
true
|
||||
end.
|
||||
|
||||
remove_user(_User) ->
|
||||
remove_user(_User, _Server) ->
|
||||
{error, not_allowed}.
|
||||
|
||||
remove_user(_User, _Password) ->
|
||||
remove_user(_User, _Server, _Password) ->
|
||||
not_allowed.
|
||||
|
||||
|
||||
|
@ -12,16 +12,16 @@
|
||||
|
||||
%% External exports
|
||||
-export([start/0,
|
||||
set_password/2,
|
||||
check_password/2,
|
||||
check_password/4,
|
||||
try_register/2,
|
||||
set_password/3,
|
||||
check_password/3,
|
||||
check_password/5,
|
||||
try_register/3,
|
||||
dirty_get_registered_users/0,
|
||||
get_password/1,
|
||||
get_password_s/1,
|
||||
is_user_exists/1,
|
||||
remove_user/1,
|
||||
get_password/2,
|
||||
get_password_s/2,
|
||||
is_user_exists/2,
|
||||
remove_user/2,
|
||||
remove_user/3,
|
||||
plain_password_required/0
|
||||
]).
|
||||
|
||||
@ -36,7 +36,7 @@ start() ->
|
||||
plain_password_required() ->
|
||||
false.
|
||||
|
||||
check_password(User, Password) ->
|
||||
check_password(User, _Server, Password) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
false;
|
||||
@ -52,7 +52,7 @@ check_password(User, Password) ->
|
||||
end
|
||||
end.
|
||||
|
||||
check_password(User, Password, StreamID, Digest) ->
|
||||
check_password(User, _Server, Password, StreamID, Digest) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
false;
|
||||
@ -78,7 +78,7 @@ check_password(User, Password, StreamID, Digest) ->
|
||||
end
|
||||
end.
|
||||
|
||||
set_password(User, Password) ->
|
||||
set_password(User, _Server, Password) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
{error, invalid_jid};
|
||||
@ -93,7 +93,7 @@ set_password(User, Password) ->
|
||||
end.
|
||||
|
||||
|
||||
try_register(User, Password) ->
|
||||
try_register(User, _Server, Password) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
{error, invalid_jid};
|
||||
@ -118,7 +118,10 @@ dirty_get_registered_users() ->
|
||||
[]
|
||||
end.
|
||||
|
||||
get_password(User) ->
|
||||
dirty_get_registered_users(Server) ->
|
||||
dirty_get_registered_users().
|
||||
|
||||
get_password(User, _Server) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
false;
|
||||
@ -134,7 +137,7 @@ get_password(User) ->
|
||||
end
|
||||
end.
|
||||
|
||||
get_password_s(User) ->
|
||||
get_password_s(User, _Server) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
"";
|
||||
@ -150,7 +153,7 @@ get_password_s(User) ->
|
||||
end
|
||||
end.
|
||||
|
||||
is_user_exists(User) ->
|
||||
is_user_exists(User, _Server) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
false;
|
||||
@ -166,7 +169,7 @@ is_user_exists(User) ->
|
||||
end
|
||||
end.
|
||||
|
||||
remove_user(User) ->
|
||||
remove_user(User, _Server) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
error;
|
||||
@ -177,7 +180,7 @@ remove_user(User) ->
|
||||
ejabberd_hooks:run(remove_user, [User])
|
||||
end.
|
||||
|
||||
remove_user(User, Password) ->
|
||||
remove_user(User, _Server, Password) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
error;
|
||||
|
@ -83,6 +83,8 @@
|
||||
xml:element_to_string(?SERR_INVALID_NAMESPACE)).
|
||||
-define(INVALID_XML_ERR,
|
||||
xml:element_to_string(?SERR_XML_NOT_WELL_FORMED)).
|
||||
-define(HOST_UNKNOWN_ERR,
|
||||
xml:element_to_string(?SERR_HOST_UNKNOWN)).
|
||||
-define(POLICY_VIOLATION_ERR(Lang, Text),
|
||||
xml:element_to_string(?SERRT_POLICY_VIOLATION(Lang, Text))).
|
||||
|
||||
@ -164,89 +166,114 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
||||
end,
|
||||
case xml:get_attr_s("xmlns:stream", Attrs) of
|
||||
?NS_STREAM ->
|
||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||
case xml:get_attr_s("version", Attrs) of
|
||||
"1.0" ->
|
||||
Header = io_lib:format(?STREAM_HEADER,
|
||||
[StateData#state.streamid,
|
||||
?MYNAME,
|
||||
" version='1.0'",
|
||||
DefaultLang]),
|
||||
send_text(StateData, Header),
|
||||
case StateData#state.authentificated of
|
||||
false ->
|
||||
SASLState =
|
||||
cyrsasl:server_new("jabber", ?MYNAME, "", []),
|
||||
Mechs = lists:map(
|
||||
fun(S) ->
|
||||
{xmlelement, "mechanism", [],
|
||||
[{xmlcdata, S}]}
|
||||
end, cyrsasl:listmech()),
|
||||
TLS = StateData#state.tls,
|
||||
TLSEnabled = StateData#state.tls_enabled,
|
||||
TLSRequired = StateData#state.tls_required,
|
||||
SockMod = StateData#state.sockmod,
|
||||
TLSFeature =
|
||||
case (TLS == true) andalso
|
||||
(TLSEnabled == false) andalso
|
||||
(SockMod == gen_tcp) of
|
||||
true ->
|
||||
case TLSRequired of
|
||||
Server = jlib:nameprep(xml:get_attr_s("to", Attrs)),
|
||||
case lists:member(Server, ?MYHOSTS) of
|
||||
true ->
|
||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||
case xml:get_attr_s("version", Attrs) of
|
||||
"1.0" ->
|
||||
Header = io_lib:format(?STREAM_HEADER,
|
||||
[StateData#state.streamid,
|
||||
Server,
|
||||
" version='1.0'",
|
||||
DefaultLang]),
|
||||
send_text(StateData, Header),
|
||||
case StateData#state.authentificated of
|
||||
false ->
|
||||
SASLState =
|
||||
cyrsasl:server_new(
|
||||
"jabber", Server, "", [],
|
||||
fun(U) ->
|
||||
ejabberd_auth:get_password(
|
||||
U, Server)
|
||||
end,
|
||||
fun(U, P) ->
|
||||
ejabberd_auth:check_password(
|
||||
U, Server, P)
|
||||
end),
|
||||
Mechs = lists:map(
|
||||
fun(S) ->
|
||||
{xmlelement, "mechanism", [],
|
||||
[{xmlcdata, S}]}
|
||||
end, cyrsasl:listmech()),
|
||||
TLS = StateData#state.tls,
|
||||
TLSEnabled = StateData#state.tls_enabled,
|
||||
TLSRequired = StateData#state.tls_required,
|
||||
SockMod = StateData#state.sockmod,
|
||||
TLSFeature =
|
||||
case (TLS == true) andalso
|
||||
(TLSEnabled == false) andalso
|
||||
(SockMod == gen_tcp) of
|
||||
true ->
|
||||
[{xmlelement, "starttls",
|
||||
[{"xmlns", ?NS_TLS}],
|
||||
[{xmlelement, "required",
|
||||
[], []}]}];
|
||||
_ ->
|
||||
[{xmlelement, "starttls",
|
||||
[{"xmlns", ?NS_TLS}], []}]
|
||||
end;
|
||||
false ->
|
||||
[]
|
||||
end,
|
||||
send_element(StateData,
|
||||
{xmlelement, "stream:features", [],
|
||||
TLSFeature ++
|
||||
[{xmlelement, "mechanisms",
|
||||
[{"xmlns", ?NS_SASL}],
|
||||
Mechs},
|
||||
{xmlelement, "register",
|
||||
[{"xmlns", ?NS_FEATURE_IQREGISTER}],
|
||||
[]}]}),
|
||||
{next_state, wait_for_feature_request,
|
||||
StateData#state{sasl_state = SASLState,
|
||||
lang = Lang}};
|
||||
_ ->
|
||||
case StateData#state.resource of
|
||||
"" ->
|
||||
send_element(
|
||||
StateData,
|
||||
{xmlelement, "stream:features", [],
|
||||
[{xmlelement, "bind",
|
||||
[{"xmlns", ?NS_BIND}], []},
|
||||
{xmlelement, "session",
|
||||
[{"xmlns", ?NS_SESSION}], []}]}),
|
||||
{next_state, wait_for_bind,
|
||||
StateData#state{lang = Lang}};
|
||||
case TLSRequired of
|
||||
true ->
|
||||
[{xmlelement, "starttls",
|
||||
[{"xmlns", ?NS_TLS}],
|
||||
[{xmlelement, "required",
|
||||
[], []}]}];
|
||||
_ ->
|
||||
[{xmlelement, "starttls",
|
||||
[{"xmlns", ?NS_TLS}], []}]
|
||||
end;
|
||||
false ->
|
||||
[]
|
||||
end,
|
||||
send_element(StateData,
|
||||
{xmlelement, "stream:features", [],
|
||||
TLSFeature ++
|
||||
[{xmlelement, "mechanisms",
|
||||
[{"xmlns", ?NS_SASL}],
|
||||
Mechs},
|
||||
{xmlelement, "register",
|
||||
[{"xmlns", ?NS_FEATURE_IQREGISTER}],
|
||||
[]}]}),
|
||||
{next_state, wait_for_feature_request,
|
||||
StateData#state{server = Server,
|
||||
sasl_state = SASLState,
|
||||
lang = Lang}};
|
||||
_ ->
|
||||
send_element(
|
||||
StateData,
|
||||
{xmlelement, "stream:features", [], []}),
|
||||
{next_state, wait_for_session,
|
||||
StateData#state{lang = Lang}}
|
||||
end
|
||||
case StateData#state.resource of
|
||||
"" ->
|
||||
send_element(
|
||||
StateData,
|
||||
{xmlelement, "stream:features", [],
|
||||
[{xmlelement, "bind",
|
||||
[{"xmlns", ?NS_BIND}], []},
|
||||
{xmlelement, "session",
|
||||
[{"xmlns", ?NS_SESSION}], []}]}),
|
||||
{next_state, wait_for_bind,
|
||||
StateData#state{server = Server,
|
||||
lang = Lang}};
|
||||
_ ->
|
||||
send_element(
|
||||
StateData,
|
||||
{xmlelement, "stream:features", [], []}),
|
||||
{next_state, wait_for_session,
|
||||
StateData#state{server = Server,
|
||||
lang = Lang}}
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
Header = io_lib:format(
|
||||
?STREAM_HEADER,
|
||||
[StateData#state.streamid, Server, "", DefaultLang]),
|
||||
send_text(StateData, Header),
|
||||
{next_state, wait_for_auth,
|
||||
StateData#state{server = Server,
|
||||
lang = Lang}}
|
||||
end;
|
||||
_ ->
|
||||
Header = io_lib:format(
|
||||
?STREAM_HEADER,
|
||||
[StateData#state.streamid, ?MYNAME, "", DefaultLang]),
|
||||
send_text(StateData, Header),
|
||||
{next_state, wait_for_auth, StateData#state{lang = Lang}}
|
||||
send_text(StateData,
|
||||
Header ++ ?HOST_UNKNOWN_ERR ++ ?STREAM_TRAILER),
|
||||
{stop, normal, StateData}
|
||||
end;
|
||||
_ ->
|
||||
Header = io_lib:format(
|
||||
?STREAM_HEADER,
|
||||
[StateData#state.streamid, ?MYNAME, "", ""]),
|
||||
[StateData#state.streamid, ?MYNAME, "", DefaultLang]),
|
||||
send_text(StateData,
|
||||
Header ++ ?INVALID_NS_ERR ++ ?STREAM_TRAILER),
|
||||
{stop, normal, StateData}
|
||||
@ -297,19 +324,20 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
|
||||
send_element(StateData, Err),
|
||||
{next_state, wait_for_auth, StateData};
|
||||
{auth, _ID, set, {U, P, D, R}} ->
|
||||
io:format("AUTH: ~p~n", [{U, P, D, R}]),
|
||||
JID = jlib:make_jid(U, StateData#state.server, R),
|
||||
case (JID /= error) andalso
|
||||
(acl:match_rule(StateData#state.access, JID) == allow) of
|
||||
true ->
|
||||
case ejabberd_auth:check_password(
|
||||
U, P, StateData#state.streamid, D) of
|
||||
U, StateData#state.server, P,
|
||||
StateData#state.streamid, D) of
|
||||
true ->
|
||||
?INFO_MSG(
|
||||
"(~w) Accepted legacy authentication for ~s",
|
||||
[StateData#state.socket,
|
||||
jlib:jid_to_string(JID)]),
|
||||
ejabberd_sm:open_session(U, R),
|
||||
ejabberd_sm:open_session(
|
||||
U, StateData#state.server, R),
|
||||
Res1 = jlib:make_result_iq_reply(El),
|
||||
Res = setelement(4, Res1, []),
|
||||
send_element(StateData, Res),
|
||||
@ -317,13 +345,14 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
|
||||
{Fs, Ts} = ejabberd_hooks:run_fold(
|
||||
roster_get_subscription_lists,
|
||||
{[], []},
|
||||
[U]),
|
||||
[U, StateData#state.server]),
|
||||
LJID = jlib:jid_tolower(
|
||||
jlib:jid_remove_resource(JID)),
|
||||
Fs1 = [LJID | Fs],
|
||||
Ts1 = [LJID | Ts],
|
||||
PrivList =
|
||||
case catch mod_privacy:get_user_list(U) of
|
||||
case catch mod_privacy:get_user_list(
|
||||
U, StateData#state.server) of
|
||||
{'EXIT', _} -> none;
|
||||
PL -> PL
|
||||
end,
|
||||
@ -368,8 +397,10 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
|
||||
case jlib:iq_query_info(El) of
|
||||
#iq{xmlns = ?NS_REGISTER} = IQ ->
|
||||
ResIQ = mod_register:process_iq(
|
||||
{"", "", ""}, {"", ?MYNAME, ""}, IQ),
|
||||
Res1 = jlib:replace_from_to({"", ?MYNAME, ""},
|
||||
{"", "", ""},
|
||||
jlib:make_jid("", StateData#state.server, ""),
|
||||
IQ),
|
||||
Res1 = jlib:replace_from_to({"", StateData#state.server, ""},
|
||||
{"", "", ""},
|
||||
jlib:iq_to_xml(ResIQ)),
|
||||
Res = jlib:remove_attr("to", Res1),
|
||||
@ -414,8 +445,7 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
|
||||
?INFO_MSG("(~w) Accepted authentication for ~s",
|
||||
[StateData#state.socket, U]),
|
||||
{next_state, wait_for_stream,
|
||||
StateData#state{streamid = new_id(),
|
||||
authentificated = true,
|
||||
StateData#state{authentificated = true,
|
||||
user = U
|
||||
}};
|
||||
{continue, ServerOut, NewSASLState} ->
|
||||
@ -445,7 +475,6 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
|
||||
{next_state, wait_for_stream,
|
||||
StateData#state{sockmod = tls,
|
||||
socket = TLSSocket,
|
||||
streamid = new_id(),
|
||||
tls_enabled = true
|
||||
}};
|
||||
_ ->
|
||||
@ -461,8 +490,10 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
|
||||
case jlib:iq_query_info(El) of
|
||||
#iq{xmlns = ?NS_REGISTER} = IQ ->
|
||||
ResIQ = mod_register:process_iq(
|
||||
{"", "", ""}, {"", ?MYNAME, ""}, IQ),
|
||||
Res1 = jlib:replace_from_to({"", ?MYNAME, ""},
|
||||
{"", "", ""},
|
||||
jlib:make_jid("", StateData#state.server, ""),
|
||||
IQ),
|
||||
Res1 = jlib:replace_from_to({"", StateData#state.server, ""},
|
||||
{"", "", ""},
|
||||
jlib:iq_to_xml(ResIQ)),
|
||||
Res = jlib:remove_attr("to", Res1),
|
||||
@ -502,8 +533,7 @@ wait_for_sasl_response({xmlstreamelement, El}, StateData) ->
|
||||
?INFO_MSG("(~w) Accepted authentication for ~s",
|
||||
[StateData#state.socket, U]),
|
||||
{next_state, wait_for_stream,
|
||||
StateData#state{streamid = new_id(),
|
||||
authentificated = true,
|
||||
StateData#state{authentificated = true,
|
||||
user = U
|
||||
}};
|
||||
{continue, ServerOut, NewSASLState} ->
|
||||
@ -525,8 +555,10 @@ wait_for_sasl_response({xmlstreamelement, El}, StateData) ->
|
||||
case jlib:iq_query_info(El) of
|
||||
#iq{xmlns = ?NS_REGISTER} = IQ ->
|
||||
ResIQ = mod_register:process_iq(
|
||||
{"", "", ""}, {"", ?MYNAME, ""}, IQ),
|
||||
Res1 = jlib:replace_from_to({"", ?MYNAME, ""},
|
||||
{"", "", ""},
|
||||
jlib:make_jid("", StateData#state.server, ""),
|
||||
IQ),
|
||||
Res1 = jlib:replace_from_to({"", StateData#state.server, ""},
|
||||
{"", "", ""},
|
||||
jlib:iq_to_xml(ResIQ)),
|
||||
Res = jlib:remove_attr("to", Res1),
|
||||
@ -601,26 +633,27 @@ wait_for_session({xmlstreamelement, El}, StateData) ->
|
||||
#iq{type = set, xmlns = ?NS_SESSION} ->
|
||||
U = StateData#state.user,
|
||||
R = StateData#state.resource,
|
||||
io:format("SASLAUTH: ~p~n", [{U, R}]),
|
||||
JID = StateData#state.jid,
|
||||
case acl:match_rule(StateData#state.access, JID) of
|
||||
allow ->
|
||||
?INFO_MSG("(~w) Opened session for ~s",
|
||||
[StateData#state.socket,
|
||||
jlib:jid_to_string(JID)]),
|
||||
ejabberd_sm:open_session(U, R),
|
||||
ejabberd_sm:open_session(
|
||||
U, StateData#state.server, R),
|
||||
Res = jlib:make_result_iq_reply(El),
|
||||
send_element(StateData, Res),
|
||||
change_shaper(StateData, JID),
|
||||
{Fs, Ts} = ejabberd_hooks:run_fold(
|
||||
roster_get_subscription_lists,
|
||||
{[], []},
|
||||
[U]),
|
||||
[U, StateData#state.server]),
|
||||
LJID = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
|
||||
Fs1 = [LJID | Fs],
|
||||
Ts1 = [LJID | Ts],
|
||||
PrivList =
|
||||
case catch mod_privacy:get_user_list(U) of
|
||||
case catch mod_privacy:get_user_list(
|
||||
U, StateData#state.server) of
|
||||
{'EXIT', _} -> none;
|
||||
PL -> PL
|
||||
end,
|
||||
@ -835,6 +868,7 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
|
||||
%-ifdef(PRIVACY_SUPPORT).
|
||||
case catch mod_privacy:check_packet(
|
||||
StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{From, To, Packet},
|
||||
in) of
|
||||
@ -884,6 +918,7 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
|
||||
#iq{} ->
|
||||
case catch mod_privacy:check_packet(
|
||||
StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{From, To, Packet},
|
||||
in) of
|
||||
@ -905,6 +940,7 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
|
||||
"message" ->
|
||||
case catch mod_privacy:check_packet(
|
||||
StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{From, To, Packet},
|
||||
in) of
|
||||
@ -955,6 +991,7 @@ terminate(_Reason, StateName, StateData) ->
|
||||
[{xmlelement, "status", [],
|
||||
[{xmlcdata, "Replaced by new connection"}]}]},
|
||||
ejabberd_sm:unset_presence(StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.resource,
|
||||
"Replaced by new connection"),
|
||||
presence_broadcast(
|
||||
@ -966,6 +1003,7 @@ terminate(_Reason, StateName, StateData) ->
|
||||
[StateData#state.socket,
|
||||
jlib:jid_to_string(StateData#state.jid)]),
|
||||
ejabberd_sm:close_session(StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.resource),
|
||||
|
||||
Tmp = ?SETS:new(),
|
||||
@ -980,6 +1018,7 @@ terminate(_Reason, StateName, StateData) ->
|
||||
Packet = {xmlelement, "presence",
|
||||
[{"type", "unavailable"}], []},
|
||||
ejabberd_sm:unset_presence(StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.resource,
|
||||
""),
|
||||
presence_broadcast(
|
||||
@ -1070,6 +1109,7 @@ process_presence_probe(From, To, StateData) ->
|
||||
%-ifdef(PRIVACY_SUPPORT).
|
||||
case catch mod_privacy:check_packet(
|
||||
StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{To, From, Packet},
|
||||
out) of
|
||||
@ -1102,6 +1142,7 @@ presence_update(From, Packet, StateData) ->
|
||||
xml:get_tag_cdata(StatusTag)
|
||||
end,
|
||||
ejabberd_sm:unset_presence(StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.resource,
|
||||
Status),
|
||||
presence_broadcast(StateData, From, StateData#state.pres_a, Packet),
|
||||
@ -1173,6 +1214,7 @@ presence_track(From, To, Packet, StateData) ->
|
||||
{xmlelement, _Name, Attrs, _Els} = Packet,
|
||||
LTo = jlib:jid_tolower(To),
|
||||
User = StateData#state.user,
|
||||
Server = StateData#state.server,
|
||||
case xml:get_attr_s("type", Attrs) of
|
||||
"unavailable" ->
|
||||
ejabberd_router:route(From, To, Packet),
|
||||
@ -1189,22 +1231,22 @@ presence_track(From, To, Packet, StateData) ->
|
||||
"subscribe" ->
|
||||
ejabberd_router:route(jlib:jid_remove_resource(From), To, Packet),
|
||||
ejabberd_hooks:run(roster_out_subscription,
|
||||
[User, To, subscribe]),
|
||||
[User, Server, To, subscribe]),
|
||||
StateData;
|
||||
"subscribed" ->
|
||||
ejabberd_router:route(jlib:jid_remove_resource(From), To, Packet),
|
||||
ejabberd_hooks:run(roster_out_subscription,
|
||||
[User, To, subscribed]),
|
||||
[User, Server, To, subscribed]),
|
||||
StateData;
|
||||
"unsubscribe" ->
|
||||
ejabberd_router:route(jlib:jid_remove_resource(From), To, Packet),
|
||||
ejabberd_hooks:run(roster_out_subscription,
|
||||
[User, To, unsubscribe]),
|
||||
[User, Server, To, unsubscribe]),
|
||||
StateData;
|
||||
"unsubscribed" ->
|
||||
ejabberd_router:route(jlib:jid_remove_resource(From), To, Packet),
|
||||
ejabberd_hooks:run(roster_out_subscription,
|
||||
[User, To, unsubscribed]),
|
||||
[User, Server, To, unsubscribed]),
|
||||
StateData;
|
||||
"error" ->
|
||||
ejabberd_router:route(From, To, Packet),
|
||||
@ -1216,6 +1258,7 @@ presence_track(From, To, Packet, StateData) ->
|
||||
%-ifdef(PRIVACY_SUPPORT).
|
||||
case catch mod_privacy:check_packet(
|
||||
StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{From, To, Packet},
|
||||
out) of
|
||||
@ -1239,6 +1282,7 @@ presence_broadcast(StateData, From, JIDSet, Packet) ->
|
||||
%-ifdef(PRIVACY_SUPPORT).
|
||||
case catch mod_privacy:check_packet(
|
||||
StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{From, FJID, Packet},
|
||||
out) of
|
||||
@ -1261,6 +1305,7 @@ presence_broadcast_to_trusted(StateData, From, T, A, Packet) ->
|
||||
%-ifdef(PRIVACY_SUPPORT).
|
||||
case catch mod_privacy:check_packet(
|
||||
StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{From, FJID, Packet},
|
||||
out) of
|
||||
@ -1300,6 +1345,7 @@ presence_broadcast_first(From, StateData, Packet) ->
|
||||
%-ifdef(PRIVACY_SUPPORT).
|
||||
case catch mod_privacy:check_packet(
|
||||
StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{From, FJID, Packet},
|
||||
out) of
|
||||
@ -1395,6 +1441,7 @@ update_priority(El, StateData) ->
|
||||
end
|
||||
end,
|
||||
ejabberd_sm:set_presence(StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.resource,
|
||||
Pri).
|
||||
|
||||
@ -1439,15 +1486,17 @@ process_privacy_iq(From, To,
|
||||
|
||||
|
||||
resend_offline_messages(#state{user = User,
|
||||
server = Server,
|
||||
privacy_list = PrivList} = StateData) ->
|
||||
case ejabberd_hooks:run_fold(resend_offline_messages_hook, [],
|
||||
[User]) of
|
||||
[User, Server]) of
|
||||
Rs when list(Rs) ->
|
||||
lists:foreach(
|
||||
fun({route,
|
||||
From, To, {xmlelement, Name, Attrs, Els} = Packet}) ->
|
||||
Pass = case catch mod_privacy:check_packet(
|
||||
User,
|
||||
Server,
|
||||
PrivList,
|
||||
{From, To, Packet},
|
||||
in) of
|
||||
|
@ -78,13 +78,15 @@ process_term(Term, State) ->
|
||||
State#state{opts = [#config{key = {shaper, Name},
|
||||
value = Data} |
|
||||
State#state.opts]};
|
||||
{host, Host} ->
|
||||
add_option(hosts, [Host], State);
|
||||
{Opt, Val} ->
|
||||
add_option(Opt, Val, State)
|
||||
end.
|
||||
|
||||
add_option(Opt, Val, State) ->
|
||||
Table = case Opt of
|
||||
host ->
|
||||
hosts ->
|
||||
config;
|
||||
language ->
|
||||
config;
|
||||
|
@ -147,7 +147,7 @@ process(Node, ["load", Path]) ->
|
||||
process(Node, ["restore", Path]) ->
|
||||
case rpc:call(Node,
|
||||
mnesia, restore, [Path, [{default_op, keep_tables}]]) of
|
||||
{atomic, _} ->
|
||||
{atomic, ok} ->
|
||||
?STATUS_SUCCESS;
|
||||
{error, Reason} ->
|
||||
io:format("Can't restore backup from ~p on node ~p: ~p~n",
|
||||
|
@ -31,7 +31,10 @@ start_link() ->
|
||||
{ok, Pid}.
|
||||
|
||||
init() ->
|
||||
ejabberd_router:register_route(?MYNAME, {apply, ?MODULE, route}),
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
ejabberd_router:register_route(Host, {apply, ?MODULE, route})
|
||||
end, ?MYHOSTS),
|
||||
catch ets:new(?IQTABLE, [named_table, public]),
|
||||
ejabberd_hooks:add(local_send_to_resource_hook,
|
||||
?MODULE, bounce_resource_packet, 100),
|
||||
|
@ -13,7 +13,9 @@
|
||||
-export([route/3,
|
||||
register_route/1,
|
||||
register_route/2,
|
||||
register_routes/1,
|
||||
unregister_route/1,
|
||||
unregister_routes/1,
|
||||
dirty_get_all_routes/0,
|
||||
dirty_get_all_domains/0
|
||||
]).
|
||||
@ -130,33 +132,58 @@ route(From, To, Packet) ->
|
||||
end.
|
||||
|
||||
register_route(Domain) ->
|
||||
Pid = self(),
|
||||
F = fun() ->
|
||||
mnesia:write(#route{domain = Domain,
|
||||
pid = Pid})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
case jlib:nameprep(Domain) of
|
||||
error ->
|
||||
[] = {invalid_domain, Domain};
|
||||
LDomain ->
|
||||
Pid = self(),
|
||||
F = fun() ->
|
||||
mnesia:write(#route{domain = LDomain,
|
||||
pid = Pid})
|
||||
end,
|
||||
mnesia:transaction(F)
|
||||
end.
|
||||
|
||||
register_route(Domain, LocalHint) ->
|
||||
Pid = self(),
|
||||
F = fun() ->
|
||||
mnesia:write(#route{domain = Domain,
|
||||
pid = Pid,
|
||||
local_hint = LocalHint})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
case jlib:nameprep(Domain) of
|
||||
error ->
|
||||
[] = {invalid_domain, Domain};
|
||||
LDomain ->
|
||||
Pid = self(),
|
||||
F = fun() ->
|
||||
mnesia:write(#route{domain = LDomain,
|
||||
pid = Pid,
|
||||
local_hint = LocalHint})
|
||||
end,
|
||||
mnesia:transaction(F)
|
||||
end.
|
||||
|
||||
register_routes(Domains) ->
|
||||
lists:foreach(fun(Domain) ->
|
||||
register_route(Domain)
|
||||
end, Domains).
|
||||
|
||||
unregister_route(Domain) ->
|
||||
Pid = self(),
|
||||
F = fun() ->
|
||||
mnesia:delete_object(#route{domain = Domain,
|
||||
pid = Pid})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
case jlib:nameprep(Domain) of
|
||||
error ->
|
||||
[] = {invalid_domain, Domain};
|
||||
LDomain ->
|
||||
Pid = self(),
|
||||
F = fun() ->
|
||||
mnesia:delete_object(#route{domain = LDomain,
|
||||
pid = Pid})
|
||||
end,
|
||||
mnesia:transaction(F)
|
||||
end.
|
||||
|
||||
unregister_routes(Domains) ->
|
||||
lists:foreach(fun(Domain) ->
|
||||
unregister_route(Domain)
|
||||
end, Domains).
|
||||
|
||||
|
||||
dirty_get_all_routes() ->
|
||||
lists:delete(?MYNAME, lists:usort(mnesia:dirty_all_keys(route))).
|
||||
lists:usort(mnesia:dirty_all_keys(route)) -- ?MYHOSTS.
|
||||
|
||||
dirty_get_all_domains() ->
|
||||
lists:usort(mnesia:dirty_all_keys(route)).
|
||||
|
@ -12,13 +12,15 @@
|
||||
|
||||
-export([start_link/0, init/0,
|
||||
route/3,
|
||||
open_session/2, close_session/2,
|
||||
open_session/3, close_session/3,
|
||||
bounce_offline_message/3,
|
||||
get_user_resources/1,
|
||||
set_presence/3,
|
||||
unset_presence/3,
|
||||
disconnect_removed_user/2,
|
||||
get_user_resources/2,
|
||||
set_presence/4,
|
||||
unset_presence/4,
|
||||
dirty_get_sessions_list/0,
|
||||
dirty_get_my_sessions_list/0,
|
||||
get_vh_session_list/1,
|
||||
register_iq_handler/3,
|
||||
register_iq_handler/4,
|
||||
unregister_iq_handler/1
|
||||
@ -27,8 +29,8 @@
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(session, {ur, user, pid}).
|
||||
-record(presence, {ur, user, priority}).
|
||||
-record(session, {usr, us, pid}).
|
||||
-record(presence, {usr, us, priority}).
|
||||
|
||||
start_link() ->
|
||||
Pid = proc_lib:spawn_link(ejabberd_sm, init, []),
|
||||
@ -39,16 +41,18 @@ init() ->
|
||||
update_tables(),
|
||||
mnesia:create_table(session, [{ram_copies, [node()]},
|
||||
{attributes, record_info(fields, session)}]),
|
||||
mnesia:add_table_index(session, user),
|
||||
mnesia:add_table_index(session, us),
|
||||
mnesia:add_table_copy(session, node(), ram_copies),
|
||||
mnesia:create_table(presence,
|
||||
[{ram_copies, [node()]},
|
||||
{attributes, record_info(fields, presence)}]),
|
||||
mnesia:add_table_index(presence, user),
|
||||
mnesia:add_table_index(presence, us),
|
||||
mnesia:subscribe(system),
|
||||
ets:new(sm_iqtable, [named_table]),
|
||||
ejabberd_hooks:add(offline_message_hook,
|
||||
ejabberd_sm, bounce_offline_message, 100),
|
||||
ejabberd_hooks:add(remove_user,
|
||||
ejabberd_sm, disconnect_removed_user, 100),
|
||||
loop().
|
||||
|
||||
loop() ->
|
||||
@ -62,12 +66,6 @@ loop() ->
|
||||
ok
|
||||
end,
|
||||
loop();
|
||||
{open_session, User, Resource, From} ->
|
||||
register_connection(User, Resource, From),
|
||||
loop();
|
||||
{close_session, User, Resource} ->
|
||||
remove_connection(User, Resource),
|
||||
loop();
|
||||
{mnesia_system_event, {mnesia_down, Node}} ->
|
||||
clean_table_from_bad_node(Node),
|
||||
loop();
|
||||
@ -100,20 +98,22 @@ route(From, To, Packet) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
open_session(User, Resource) ->
|
||||
register_connection(User, Resource, self()).
|
||||
open_session(User, Server, Resource) ->
|
||||
register_connection(User, Server, Resource, self()).
|
||||
|
||||
close_session(User, Resource) ->
|
||||
remove_connection(User, Resource).
|
||||
close_session(User, Server, Resource) ->
|
||||
remove_connection(User, Server, Resource).
|
||||
|
||||
|
||||
register_connection(User, Resource, Pid) ->
|
||||
register_connection(User, Server, Resource, Pid) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
LResource = jlib:resourceprep(Resource),
|
||||
UR = {LUser, LResource},
|
||||
US = {LUser, LServer},
|
||||
USR = {LUser, LServer, LResource},
|
||||
F = fun() ->
|
||||
Ss = mnesia:wread({session, UR}),
|
||||
mnesia:write(#session{ur = UR, user = LUser, pid = Pid}),
|
||||
Ss = mnesia:wread({session, USR}),
|
||||
mnesia:write(#session{usr = USR, us = US, pid = Pid}),
|
||||
Ss
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
@ -127,12 +127,13 @@ register_connection(User, Resource, Pid) ->
|
||||
end.
|
||||
|
||||
|
||||
remove_connection(User, Resource) ->
|
||||
remove_connection(User, Server, Resource) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LResource = jlib:resourceprep(Resource),
|
||||
UR = {LUser, LResource},
|
||||
LServer = jlib:nameprep(Server),
|
||||
USR = {LUser, LServer, LResource},
|
||||
F = fun() ->
|
||||
mnesia:delete({session, UR})
|
||||
mnesia:delete({session, USR})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
@ -145,8 +146,7 @@ clean_table_from_bad_node(Node) ->
|
||||
[{'==', {node, '$1'}, Node}],
|
||||
['$_']}]),
|
||||
lists:foreach(fun(E) ->
|
||||
mnesia:delete_object(E),
|
||||
mnesia:delete({presence, E#session.ur})
|
||||
mnesia:delete_object(E)
|
||||
end, Es)
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
@ -157,7 +157,7 @@ clean_table_from_bad_node(Node) ->
|
||||
do_route(From, To, Packet) ->
|
||||
?DEBUG("session manager~n\tfrom ~p~n\tto ~p~n\tpacket ~P~n",
|
||||
[From, To, Packet, 8]),
|
||||
#jid{user = User,
|
||||
#jid{user = User, server = Server,
|
||||
luser = LUser, lserver = LServer, lresource = LResource} = To,
|
||||
{xmlelement, Name, Attrs, _Els} = Packet,
|
||||
case LResource of
|
||||
@ -170,32 +170,33 @@ do_route(From, To, Packet) ->
|
||||
{ejabberd_hooks:run_fold(
|
||||
roster_in_subscription,
|
||||
false,
|
||||
[User, From, subscribe]),
|
||||
[User, Server, From, subscribe]),
|
||||
true};
|
||||
"subscribed" ->
|
||||
{ejabberd_hooks:run_fold(
|
||||
roster_in_subscription,
|
||||
false,
|
||||
[User, From, subscribed]),
|
||||
[User, Server, From, subscribed]),
|
||||
true};
|
||||
"unsubscribe" ->
|
||||
{ejabberd_hooks:run_fold(
|
||||
roster_in_subscription,
|
||||
false,
|
||||
[User, From, unsubscribe]),
|
||||
[User, Server, From, unsubscribe]),
|
||||
true};
|
||||
"unsubscribed" ->
|
||||
{ejabberd_hooks:run_fold(
|
||||
roster_in_subscription,
|
||||
false,
|
||||
[User, From, unsubscribed]),
|
||||
[User, Server, From, unsubscribed]),
|
||||
true};
|
||||
_ ->
|
||||
{true, false}
|
||||
end,
|
||||
if Pass ->
|
||||
LFrom = jlib:jid_tolower(From),
|
||||
PResources = get_user_present_resources(User),
|
||||
PResources = get_user_present_resources(
|
||||
LUser, LServer),
|
||||
if
|
||||
PResources /= [] ->
|
||||
lists:foreach(
|
||||
@ -213,7 +214,8 @@ do_route(From, To, Packet) ->
|
||||
true ->
|
||||
if
|
||||
Subsc ->
|
||||
case ejabberd_auth:is_user_exists(LUser) of
|
||||
case ejabberd_auth:is_user_exists(
|
||||
LUser, LServer) of
|
||||
true ->
|
||||
ejabberd_hooks:run(
|
||||
offline_subscription_hook,
|
||||
@ -240,13 +242,13 @@ do_route(From, To, Packet) ->
|
||||
do_route(From,
|
||||
jlib:jid_replace_resource(To, R),
|
||||
Packet)
|
||||
end, get_user_resources(User));
|
||||
end, get_user_resources(User, Server));
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
LUR = {LUser, LResource},
|
||||
case mnesia:dirty_read({session, LUR}) of
|
||||
USR = {LUser, LServer, LResource},
|
||||
case mnesia:dirty_read({session, USR}) of
|
||||
[] ->
|
||||
case Name of
|
||||
"message" ->
|
||||
@ -273,12 +275,13 @@ do_route(From, To, Packet) ->
|
||||
|
||||
route_message(From, To, Packet) ->
|
||||
LUser = To#jid.luser,
|
||||
case catch lists:max(get_user_present_resources(LUser)) of
|
||||
LServer = To#jid.lserver,
|
||||
case catch lists:max(get_user_present_resources(LUser, LServer)) of
|
||||
{Priority, R} when is_integer(Priority),
|
||||
Priority >= 0 ->
|
||||
LResource = jlib:resourceprep(R),
|
||||
LUR = {LUser, LResource},
|
||||
case mnesia:dirty_read({session, LUR}) of
|
||||
USR = {LUser, LServer, LResource},
|
||||
case mnesia:dirty_read({session, USR}) of
|
||||
[] ->
|
||||
ok; % Race condition
|
||||
[Sess] ->
|
||||
@ -291,7 +294,7 @@ route_message(From, To, Packet) ->
|
||||
"error" ->
|
||||
ok;
|
||||
_ ->
|
||||
case ejabberd_auth:is_user_exists(LUser) of
|
||||
case ejabberd_auth:is_user_exists(LUser, LServer) of
|
||||
true ->
|
||||
ejabberd_hooks:run(offline_message_hook,
|
||||
[From, To, Packet]);
|
||||
@ -308,46 +311,58 @@ bounce_offline_message(From, To, Packet) ->
|
||||
ejabberd_router:route(To, From, Err),
|
||||
stop.
|
||||
|
||||
disconnect_removed_user(User, Server) ->
|
||||
ejabberd_sm:route(jlib:make_jid("", "", ""),
|
||||
jlib:make_jid(User, Server, ""),
|
||||
{xmlelement, "broadcast", [],
|
||||
[{exit, "User removed"}]}).
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
get_user_resources(User) ->
|
||||
get_user_resources(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
case catch mnesia:dirty_index_read(session, LUser, #session.user) of
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_index_read(session, US, #session.us) of
|
||||
{'EXIT', _Reason} ->
|
||||
[];
|
||||
Rs ->
|
||||
lists:map(fun(R) ->
|
||||
element(2, R#session.ur)
|
||||
element(3, R#session.usr)
|
||||
end, Rs)
|
||||
end.
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
set_presence(User, Resource, Priority) ->
|
||||
set_presence(User, Server, Resource, Priority) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
USR = {User, Server, Resource},
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
UR = {User, Resource},
|
||||
mnesia:write(#presence{ur = UR, user = LUser,
|
||||
mnesia:write(#presence{usr = USR, us = US,
|
||||
priority = Priority})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
unset_presence(User, Resource, Status) ->
|
||||
unset_presence(User, Server, Resource, Status) ->
|
||||
USR = {User, Server, Resource},
|
||||
F = fun() ->
|
||||
UR = {User, Resource},
|
||||
mnesia:delete({presence, UR})
|
||||
mnesia:delete({presence, USR})
|
||||
end,
|
||||
mnesia:transaction(F),
|
||||
ejabberd_hooks:run(unset_presence_hook, [User, Resource, Status]).
|
||||
ejabberd_hooks:run(unset_presence_hook, [User, Server, Resource, Status]).
|
||||
|
||||
get_user_present_resources(LUser) ->
|
||||
case catch mnesia:dirty_index_read(presence, LUser, #presence.user) of
|
||||
get_user_present_resources(LUser, LServer) ->
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_index_read(presence, US, #presence.us) of
|
||||
{'EXIT', _Reason} ->
|
||||
[];
|
||||
Rs ->
|
||||
lists:map(fun(R) ->
|
||||
{R#presence.priority, element(2, R#presence.ur)}
|
||||
{R#presence.priority, element(3, R#presence.usr)}
|
||||
end, Rs)
|
||||
end.
|
||||
|
||||
@ -361,6 +376,14 @@ dirty_get_my_sessions_list() ->
|
||||
[{'==', {node, '$1'}, node()}],
|
||||
['$_']}]).
|
||||
|
||||
get_vh_session_list(Server) ->
|
||||
LServer = jlib:nameprep(Server),
|
||||
mnesia:dirty_select(
|
||||
session,
|
||||
[{#session{usr = '$1', _ = '_'},
|
||||
[{'==', {element, 2, '$1'}, LServer}],
|
||||
['$1']}]).
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
@ -410,6 +433,16 @@ update_tables() ->
|
||||
[ur, user, node] ->
|
||||
mnesia:delete_table(session);
|
||||
[ur, user, pid] ->
|
||||
mnesia:delete_table(session);
|
||||
[usr, us, pid] ->
|
||||
ok;
|
||||
{'EXIT', _} ->
|
||||
ok
|
||||
end,
|
||||
case catch mnesia:table_info(presence, attributes) of
|
||||
[ur, user, priority] ->
|
||||
mnesia:delete_table(presence);
|
||||
[usr, us, priority] ->
|
||||
ok;
|
||||
{'EXIT', _} ->
|
||||
ok
|
||||
|
@ -26,6 +26,13 @@ init([]) ->
|
||||
brutal_kill,
|
||||
worker,
|
||||
[ejabberd_hooks]},
|
||||
StringPrep =
|
||||
{stringprep,
|
||||
{stringprep, start_link, []},
|
||||
permanent,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[stringprep]},
|
||||
Router =
|
||||
{ejabberd_router,
|
||||
{ejabberd_router, start_link, []},
|
||||
@ -61,13 +68,6 @@ init([]) ->
|
||||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_listener]},
|
||||
StringPrep =
|
||||
{stringprep,
|
||||
{stringprep, start_link, []},
|
||||
permanent,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[stringprep]},
|
||||
C2SSupervisor =
|
||||
{ejabberd_c2s_sup,
|
||||
{ejabberd_tmp_sup, start_link, [ejabberd_c2s_sup, ejabberd_c2s]},
|
||||
@ -125,11 +125,11 @@ init([]) ->
|
||||
[ejabberd_tmp_sup]},
|
||||
{ok, {{one_for_one, 10, 1},
|
||||
[Hooks,
|
||||
StringPrep,
|
||||
Router,
|
||||
SM,
|
||||
S2S,
|
||||
Local,
|
||||
StringPrep,
|
||||
C2SSupervisor,
|
||||
S2SInSupervisor,
|
||||
S2SOutSupervisor,
|
||||
|
2972
src/eldap/ELDAPv3.erl
Normal file
2972
src/eldap/ELDAPv3.erl
Normal file
File diff suppressed because it is too large
Load Diff
74
src/eldap/ELDAPv3.hrl
Normal file
74
src/eldap/ELDAPv3.hrl
Normal file
@ -0,0 +1,74 @@
|
||||
%% Generated by the Erlang ASN.1 compiler version:1.3.2
|
||||
%% Purpose: Erlang record definitions for each named and unnamed
|
||||
%% SEQUENCE and SET, and macro definitions for each value
|
||||
%% definition,in module ELDAPv3
|
||||
|
||||
|
||||
|
||||
-record('LDAPMessage',{
|
||||
messageID, protocolOp, controls = asn1_NOVALUE}).
|
||||
|
||||
-record('AttributeValueAssertion',{
|
||||
attributeDesc, assertionValue}).
|
||||
|
||||
-record('Attribute',{
|
||||
type, vals}).
|
||||
|
||||
-record('LDAPResult',{
|
||||
resultCode, matchedDN, errorMessage, referral = asn1_NOVALUE}).
|
||||
|
||||
-record('Control',{
|
||||
controlType, criticality = asn1_DEFAULT, controlValue = asn1_NOVALUE}).
|
||||
|
||||
-record('BindRequest',{
|
||||
version, name, authentication}).
|
||||
|
||||
-record('SaslCredentials',{
|
||||
mechanism, credentials = asn1_NOVALUE}).
|
||||
|
||||
-record('BindResponse',{
|
||||
resultCode, matchedDN, errorMessage, referral = asn1_NOVALUE, serverSaslCreds = asn1_NOVALUE}).
|
||||
|
||||
-record('SearchRequest',{
|
||||
baseObject, scope, derefAliases, sizeLimit, timeLimit, typesOnly, filter, attributes}).
|
||||
|
||||
-record('SubstringFilter',{
|
||||
type, substrings}).
|
||||
|
||||
-record('MatchingRuleAssertion',{
|
||||
matchingRule = asn1_NOVALUE, type = asn1_NOVALUE, matchValue, dnAttributes = asn1_DEFAULT}).
|
||||
|
||||
-record('SearchResultEntry',{
|
||||
objectName, attributes}).
|
||||
|
||||
-record('PartialAttributeList_SEQOF',{
|
||||
type, vals}).
|
||||
|
||||
-record('ModifyRequest',{
|
||||
object, modification}).
|
||||
|
||||
-record('ModifyRequest_modification_SEQOF',{
|
||||
operation, modification}).
|
||||
|
||||
-record('AttributeTypeAndValues',{
|
||||
type, vals}).
|
||||
|
||||
-record('AddRequest',{
|
||||
entry, attributes}).
|
||||
|
||||
-record('AttributeList_SEQOF',{
|
||||
type, vals}).
|
||||
|
||||
-record('ModifyDNRequest',{
|
||||
entry, newrdn, deleteoldrdn, newSuperior = asn1_NOVALUE}).
|
||||
|
||||
-record('CompareRequest',{
|
||||
entry, ava}).
|
||||
|
||||
-record('ExtendedRequest',{
|
||||
requestName, requestValue = asn1_NOVALUE}).
|
||||
|
||||
-record('ExtendedResponse',{
|
||||
resultCode, matchedDN, errorMessage, referral = asn1_NOVALUE, responseName = asn1_NOVALUE, response = asn1_NOVALUE}).
|
||||
|
||||
-define('maxInt', 2147483647).
|
@ -16,7 +16,8 @@
|
||||
get_opt/2,
|
||||
get_opt/3,
|
||||
get_module_opt/3,
|
||||
loaded_modules/0]).
|
||||
loaded_modules/0,
|
||||
get_hosts/2]).
|
||||
|
||||
-export([behaviour_info/1]).
|
||||
|
||||
@ -27,7 +28,7 @@
|
||||
behaviour_info(callbacks) ->
|
||||
[{start, 1},
|
||||
{stop, 0}];
|
||||
behaviour_info(Other) ->
|
||||
behaviour_info(_Other) ->
|
||||
undefined.
|
||||
|
||||
start() ->
|
||||
@ -60,8 +61,8 @@ stop_module(Module) ->
|
||||
get_opt(Opt, Opts) ->
|
||||
case lists:keysearch(Opt, 1, Opts) of
|
||||
false ->
|
||||
% TODO: replace with more appropriate function
|
||||
[] = {undefined_option, Opt};
|
||||
% TODO: replace with more appropriate function
|
||||
[] = {undefined_option, Opt};
|
||||
{value, {_, Val}} ->
|
||||
Val
|
||||
end.
|
||||
@ -87,3 +88,15 @@ loaded_modules() ->
|
||||
ets:select(ejabberd_modules,
|
||||
[{#ejabberd_module{_ = '_', module = '$1'}, [],['$1']}]).
|
||||
|
||||
get_hosts(Opts, Prefix) ->
|
||||
case catch gen_mod:get_opt(hosts, Opts) of
|
||||
{'EXIT', _Error1} ->
|
||||
case catch gen_mod:get_opt(host, Opts) of
|
||||
{'EXIT', _Error2} ->
|
||||
[Prefix ++ Host || Host <- ?MYHOSTS];
|
||||
Host ->
|
||||
[Host]
|
||||
end;
|
||||
Hosts ->
|
||||
Hosts
|
||||
end.
|
||||
|
323
src/jd2ejd.erl
323
src/jd2ejd.erl
@ -10,248 +10,48 @@
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-behaviour(gen_fsm).
|
||||
|
||||
%% External exports
|
||||
-export([start/1,
|
||||
start/2,
|
||||
import_file/1,
|
||||
-export([import_file/1,
|
||||
import_dir/1]).
|
||||
|
||||
%% gen_fsm callbacks
|
||||
-export([init/1,
|
||||
wait_for_xdb/2,
|
||||
xdb_data/2,
|
||||
handle_event/3,
|
||||
handle_sync_event/4,
|
||||
code_change/4,
|
||||
handle_info/3,
|
||||
terminate/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(state, {socket, pid, xml_stream_pid,
|
||||
user = "", server = ?MYNAME, resource = ""
|
||||
}).
|
||||
|
||||
%-define(DBGFSM, true).
|
||||
|
||||
-ifdef(DBGFSM).
|
||||
-define(FSMOPTS, [{debug, [trace]}]).
|
||||
-else.
|
||||
-define(FSMOPTS, []).
|
||||
-endif.
|
||||
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start(File) ->
|
||||
User = filename:rootname(filename:basename(File)),
|
||||
start(File, User).
|
||||
|
||||
start(File, User) ->
|
||||
gen_fsm:start(?MODULE, [File, User, self()], ?FSMOPTS).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Callback functions from gen_fsm
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: init/1
|
||||
%% Returns: {ok, StateName, StateData} |
|
||||
%% {ok, StateName, StateData, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, StopReason}
|
||||
%%----------------------------------------------------------------------
|
||||
init([File, User, Pid]) ->
|
||||
% Profiling
|
||||
%eprof:start(),
|
||||
%eprof:profile([self()]),
|
||||
XMLStreamPid = xml_stream:start(self()),
|
||||
{ok, Text} = file:read_file(File),
|
||||
xml_stream:send_text(XMLStreamPid, Text),
|
||||
{ok, wait_for_xdb, #state{user = User, pid = Pid,
|
||||
xml_stream_pid = XMLStreamPid}}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/2
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
wait_for_xdb({xmlstreamstart, Name, _Attrs}, StateData) ->
|
||||
case Name of
|
||||
"xdb" ->
|
||||
{next_state, xdb_data, StateData};
|
||||
_ ->
|
||||
{stop, normal, StateData}
|
||||
end;
|
||||
|
||||
wait_for_xdb(closed, StateData) ->
|
||||
{stop, normal, StateData}.
|
||||
|
||||
|
||||
xdb_data({xmlstreamelement, El}, StateData) ->
|
||||
{xmlelement, _Name, Attrs, _Els} = El,
|
||||
Server = StateData#state.server,
|
||||
From = jlib:make_jid(StateData#state.user, Server, ""),
|
||||
NewState =
|
||||
case xml:get_attr_s("xmlns", Attrs) of
|
||||
?NS_AUTH ->
|
||||
Password = xml:get_tag_cdata(El),
|
||||
ejabberd_auth:set_password(StateData#state.user, Password),
|
||||
StateData;
|
||||
?NS_ROSTER ->
|
||||
%catch mod_roster:process_iq(
|
||||
% From,
|
||||
% {"", ?MYNAME, ""},
|
||||
% #iq{type = set, xmlns = ?NS_ROSTER, sub_el = El}),
|
||||
catch mod_roster:set_items(StateData#state.user, El),
|
||||
StateData;
|
||||
?NS_VCARD ->
|
||||
catch mod_vcard:process_sm_iq(
|
||||
From,
|
||||
jlib:make_jid("", ?MYNAME, ""),
|
||||
#iq{type = set, xmlns = ?NS_VCARD, sub_el = El}),
|
||||
StateData;
|
||||
"jabber:x:offline" ->
|
||||
process_offline(From, El),
|
||||
StateData;
|
||||
%?NS_REGISTER ->
|
||||
% catch mod_register:process_iq(
|
||||
% {"", "", ""}, {"", ?MYNAME, ""},
|
||||
% #iq{type =set, xmlns = ?NS_REGISTER, xub_el = El}),
|
||||
% User = xml:get_path_s(El, [{elem, "username"}, cdata]),
|
||||
% io:format("user ~s~n", [User]),
|
||||
% StateData;
|
||||
XMLNS ->
|
||||
case xml:get_attr_s("j_private_flag", Attrs) of
|
||||
"1" ->
|
||||
catch mod_private:process_local_iq(
|
||||
From,
|
||||
jlib:make_jid("", ?MYNAME, ""),
|
||||
#iq{type = set, xmlns = ?NS_PRIVATE,
|
||||
sub_el = {xmlelement, "query", [],
|
||||
[jlib:remove_attr(
|
||||
"j_private_flag",
|
||||
jlib:remove_attr("xdbns", El))]}}),
|
||||
StateData;
|
||||
_ ->
|
||||
io:format("jd2ejd: Unknown namespace \"~s\"~n",
|
||||
[XMLNS]),
|
||||
StateData
|
||||
end
|
||||
end,
|
||||
{next_state, xdb_data, NewState};
|
||||
|
||||
xdb_data({xmlstreamend, _Name}, StateData) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
xdb_data(closed, StateData) ->
|
||||
{stop, normal, StateData}.
|
||||
|
||||
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {reply, Reply, NextStateName, NextStateData} |
|
||||
%% {reply, Reply, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData} |
|
||||
%% {stop, Reason, Reply, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
%state_name(Event, From, StateData) ->
|
||||
% Reply = ok,
|
||||
% {reply, Reply, state_name, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_event/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_event(_Event, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_sync_event/4
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {reply, Reply, NextStateName, NextStateData} |
|
||||
%% {reply, Reply, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData} |
|
||||
%% {stop, Reason, Reply, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_sync_event(_Event, _From, StateName, StateData) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, StateName, StateData}.
|
||||
|
||||
code_change(_OldVsn, StateName, StateData, _Extra) ->
|
||||
{ok, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_info/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_info(_, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: terminate/3
|
||||
%% Purpose: Shutdown the fsm
|
||||
%% Returns: any
|
||||
%%----------------------------------------------------------------------
|
||||
terminate(Reason, _StateName, StateData) ->
|
||||
exit(StateData#state.xml_stream_pid, closed),
|
||||
StateData#state.pid ! {jd2ejd, Reason},
|
||||
% Profiling
|
||||
%eprof:log("/tmp/eprof"),
|
||||
%eprof:analyse(),
|
||||
%eprof:stop(),
|
||||
ok.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
process_offline(To, {xmlelement, _, _, Els}) ->
|
||||
lists:foreach(fun({xmlelement, _, Attrs, _} = El) ->
|
||||
FromS = xml:get_attr_s("from", Attrs),
|
||||
From = case FromS of
|
||||
"" ->
|
||||
{"", ?MYNAME, ""};
|
||||
_ ->
|
||||
jlib:string_to_jid(FromS)
|
||||
end,
|
||||
case From of
|
||||
error ->
|
||||
ok;
|
||||
_ ->
|
||||
catch mod_offline:store_packet(From, To, El)
|
||||
end
|
||||
end, Els).
|
||||
|
||||
|
||||
import_file(File) ->
|
||||
clear_queue(),
|
||||
start(File),
|
||||
receive
|
||||
{jd2ejd, Result} -> Result
|
||||
%after 4000 -> timeout
|
||||
User = filename:rootname(filename:basename(File)),
|
||||
Server = filename:basename(filename:dirname(File)),
|
||||
case (jlib:nodeprep(User) /= error) andalso
|
||||
(jlib:nameprep(Server) /= error) of
|
||||
true ->
|
||||
case file:read_file(File) of
|
||||
{ok, Text} ->
|
||||
case xml_stream:parse_element(Text) of
|
||||
El when element(1, El) == xmlelement ->
|
||||
case catch process_xdb(User, Server, El) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG(
|
||||
"Error while processing file \"~s\": ~p~n",
|
||||
[File, Reason]);
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("Can't parse file \"~s\": ~p~n",
|
||||
[File, Reason])
|
||||
end;
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("Can't read file \"~s\": ~p~n", [File, Reason])
|
||||
end;
|
||||
false ->
|
||||
?ERROR_MSG("Incorrect user/server name in file \"~s\"~n", [File])
|
||||
end.
|
||||
|
||||
clear_queue() ->
|
||||
receive
|
||||
{jd2ejd, _Result} -> clear_queue
|
||||
after 0 -> ok
|
||||
end.
|
||||
|
||||
|
||||
import_dir(Dir) ->
|
||||
{ok, Files} = file:list_dir(Dir),
|
||||
@ -271,5 +71,74 @@ import_dir(Dir) ->
|
||||
end, MsgFiles),
|
||||
ok.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
process_xdb(User, Server, {xmlelement, Name, _Attrs, Els}) ->
|
||||
case Name of
|
||||
"xdb" ->
|
||||
lists:foreach(
|
||||
fun(El) ->
|
||||
xdb_data(User, Server, El)
|
||||
end, Els);
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
|
||||
xdb_data(User, Server, El) ->
|
||||
{xmlelement, _Name, Attrs, _Els} = El,
|
||||
From = jlib:make_jid(User, Server, ""),
|
||||
case xml:get_attr_s("xmlns", Attrs) of
|
||||
?NS_AUTH ->
|
||||
Password = xml:get_tag_cdata(El),
|
||||
ejabberd_auth:set_password(User, Server, Password),
|
||||
ok;
|
||||
?NS_ROSTER ->
|
||||
catch mod_roster:set_items(User, Server, El),
|
||||
ok;
|
||||
?NS_VCARD ->
|
||||
catch mod_vcard:process_sm_iq(
|
||||
From,
|
||||
jlib:make_jid("", ?MYNAME, ""),
|
||||
#iq{type = set, xmlns = ?NS_VCARD, sub_el = El}),
|
||||
ok;
|
||||
"jabber:x:offline" ->
|
||||
process_offline(From, El),
|
||||
ok;
|
||||
XMLNS ->
|
||||
case xml:get_attr_s("j_private_flag", Attrs) of
|
||||
"1" ->
|
||||
catch mod_private:process_local_iq(
|
||||
From,
|
||||
jlib:make_jid("", ?MYNAME, ""),
|
||||
#iq{type = set, xmlns = ?NS_PRIVATE,
|
||||
sub_el = {xmlelement, "query", [],
|
||||
[jlib:remove_attr(
|
||||
"j_private_flag",
|
||||
jlib:remove_attr("xdbns", El))]}});
|
||||
_ ->
|
||||
?DEBUG("jd2ejd: Unknown namespace \"~s\"~n", [XMLNS])
|
||||
end,
|
||||
ok
|
||||
end.
|
||||
|
||||
|
||||
process_offline(To, {xmlelement, _, _, Els}) ->
|
||||
lists:foreach(fun({xmlelement, _, Attrs, _} = El) ->
|
||||
FromS = xml:get_attr_s("from", Attrs),
|
||||
From = case FromS of
|
||||
"" ->
|
||||
jlib:make_jid("", ?MYNAME, "");
|
||||
_ ->
|
||||
jlib:string_to_jid(FromS)
|
||||
end,
|
||||
case From of
|
||||
error ->
|
||||
ok;
|
||||
_ ->
|
||||
catch mod_offline:store_packet(From, To, El)
|
||||
end
|
||||
end, Els).
|
||||
|
||||
|
@ -20,8 +20,8 @@
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(motd, {id, packet}).
|
||||
-record(motd_users, {luser, dummy = []}).
|
||||
-record(motd, {server, packet}).
|
||||
-record(motd_users, {us, dummy = []}).
|
||||
|
||||
-define(PROCNAME, ejabberd_announce).
|
||||
|
||||
@ -30,6 +30,7 @@ start(_) ->
|
||||
{attributes, record_info(fields, motd)}]),
|
||||
mnesia:create_table(motd_users, [{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, motd_users)}]),
|
||||
update_tables(),
|
||||
ejabberd_hooks:add(local_send_to_resource_hook,
|
||||
?MODULE, announce, 50),
|
||||
ejabberd_hooks:add(user_available_hook,
|
||||
@ -47,6 +48,9 @@ loop() ->
|
||||
{announce_online, From, To, Packet} ->
|
||||
announce_online(From, To, Packet),
|
||||
loop();
|
||||
{announce_all_hosts_online, From, To, Packet} ->
|
||||
announce_all_hosts_online(From, To, Packet),
|
||||
loop();
|
||||
{announce_motd, From, To, Packet} ->
|
||||
announce_motd(From, To, Packet),
|
||||
loop();
|
||||
@ -79,6 +83,9 @@ announce(From, To, Packet) ->
|
||||
{"announce/online", "message"} ->
|
||||
?PROCNAME ! {announce_online, From, To, Packet},
|
||||
stop;
|
||||
{"announce/all-hosts/online", "message"} ->
|
||||
?PROCNAME ! {announce_all_hosts_online, From, To, Packet},
|
||||
stop;
|
||||
{"announce/motd", "message"} ->
|
||||
?PROCNAME ! {announce_motd, From, To, Packet},
|
||||
stop;
|
||||
@ -102,13 +109,12 @@ announce_all(From, To, Packet) ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
|
||||
ejabberd_router:route(To, From, Err);
|
||||
allow ->
|
||||
Server = ?MYNAME,
|
||||
Local = jlib:make_jid("", Server, ""),
|
||||
Local = jlib:make_jid("", To#jid.server, ""),
|
||||
lists:foreach(
|
||||
fun(U) ->
|
||||
Dest = jlib:make_jid(U, Server, ""),
|
||||
fun({User, Server}) ->
|
||||
Dest = jlib:make_jid(User, Server, ""),
|
||||
ejabberd_router:route(Local, Dest, Packet)
|
||||
end, ejabberd_auth:dirty_get_registered_users())
|
||||
end, ejabberd_auth:get_vh_registered_users(To#jid.lserver))
|
||||
end.
|
||||
|
||||
announce_online(From, To, Packet) ->
|
||||
@ -118,15 +124,28 @@ announce_online(From, To, Packet) ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
|
||||
ejabberd_router:route(To, From, Err);
|
||||
allow ->
|
||||
announce_online1(ejabberd_sm:dirty_get_sessions_list(), Packet)
|
||||
announce_online1(ejabberd_sm:get_vh_session_list(To#jid.lserver),
|
||||
To#jid.server,
|
||||
Packet)
|
||||
end.
|
||||
|
||||
announce_online1(Sessions, Packet) ->
|
||||
Server = ?MYNAME,
|
||||
announce_all_hosts_online(From, To, Packet) ->
|
||||
Access = gen_mod:get_module_opt(?MODULE, access, none),
|
||||
case acl:match_rule(Access, From) of
|
||||
deny ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
|
||||
ejabberd_router:route(To, From, Err);
|
||||
allow ->
|
||||
announce_online1(ejabberd_sm:dirty_get_sessions_list(),
|
||||
To#jid.server,
|
||||
Packet)
|
||||
end.
|
||||
|
||||
announce_online1(Sessions, Server, Packet) ->
|
||||
Local = jlib:make_jid("", Server, ""),
|
||||
lists:foreach(
|
||||
fun({U, R}) ->
|
||||
Dest = jlib:make_jid(U, Server, R),
|
||||
fun({U, S, R}) ->
|
||||
Dest = jlib:make_jid(U, S, R),
|
||||
ejabberd_router:route(Local, Dest, Packet)
|
||||
end, Sessions).
|
||||
|
||||
@ -137,13 +156,13 @@ announce_motd(From, To, Packet) ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
|
||||
ejabberd_router:route(To, From, Err);
|
||||
allow ->
|
||||
announce_motd_update(Packet),
|
||||
Sessions = ejabberd_sm:dirty_get_sessions_list(),
|
||||
announce_online1(Sessions, Packet),
|
||||
announce_motd_update(To#jid.lserver, Packet),
|
||||
Sessions = ejabberd_sm:get_vh_session_list(To#jid.lserver),
|
||||
announce_online1(Sessions, To#jid.server, Packet),
|
||||
F = fun() ->
|
||||
lists:foreach(
|
||||
fun({U, _R}) ->
|
||||
mnesia:write(#motd_users{luser = U})
|
||||
fun({U, S, _R}) ->
|
||||
mnesia:write(#motd_users{us = {U, S}})
|
||||
end, Sessions)
|
||||
end,
|
||||
mnesia:transaction(F)
|
||||
@ -156,13 +175,13 @@ announce_motd_update(From, To, Packet) ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
|
||||
ejabberd_router:route(To, From, Err);
|
||||
allow ->
|
||||
announce_motd_update(Packet)
|
||||
announce_motd_update(To#jid.lserver, Packet)
|
||||
end.
|
||||
|
||||
announce_motd_update(Packet) ->
|
||||
announce_motd_delete(),
|
||||
announce_motd_update(LServer, Packet) ->
|
||||
announce_motd_delete(LServer),
|
||||
F = fun() ->
|
||||
mnesia:write(#motd{id = motd, packet = Packet})
|
||||
mnesia:write(#motd{server = LServer, packet = Packet})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
@ -173,24 +192,36 @@ announce_motd_delete(From, To, Packet) ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
|
||||
ejabberd_router:route(To, From, Err);
|
||||
allow ->
|
||||
announce_motd_delete()
|
||||
announce_motd_delete(To#jid.lserver)
|
||||
end.
|
||||
|
||||
announce_motd_delete() ->
|
||||
mnesia:clear_table(motd),
|
||||
mnesia:clear_table(motd_users).
|
||||
announce_motd_delete(LServer) ->
|
||||
F = fun() ->
|
||||
mnesia:delete({motd, LServer}),
|
||||
mnesia:write_lock_table(motd_users),
|
||||
Users = mnesia:select(
|
||||
motd_users,
|
||||
[{#motd_users{us = '$1', _ = '_'},
|
||||
[{'==', {element, 2, '$1'}, LServer}],
|
||||
['$1']}]),
|
||||
lists:foreach(fun(US) ->
|
||||
mnesia:delete({motd_users, US})
|
||||
end, Users)
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
send_motd(#jid{luser = LUser} = JID) ->
|
||||
case catch mnesia:dirty_read({motd, motd}) of
|
||||
send_motd(#jid{luser = LUser, lserver = LServer} = JID) ->
|
||||
case catch mnesia:dirty_read({motd, LServer}) of
|
||||
[#motd{packet = Packet}] ->
|
||||
case catch mnesia:dirty_read({motd_users, LUser}) of
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read({motd_users, US}) of
|
||||
[#motd_users{}] ->
|
||||
ok;
|
||||
_ ->
|
||||
Local = jlib:make_jid("", ?MYNAME, ""),
|
||||
Local = jlib:make_jid("", LServer, ""),
|
||||
ejabberd_router:route(Local, JID, Packet),
|
||||
F = fun() ->
|
||||
mnesia:write(#motd_users{luser = LUser})
|
||||
mnesia:write(#motd_users{us = US})
|
||||
end,
|
||||
mnesia:transaction(F)
|
||||
end;
|
||||
@ -198,3 +229,92 @@ send_motd(#jid{luser = LUser} = JID) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
|
||||
update_tables() ->
|
||||
update_motd_table(),
|
||||
update_motd_users_table().
|
||||
|
||||
update_motd_table() ->
|
||||
Fields = record_info(fields, motd),
|
||||
case mnesia:table_info(motd, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[id, packet] ->
|
||||
?INFO_MSG("Converting motd table from "
|
||||
"{id, packet} format", []),
|
||||
Host = ?MYNAME,
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_announce_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, motd},
|
||||
{attributes, record_info(fields, motd)}]),
|
||||
mnesia:transform_table(motd, ignore, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_announce_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#motd{server = _} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_announce_tmp_table,
|
||||
R#motd{server = Host})
|
||||
end, ok, motd)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(motd),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(motd),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, mod_announce_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(mod_announce_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating motd table", []),
|
||||
mnesia:transform_table(motd, ignore, Fields)
|
||||
end.
|
||||
|
||||
|
||||
update_motd_users_table() ->
|
||||
Fields = record_info(fields, motd_users),
|
||||
case mnesia:table_info(motd_users, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[luser, dummy] ->
|
||||
?INFO_MSG("Converting motd_users table from "
|
||||
"{luser, dummy} format", []),
|
||||
Host = ?MYNAME,
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_announce_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, motd_users},
|
||||
{attributes, record_info(fields, motd_users)}]),
|
||||
mnesia:transform_table(motd_users, ignore, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_announce_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#motd_users{us = U} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_announce_tmp_table,
|
||||
R#motd_users{us = {U, Host}})
|
||||
end, ok, motd_users)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(motd_users),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(motd_users),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, mod_announce_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(mod_announce_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating motd_users table", []),
|
||||
mnesia:transform_table(motd_users, ignore, Fields)
|
||||
end.
|
||||
|
@ -690,11 +690,6 @@ set_form(["config", "remusers"], Lang, XData) ->
|
||||
fun({Var, Vals}) ->
|
||||
case Vals of
|
||||
["1"] ->
|
||||
ejabberd_sm ! {route,
|
||||
jlib:make_jid("", "", ""),
|
||||
jlib:make_jid(Var, "", ""),
|
||||
{xmlelement, "broadcast", [],
|
||||
[{exit, "User removed"}]}},
|
||||
catch ejabberd_auth:remove_user(Var);
|
||||
_ ->
|
||||
ok
|
||||
@ -728,7 +723,7 @@ process_sm_iq(From, To,
|
||||
deny ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
allow ->
|
||||
#jid{user = User} = To,
|
||||
#jid{user = User, server = Server} = To,
|
||||
case Type of
|
||||
set ->
|
||||
XDataEl = find_xdata_el(SubEl),
|
||||
@ -753,10 +748,11 @@ process_sm_iq(From, To,
|
||||
xml:get_tag_attr_s("node", SubEl),
|
||||
"/"),
|
||||
case set_sm_form(
|
||||
User, Node, Lang, XData) of
|
||||
User, Server, Node,
|
||||
Lang, XData) of
|
||||
{result, Res} ->
|
||||
IQ#iq{type = result,
|
||||
sub_el =
|
||||
sub_el =
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}],
|
||||
Res
|
||||
@ -774,7 +770,7 @@ process_sm_iq(From, To,
|
||||
get ->
|
||||
Node =
|
||||
string:tokens(xml:get_tag_attr_s("node", SubEl), "/"),
|
||||
case get_sm_form(User, Node, Lang) of
|
||||
case get_sm_form(User, Server, Node, Lang) of
|
||||
{result, Res} ->
|
||||
IQ#iq{type = result,
|
||||
sub_el =
|
||||
@ -788,7 +784,7 @@ process_sm_iq(From, To,
|
||||
end.
|
||||
|
||||
|
||||
get_sm_form(User, [], Lang) ->
|
||||
get_sm_form(User, Server, [], Lang) ->
|
||||
{result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
|
||||
[{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
@ -811,7 +807,7 @@ get_sm_form(User, [], Lang) ->
|
||||
[{xmlelement, "value", [], [{xmlcdata, "remove"}]}]}
|
||||
]},
|
||||
?XFIELD("text-private", "Password", "password",
|
||||
ejabberd_auth:get_password_s(User))
|
||||
ejabberd_auth:get_password_s(User, Server))
|
||||
%{xmlelement, "field", [{"type", "text-single"},
|
||||
% {"label",
|
||||
% translate:translate(Lang, "Host name")},
|
||||
@ -819,32 +815,27 @@ get_sm_form(User, [], Lang) ->
|
||||
% [{xmlelement, "value", [], [{xmlcdata, ?MYNAME}]}]}
|
||||
]}]};
|
||||
|
||||
get_sm_form(_, _, Lang) ->
|
||||
get_sm_form(_User, _Server, _Node, Lang) ->
|
||||
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
||||
|
||||
|
||||
set_sm_form(User, [], Lang, XData) ->
|
||||
set_sm_form(User, Server, [], Lang, XData) ->
|
||||
case lists:keysearch("action", 1, XData) of
|
||||
{value, {_, ["edit"]}} ->
|
||||
case lists:keysearch("password", 1, XData) of
|
||||
{value, {_, [Password]}} ->
|
||||
ejabberd_auth:set_password(User, Password),
|
||||
ejabberd_auth:set_password(User, Server, Password),
|
||||
{result, []};
|
||||
_ ->
|
||||
{error, ?ERR_BAD_REQUEST}
|
||||
end;
|
||||
{value, {_, ["remove"]}} ->
|
||||
ejabberd_sm ! {route,
|
||||
jlib:make_jid("", "", ""),
|
||||
jlib:make_jid(User, "", ""),
|
||||
{xmlelement, "broadcast", [],
|
||||
[{exit, "User removed"}]}},
|
||||
catch ejabberd_auth:remove_user(User),
|
||||
catch ejabberd_auth:remove_user(User, Server),
|
||||
{result, []};
|
||||
_ ->
|
||||
{error, ?ERR_BAD_REQUEST}
|
||||
end;
|
||||
set_sm_form(_, _, Lang, XData) ->
|
||||
set_sm_form(_User, _Server, _Node, Lang, XData) ->
|
||||
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
||||
|
||||
find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
|
||||
|
@ -102,10 +102,11 @@ process_local_iq_items(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} =
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_DISCO_ITEMS}],
|
||||
get_services_only()
|
||||
get_services_only(To#jid.lserver)
|
||||
}]};
|
||||
_ ->
|
||||
case get_local_items(Node, jlib:jid_to_string(To), Lang) of
|
||||
case get_local_items(To#jid.lserver, Node,
|
||||
jlib:jid_to_string(To), Lang) of
|
||||
{result, Res} ->
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
@ -222,15 +223,15 @@ domain_to_xml(Domain) ->
|
||||
{"node", Node}], []}).
|
||||
|
||||
|
||||
get_services_only() ->
|
||||
get_services_only(Host) ->
|
||||
lists:map(fun domain_to_xml/1,
|
||||
ejabberd_router:dirty_get_all_routes()) ++
|
||||
get_vh_services(Host)) ++
|
||||
lists:map(fun domain_to_xml/1, ets:tab2list(disco_extra_domains)).
|
||||
|
||||
get_local_items([], Server, Lang) ->
|
||||
get_local_items(Host, [], Server, Lang) ->
|
||||
Domains =
|
||||
lists:map(fun domain_to_xml/1,
|
||||
ejabberd_router:dirty_get_all_routes()) ++
|
||||
get_vh_services(Host)) ++
|
||||
lists:map(fun domain_to_xml/1, ets:tab2list(disco_extra_domains)),
|
||||
{result,
|
||||
Domains ++
|
||||
@ -242,61 +243,61 @@ get_local_items([], Server, Lang) ->
|
||||
?NODE("Stopped Nodes", "stopped nodes")
|
||||
]};
|
||||
|
||||
get_local_items(["config"], Server, Lang) ->
|
||||
get_local_items(Host, ["config"], Server, Lang) ->
|
||||
{result,
|
||||
[?NODE("Host Name", "config/hostname"),
|
||||
?NODE("Access Control Lists", "config/acls"),
|
||||
?NODE("Access Rules", "config/access"),
|
||||
?NODE("Remove Users", "config/remusers")
|
||||
?NODE("Access Rules", "config/access")
|
||||
% Too expensive on big hosts
|
||||
%?NODE("Remove Users", "config/remusers")
|
||||
]};
|
||||
|
||||
get_local_items(["config", _], Server, Lang) ->
|
||||
get_local_items(Host, ["config", _], Server, Lang) ->
|
||||
{result, []};
|
||||
|
||||
get_local_items(["online users"], Server, Lang) ->
|
||||
{result, get_online_users()};
|
||||
get_local_items(Host, ["online users"], Server, Lang) ->
|
||||
{result, get_online_vh_users(Host)};
|
||||
|
||||
get_local_items(["all users"], Server, Lang) ->
|
||||
{result, get_all_users()};
|
||||
get_local_items(Host, ["all users"], Server, Lang) ->
|
||||
{result, get_all_vh_users(Host)};
|
||||
|
||||
get_local_items(["all users", [$@ | Diap]], Server, Lang) ->
|
||||
get_local_items(Host, ["all users", [$@ | Diap]], Server, Lang) ->
|
||||
case catch ejabberd_auth:dirty_get_registered_users() of
|
||||
{'EXIT', Reason} ->
|
||||
?ERR_INTERNAL_SERVER_ERROR;
|
||||
Users ->
|
||||
SUsers = lists:sort(Users),
|
||||
SUsers = lists:sort([{S, U} || {U, S} <- Users]),
|
||||
case catch begin
|
||||
{ok, [S1, S2]} = regexp:split(Diap, "-"),
|
||||
N1 = list_to_integer(S1),
|
||||
N2 = list_to_integer(S2),
|
||||
Sub = lists:sublist(SUsers, N1, N2 - N1 + 1),
|
||||
lists:map(fun(U) ->
|
||||
lists:map(fun({S, U}) ->
|
||||
{xmlelement, "item",
|
||||
[{"jid", U ++ "@" ++ ?MYNAME},
|
||||
{"name", U}], []}
|
||||
[{"jid", U ++ "@" ++ S},
|
||||
{"name", U ++ "@" ++ S}], []}
|
||||
end, Sub)
|
||||
end of
|
||||
{'EXIT', Reason} ->
|
||||
% TODO: must be "not acceptable"
|
||||
?ERR_BAD_REQUEST;
|
||||
?ERR_NOT_ACCEPTABLE;
|
||||
Res ->
|
||||
{result, Res}
|
||||
end
|
||||
end;
|
||||
|
||||
get_local_items(["outgoing s2s"], Server, Lang) ->
|
||||
{result, get_outgoing_s2s(Lang)};
|
||||
get_local_items(Host, ["outgoing s2s"], Server, Lang) ->
|
||||
{result, get_outgoing_s2s(Host, Lang)};
|
||||
|
||||
get_local_items(["outgoing s2s", To], Server, Lang) ->
|
||||
{result, get_outgoing_s2s(Lang, To)};
|
||||
get_local_items(Host, ["outgoing s2s", To], Server, Lang) ->
|
||||
{result, get_outgoing_s2s(Host, Lang, To)};
|
||||
|
||||
get_local_items(["running nodes"], Server, Lang) ->
|
||||
get_local_items(Host, ["running nodes"], Server, Lang) ->
|
||||
{result, get_running_nodes(Lang)};
|
||||
|
||||
get_local_items(["stopped nodes"], Server, Lang) ->
|
||||
get_local_items(Host, ["stopped nodes"], Server, Lang) ->
|
||||
{result, get_stopped_nodes(Lang)};
|
||||
|
||||
get_local_items(["running nodes", ENode], Server, Lang) ->
|
||||
get_local_items(Host, ["running nodes", ENode], Server, Lang) ->
|
||||
{result,
|
||||
[?NODE("DB", "running nodes/" ++ ENode ++ "/DB"),
|
||||
?NODE("Modules", "running nodes/" ++ ENode ++ "/modules"),
|
||||
@ -305,19 +306,19 @@ get_local_items(["running nodes", ENode], Server, Lang) ->
|
||||
"running nodes/" ++ ENode ++ "/import")
|
||||
]};
|
||||
|
||||
get_local_items(["running nodes", ENode, "DB"], Server, Lang) ->
|
||||
get_local_items(Host, ["running nodes", ENode, "DB"], Server, Lang) ->
|
||||
{result, []};
|
||||
|
||||
get_local_items(["running nodes", ENode, "modules"], Server, Lang) ->
|
||||
get_local_items(Host, ["running nodes", ENode, "modules"], Server, Lang) ->
|
||||
{result,
|
||||
[?NODE("Start Modules", "running nodes/" ++ ENode ++ "/modules/start"),
|
||||
?NODE("Stop Modules", "running nodes/" ++ ENode ++ "/modules/stop")
|
||||
]};
|
||||
|
||||
get_local_items(["running nodes", ENode, "modules", _], Server, Lang) ->
|
||||
get_local_items(Host, ["running nodes", ENode, "modules", _], Server, Lang) ->
|
||||
{result, []};
|
||||
|
||||
get_local_items(["running nodes", ENode, "backup"], Server, Lang) ->
|
||||
get_local_items(Host, ["running nodes", ENode, "backup"], Server, Lang) ->
|
||||
{result,
|
||||
[?NODE("Backup", "running nodes/" ++ ENode ++ "/backup/backup"),
|
||||
?NODE("Restore", "running nodes/" ++ ENode ++ "/backup/restore"),
|
||||
@ -325,49 +326,54 @@ get_local_items(["running nodes", ENode, "backup"], Server, Lang) ->
|
||||
"running nodes/" ++ ENode ++ "/backup/textfile")
|
||||
]};
|
||||
|
||||
get_local_items(["running nodes", ENode, "backup", _], Server, Lang) ->
|
||||
get_local_items(Host, ["running nodes", ENode, "backup", _], Server, Lang) ->
|
||||
{result, []};
|
||||
|
||||
get_local_items(["running nodes", ENode, "import"], Server, Lang) ->
|
||||
get_local_items(Host, ["running nodes", ENode, "import"], Server, Lang) ->
|
||||
{result,
|
||||
[?NODE("Import File", "running nodes/" ++ ENode ++ "/import/file"),
|
||||
?NODE("Import Directory", "running nodes/" ++ ENode ++ "/import/dir")
|
||||
]};
|
||||
|
||||
get_local_items(["running nodes", ENode, "import", _], Server, Lang) ->
|
||||
get_local_items(Host, ["running nodes", ENode, "import", _], Server, Lang) ->
|
||||
{result, []};
|
||||
|
||||
get_local_items(_, _, _) ->
|
||||
get_local_items(_Host, _, _, _) ->
|
||||
{error, ?ERR_FEATURE_NOT_IMPLEMENTED}.
|
||||
|
||||
|
||||
|
||||
get_vh_services(Host) ->
|
||||
DotHost = "." ++ Host,
|
||||
lists:filter(fun(H) ->
|
||||
lists:suffix(DotHost, H)
|
||||
end, ejabberd_router:dirty_get_all_routes()).
|
||||
|
||||
|
||||
get_online_users() ->
|
||||
case catch ejabberd_sm:dirty_get_sessions_list() of
|
||||
get_online_vh_users(Host) ->
|
||||
case catch ejabberd_sm:get_vh_session_list(Host) of
|
||||
{'EXIT', Reason} ->
|
||||
[];
|
||||
URs ->
|
||||
lists:map(fun({U, R}) ->
|
||||
USRs ->
|
||||
SURs = lists:sort([{S, U, R} || {U, S, R} <- USRs]),
|
||||
lists:map(fun({S, U, R}) ->
|
||||
{xmlelement, "item",
|
||||
[{"jid", U ++ "@" ++ ?MYNAME ++ "/" ++ R},
|
||||
{"name", U}], []}
|
||||
end, lists:sort(URs))
|
||||
[{"jid", U ++ "@" ++ S ++ "/" ++ R},
|
||||
{"name", U ++ "@" ++ S}], []}
|
||||
end, SURs)
|
||||
end.
|
||||
|
||||
get_all_users() ->
|
||||
case catch ejabberd_auth:dirty_get_registered_users() of
|
||||
get_all_vh_users(Host) ->
|
||||
case catch ejabberd_auth:get_vh_registered_users(Host) of
|
||||
{'EXIT', Reason} ->
|
||||
[];
|
||||
Users ->
|
||||
SUsers = lists:sort(Users),
|
||||
SUsers = lists:sort([{S, U} || {U, S} <- Users]),
|
||||
case length(SUsers) of
|
||||
N when N =< 100 ->
|
||||
lists:map(fun(U) ->
|
||||
lists:map(fun({S, U}) ->
|
||||
{xmlelement, "item",
|
||||
[{"jid", U ++ "@" ++ ?MYNAME},
|
||||
{"name", U}], []}
|
||||
[{"jid", U ++ "@" ++ S},
|
||||
{"name", U ++ "@" ++ S}], []}
|
||||
end, SUsers);
|
||||
N ->
|
||||
NParts = trunc(math:sqrt(N * 0.618)) + 1,
|
||||
@ -377,30 +383,35 @@ get_all_users() ->
|
||||
Node =
|
||||
"@" ++ integer_to_list(K) ++
|
||||
"-" ++ integer_to_list(L),
|
||||
Last = if L < N -> lists:nth(L, SUsers);
|
||||
true -> lists:last(SUsers)
|
||||
end,
|
||||
{FS, FU} = lists:nth(K, SUsers),
|
||||
{LS, LU} =
|
||||
if L < N -> lists:nth(L, SUsers);
|
||||
true -> lists:last(SUsers)
|
||||
end,
|
||||
Name =
|
||||
lists:nth(K, SUsers) ++ " -- " ++
|
||||
Last,
|
||||
FU ++ "@" ++ FS ++
|
||||
" -- " ++
|
||||
LU ++ "@" ++ LS,
|
||||
{xmlelement, "item",
|
||||
[{"jid", ?MYNAME},
|
||||
[{"jid", Host},
|
||||
{"node", "all users/" ++ Node},
|
||||
{"name", Name}], []}
|
||||
end, lists:seq(1, N, M))
|
||||
end
|
||||
end.
|
||||
|
||||
get_outgoing_s2s(Lang) ->
|
||||
get_outgoing_s2s(Host, Lang) ->
|
||||
case catch ejabberd_s2s:dirty_get_connections() of
|
||||
{'EXIT', Reason} ->
|
||||
[];
|
||||
Connections ->
|
||||
TConns = [element(2, C) || C <- Connections],
|
||||
DotHost = "." ++ Host,
|
||||
TConns = [TH || {FH, TH} <- Connections,
|
||||
Host == FH orelse lists:suffix(DotHost, FH)],
|
||||
lists:map(
|
||||
fun(T) ->
|
||||
{xmlelement, "item",
|
||||
[{"jid", ?MYNAME},
|
||||
[{"jid", Host},
|
||||
{"node", "outgoing s2s/" ++ T},
|
||||
{"name",
|
||||
lists:flatten(
|
||||
@ -410,7 +421,7 @@ get_outgoing_s2s(Lang) ->
|
||||
end, lists:usort(TConns))
|
||||
end.
|
||||
|
||||
get_outgoing_s2s(Lang, To) ->
|
||||
get_outgoing_s2s(Host, Lang, To) ->
|
||||
case catch ejabberd_s2s:dirty_get_connections() of
|
||||
{'EXIT', Reason} ->
|
||||
[];
|
||||
@ -418,7 +429,7 @@ get_outgoing_s2s(Lang, To) ->
|
||||
lists:map(
|
||||
fun({F, T}) ->
|
||||
{xmlelement, "item",
|
||||
[{"jid", ?MYNAME},
|
||||
[{"jid", Host},
|
||||
{"node", "outgoing s2s/" ++ To ++ "/" ++ F},
|
||||
{"name",
|
||||
lists:flatten(
|
||||
@ -576,7 +587,7 @@ process_sm_iq_info(From, To, #iq{type = Type, xmlns = XMLNS,
|
||||
|
||||
|
||||
get_user_resources(User) ->
|
||||
Rs = ejabberd_sm:get_user_resources(User),
|
||||
Rs = ejabberd_sm:get_user_resources(User, 'TODO'),
|
||||
lists:map(fun(R) ->
|
||||
{xmlelement, "item",
|
||||
[{"jid", User ++ "@" ++ ?MYNAME ++ "/" ++ R},
|
||||
|
@ -12,48 +12,51 @@
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/1, init/2, stop/0, closed_conection/2,
|
||||
get_user_and_encoding/2]).
|
||||
-export([start/1, init/2, stop/0,
|
||||
closed_connection/3,
|
||||
get_user_and_encoding/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-define(DEFAULT_IRC_ENCODING, "koi8-r").
|
||||
|
||||
-record(irc_connection, {userserver, pid}).
|
||||
-record(irc_custom, {userserver, data}).
|
||||
-record(irc_connection, {jid_server_host, pid}).
|
||||
-record(irc_custom, {us_host, data}).
|
||||
|
||||
start(Opts) ->
|
||||
iconv:start(),
|
||||
mnesia:create_table(irc_custom,
|
||||
[{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, irc_custom)}]),
|
||||
Host = gen_mod:get_opt(host, Opts, "irc." ++ ?MYNAME),
|
||||
Hosts = gen_mod:get_hosts(Opts, "irc."),
|
||||
Host = hd(Hosts),
|
||||
update_table(Host),
|
||||
Access = gen_mod:get_opt(access, Opts, all),
|
||||
register(ejabberd_mod_irc, spawn(?MODULE, init, [Host, Access])).
|
||||
register(ejabberd_mod_irc, spawn(?MODULE, init, [Hosts, Access])).
|
||||
|
||||
init(Host, Access) ->
|
||||
init(Hosts, Access) ->
|
||||
catch ets:new(irc_connection, [named_table,
|
||||
public,
|
||||
{keypos, #irc_connection.userserver}]),
|
||||
ejabberd_router:register_route(Host),
|
||||
loop(Host, Access).
|
||||
{keypos, #irc_connection.jid_server_host}]),
|
||||
ejabberd_router:register_routes(Hosts),
|
||||
loop(Hosts, Access).
|
||||
|
||||
loop(Host, Access) ->
|
||||
loop(Hosts, Access) ->
|
||||
receive
|
||||
{route, From, To, Packet} ->
|
||||
case catch do_route(Host, Access, From, To, Packet) of
|
||||
case catch do_route(To#jid.lserver, Access, From, To, Packet) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p", [Reason]);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
loop(Host, Access);
|
||||
loop(Hosts, Access);
|
||||
stop ->
|
||||
ejabberd_router:unregister_global_route(Host),
|
||||
ejabberd_router:unregister_routes(Hosts),
|
||||
ok;
|
||||
_ ->
|
||||
loop(Host, Access)
|
||||
loop(Hosts, Access)
|
||||
end.
|
||||
|
||||
|
||||
@ -96,7 +99,7 @@ do_route1(Host, From, To, Packet) ->
|
||||
From,
|
||||
jlib:iq_to_xml(Res));
|
||||
#iq{xmlns = ?NS_REGISTER} = IQ ->
|
||||
process_register(From, To, IQ);
|
||||
process_register(Host, From, To, IQ);
|
||||
#iq{type = get, xmlns = ?NS_VCARD = XMLNS,
|
||||
lang = Lang} = IQ ->
|
||||
Res = IQ#iq{type = result,
|
||||
@ -121,17 +124,17 @@ do_route1(Host, From, To, Packet) ->
|
||||
_ ->
|
||||
case string:tokens(ChanServ, "%") of
|
||||
[[_ | _] = Channel, [_ | _] = Server] ->
|
||||
case ets:lookup(irc_connection, {From, Server}) of
|
||||
case ets:lookup(irc_connection, {From, Server, Host}) of
|
||||
[] ->
|
||||
io:format("open new connection~n"),
|
||||
{Username, Encoding} = get_user_and_encoding(
|
||||
From, Server),
|
||||
Host, From, Server),
|
||||
{ok, Pid} = mod_irc_connection:start(
|
||||
From, Host, Server,
|
||||
Username, Encoding),
|
||||
ets:insert(
|
||||
irc_connection,
|
||||
#irc_connection{userserver = {From, Server},
|
||||
#irc_connection{jid_server_host = {From, Server, Host},
|
||||
pid = Pid}),
|
||||
mod_irc_connection:route_chan(
|
||||
Pid, Channel, Resource, Packet),
|
||||
@ -147,7 +150,7 @@ do_route1(Host, From, To, Packet) ->
|
||||
_ ->
|
||||
case string:tokens(ChanServ, "!") of
|
||||
[[_ | _] = Nick, [_ | _] = Server] ->
|
||||
case ets:lookup(irc_connection, {From, Server}) of
|
||||
case ets:lookup(irc_connection, {From, Server, Host}) of
|
||||
[] ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_SERVICE_UNAVAILABLE),
|
||||
@ -175,8 +178,8 @@ stop() ->
|
||||
ejabberd_mod_irc ! stop,
|
||||
ok.
|
||||
|
||||
closed_conection(From, Server) ->
|
||||
ets:delete(irc_connection, {From, Server}).
|
||||
closed_connection(Host, From, Server) ->
|
||||
ets:delete(irc_connection, {From, Server, Host}).
|
||||
|
||||
|
||||
iq_disco() ->
|
||||
@ -201,8 +204,8 @@ iq_get_vcard(Lang) ->
|
||||
[{xmlcdata, translate:translate(Lang, "ejabberd IRC module\n"
|
||||
"Copyright (c) 2003-2005 Alexey Shchepin")}]}].
|
||||
|
||||
process_register(From, To, #iq{} = IQ) ->
|
||||
case catch process_irc_register(From, To, IQ) of
|
||||
process_register(Host, From, To, #iq{} = IQ) ->
|
||||
case catch process_irc_register(Host, From, To, IQ) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p", [Reason]);
|
||||
ResIQ ->
|
||||
@ -232,7 +235,7 @@ find_xdata_el1([{xmlelement, Name, Attrs, SubEls} | Els]) ->
|
||||
find_xdata_el1([_ | Els]) ->
|
||||
find_xdata_el1(Els).
|
||||
|
||||
process_irc_register(From, To,
|
||||
process_irc_register(Host, From, To,
|
||||
#iq{type = Type, xmlns = XMLNS,
|
||||
lang = Lang, sub_el = SubEl} = IQ) ->
|
||||
case Type of
|
||||
@ -257,7 +260,8 @@ process_irc_register(From, To,
|
||||
Node = string:tokens(
|
||||
xml:get_tag_attr_s("node", SubEl),
|
||||
"/"),
|
||||
case set_form(From, Node, Lang, XData) of
|
||||
case set_form(
|
||||
Host, From, Node, Lang, XData) of
|
||||
{result, Res} ->
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
@ -277,7 +281,7 @@ process_irc_register(From, To,
|
||||
get ->
|
||||
Node =
|
||||
string:tokens(xml:get_tag_attr_s("node", SubEl), "/"),
|
||||
case get_form(From, Node, Lang) of
|
||||
case get_form(Host, From, Node, Lang) of
|
||||
{result, Res} ->
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
@ -292,11 +296,12 @@ process_irc_register(From, To,
|
||||
|
||||
|
||||
|
||||
get_form(From, [], Lang) ->
|
||||
get_form(Host, From, [], Lang) ->
|
||||
#jid{user = User, server = Server,
|
||||
luser = LUser, lserver = LServer} = From,
|
||||
US = {LUser, LServer},
|
||||
Customs =
|
||||
case catch mnesia:dirty_read({irc_custom, {LUser, LServer}}) of
|
||||
case catch mnesia:dirty_read({irc_custom, {US, Host}}) of
|
||||
{'EXIT', Reason} ->
|
||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||
[] ->
|
||||
@ -306,7 +311,7 @@ get_form(From, [], Lang) ->
|
||||
xml:get_attr_s(encodings, Data)}
|
||||
end,
|
||||
case Customs of
|
||||
{error, _, _} ->
|
||||
{error, _Error} ->
|
||||
Customs;
|
||||
{Username, Encodings} ->
|
||||
{result,
|
||||
@ -370,15 +375,15 @@ get_form(From, [], Lang) ->
|
||||
]}]}
|
||||
end;
|
||||
|
||||
|
||||
get_form(_, _, Lang) ->
|
||||
get_form(_Host, _, _, Lang) ->
|
||||
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
||||
|
||||
|
||||
|
||||
|
||||
set_form(From, [], Lang, XData) ->
|
||||
set_form(Host, From, [], Lang, XData) ->
|
||||
{LUser, LServer, _} = jlib:jid_tolower(From),
|
||||
US = {LUser, LServer},
|
||||
case {lists:keysearch("username", 1, XData),
|
||||
lists:keysearch("encodings", 1, XData)} of
|
||||
{{value, {_, [Username]}}, {value, {_, Strings}}} ->
|
||||
@ -392,8 +397,8 @@ set_form(From, [], Lang, XData) ->
|
||||
case mnesia:transaction(
|
||||
fun() ->
|
||||
mnesia:write(
|
||||
#irc_custom{userserver =
|
||||
{LUser, LServer},
|
||||
#irc_custom{us_host =
|
||||
{US, Host},
|
||||
data =
|
||||
[{username,
|
||||
Username},
|
||||
@ -416,14 +421,15 @@ set_form(From, [], Lang, XData) ->
|
||||
end;
|
||||
|
||||
|
||||
set_form(_, _, Lang, XData) ->
|
||||
set_form(_Host, _, _, Lang, XData) ->
|
||||
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
||||
|
||||
|
||||
get_user_and_encoding(From, IRCServer) ->
|
||||
get_user_and_encoding(Host, From, IRCServer) ->
|
||||
#jid{user = User, server = Server,
|
||||
luser = LUser, lserver = LServer} = From,
|
||||
case catch mnesia:dirty_read({irc_custom, {LUser, LServer}}) of
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read({irc_custom, {US, Host}}) of
|
||||
{'EXIT', Reason} ->
|
||||
{User, ?DEFAULT_IRC_ENCODING};
|
||||
[] ->
|
||||
@ -436,3 +442,44 @@ get_user_and_encoding(From, IRCServer) ->
|
||||
end}
|
||||
end.
|
||||
|
||||
|
||||
update_table(Host) ->
|
||||
Fields = record_info(fields, irc_custom),
|
||||
case mnesia:table_info(irc_custom, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[userserver, data] ->
|
||||
?INFO_MSG("Converting irc_custom table from "
|
||||
"{userserver, data} format", []),
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_irc_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, irc_custom},
|
||||
{attributes, record_info(fields, irc_custom)}]),
|
||||
mnesia:transform_table(irc_custom, ignore, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_irc_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#irc_custom{us_host = US} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_irc_tmp_table,
|
||||
R#irc_custom{us_host = {US, Host}})
|
||||
end, ok, irc_custom)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(irc_custom),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(irc_custom),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, mod_irc_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(mod_irc_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating irc_custom table", []),
|
||||
mnesia:transform_table(irc_custom, ignore, Fields)
|
||||
end.
|
||||
|
@ -32,7 +32,7 @@
|
||||
-define(SETS, gb_sets).
|
||||
|
||||
-record(state, {socket, encoding, receiver, queue,
|
||||
user, myname, server, nick,
|
||||
user, host, server, nick,
|
||||
channels = dict:new(),
|
||||
inbuf = "", outbuf = ""}).
|
||||
|
||||
@ -67,7 +67,7 @@ init([From, Host, Server, Username, Encoding]) ->
|
||||
encoding = Encoding,
|
||||
user = From,
|
||||
nick = Username,
|
||||
myname = Host,
|
||||
host = Host,
|
||||
server = Server}}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
@ -90,7 +90,7 @@ open_socket(init, StateData) ->
|
||||
"USER ~s ~s ~s :~s\r\n",
|
||||
[StateData#state.nick,
|
||||
StateData#state.nick,
|
||||
StateData#state.myname,
|
||||
StateData#state.host,
|
||||
StateData#state.nick])),
|
||||
send_text(NewStateData,
|
||||
io_lib:format("CODEPAGE ~s\r\n", [StateData#state.encoding])),
|
||||
@ -231,7 +231,7 @@ handle_info({route_chan, Channel, Resource,
|
||||
jlib:make_jid(
|
||||
lists:concat(
|
||||
[Channel, "%", StateData#state.server]),
|
||||
StateData#state.myname, StateData#state.nick),
|
||||
StateData#state.host, StateData#state.nick),
|
||||
StateData#state.user, El),
|
||||
Body = xml:get_path_s(El, [{elem, "body"}, cdata]),
|
||||
case Body of
|
||||
@ -304,7 +304,7 @@ handle_info({route_chan, Channel, Resource,
|
||||
StateName, StateData) ->
|
||||
From = StateData#state.user,
|
||||
To = jlib:make_jid(lists:concat([Channel, "%", StateData#state.server]),
|
||||
StateData#state.myname, StateData#state.nick),
|
||||
StateData#state.host, StateData#state.nick),
|
||||
case jlib:iq_query_info(El) of
|
||||
#iq{xmlns = ?NS_MUC_ADMIN} = IQ ->
|
||||
iq_admin(StateData, Channel, From, To, IQ);
|
||||
@ -474,15 +474,16 @@ handle_info({tcp_error, Socket, Reason}, StateName, StateData) ->
|
||||
%% Returns: any
|
||||
%%----------------------------------------------------------------------
|
||||
terminate(Reason, StateName, StateData) ->
|
||||
mod_irc:closed_conection(StateData#state.user,
|
||||
StateData#state.server),
|
||||
mod_irc:closed_connection(StateData#state.host,
|
||||
StateData#state.user,
|
||||
StateData#state.server),
|
||||
bounce_messages("Server Connect Failed"),
|
||||
lists:foreach(
|
||||
fun(Chan) ->
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(
|
||||
lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.myname, StateData#state.nick),
|
||||
StateData#state.host, StateData#state.nick),
|
||||
StateData#state.user,
|
||||
{xmlelement, "presence", [{"type", "error"}],
|
||||
[{xmlelement, "error", [{"code", "502"}],
|
||||
@ -592,7 +593,7 @@ process_channel_list_user(StateData, Chan, User) ->
|
||||
end,
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.myname, User2),
|
||||
StateData#state.host, User2),
|
||||
StateData#state.user,
|
||||
{xmlelement, "presence", [],
|
||||
[{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}],
|
||||
@ -618,7 +619,7 @@ process_channel_topic(StateData, Chan, String) ->
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(
|
||||
lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.myname, FromUser),
|
||||
StateData#state.host, FromUser),
|
||||
StateData#state.user,
|
||||
{xmlelement, "message", [{"type", "groupchat"}],
|
||||
[{xmlelement, "subject", [], [{xmlcdata, Msg1}]}]}).
|
||||
@ -636,7 +637,7 @@ process_chanprivmsg(StateData, Chan, From, String) ->
|
||||
Msg2 = filter_message(Msg1),
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.myname, FromUser),
|
||||
StateData#state.host, FromUser),
|
||||
StateData#state.user,
|
||||
{xmlelement, "message", [{"type", "groupchat"}],
|
||||
[{xmlelement, "body", [], [{xmlcdata, Msg2}]}]}).
|
||||
@ -655,7 +656,7 @@ process_channotice(StateData, Chan, From, String) ->
|
||||
Msg2 = filter_message(Msg1),
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.myname, FromUser),
|
||||
StateData#state.host, FromUser),
|
||||
StateData#state.user,
|
||||
{xmlelement, "message", [{"type", "groupchat"}],
|
||||
[{xmlelement, "body", [], [{xmlcdata, "NOTICE: " ++ Msg2}]}]}).
|
||||
@ -675,7 +676,7 @@ process_privmsg(StateData, Nick, From, String) ->
|
||||
Msg2 = filter_message(Msg1),
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([FromUser, "!", StateData#state.server]),
|
||||
StateData#state.myname, ""),
|
||||
StateData#state.host, ""),
|
||||
StateData#state.user,
|
||||
{xmlelement, "message", [{"type", "chat"}],
|
||||
[{xmlelement, "body", [], [{xmlcdata, Msg2}]}]}).
|
||||
@ -693,7 +694,7 @@ process_notice(StateData, Nick, From, String) ->
|
||||
Msg2 = filter_message(Msg1),
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([FromUser, "!", StateData#state.server]),
|
||||
StateData#state.myname, ""),
|
||||
StateData#state.host, ""),
|
||||
StateData#state.user,
|
||||
{xmlelement, "message", [{"type", "chat"}],
|
||||
[{xmlelement, "body", [], [{xmlcdata, "NOTICE: " ++ Msg2}]}]}).
|
||||
@ -719,7 +720,7 @@ process_topic(StateData, Chan, From, String) ->
|
||||
Msg1 = filter_message(Msg),
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.myname, FromUser),
|
||||
StateData#state.host, FromUser),
|
||||
StateData#state.user,
|
||||
{xmlelement, "message", [{"type", "groupchat"}],
|
||||
[{xmlelement, "subject", [], [{xmlcdata, Msg1}]},
|
||||
@ -733,7 +734,7 @@ process_part(StateData, Chan, From, String) ->
|
||||
Msg1 = filter_message(Msg),
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.myname, FromUser),
|
||||
StateData#state.host, FromUser),
|
||||
StateData#state.user,
|
||||
{xmlelement, "message", [{"type", "groupchat"}],
|
||||
[{xmlelement, "body", [],
|
||||
@ -742,7 +743,7 @@ process_part(StateData, Chan, From, String) ->
|
||||
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.myname, FromUser),
|
||||
StateData#state.host, FromUser),
|
||||
StateData#state.user,
|
||||
{xmlelement, "presence", [{"type", "unavailable"}],
|
||||
[{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}],
|
||||
@ -777,7 +778,7 @@ process_quit(StateData, From, String) ->
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(
|
||||
lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.myname, FromUser),
|
||||
StateData#state.host, FromUser),
|
||||
StateData#state.user,
|
||||
{xmlelement, "message", [{"type", "groupchat"}],
|
||||
[{xmlelement, "body", [],
|
||||
@ -787,7 +788,7 @@ process_quit(StateData, From, String) ->
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(
|
||||
lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.myname, FromUser),
|
||||
StateData#state.host, FromUser),
|
||||
StateData#state.user,
|
||||
{xmlelement, "presence", [{"type", "unavailable"}],
|
||||
[{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}],
|
||||
@ -811,7 +812,7 @@ process_join(StateData, Channel, From, String) ->
|
||||
Chan = lists:subtract(Channel, ":#"),
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.myname, FromUser),
|
||||
StateData#state.host, FromUser),
|
||||
StateData#state.user,
|
||||
{xmlelement, "presence", [],
|
||||
[{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}],
|
||||
@ -825,7 +826,7 @@ process_join(StateData, Channel, From, String) ->
|
||||
Msg1 = filter_message(Msg),
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.myname, FromUser),
|
||||
StateData#state.host, FromUser),
|
||||
StateData#state.user,
|
||||
{xmlelement, "message", [{"type", "groupchat"}],
|
||||
[{xmlelement, "body", [],
|
||||
@ -848,7 +849,7 @@ process_mode_o(StateData, Chan, From, Nick, Affiliation, Role) ->
|
||||
%Msg = lists:last(string:tokens(String, ":")),
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.myname, Nick),
|
||||
StateData#state.host, Nick),
|
||||
StateData#state.user,
|
||||
{xmlelement, "presence", [],
|
||||
[{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}],
|
||||
@ -861,7 +862,7 @@ process_kick(StateData, Chan, From, Nick) ->
|
||||
%Msg = lists:last(string:tokens(String, ":")),
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.myname, Nick),
|
||||
StateData#state.host, Nick),
|
||||
StateData#state.user,
|
||||
{xmlelement, "presence", [{"type", "unavailable"}],
|
||||
[{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}],
|
||||
@ -883,7 +884,7 @@ process_nick(StateData, From, NewNick) ->
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(
|
||||
lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.myname, FromUser),
|
||||
StateData#state.host, FromUser),
|
||||
StateData#state.user,
|
||||
{xmlelement, "presence", [{"type", "unavailable"}],
|
||||
[{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}],
|
||||
@ -897,7 +898,7 @@ process_nick(StateData, From, NewNick) ->
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(
|
||||
lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.myname, Nick),
|
||||
StateData#state.host, Nick),
|
||||
StateData#state.user,
|
||||
{xmlelement, "presence", [],
|
||||
[{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}],
|
||||
@ -921,7 +922,7 @@ process_error(StateData, String) ->
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(
|
||||
lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.myname, StateData#state.nick),
|
||||
StateData#state.host, StateData#state.nick),
|
||||
StateData#state.user,
|
||||
{xmlelement, "presence", [{"type", "error"}],
|
||||
[{xmlelement, "error", [{"code", "502"}],
|
||||
|
@ -16,13 +16,13 @@
|
||||
stop/0,
|
||||
process_local_iq/3,
|
||||
process_sm_iq/3,
|
||||
on_presence_update/3,
|
||||
remove_user/1]).
|
||||
on_presence_update/4,
|
||||
remove_user/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(last_activity, {user, timestamp, status}).
|
||||
-record(last_activity, {us, timestamp, status}).
|
||||
|
||||
|
||||
start(Opts) ->
|
||||
@ -68,24 +68,25 @@ process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
get ->
|
||||
User = To#jid.luser,
|
||||
Server = To#jid.lserver,
|
||||
{Subscription, _Groups} =
|
||||
ejabberd_hooks:run_fold(
|
||||
roster_get_jid_info, {none, []}, [User, From]),
|
||||
roster_get_jid_info, {none, []}, [User, Server, From]),
|
||||
if
|
||||
(Subscription == both) or (Subscription == from) ->
|
||||
case catch mod_privacy:get_user_list(User) of
|
||||
{'EXIT', _Reason} ->
|
||||
get_last(IQ, SubEl, User);
|
||||
get_last(IQ, SubEl, User, Server);
|
||||
List ->
|
||||
case catch mod_privacy:check_packet(
|
||||
User, List,
|
||||
User, Server, List,
|
||||
{From, To,
|
||||
{xmlelement, "presence", [], []}},
|
||||
out) of
|
||||
{'EXIT', _Reason} ->
|
||||
get_last(IQ, SubEl, User);
|
||||
get_last(IQ, SubEl, User, Server);
|
||||
allow ->
|
||||
get_last(IQ, SubEl, User);
|
||||
get_last(IQ, SubEl, User, Server);
|
||||
deny ->
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
|
||||
@ -97,8 +98,8 @@ process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||
end
|
||||
end.
|
||||
|
||||
get_last(IQ, SubEl, LUser) ->
|
||||
case catch mnesia:dirty_read(last_activity, LUser) of
|
||||
get_last(IQ, SubEl, LUser, LServer) ->
|
||||
case catch mnesia:dirty_read(last_activity, {LUser, LServer}) of
|
||||
{'EXIT', _Reason} ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]};
|
||||
[] ->
|
||||
@ -116,22 +117,26 @@ get_last(IQ, SubEl, LUser) ->
|
||||
|
||||
|
||||
|
||||
on_presence_update(User, _Resource, Status) ->
|
||||
on_presence_update(User, Server, _Resource, Status) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
{MegaSecs, Secs, _MicroSecs} = now(),
|
||||
TimeStamp = MegaSecs * 1000000 + Secs,
|
||||
F = fun() ->
|
||||
mnesia:write(#last_activity{user = LUser,
|
||||
mnesia:write(#last_activity{us = US,
|
||||
timestamp = TimeStamp,
|
||||
status = Status})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
|
||||
remove_user(User) ->
|
||||
remove_user(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
mnesia:delete({last_activity, LUser})
|
||||
mnesia:delete({last_activity, US})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
@ -141,13 +146,44 @@ update_table() ->
|
||||
case mnesia:table_info(last_activity, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[user, timestamp, status] ->
|
||||
?INFO_MSG("Converting last_activity table from {user, timestamp, status} format", []),
|
||||
Host = ?MYNAME,
|
||||
mnesia:transform_table(last_activity, ignore, Fields),
|
||||
F = fun() ->
|
||||
mnesia:write_lock_table(last_activity),
|
||||
mnesia:foldl(
|
||||
fun({_, U, T, S} = R, _) ->
|
||||
mnesia:delete_object(R),
|
||||
mnesia:write(
|
||||
#last_activity{us = {U, Host},
|
||||
timestamp = T,
|
||||
status = S})
|
||||
end, ok, last_activity)
|
||||
end,
|
||||
mnesia:transaction(F);
|
||||
[user, timestamp] ->
|
||||
?INFO_MSG("Converting last_activity table from {user, timestamp} format", []),
|
||||
Host = ?MYNAME,
|
||||
mnesia:transform_table(
|
||||
last_activity,
|
||||
fun({_, U, T}) ->
|
||||
#last_activity{user = U, timestamp = T, status = ""}
|
||||
end, Fields);
|
||||
#last_activity{us = U,
|
||||
timestamp = T,
|
||||
status = ""}
|
||||
end, Fields),
|
||||
F = fun() ->
|
||||
mnesia:write_lock_table(last_activity),
|
||||
mnesia:foldl(
|
||||
fun({_, U, T, S} = R, _) ->
|
||||
mnesia:delete_object(R),
|
||||
mnesia:write(
|
||||
#last_activity{us = {U, Host},
|
||||
timestamp = T,
|
||||
status = S})
|
||||
end, ok, last_activity)
|
||||
end,
|
||||
mnesia:transaction(F);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating last_activity table", []),
|
||||
mnesia:transform_table(last_activity, ignore, Fields)
|
||||
|
@ -72,7 +72,7 @@ process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||
get_last(IQ, SubEl, User);
|
||||
List ->
|
||||
case catch mod_privacy:check_packet(
|
||||
User, List,
|
||||
User, ?MYNAME, List, % TODO
|
||||
{From, To,
|
||||
{xmlelement, "presence", [], []}},
|
||||
out) of
|
||||
|
@ -15,20 +15,20 @@
|
||||
-export([start/1,
|
||||
init/2,
|
||||
stop/0,
|
||||
room_destroyed/1,
|
||||
store_room/2,
|
||||
restore_room/1,
|
||||
forget_room/1,
|
||||
room_destroyed/2,
|
||||
store_room/3,
|
||||
restore_room/2,
|
||||
forget_room/2,
|
||||
process_iq_disco_items/4,
|
||||
can_use_nick/2]).
|
||||
can_use_nick/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
|
||||
-record(muc_room, {name, opts}).
|
||||
-record(muc_online_room, {name, pid}).
|
||||
-record(muc_registered, {user, nick}).
|
||||
-record(muc_room, {name_host, opts}).
|
||||
-record(muc_online_room, {name_host, pid}).
|
||||
-record(muc_registered, {us_host, nick}).
|
||||
|
||||
|
||||
start(Opts) ->
|
||||
@ -38,43 +38,45 @@ start(Opts) ->
|
||||
mnesia:create_table(muc_registered,
|
||||
[{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, muc_registered)}]),
|
||||
Hosts = gen_mod:get_hosts(Opts, "conference."),
|
||||
Host = hd(Hosts),
|
||||
update_tables(Host),
|
||||
mnesia:add_table_index(muc_registered, nick),
|
||||
Host = gen_mod:get_opt(host, Opts, "conference." ++ ?MYNAME),
|
||||
Access = gen_mod:get_opt(access, Opts, all),
|
||||
AccessCreate = gen_mod:get_opt(access_create, Opts, all),
|
||||
AccessAdmin = gen_mod:get_opt(access_admin, Opts, none),
|
||||
register(ejabberd_mod_muc,
|
||||
spawn(?MODULE, init, [Host, {Access, AccessCreate, AccessAdmin}])).
|
||||
spawn(?MODULE, init,
|
||||
[Hosts, {Access, AccessCreate, AccessAdmin}])).
|
||||
|
||||
|
||||
|
||||
init(Host, Access) ->
|
||||
init(Hosts, Access) ->
|
||||
catch ets:new(muc_online_room, [named_table,
|
||||
public,
|
||||
{keypos, #muc_online_room.name}]),
|
||||
ejabberd_router:register_route(Host),
|
||||
load_permanent_rooms(Host, Access),
|
||||
loop(Host, Access).
|
||||
{keypos, #muc_online_room.name_host}]),
|
||||
ejabberd_router:register_routes(Hosts),
|
||||
load_permanent_rooms(Access),
|
||||
loop(Hosts, Access).
|
||||
|
||||
loop(Host, Access) ->
|
||||
loop(Hosts, Access) ->
|
||||
receive
|
||||
{route, From, To, Packet} ->
|
||||
case catch do_route(Host, Access, From, To, Packet) of
|
||||
case catch do_route(To#jid.lserver, Access, From, To, Packet) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p", [Reason]);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
loop(Host, Access);
|
||||
{room_destroyed, Room} ->
|
||||
ets:delete(muc_online_room, Room),
|
||||
loop(Host, Access);
|
||||
loop(Hosts, Access);
|
||||
{room_destroyed, RoomHost} ->
|
||||
ets:delete(muc_online_room, RoomHost),
|
||||
loop(Hosts, Access);
|
||||
stop ->
|
||||
% TODO
|
||||
ejabberd_router:unregister_global_route(Host),
|
||||
ejabberd_router:unregister_routes(Hosts),
|
||||
ok;
|
||||
_ ->
|
||||
loop(Host, Access)
|
||||
loop(Hosts, Access)
|
||||
end.
|
||||
|
||||
|
||||
@ -127,7 +129,7 @@ do_route1(Host, Access, From, To, Packet) ->
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}],
|
||||
iq_get_register_info(
|
||||
From, Host, Lang)}]},
|
||||
Host, From, Lang)}]},
|
||||
ejabberd_router:route(To,
|
||||
From,
|
||||
jlib:iq_to_xml(Res));
|
||||
@ -135,7 +137,7 @@ do_route1(Host, Access, From, To, Packet) ->
|
||||
xmlns = ?NS_REGISTER = XMLNS,
|
||||
lang = Lang,
|
||||
sub_el = SubEl} = IQ ->
|
||||
case process_iq_register_set(From, SubEl, Lang) of
|
||||
case process_iq_register_set(Host, From, SubEl, Lang) of
|
||||
{result, IQRes} ->
|
||||
Res = IQ#iq{type = result,
|
||||
sub_el =
|
||||
@ -180,7 +182,7 @@ do_route1(Host, Access, From, To, Packet) ->
|
||||
Msg = xml:get_path_s(
|
||||
Packet,
|
||||
[{elem, "body"}, cdata]),
|
||||
broadcast_service_message(Msg);
|
||||
broadcast_service_message(Host, Msg);
|
||||
_ ->
|
||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||
ErrText = "Only service administrators "
|
||||
@ -208,7 +210,7 @@ do_route1(Host, Access, From, To, Packet) ->
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
case ets:lookup(muc_online_room, Room) of
|
||||
case ets:lookup(muc_online_room, {Room, Host}) of
|
||||
[] ->
|
||||
Type = xml:get_attr_s("type", Attrs),
|
||||
case {Name, Type} of
|
||||
@ -220,7 +222,8 @@ do_route1(Host, Access, From, To, Packet) ->
|
||||
Host, Access, Room, From, Nick),
|
||||
ets:insert(
|
||||
muc_online_room,
|
||||
#muc_online_room{name = Room, pid = Pid}),
|
||||
#muc_online_room{name_host = {Room, Host},
|
||||
pid = Pid}),
|
||||
mod_muc_room:route(Pid, From, Nick, Packet),
|
||||
ok;
|
||||
_ ->
|
||||
@ -248,8 +251,8 @@ do_route1(Host, Access, From, To, Packet) ->
|
||||
|
||||
|
||||
|
||||
room_destroyed(Room) ->
|
||||
ejabberd_mod_muc ! {room_destroyed, Room},
|
||||
room_destroyed(Host, Room) ->
|
||||
ejabberd_mod_muc ! {room_destroyed, {Room, Host}},
|
||||
ok.
|
||||
|
||||
stop() ->
|
||||
@ -257,15 +260,15 @@ stop() ->
|
||||
ok.
|
||||
|
||||
|
||||
store_room(Name, Opts) ->
|
||||
store_room(Host, Name, Opts) ->
|
||||
F = fun() ->
|
||||
mnesia:write(#muc_room{name = Name,
|
||||
mnesia:write(#muc_room{name_host = {Name, Host},
|
||||
opts = Opts})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
restore_room(Name) ->
|
||||
case catch mnesia:dirty_read(muc_room, Name) of
|
||||
restore_room(Host, Name) ->
|
||||
case catch mnesia:dirty_read(muc_room, {Name, Host}) of
|
||||
[#muc_room{opts = Opts}] ->
|
||||
Opts;
|
||||
_ ->
|
||||
@ -273,21 +276,21 @@ restore_room(Name) ->
|
||||
end.
|
||||
|
||||
|
||||
forget_room(Name) ->
|
||||
forget_room(Host, Name) ->
|
||||
F = fun() ->
|
||||
mnesia:delete({muc_room, Name})
|
||||
mnesia:delete({muc_room, {Name, Host}})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
|
||||
load_permanent_rooms(Host, Access) ->
|
||||
load_permanent_rooms(Access) ->
|
||||
case catch mnesia:dirty_select(muc_room, [{'_', [], ['$_']}]) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p", [Reason]),
|
||||
ok;
|
||||
Rs ->
|
||||
lists:foreach(fun(R) ->
|
||||
Room = R#muc_room.name,
|
||||
{Room, Host} = R#muc_room.name_host,
|
||||
{ok, Pid} = mod_muc_room:start(
|
||||
Host,
|
||||
Access,
|
||||
@ -295,7 +298,8 @@ load_permanent_rooms(Host, Access) ->
|
||||
R#muc_room.opts),
|
||||
ets:insert(
|
||||
muc_online_room,
|
||||
#muc_online_room{name = Room, pid = Pid})
|
||||
#muc_online_room{name_host = {Room, Host},
|
||||
pid = Pid})
|
||||
end, Rs)
|
||||
end.
|
||||
|
||||
@ -320,7 +324,7 @@ process_iq_disco_items(Host, From, To, #iq{lang = Lang} = IQ) ->
|
||||
jlib:iq_to_xml(Res)).
|
||||
|
||||
iq_disco_items(Host, From, Lang) ->
|
||||
lists:zf(fun(#muc_online_room{name = Name, pid = Pid}) ->
|
||||
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} ->
|
||||
@ -331,7 +335,7 @@ iq_disco_items(Host, From, Lang) ->
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end, ets:tab2list(muc_online_room)).
|
||||
end, get_vh_rooms(Host)).
|
||||
|
||||
|
||||
-define(XFIELD(Type, Label, Var, Val),
|
||||
@ -340,17 +344,18 @@ iq_disco_items(Host, From, Lang) ->
|
||||
{"var", Var}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, Val}]}]}).
|
||||
|
||||
iq_get_register_info(From, Host, Lang) ->
|
||||
iq_get_register_info(Host, From, Lang) ->
|
||||
{LUser, LServer, _} = jlib:jid_tolower(From),
|
||||
LUS = {LUser, LServer},
|
||||
{Nick, Registered} = case catch mnesia:dirty_read(muc_registered, LUS) of
|
||||
{'EXIT', _Reason} ->
|
||||
{"", []};
|
||||
[] ->
|
||||
{"", []};
|
||||
[#muc_registered{nick = N}] ->
|
||||
{N, [{xmlelement, "registered", [], []}]}
|
||||
end,
|
||||
{Nick, Registered} =
|
||||
case catch mnesia:dirty_read(muc_registered, {LUS, Host}) of
|
||||
{'EXIT', _Reason} ->
|
||||
{"", []};
|
||||
[] ->
|
||||
{"", []};
|
||||
[#muc_registered{nick = N}] ->
|
||||
{N, [{xmlelement, "registered", [], []}]}
|
||||
end,
|
||||
Registered ++
|
||||
[{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
@ -368,7 +373,7 @@ 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, Lang) ->
|
||||
iq_set_register_info(Host, From, XData, Lang) ->
|
||||
{LUser, LServer, _} = jlib:jid_tolower(From),
|
||||
LUS = {LUser, LServer},
|
||||
case lists:keysearch("nick", 1, XData) of
|
||||
@ -379,22 +384,26 @@ iq_set_register_info(From, XData, Lang) ->
|
||||
F = fun() ->
|
||||
case Nick of
|
||||
"" ->
|
||||
mnesia:delete({muc_registered, LUS}),
|
||||
mnesia:delete({muc_registered, {LUS, Host}}),
|
||||
ok;
|
||||
_ ->
|
||||
Allow = case mnesia:index_read(
|
||||
muc_registered,
|
||||
Nick,
|
||||
#muc_registered.nick) of
|
||||
[] ->
|
||||
true;
|
||||
[#muc_registered{user = U}] ->
|
||||
U == LUS
|
||||
end,
|
||||
Allow =
|
||||
case mnesia:select(
|
||||
muc_registered,
|
||||
[{#muc_registered{us_host = '$1',
|
||||
nick = Nick,
|
||||
_ = '_'},
|
||||
[{'==', {element, 2, '$1'}, Host}],
|
||||
['$_']}]) of
|
||||
[] ->
|
||||
true;
|
||||
[#muc_registered{us_host = {U, _Host}}] ->
|
||||
U == LUS
|
||||
end,
|
||||
if
|
||||
Allow ->
|
||||
mnesia:write(
|
||||
#muc_registered{user = LUS,
|
||||
#muc_registered{us_host = {LUS, Host},
|
||||
nick = Nick}),
|
||||
ok;
|
||||
true ->
|
||||
@ -413,7 +422,7 @@ iq_set_register_info(From, XData, Lang) ->
|
||||
end
|
||||
end.
|
||||
|
||||
process_iq_register_set(From, SubEl, Lang) ->
|
||||
process_iq_register_set(Host, From, SubEl, Lang) ->
|
||||
{xmlelement, _Name, _Attrs, Els} = SubEl,
|
||||
case xml:remove_cdata(Els) of
|
||||
[{xmlelement, "x", _Attrs1, _Els1} = XEl] ->
|
||||
@ -427,7 +436,7 @@ process_iq_register_set(From, SubEl, Lang) ->
|
||||
invalid ->
|
||||
{error, ?ERR_BAD_REQUEST};
|
||||
_ ->
|
||||
iq_set_register_info(From, XData, Lang)
|
||||
iq_set_register_info(Host, From, XData, Lang)
|
||||
end;
|
||||
_ ->
|
||||
{error, ?ERR_BAD_REQUEST}
|
||||
@ -447,30 +456,125 @@ iq_get_vcard(Lang) ->
|
||||
"Copyright (c) 2003-2005 Alexey Shchepin")}]}].
|
||||
|
||||
|
||||
broadcast_service_message(Msg) ->
|
||||
broadcast_service_message(Host, Msg) ->
|
||||
lists:foreach(
|
||||
fun(#muc_online_room{pid = Pid}) ->
|
||||
gen_fsm:send_all_state_event(
|
||||
Pid, {service_message, Msg})
|
||||
end, ets:tab2list(muc_online_room)).
|
||||
end, get_vh_rooms(Host)).
|
||||
|
||||
get_vh_rooms(Host) ->
|
||||
ets:select(muc_online_room,
|
||||
[{#muc_online_room{name_host = '$1', _ = '_'},
|
||||
[{'==', {element, 2, '$1'}, Host}],
|
||||
['$_']}]).
|
||||
|
||||
|
||||
|
||||
can_use_nick(_JID, "") ->
|
||||
can_use_nick(_Host, _JID, "") ->
|
||||
false;
|
||||
can_use_nick(JID, Nick) ->
|
||||
can_use_nick(Host, JID, Nick) ->
|
||||
{LUser, LServer, _} = jlib:jid_tolower(JID),
|
||||
LUS = {LUser, LServer},
|
||||
case catch mnesia:dirty_index_read(muc_registered,
|
||||
Nick,
|
||||
#muc_registered.nick) of
|
||||
case catch mnesia:dirty_select(
|
||||
muc_registered,
|
||||
[{#muc_registered{us_host = '$1',
|
||||
nick = Nick,
|
||||
_ = '_'},
|
||||
[{'==', {element, 2, '$1'}, Host}],
|
||||
['$_']}]) of
|
||||
{'EXIT', _Reason} ->
|
||||
true;
|
||||
[] ->
|
||||
true;
|
||||
[#muc_registered{user = U}] ->
|
||||
[#muc_registered{us_host = {U, _Host}}] ->
|
||||
U == LUS
|
||||
end.
|
||||
|
||||
|
||||
update_tables(Host) ->
|
||||
update_muc_room_table(Host),
|
||||
update_muc_registered_table(Host).
|
||||
|
||||
update_muc_room_table(Host) ->
|
||||
Fields = record_info(fields, muc_room),
|
||||
case mnesia:table_info(muc_room, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[name, opts] ->
|
||||
?INFO_MSG("Converting muc_room table from "
|
||||
"{name, opts} format", []),
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_muc_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, muc_room},
|
||||
{attributes, record_info(fields, muc_room)}]),
|
||||
mnesia:transform_table(muc_room, ignore, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_muc_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#muc_room{name_host = Name} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_muc_tmp_table,
|
||||
R#muc_room{name_host = {Name, Host}})
|
||||
end, ok, muc_room)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(muc_room),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(muc_room),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, mod_muc_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(mod_muc_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating muc_room table", []),
|
||||
mnesia:transform_table(muc_room, ignore, Fields)
|
||||
end.
|
||||
|
||||
|
||||
update_muc_registered_table(Host) ->
|
||||
Fields = record_info(fields, muc_registered),
|
||||
case mnesia:table_info(muc_registered, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[user, nick] ->
|
||||
?INFO_MSG("Converting muc_registered table from "
|
||||
"{user, nick} format", []),
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_muc_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, muc_registered},
|
||||
{attributes, record_info(fields, muc_registered)}]),
|
||||
mnesia:del_table_index(muc_registered, nick),
|
||||
mnesia:transform_table(muc_registered, ignore, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_muc_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#muc_registered{us_host = US} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_muc_tmp_table,
|
||||
R#muc_registered{us_host = {US, Host}})
|
||||
end, ok, muc_registered)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(muc_registered),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(muc_registered),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, mod_muc_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(mod_muc_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating muc_registered table", []),
|
||||
mnesia:transform_table(muc_registered, ignore, Fields)
|
||||
end.
|
||||
|
@ -149,6 +149,7 @@ normal_state({route, From, "",
|
||||
case (NSD#state.config)#config.persistent of
|
||||
true ->
|
||||
mod_muc:store_room(
|
||||
NSD#state.host,
|
||||
NSD#state.room,
|
||||
make_opts(NSD));
|
||||
_ ->
|
||||
@ -364,7 +365,8 @@ normal_state({route, From, Nick,
|
||||
case is_nick_change(From, Nick, StateData) of
|
||||
true ->
|
||||
case {is_nick_exists(Nick, StateData),
|
||||
mod_muc:can_use_nick(From, Nick)} of
|
||||
mod_muc:can_use_nick(
|
||||
StateData#state.host, From, Nick)} of
|
||||
{true, _} ->
|
||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||
ErrText = "Nickname is already in use by another occupant",
|
||||
@ -639,7 +641,7 @@ handle_info(_Info, StateName, StateData) ->
|
||||
%% Returns: any
|
||||
%%----------------------------------------------------------------------
|
||||
terminate(_Reason, _StateName, StateData) ->
|
||||
mod_muc:room_destroyed(StateData#state.room),
|
||||
mod_muc:room_destroyed(StateData#state.host, StateData#state.room),
|
||||
ok.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
@ -806,8 +808,8 @@ filter_presence({xmlelement, "presence", Attrs, Els}) ->
|
||||
case El of
|
||||
{xmlcdata, _} ->
|
||||
false;
|
||||
{xmlelement, Name1, Attrs1, _Els1} ->
|
||||
XMLNS = xml:get_attr_s("xmlns", Attrs1),
|
||||
{xmlelement, Name1, _Attrs1, _Els1} ->
|
||||
XMLNS = xml:get_attr_s("xmlns", Attrs),
|
||||
case {Name1, XMLNS} of
|
||||
{"show", ""} ->
|
||||
true;
|
||||
@ -872,7 +874,7 @@ is_nick_change(JID, Nick, StateData) ->
|
||||
add_new_user(From, Nick, {xmlelement, _, Attrs, Els} = Packet, StateData) ->
|
||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||
case {is_nick_exists(Nick, StateData),
|
||||
mod_muc:can_use_nick(From, Nick)} of
|
||||
mod_muc:can_use_nick(StateData#state.host, From, Nick)} of
|
||||
{true, _} ->
|
||||
ErrText = "Nickname is already in use by another occupant",
|
||||
Err = jlib:make_error_reply(Packet, ?ERRT_CONFLICT(Lang, ErrText)),
|
||||
@ -1517,7 +1519,8 @@ process_admin_items_set(UJID, Items, Lang, StateData) ->
|
||||
io:format("MUC SET: ~p~n", [Res]),
|
||||
case (NSD#state.config)#config.persistent of
|
||||
true ->
|
||||
mod_muc:store_room(NSD#state.room, make_opts(NSD));
|
||||
mod_muc:store_room(NSD#state.host, NSD#state.room,
|
||||
make_opts(NSD));
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
@ -2043,9 +2046,9 @@ change_config(Config, StateData) ->
|
||||
case {(StateData#state.config)#config.persistent,
|
||||
Config#config.persistent} of
|
||||
{_, true} ->
|
||||
mod_muc:store_room(NSD#state.room, make_opts(NSD));
|
||||
mod_muc:store_room(NSD#state.host, NSD#state.room, make_opts(NSD));
|
||||
{true, false} ->
|
||||
mod_muc:forget_room(NSD#state.room);
|
||||
mod_muc:forget_room(NSD#state.host, NSD#state.room);
|
||||
{false, false} ->
|
||||
ok
|
||||
end,
|
||||
@ -2130,7 +2133,7 @@ destroy_room(DEls, StateData) ->
|
||||
end, ?DICT:to_list(StateData#state.users)),
|
||||
case (StateData#state.config)#config.persistent of
|
||||
true ->
|
||||
mod_muc:forget_room(StateData#state.room);
|
||||
mod_muc:forget_room(StateData#state.host, StateData#state.room);
|
||||
false ->
|
||||
ok
|
||||
end,
|
||||
|
@ -15,16 +15,16 @@
|
||||
init/0,
|
||||
stop/0,
|
||||
store_packet/3,
|
||||
resend_offline_messages/1,
|
||||
pop_offline_messages/2,
|
||||
resend_offline_messages/2,
|
||||
pop_offline_messages/3,
|
||||
remove_expired_messages/0,
|
||||
remove_old_messages/1,
|
||||
remove_user/1]).
|
||||
remove_user/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(offline_msg, {user, timestamp, expire, from, to, packet}).
|
||||
-record(offline_msg, {us, timestamp, expire, from, to, packet}).
|
||||
|
||||
-define(PROCNAME, ejabberd_offline).
|
||||
-define(OFFLINE_TABLE_LOCK_THRESHOLD, 1000).
|
||||
@ -97,11 +97,11 @@ store_packet(From, To, Packet) ->
|
||||
(Type /= "error") and (Type /= "groupchat") ->
|
||||
case check_event(From, To, Packet) of
|
||||
true ->
|
||||
#jid{luser = LUser} = To,
|
||||
#jid{luser = LUser, lserver = LServer} = To,
|
||||
TimeStamp = now(),
|
||||
{xmlelement, _Name, _Attrs, Els} = Packet,
|
||||
Expire = find_x_expire(TimeStamp, Els),
|
||||
?PROCNAME ! #offline_msg{user = LUser,
|
||||
?PROCNAME ! #offline_msg{us = {LUser, LServer},
|
||||
timestamp = TimeStamp,
|
||||
expire = Expire,
|
||||
from = From,
|
||||
@ -189,11 +189,13 @@ find_x_expire(TimeStamp, [El | Els]) ->
|
||||
end.
|
||||
|
||||
|
||||
resend_offline_messages(User) ->
|
||||
resend_offline_messages(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
Rs = mnesia:wread({offline_msg, LUser}),
|
||||
mnesia:delete({offline_msg, LUser}),
|
||||
Rs = mnesia:wread({offline_msg, US}),
|
||||
mnesia:delete({offline_msg, US}),
|
||||
Rs
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
@ -216,11 +218,13 @@ resend_offline_messages(User) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
pop_offline_messages(Ls, User) ->
|
||||
pop_offline_messages(Ls, User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
Rs = mnesia:wread({offline_msg, LUser}),
|
||||
mnesia:delete({offline_msg, LUser}),
|
||||
Rs = mnesia:wread({offline_msg, US}),
|
||||
mnesia:delete({offline_msg, US}),
|
||||
Rs
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
@ -290,10 +294,12 @@ remove_old_messages(Days) ->
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
remove_user(User) ->
|
||||
remove_user(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
mnesia:delete({offline_msg, LUser})
|
||||
mnesia:delete({offline_msg, US})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
@ -302,23 +308,83 @@ update_table() ->
|
||||
case mnesia:table_info(offline_msg, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[user, timestamp, expire, from, to, packet] ->
|
||||
?INFO_MSG("Converting offline_msg table from "
|
||||
"{user, timestamp, expire, from, to, packet} format", []),
|
||||
Host = ?MYNAME,
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_offline_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, offline_msg},
|
||||
{attributes, record_info(fields, offline_msg)}]),
|
||||
mnesia:transform_table(offline_msg, ignore, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_offline_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#offline_msg{us = U} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_offline_tmp_table,
|
||||
R#offline_msg{us = {U, Host}})
|
||||
end, ok, offline_msg)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(offline_msg),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(offline_msg),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, mod_offline_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(mod_offline_tmp_table);
|
||||
[user, timestamp, from, to, packet] ->
|
||||
?INFO_MSG("Converting offline_msg table from "
|
||||
"{user, timestamp, from, to, packet} format", []),
|
||||
Host = ?MYNAME,
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_offline_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, offline_msg},
|
||||
{attributes, record_info(fields, offline_msg)}]),
|
||||
mnesia:transform_table(
|
||||
offline_msg,
|
||||
fun({_, U, TS, F, T, P}) ->
|
||||
{xmlelement, _Name, _Attrs, Els} = P,
|
||||
Expire = find_x_expire(TS, Els),
|
||||
#offline_msg{user = U,
|
||||
#offline_msg{us = U,
|
||||
timestamp = TS,
|
||||
expire = Expire,
|
||||
from = F,
|
||||
to = T,
|
||||
packet = P}
|
||||
end, Fields);
|
||||
end, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_offline_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#offline_msg{us = U} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_offline_tmp_table,
|
||||
R#offline_msg{us = {U, Host}})
|
||||
end, ok, offline_msg)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(offline_msg),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(offline_msg),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, mod_offline_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(mod_offline_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating offline_msg table", []),
|
||||
mnesia:transform_table(last_activity, ignore, Fields)
|
||||
mnesia:transform_table(offline_msg, ignore, Fields)
|
||||
end.
|
||||
|
||||
|
@ -16,15 +16,15 @@
|
||||
process_iq/3,
|
||||
process_iq_set/3,
|
||||
process_iq_get/4,
|
||||
get_user_list/1,
|
||||
check_packet/4,
|
||||
get_user_list/2,
|
||||
check_packet/5,
|
||||
updated_list/2]).
|
||||
|
||||
%-include_lib("mnemosyne/include/mnemosyne.hrl").
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(privacy, {user,
|
||||
-record(privacy, {us,
|
||||
default = none,
|
||||
lists = []}).
|
||||
|
||||
@ -46,6 +46,7 @@ start(Opts) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
||||
mnesia:create_table(privacy, [{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, privacy)}]),
|
||||
update_table(),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_PRIVACY,
|
||||
?MODULE, process_iq, IQDisc).
|
||||
|
||||
@ -78,16 +79,16 @@ process_iq(From, _To, IQ) ->
|
||||
|
||||
process_iq_get(From, _To, #iq{sub_el = SubEl},
|
||||
#userlist{name = Active}) ->
|
||||
#jid{luser = LUser} = From,
|
||||
#jid{luser = LUser, lserver = LServer} = From,
|
||||
{xmlelement, _, _, Els} = SubEl,
|
||||
case xml:remove_cdata(Els) of
|
||||
[] ->
|
||||
process_lists_get(LUser, Active);
|
||||
process_lists_get(LUser, LServer, Active);
|
||||
[{xmlelement, Name, Attrs, _SubEls}] ->
|
||||
case Name of
|
||||
"list" ->
|
||||
ListName = xml:get_attr("name", Attrs),
|
||||
process_list_get(LUser, ListName);
|
||||
process_list_get(LUser, LServer, ListName);
|
||||
_ ->
|
||||
{error, ?ERR_BAD_REQUEST}
|
||||
end;
|
||||
@ -96,8 +97,8 @@ process_iq_get(From, _To, #iq{sub_el = SubEl},
|
||||
end.
|
||||
|
||||
|
||||
process_lists_get(LUser, Active) ->
|
||||
case catch mnesia:dirty_read(privacy, LUser) of
|
||||
process_lists_get(LUser, LServer, Active) ->
|
||||
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
||||
{'EXIT', _Reason} ->
|
||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||
[] ->
|
||||
@ -135,8 +136,8 @@ process_lists_get(LUser, Active) ->
|
||||
end
|
||||
end.
|
||||
|
||||
process_list_get(LUser, {value, Name}) ->
|
||||
case catch mnesia:dirty_read(privacy, LUser) of
|
||||
process_list_get(LUser, LServer, {value, Name}) ->
|
||||
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
||||
{'EXIT', _Reason} ->
|
||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||
[] ->
|
||||
@ -155,7 +156,7 @@ process_list_get(LUser, {value, Name}) ->
|
||||
end
|
||||
end;
|
||||
|
||||
process_list_get(_LUser, false) ->
|
||||
process_list_get(_LUser, _LServer, false) ->
|
||||
{error, ?ERR_BAD_REQUEST}.
|
||||
|
||||
item_to_xml(Item) ->
|
||||
@ -242,18 +243,19 @@ list_to_action(S) ->
|
||||
|
||||
|
||||
process_iq_set(From, _To, #iq{sub_el = SubEl}) ->
|
||||
#jid{luser = LUser} = From,
|
||||
#jid{luser = LUser, lserver = LServer} = From,
|
||||
{xmlelement, _, _, Els} = SubEl,
|
||||
case xml:remove_cdata(Els) of
|
||||
[{xmlelement, Name, Attrs, SubEls}] ->
|
||||
ListName = xml:get_attr("name", Attrs),
|
||||
case Name of
|
||||
"list" ->
|
||||
process_list_set(LUser, ListName, xml:remove_cdata(SubEls));
|
||||
process_list_set(LUser, LServer, ListName,
|
||||
xml:remove_cdata(SubEls));
|
||||
"active" ->
|
||||
process_active_set(LUser, ListName);
|
||||
process_active_set(LUser, LServer, ListName);
|
||||
"default" ->
|
||||
process_default_set(LUser, ListName);
|
||||
process_default_set(LUser, LServer, ListName);
|
||||
_ ->
|
||||
{error, ?ERR_BAD_REQUEST}
|
||||
end;
|
||||
@ -262,9 +264,9 @@ process_iq_set(From, _To, #iq{sub_el = SubEl}) ->
|
||||
end.
|
||||
|
||||
|
||||
process_default_set(LUser, {value, Name}) ->
|
||||
process_default_set(LUser, LServer, {value, Name}) ->
|
||||
F = fun() ->
|
||||
case mnesia:read({privacy, LUser}) of
|
||||
case mnesia:read({privacy, {LUser, LServer}}) of
|
||||
[] ->
|
||||
{error, ?ERR_ITEM_NOT_FOUND};
|
||||
[#privacy{lists = Lists} = P] ->
|
||||
@ -287,9 +289,9 @@ process_default_set(LUser, {value, Name}) ->
|
||||
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
||||
end;
|
||||
|
||||
process_default_set(LUser, false) ->
|
||||
process_default_set(LUser, LServer, false) ->
|
||||
F = fun() ->
|
||||
case mnesia:read({privacy, LUser}) of
|
||||
case mnesia:read({privacy, {LUser, LServer}}) of
|
||||
[] ->
|
||||
{result, []};
|
||||
[R] ->
|
||||
@ -307,8 +309,8 @@ process_default_set(LUser, false) ->
|
||||
end.
|
||||
|
||||
|
||||
process_active_set(LUser, {value, Name}) ->
|
||||
case catch mnesia:dirty_read(privacy, LUser) of
|
||||
process_active_set(LUser, LServer, {value, Name}) ->
|
||||
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
||||
[] ->
|
||||
{error, ?ERR_ITEM_NOT_FOUND};
|
||||
[#privacy{lists = Lists}] ->
|
||||
@ -320,7 +322,7 @@ process_active_set(LUser, {value, Name}) ->
|
||||
end
|
||||
end;
|
||||
|
||||
process_active_set(_LUser, false) ->
|
||||
process_active_set(_LUser, _LServer, false) ->
|
||||
{result, [], #userlist{}}.
|
||||
|
||||
|
||||
@ -328,14 +330,14 @@ process_active_set(_LUser, false) ->
|
||||
|
||||
|
||||
|
||||
process_list_set(LUser, {value, Name}, Els) ->
|
||||
process_list_set(LUser, LServer, {value, Name}, Els) ->
|
||||
case parse_items(Els) of
|
||||
false ->
|
||||
{error, ?ERR_BAD_REQUEST};
|
||||
remove ->
|
||||
F =
|
||||
fun() ->
|
||||
case mnesia:read({privacy, LUser}) of
|
||||
case mnesia:read({privacy, {LUser, LServer}}) of
|
||||
[] ->
|
||||
{result, []};
|
||||
[#privacy{default = Default, lists = Lists} = P] ->
|
||||
@ -368,10 +370,10 @@ process_list_set(LUser, {value, Name}, Els) ->
|
||||
List ->
|
||||
F =
|
||||
fun() ->
|
||||
case mnesia:wread({privacy, LUser}) of
|
||||
case mnesia:wread({privacy, {LUser, LServer}}) of
|
||||
[] ->
|
||||
NewLists = [{Name, List}],
|
||||
mnesia:write(#privacy{user = LUser,
|
||||
mnesia:write(#privacy{us = {LUser, LServer},
|
||||
lists = NewLists}),
|
||||
{result, []};
|
||||
[#privacy{lists = Lists} = P] ->
|
||||
@ -396,7 +398,7 @@ process_list_set(LUser, {value, Name}, Els) ->
|
||||
end
|
||||
end;
|
||||
|
||||
process_list_set(_LUser, false, _Els) ->
|
||||
process_list_set(_LUser, _LServer, false, _Els) ->
|
||||
{error, ?ERR_BAD_REQUEST}.
|
||||
|
||||
|
||||
@ -511,9 +513,10 @@ parse_matches1(_Item, [{xmlelement, _, _, _} | _Els]) ->
|
||||
|
||||
|
||||
|
||||
get_user_list(User) ->
|
||||
get_user_list(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
case catch mnesia:dirty_read(privacy, LUser) of
|
||||
LServer = jlib:nameprep(Server),
|
||||
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
||||
[] ->
|
||||
#userlist{};
|
||||
[#privacy{default = Default, lists = Lists}] ->
|
||||
@ -534,7 +537,7 @@ get_user_list(User) ->
|
||||
end.
|
||||
|
||||
|
||||
check_packet(User,
|
||||
check_packet(User, Server,
|
||||
#userlist{list = List},
|
||||
{From, To, {xmlelement, PName, _, _}},
|
||||
Dir) ->
|
||||
@ -552,28 +555,32 @@ check_packet(User,
|
||||
LJID = jlib:jid_tolower(From),
|
||||
{Subscription, Groups} =
|
||||
ejabberd_hooks:run_fold(
|
||||
roster_get_jid_info, {none, []}, [User, LJID]),
|
||||
roster_get_jid_info, {none, []},
|
||||
[User, Server, LJID]),
|
||||
check_packet_aux(List, message,
|
||||
LJID, Subscription, Groups);
|
||||
{iq, in} ->
|
||||
LJID = jlib:jid_tolower(From),
|
||||
{Subscription, Groups} =
|
||||
ejabberd_hooks:run_fold(
|
||||
roster_get_jid_info, {none, []}, [User, LJID]),
|
||||
roster_get_jid_info, {none, []},
|
||||
[User, Server, LJID]),
|
||||
check_packet_aux(List, iq,
|
||||
LJID, Subscription, Groups);
|
||||
{presence, in} ->
|
||||
LJID = jlib:jid_tolower(From),
|
||||
{Subscription, Groups} =
|
||||
ejabberd_hooks:run_fold(
|
||||
roster_get_jid_info, {none, []}, [User, LJID]),
|
||||
roster_get_jid_info, {none, []},
|
||||
[User, Server, LJID]),
|
||||
check_packet_aux(List, presence_in,
|
||||
LJID, Subscription, Groups);
|
||||
{presence, out} ->
|
||||
LJID = jlib:jid_tolower(To),
|
||||
{Subscription, Groups} =
|
||||
ejabberd_hooks:run_fold(
|
||||
roster_get_jid_info, {none, []}, [User, LJID]),
|
||||
roster_get_jid_info, {none, []},
|
||||
[User, Server, LJID]),
|
||||
check_packet_aux(List, presence_out,
|
||||
LJID, Subscription, Groups);
|
||||
_ ->
|
||||
@ -662,4 +669,46 @@ updated_list(#userlist{name = OldName} = Old,
|
||||
|
||||
|
||||
|
||||
update_table() ->
|
||||
Fields = record_info(fields, privacy),
|
||||
case mnesia:table_info(privacy, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[user, default, lists] ->
|
||||
?INFO_MSG("Converting privacy table from "
|
||||
"{user, default, lists} format", []),
|
||||
Host = ?MYNAME,
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_privacy_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, privacy},
|
||||
{attributes, record_info(fields, privacy)}]),
|
||||
mnesia:transform_table(privacy, ignore, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_privacy_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#privacy{us = U} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_privacy_tmp_table,
|
||||
R#privacy{us = {U, Host}})
|
||||
end, ok, privacy)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(privacy),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(privacy),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, mod_privacy_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(mod_privacy_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating privacy table", []),
|
||||
mnesia:transform_table(privacy, ignore, Fields)
|
||||
end.
|
||||
|
||||
|
||||
|
@ -15,18 +15,19 @@
|
||||
-export([start/1,
|
||||
stop/0,
|
||||
process_sm_iq/3,
|
||||
remove_user/1]).
|
||||
remove_user/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(private_storage, {userns, xml}).
|
||||
-record(private_storage, {usns, xml}).
|
||||
|
||||
start(Opts) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
||||
mnesia:create_table(private_storage,
|
||||
[{disc_only_copies, [node()]},
|
||||
{attributes, record_info(fields, private_storage)}]),
|
||||
update_table(),
|
||||
ejabberd_hooks:add(remove_user,
|
||||
?MODULE, remove_user, 50),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_PRIVATE,
|
||||
@ -40,22 +41,22 @@ stop() ->
|
||||
|
||||
process_sm_iq(From, _To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||
#jid{luser = LUser, lserver = LServer} = From,
|
||||
case ?MYNAME of
|
||||
LServer ->
|
||||
case lists:member(LServer, ?MYHOSTS) of
|
||||
true ->
|
||||
{xmlelement, Name, Attrs, Els} = SubEl,
|
||||
case Type of
|
||||
set ->
|
||||
F = fun() ->
|
||||
lists:foreach(
|
||||
fun(El) ->
|
||||
set_data(LUser, El)
|
||||
set_data(LUser, LServer, El)
|
||||
end, Els)
|
||||
end,
|
||||
mnesia:transaction(F),
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, Name, Attrs, []}]};
|
||||
get ->
|
||||
case catch get_data(LUser, Els) of
|
||||
case catch get_data(LUser, LServer, Els) of
|
||||
{'EXIT', _Reason} ->
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl,
|
||||
@ -65,11 +66,11 @@ process_sm_iq(From, _To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||
sub_el = [{xmlelement, Name, Attrs, Res}]}
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
false ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
|
||||
end.
|
||||
|
||||
set_data(LUser, El) ->
|
||||
set_data(LUser, LServer, El) ->
|
||||
case El of
|
||||
{xmlelement, _Name, Attrs, _Els} ->
|
||||
XMLNS = xml:get_attr_s("xmlns", Attrs),
|
||||
@ -77,40 +78,45 @@ set_data(LUser, El) ->
|
||||
"" ->
|
||||
ignore;
|
||||
_ ->
|
||||
mnesia:write(#private_storage{userns = {LUser, XMLNS},
|
||||
xml = El})
|
||||
mnesia:write(
|
||||
#private_storage{usns = {LUser, LServer, XMLNS},
|
||||
xml = El})
|
||||
end;
|
||||
_ ->
|
||||
ignore
|
||||
end.
|
||||
|
||||
get_data(LUser, Els) ->
|
||||
get_data(LUser, Els, []).
|
||||
get_data(LUser, LServer, Els) ->
|
||||
get_data(LUser, LServer, Els, []).
|
||||
|
||||
get_data(_LUser, [], Res) ->
|
||||
get_data(_LUser, _LServer, [], Res) ->
|
||||
lists:reverse(Res);
|
||||
get_data(LUser, [El | Els], Res) ->
|
||||
get_data(LUser, LServer, [El | Els], Res) ->
|
||||
case El of
|
||||
{xmlelement, _Name, Attrs, _} ->
|
||||
XMLNS = xml:get_attr_s("xmlns", Attrs),
|
||||
case mnesia:dirty_read(private_storage, {LUser, XMLNS}) of
|
||||
case mnesia:dirty_read(private_storage, {LUser, LServer, XMLNS}) of
|
||||
[R] ->
|
||||
get_data(LUser, Els, [R#private_storage.xml | Res]);
|
||||
get_data(LUser, LServer, Els,
|
||||
[R#private_storage.xml | Res]);
|
||||
[] ->
|
||||
get_data(LUser, Els, [El | Res])
|
||||
get_data(LUser, LServer, Els,
|
||||
[El | Res])
|
||||
end;
|
||||
_ ->
|
||||
get_data(LUser, Els, Res)
|
||||
get_data(LUser, LServer, Els, Res)
|
||||
end.
|
||||
|
||||
|
||||
remove_user(User) ->
|
||||
% TODO: use mnesia:select
|
||||
remove_user(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
F = fun() ->
|
||||
lists:foreach(
|
||||
fun({U, _} = Key) ->
|
||||
fun({U, S, _} = Key) ->
|
||||
if
|
||||
U == LUser ->
|
||||
(U == LUser) and (S == LServer) ->
|
||||
mnesia:delete({private_storage, Key});
|
||||
true ->
|
||||
ok
|
||||
@ -119,3 +125,47 @@ remove_user(User) ->
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
|
||||
update_table() ->
|
||||
Fields = record_info(fields, private_storage),
|
||||
case mnesia:table_info(private_storage, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[userns, xml] ->
|
||||
?INFO_MSG("Converting private_storage table from "
|
||||
"{user, default, lists} format", []),
|
||||
Host = ?MYNAME,
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_private_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, private_storage},
|
||||
{attributes, record_info(fields, private_storage)}]),
|
||||
mnesia:transform_table(private_storage, ignore, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_private_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#private_storage{usns = {U, NS}} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_private_tmp_table,
|
||||
R#private_storage{usns = {U, Host, NS}})
|
||||
end, ok, private_storage)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(private_storage),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(private_storage),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, mod_private_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(mod_private_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating private_storage table", []),
|
||||
mnesia:transform_table(private_storage, ignore, Fields)
|
||||
end.
|
||||
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
||||
-define(DICT, dict).
|
||||
-define(MAXITEMS, 10).
|
||||
|
||||
-record(pubsub_node, {node, parent, info}).
|
||||
-record(pubsub_node, {host_node, host_parent, info}).
|
||||
-record(nodeinfo, {items = [],
|
||||
options = [],
|
||||
entities = ?DICT:new()
|
||||
@ -40,48 +40,54 @@ start(Opts) ->
|
||||
mnesia:create_table(pubsub_node,
|
||||
[{disc_only_copies, [node()]},
|
||||
{attributes, record_info(fields, pubsub_node)}]),
|
||||
mnesia:add_table_index(pubsub_node, parent),
|
||||
Host = gen_mod:get_opt(host, Opts, "pubsub." ++ ?MYNAME),
|
||||
ServedHosts = gen_mod:get_opt(served_hosts, Opts, [?MYNAME]),
|
||||
Hosts = gen_mod:get_hosts(Opts, "pubsub."),
|
||||
Host = hd(Hosts),
|
||||
update_table(Host),
|
||||
mnesia:add_table_index(pubsub_node, host_parent),
|
||||
ServedHosts = gen_mod:get_opt(served_hosts, Opts, []),
|
||||
register(ejabberd_mod_pubsub,
|
||||
proc_lib:spawn_link(?MODULE, init, [Host, ServedHosts, self()])).
|
||||
proc_lib:spawn_link(?MODULE, init, [Hosts, ServedHosts, self()])).
|
||||
|
||||
|
||||
-define(MYJID, #jid{user = "", server = Host, resource = "",
|
||||
luser = "", lserver = Host, lresource = ""}).
|
||||
|
||||
init(Host, ServedHosts, Parent) ->
|
||||
ejabberd_router:register_route(Host),
|
||||
create_new_node(Host, ["pubsub"], ?MYJID),
|
||||
create_new_node(Host, ["pubsub", "nodes"], ?MYJID),
|
||||
create_new_node(Host, ["home"], ?MYJID),
|
||||
lists:foreach(fun(H) ->
|
||||
create_new_node(Host, ["home", H], ?MYJID)
|
||||
end, ServedHosts),
|
||||
loop(Host, Parent).
|
||||
init(Hosts, ServedHosts, Parent) ->
|
||||
ejabberd_router:register_routes(Hosts),
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
create_new_node(Host, ["pubsub"], ?MYJID),
|
||||
create_new_node(Host, ["pubsub", "nodes"], ?MYJID),
|
||||
create_new_node(Host, ["home"], ?MYJID),
|
||||
create_new_node(Host, ["home", find_my_host(Host)], ?MYJID),
|
||||
lists:foreach(fun(H) ->
|
||||
create_new_node(Host, ["home", H], ?MYJID)
|
||||
end, ServedHosts)
|
||||
end, Hosts),
|
||||
loop(Hosts, Parent).
|
||||
|
||||
loop(Host, Parent) ->
|
||||
loop(Hosts, Parent) ->
|
||||
receive
|
||||
{route, From, To, Packet} ->
|
||||
case catch do_route(Host, From, To, Packet) of
|
||||
case catch do_route(To#jid.lserver, From, To, Packet) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p", [Reason]);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
loop(Host, Parent);
|
||||
loop(Hosts, Parent);
|
||||
{room_destroyed, Room} ->
|
||||
ets:delete(muc_online_room, Room),
|
||||
loop(Host, Parent);
|
||||
loop(Hosts, Parent);
|
||||
stop ->
|
||||
ejabberd_router:unregister_global_route(Host),
|
||||
ejabberd_router:unregister_global_routes(Hosts),
|
||||
ok;
|
||||
reload ->
|
||||
?MODULE:loop(Host, Parent);
|
||||
?MODULE:loop(Hosts, Parent);
|
||||
{system, From, Request} ->
|
||||
sys:handle_system_msg(Request, From, Parent, ?MODULE, [], Host);
|
||||
sys:handle_system_msg(Request, From, Parent, ?MODULE, [], Hosts);
|
||||
_ ->
|
||||
loop(Host, Parent)
|
||||
loop(Hosts, Parent)
|
||||
end.
|
||||
|
||||
|
||||
@ -228,13 +234,13 @@ iq_disco_info(SNode) ->
|
||||
iq_disco_items(Host, From, SNode) ->
|
||||
Node = string:tokens(SNode, "/"),
|
||||
F = fun() ->
|
||||
case mnesia:read({pubsub_node, Node}) of
|
||||
case mnesia:read({pubsub_node, {Host, Node}}) of
|
||||
[#pubsub_node{info = Info}] ->
|
||||
SubNodes = mnesia:index_read(pubsub_node,
|
||||
Node,
|
||||
#pubsub_node.parent),
|
||||
{Host, Node},
|
||||
#pubsub_node.host_parent),
|
||||
SubItems =
|
||||
lists:map(fun(#pubsub_node{node = N}) ->
|
||||
lists:map(fun(#pubsub_node{host_node = {_, N}}) ->
|
||||
SN = node_to_string(N),
|
||||
{xmlelement, "item",
|
||||
[{"jid", Host},
|
||||
@ -255,10 +261,10 @@ iq_disco_items(Host, From, SNode) ->
|
||||
[] ->
|
||||
SubNodes = mnesia:index_read(
|
||||
pubsub_node,
|
||||
Node,
|
||||
#pubsub_node.parent),
|
||||
{Host, Node},
|
||||
#pubsub_node.host_parent),
|
||||
lists:map(
|
||||
fun(#pubsub_node{node = N}) ->
|
||||
fun(#pubsub_node{host_node = {_, N}}) ->
|
||||
SN = node_to_string(N),
|
||||
{xmlelement, "item",
|
||||
[{"jid", Host},
|
||||
@ -432,9 +438,9 @@ iq_pubsub(Host, From, Type, SubEl) ->
|
||||
{set, "purge"} ->
|
||||
purge_node(Host, From, Node);
|
||||
{get, "entities"} ->
|
||||
get_entities(From, Node);
|
||||
get_entities(Host, From, Node);
|
||||
{set, "entities"} ->
|
||||
set_entities(From, Node, xml:remove_cdata(Els));
|
||||
set_entities(Host, From, Node, xml:remove_cdata(Els));
|
||||
%{get, "configure"} ->
|
||||
% get_node_config(From, Node);
|
||||
_ ->
|
||||
@ -467,7 +473,7 @@ create_new_node(Host, Node, Owner) ->
|
||||
Parent = lists:sublist(Node, length(Node) - 1),
|
||||
F = fun() ->
|
||||
ParentExists = (Parent == []) orelse
|
||||
case mnesia:read({pubsub_node, Parent}) of
|
||||
case mnesia:read({pubsub_node, {Host, Parent}}) of
|
||||
[_] ->
|
||||
true;
|
||||
[] ->
|
||||
@ -477,7 +483,7 @@ create_new_node(Host, Node, Owner) ->
|
||||
false ->
|
||||
{error, ?ERR_CONFLICT};
|
||||
_ ->
|
||||
case mnesia:read({pubsub_node, Node}) of
|
||||
case mnesia:read({pubsub_node, {Host, Node}}) of
|
||||
[_] ->
|
||||
{error, ?ERR_CONFLICT};
|
||||
[] ->
|
||||
@ -488,8 +494,8 @@ create_new_node(Host, Node, Owner) ->
|
||||
subscription = none},
|
||||
?DICT:new()),
|
||||
mnesia:write(
|
||||
#pubsub_node{node = Node,
|
||||
parent = Parent,
|
||||
#pubsub_node{host_node = {Host, Node},
|
||||
host_parent = {Host, Parent},
|
||||
info = #nodeinfo{
|
||||
entities = Entities}}),
|
||||
ok
|
||||
@ -530,7 +536,7 @@ create_new_node(Host, Node, Owner) ->
|
||||
publish_item(Host, JID, Node, ItemID, Payload) ->
|
||||
Publisher = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
|
||||
F = fun() ->
|
||||
case mnesia:read({pubsub_node, Node}) of
|
||||
case mnesia:read({pubsub_node, {Host, Node}}) of
|
||||
[#pubsub_node{info = Info} = N] ->
|
||||
Affiliation = get_affiliation(Info, Publisher),
|
||||
if
|
||||
@ -563,7 +569,7 @@ publish_item(Host, JID, Node, ItemID, Payload) ->
|
||||
delete_item(Host, JID, Node, ItemID) ->
|
||||
Publisher = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
|
||||
F = fun() ->
|
||||
case mnesia:read({pubsub_node, Node}) of
|
||||
case mnesia:read({pubsub_node, {Host, Node}}) of
|
||||
[#pubsub_node{info = Info} = N] ->
|
||||
case check_item_publisher(Info, ItemID, Publisher)
|
||||
orelse
|
||||
@ -603,7 +609,7 @@ subscribe_node(Host, From, JID, Node) ->
|
||||
end,
|
||||
Subscriber = jlib:jid_tolower(SubscriberJID),
|
||||
F = fun() ->
|
||||
case mnesia:read({pubsub_node, Node}) of
|
||||
case mnesia:read({pubsub_node, {Host, Node}}) of
|
||||
[#pubsub_node{info = Info} = N] ->
|
||||
Affiliation = get_affiliation(Info, Subscriber),
|
||||
if
|
||||
@ -646,7 +652,7 @@ unsubscribe_node(Host, From, JID, Node) ->
|
||||
end,
|
||||
Subscriber = jlib:jid_tolower(SubscriberJID),
|
||||
F = fun() ->
|
||||
case mnesia:read({pubsub_node, Node}) of
|
||||
case mnesia:read({pubsub_node, {Host, Node}}) of
|
||||
[#pubsub_node{info = Info} = N] ->
|
||||
Subscription = get_subscription(Info, Subscriber),
|
||||
if
|
||||
@ -695,7 +701,7 @@ get_items(Host, JID, Node, SMaxItems) ->
|
||||
{error, _} = Error ->
|
||||
Error;
|
||||
_ ->
|
||||
case catch mnesia:dirty_read(pubsub_node, Node) of
|
||||
case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of
|
||||
[#pubsub_node{info = Info}] ->
|
||||
Items = lists:sublist(Info#nodeinfo.items, MaxItems),
|
||||
ItemsEls =
|
||||
@ -722,14 +728,14 @@ get_items(Host, JID, Node, SMaxItems) ->
|
||||
delete_node(Host, JID, Node) ->
|
||||
Owner = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
|
||||
F = fun() ->
|
||||
case mnesia:read({pubsub_node, Node}) of
|
||||
case mnesia:read({pubsub_node, {Host, Node}}) of
|
||||
[#pubsub_node{info = Info}] ->
|
||||
case get_affiliation(Info, Owner) of
|
||||
owner ->
|
||||
% TODO: don't iterate over all table
|
||||
% TODO: don't iterate over entire table
|
||||
Removed =
|
||||
mnesia:foldl(
|
||||
fun(#pubsub_node{node = N,
|
||||
fun(#pubsub_node{host_node = {_, N},
|
||||
info = #nodeinfo{
|
||||
entities = Entities
|
||||
}}, Acc) ->
|
||||
@ -742,7 +748,7 @@ delete_node(Host, JID, Node) ->
|
||||
end, [], pubsub_node),
|
||||
lists:foreach(
|
||||
fun({N, _}) ->
|
||||
mnesia:delete({pubsub_node, N})
|
||||
mnesia:delete({pubsub_node, {Host, N}})
|
||||
end, Removed),
|
||||
{removed, Removed};
|
||||
_ ->
|
||||
@ -769,7 +775,7 @@ delete_node(Host, JID, Node) ->
|
||||
purge_node(Host, JID, Node) ->
|
||||
Owner = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
|
||||
F = fun() ->
|
||||
case mnesia:read({pubsub_node, Node}) of
|
||||
case mnesia:read({pubsub_node, {Host, Node}}) of
|
||||
[#pubsub_node{info = Info} = N] ->
|
||||
case get_affiliation(Info, Owner) of
|
||||
owner ->
|
||||
@ -798,9 +804,9 @@ purge_node(Host, JID, Node) ->
|
||||
end.
|
||||
|
||||
|
||||
get_entities(OJID, Node) ->
|
||||
get_entities(Host, OJID, Node) ->
|
||||
Owner = jlib:jid_tolower(jlib:jid_remove_resource(OJID)),
|
||||
case catch mnesia:dirty_read(pubsub_node, Node) of
|
||||
case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of
|
||||
[#pubsub_node{info = Info}] ->
|
||||
case get_affiliation(Info, Owner) of
|
||||
owner ->
|
||||
@ -832,7 +838,7 @@ get_entities(OJID, Node) ->
|
||||
end.
|
||||
|
||||
|
||||
set_entities(OJID, Node, EntitiesEls) ->
|
||||
set_entities(Host, OJID, Node, EntitiesEls) ->
|
||||
Owner = jlib:jid_tolower(jlib:jid_remove_resource(OJID)),
|
||||
Entities =
|
||||
lists:foldl(
|
||||
@ -883,7 +889,7 @@ set_entities(OJID, Node, EntitiesEls) ->
|
||||
{error, ?ERR_BAD_REQUEST};
|
||||
_ ->
|
||||
F = fun() ->
|
||||
case mnesia:read({pubsub_node, Node}) of
|
||||
case mnesia:read({pubsub_node, {Host, Node}}) of
|
||||
[#pubsub_node{info = Info} = N] ->
|
||||
case get_affiliation(Info, Owner) of
|
||||
owner ->
|
||||
@ -1071,7 +1077,7 @@ set_info_entities(Info, Entities) ->
|
||||
|
||||
|
||||
broadcast_publish_item(Host, Node, ItemID, Payload) ->
|
||||
case catch mnesia:dirty_read(pubsub_node, Node) of
|
||||
case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of
|
||||
[#pubsub_node{info = Info}] ->
|
||||
?DICT:fold(
|
||||
fun(JID, #entity{subscription = Subscription}, _) ->
|
||||
@ -1103,7 +1109,7 @@ broadcast_publish_item(Host, Node, ItemID, Payload) ->
|
||||
|
||||
|
||||
broadcast_retract_item(Host, Node, ItemID) ->
|
||||
case catch mnesia:dirty_read(pubsub_node, Node) of
|
||||
case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of
|
||||
[#pubsub_node{info = Info}] ->
|
||||
?DICT:fold(
|
||||
fun(JID, #entity{subscription = Subscription}, _) ->
|
||||
@ -1166,3 +1172,73 @@ system_terminate(Reason, Parent, _, State) ->
|
||||
|
||||
system_code_change(State, _Mod, Ver, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
|
||||
|
||||
find_my_host(LServer) ->
|
||||
Parts = string:tokens(LServer, "."),
|
||||
find_my_host(Parts, ?MYHOSTS).
|
||||
|
||||
find_my_host([], _Hosts) ->
|
||||
?MYNAME;
|
||||
find_my_host([_ | Tail] = Parts, Hosts) ->
|
||||
Domain = parts_to_string(Parts),
|
||||
case lists:member(Domain, Hosts) of
|
||||
true ->
|
||||
Domain;
|
||||
false ->
|
||||
find_my_host(Tail, Hosts)
|
||||
end.
|
||||
|
||||
parts_to_string(Parts) ->
|
||||
string:strip(lists:flatten(lists:map(fun(S) -> [S, $.] end, Parts)),
|
||||
right, $.).
|
||||
|
||||
|
||||
|
||||
update_table(Host) ->
|
||||
Fields = record_info(fields, pubsub_node),
|
||||
case mnesia:table_info(pubsub_node, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[node, parent, info] ->
|
||||
?INFO_MSG("Converting pubsub_node table from "
|
||||
"{node, parent, info} format", []),
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_pubsub_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, pubsub_node},
|
||||
{attributes, record_info(fields, pubsub_node)}]),
|
||||
mnesia:del_table_index(pubsub_node, parent),
|
||||
mnesia:transform_table(pubsub_node, ignore, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_pubsub_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#pubsub_node{host_node = N,
|
||||
host_parent = P} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_pubsub_tmp_table,
|
||||
R#pubsub_node{host_node = {Host, N},
|
||||
host_parent = {Host, P}})
|
||||
end, ok, pubsub_node)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(pubsub_node),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(pubsub_node),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, mod_pubsub_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(mod_pubsub_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating pubsub_node table", []),
|
||||
mnesia:transform_table(pubsub_node, ignore, Fields)
|
||||
end.
|
||||
|
||||
|
||||
|
||||
|
@ -29,25 +29,26 @@ stop() ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_REGISTER),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_REGISTER).
|
||||
|
||||
process_iq(From, _To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
|
||||
process_iq(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
|
||||
case Type of
|
||||
set ->
|
||||
UTag = xml:get_subtag(SubEl, "username"),
|
||||
PTag = xml:get_subtag(SubEl, "password"),
|
||||
RTag = xml:get_subtag(SubEl, "remove"),
|
||||
Server = ?MYNAME,
|
||||
Server = To#jid.lserver,
|
||||
if
|
||||
(UTag /= false) and (RTag /= false) ->
|
||||
User = xml:get_tag_cdata(UTag),
|
||||
case From of
|
||||
#jid{user = User, lserver = Server} ->
|
||||
ejabberd_auth:remove_user(User),
|
||||
ejabberd_auth:remove_user(User, Server),
|
||||
IQ#iq{type = result, sub_el = [SubEl]};
|
||||
_ ->
|
||||
if
|
||||
PTag /= false ->
|
||||
Password = xml:get_tag_cdata(PTag),
|
||||
case ejabberd_auth:remove_user(User,
|
||||
Server,
|
||||
Password) of
|
||||
ok ->
|
||||
IQ#iq{type = result,
|
||||
@ -74,7 +75,7 @@ process_iq(From, _To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
|
||||
(UTag == false) and (RTag /= false) ->
|
||||
case From of
|
||||
#jid{user = User, lserver = Server} ->
|
||||
ejabberd_auth:remove_user(User),
|
||||
ejabberd_auth:remove_user(User, Server),
|
||||
IQ#iq{type = result, sub_el = [SubEl]};
|
||||
_ ->
|
||||
IQ#iq{type = error,
|
||||
@ -85,10 +86,10 @@ process_iq(From, _To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
|
||||
Password = xml:get_tag_cdata(PTag),
|
||||
case From of
|
||||
#jid{user = User, lserver = Server} ->
|
||||
ejabberd_auth:set_password(User, Password),
|
||||
ejabberd_auth:set_password(User, Server, Password),
|
||||
IQ#iq{type = result, sub_el = [SubEl]};
|
||||
_ ->
|
||||
case try_register(User, Password) of
|
||||
case try_register(User, Server, Password) of
|
||||
ok ->
|
||||
IQ#iq{type = result, sub_el = [SubEl]};
|
||||
{error, Error} ->
|
||||
@ -116,18 +117,18 @@ process_iq(From, _To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
|
||||
end.
|
||||
|
||||
|
||||
try_register(User, Password) ->
|
||||
try_register(User, Server, Password) ->
|
||||
case jlib:is_nodename(User) of
|
||||
false ->
|
||||
{error, ?ERR_BAD_REQUEST};
|
||||
_ ->
|
||||
JID = jlib:make_jid(User, ?MYNAME, ""),
|
||||
JID = jlib:make_jid(User, Server, ""),
|
||||
Access = gen_mod:get_module_opt(?MODULE, access, all),
|
||||
case acl:match_rule(Access, JID) of
|
||||
deny ->
|
||||
{error, ?ERR_CONFLICT};
|
||||
allow ->
|
||||
case ejabberd_auth:try_register(User, Password) of
|
||||
case ejabberd_auth:try_register(User, Server, Password) of
|
||||
{atomic, ok} ->
|
||||
send_welcome_message(JID),
|
||||
send_registration_notifications(JID),
|
||||
|
@ -15,31 +15,27 @@
|
||||
-export([start/1, stop/0,
|
||||
process_iq/3,
|
||||
process_local_iq/3,
|
||||
get_subscription_lists/2,
|
||||
in_subscription/4,
|
||||
out_subscription/3,
|
||||
set_items/2,
|
||||
remove_user/1,
|
||||
get_jid_info/3]).
|
||||
get_user_roster/2,
|
||||
get_subscription_lists/3,
|
||||
in_subscription/5,
|
||||
out_subscription/4,
|
||||
set_items/3,
|
||||
remove_user/2,
|
||||
get_jid_info/4]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
-include("mod_roster.hrl").
|
||||
|
||||
-record(roster, {uj,
|
||||
user,
|
||||
jid,
|
||||
name = "",
|
||||
subscription = none,
|
||||
ask = none,
|
||||
groups = [],
|
||||
xattrs = [],
|
||||
xs = []}).
|
||||
|
||||
start(Opts) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
||||
mnesia:create_table(roster,[{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, roster)}]),
|
||||
mnesia:add_table_index(roster, user),
|
||||
update_table(),
|
||||
mnesia:add_table_index(roster, us),
|
||||
ejabberd_hooks:add(roster_get,
|
||||
?MODULE, get_user_roster, 50),
|
||||
ejabberd_hooks:add(roster_in_subscription,
|
||||
?MODULE, in_subscription, 50),
|
||||
ejabberd_hooks:add(roster_out_subscription,
|
||||
@ -54,6 +50,8 @@ start(Opts) ->
|
||||
?MODULE, process_iq, IQDisc).
|
||||
|
||||
stop() ->
|
||||
ejabberd_hooks:delete(roster_get,
|
||||
?MODULE, get_user_roster, 50),
|
||||
ejabberd_hooks:delete(roster_in_subscription,
|
||||
?MODULE, in_subscription, 50),
|
||||
ejabberd_hooks:delete(roster_out_subscription,
|
||||
@ -74,8 +72,8 @@ stop() ->
|
||||
process_iq(From, To, IQ) ->
|
||||
#iq{sub_el = SubEl} = IQ,
|
||||
#jid{lserver = LServer} = From,
|
||||
case ?MYNAME of
|
||||
LServer ->
|
||||
case lists:member(LServer, ?MYHOSTS) of
|
||||
true ->
|
||||
ResIQ = process_local_iq(From, To, IQ),
|
||||
ejabberd_router:route(From, From,
|
||||
jlib:iq_to_xml(ResIQ)),
|
||||
@ -89,8 +87,8 @@ process_iq(From, To, IQ) ->
|
||||
process_iq(From, To, IQ) ->
|
||||
#iq{sub_el = SubEl} = IQ,
|
||||
#jid{lserver = LServer} = From,
|
||||
case ?MYNAME of
|
||||
LServer ->
|
||||
case lists:member(LServer, ?MYHOSTS) of
|
||||
true ->
|
||||
process_local_iq(From, To, IQ);
|
||||
_ ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]}
|
||||
@ -109,8 +107,10 @@ process_local_iq(From, To, #iq{type = Type} = IQ) ->
|
||||
|
||||
|
||||
process_iq_get(From, _To, #iq{sub_el = SubEl} = IQ) ->
|
||||
#jid{luser = LUser} = From,
|
||||
case catch mnesia:dirty_index_read(roster, LUser, #roster.user) of
|
||||
LUser = From#jid.luser,
|
||||
LServer = From#jid.lserver,
|
||||
US = {LUser, LServer},
|
||||
case catch ejabberd_hooks:run_fold(roster_get, [], [US]) of
|
||||
Items when is_list(Items) ->
|
||||
XItems = lists:map(fun item_to_xml/1, Items),
|
||||
IQ#iq{type = result,
|
||||
@ -121,6 +121,17 @@ process_iq_get(From, _To, #iq{sub_el = SubEl} = IQ) ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]}
|
||||
end.
|
||||
|
||||
get_user_roster(Acc, US) ->
|
||||
case catch mnesia:dirty_index_read(roster, US, #roster.us) of
|
||||
Items when is_list(Items) ->
|
||||
Items ++ Acc;
|
||||
_ ->
|
||||
Acc
|
||||
end.
|
||||
|
||||
|
||||
|
||||
|
||||
item_to_xml(Item) ->
|
||||
Attrs1 = [{"jid", jlib:jid_to_string(Item#roster.jid)}],
|
||||
Attrs2 = case Item#roster.name of
|
||||
@ -164,7 +175,7 @@ process_iq_set(From, To, #iq{sub_el = SubEl} = IQ) ->
|
||||
|
||||
process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) ->
|
||||
JID1 = jlib:string_to_jid(xml:get_attr_s("jid", Attrs)),
|
||||
#jid{user = User, luser = LUser} = From,
|
||||
#jid{user = User, luser = LUser, lserver = LServer} = From,
|
||||
case JID1 of
|
||||
error ->
|
||||
ok;
|
||||
@ -172,15 +183,14 @@ process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) ->
|
||||
JID = {JID1#jid.user, JID1#jid.server, JID1#jid.resource},
|
||||
LJID = jlib:jid_tolower(JID1),
|
||||
F = fun() ->
|
||||
Res = mnesia:read({roster, {LUser, LJID}}),
|
||||
Res = mnesia:read({roster, {LUser, LServer, LJID}}),
|
||||
Item = case Res of
|
||||
[] ->
|
||||
#roster{uj = {LUser, LJID},
|
||||
user = LUser,
|
||||
#roster{usj = {LUser, LServer, LJID},
|
||||
us = {LUser, LServer},
|
||||
jid = JID};
|
||||
[I] ->
|
||||
I#roster{user = LUser,
|
||||
jid = JID,
|
||||
I#roster{jid = JID,
|
||||
name = "",
|
||||
groups = [],
|
||||
xattrs = [],
|
||||
@ -190,7 +200,7 @@ process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) ->
|
||||
Item2 = process_item_els(Item1, Els),
|
||||
case Item2#roster.subscription of
|
||||
remove ->
|
||||
mnesia:delete({roster, {LUser, LJID}});
|
||||
mnesia:delete({roster, {LUser, LServer, LJID}});
|
||||
_ ->
|
||||
mnesia:write(Item2)
|
||||
end,
|
||||
@ -198,7 +208,7 @@ process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) ->
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, {OldItem, Item}} ->
|
||||
push_item(User, To, Item),
|
||||
push_item(User, LServer, To, Item),
|
||||
case Item#roster.subscription of
|
||||
remove ->
|
||||
IsTo = case OldItem#roster.subscription of
|
||||
@ -292,47 +302,49 @@ process_item_els(Item, []) ->
|
||||
Item.
|
||||
|
||||
|
||||
push_item(User, From, Item) ->
|
||||
push_item(User, Server, From, Item) ->
|
||||
ejabberd_sm:route(jlib:make_jid("", "", ""),
|
||||
jlib:make_jid(User, "", ""),
|
||||
jlib:make_jid(User, Server, ""),
|
||||
{xmlelement, "broadcast", [],
|
||||
[{item,
|
||||
Item#roster.jid,
|
||||
Item#roster.subscription}]}),
|
||||
lists:foreach(fun(Resource) ->
|
||||
push_item(User, Resource, From, Item)
|
||||
end, ejabberd_sm:get_user_resources(User)).
|
||||
push_item(User, Server, Resource, From, Item)
|
||||
end, ejabberd_sm:get_user_resources(User, Server)).
|
||||
|
||||
% TODO: don't push to those who not load roster
|
||||
-ifdef(PSI_ROSTER_WORKAROUND).
|
||||
|
||||
push_item(User, Resource, _From, Item) ->
|
||||
push_item(User, Server, Resource, _From, Item) ->
|
||||
ResIQ = #iq{type = set, xmlns = ?NS_ROSTER,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_ROSTER}],
|
||||
[item_to_xml(Item)]}]},
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(User, ?MYNAME, Resource),
|
||||
jlib:make_jid(User, ?MYNAME, Resource),
|
||||
jlib:make_jid(User, Server, Resource),
|
||||
jlib:make_jid(User, Server, Resource),
|
||||
jlib:iq_to_xml(ResIQ)).
|
||||
|
||||
-else.
|
||||
|
||||
push_item(User, Resource, From, Item) ->
|
||||
push_item(User, Server, Resource, From, Item) ->
|
||||
ResIQ = #iq{type = set, xmlns = ?NS_ROSTER,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_ROSTER}],
|
||||
[item_to_xml(Item)]}]},
|
||||
ejabberd_router:route(
|
||||
From,
|
||||
jlib:make_jid(User, ?MYNAME, Resource),
|
||||
jlib:make_jid(User, Server, Resource),
|
||||
jlib:iq_to_xml(ResIQ)).
|
||||
|
||||
-endif.
|
||||
|
||||
get_subscription_lists(_, User) ->
|
||||
get_subscription_lists(_, User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
case mnesia:dirty_index_read(roster, LUser, #roster.user) of
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case mnesia:dirty_index_read(roster, US, #roster.us) of
|
||||
Items when is_list(Items) ->
|
||||
fill_subscription_lists(Items, [], []);
|
||||
_ ->
|
||||
@ -340,7 +352,7 @@ get_subscription_lists(_, User) ->
|
||||
end.
|
||||
|
||||
fill_subscription_lists([I | Is], F, T) ->
|
||||
J = element(2, I#roster.uj),
|
||||
J = element(3, I#roster.usj),
|
||||
case I#roster.subscription of
|
||||
both ->
|
||||
fill_subscription_lists(Is, [J | F], [J | T]);
|
||||
@ -360,23 +372,25 @@ ask_to_pending(Ask) -> Ask.
|
||||
|
||||
|
||||
|
||||
in_subscription(_, User, JID, Type) ->
|
||||
process_subscription(in, User, JID, Type).
|
||||
in_subscription(_, User, Server, JID, Type) ->
|
||||
process_subscription(in, User, Server, JID, Type).
|
||||
|
||||
out_subscription(User, JID, Type) ->
|
||||
process_subscription(out, User, JID, Type).
|
||||
out_subscription(User, Server, JID, Type) ->
|
||||
process_subscription(out, User, Server, JID, Type).
|
||||
|
||||
process_subscription(Direction, User, JID1, Type) ->
|
||||
process_subscription(Direction, User, Server, JID1, Type) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
LJID = jlib:jid_tolower(JID1),
|
||||
F = fun() ->
|
||||
Item = case mnesia:read({roster, {LUser, LJID}}) of
|
||||
Item = case mnesia:read({roster, {LUser, LServer, LJID}}) of
|
||||
[] ->
|
||||
JID = {JID1#jid.user,
|
||||
JID1#jid.server,
|
||||
JID1#jid.resource},
|
||||
#roster{uj = {LUser, LJID},
|
||||
user = LUser,
|
||||
#roster{usj = {LUser, LServer, LJID},
|
||||
us = US,
|
||||
jid = JID};
|
||||
[I] ->
|
||||
I
|
||||
@ -420,12 +434,13 @@ process_subscription(Direction, User, JID1, Type) ->
|
||||
unsubscribed -> "unsubscribed"
|
||||
end,
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(User, ?MYNAME, ""), JID1,
|
||||
jlib:make_jid(User, Server, ""), JID1,
|
||||
{xmlelement, "presence", [{"type", T}], []})
|
||||
end,
|
||||
case Push of
|
||||
{push, Item} ->
|
||||
push_item(User, jlib:make_jid("", ?MYNAME, ""), Item),
|
||||
push_item(User, Server,
|
||||
jlib:make_jid("", Server, ""), Item),
|
||||
true;
|
||||
none ->
|
||||
false
|
||||
@ -525,27 +540,32 @@ in_auto_reply(both, none, unsubscribe) -> unsubscribed;
|
||||
in_auto_reply(_, _, _) -> none.
|
||||
|
||||
|
||||
remove_user(User) ->
|
||||
remove_user(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
lists:foreach(fun(R) ->
|
||||
mnesia:delete_object(R)
|
||||
end,
|
||||
mnesia:index_read(roster, LUser, #roster.user))
|
||||
mnesia:index_read(roster, US, #roster.us))
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
set_items(User, SubEl) ->
|
||||
set_items(User, Server, SubEl) ->
|
||||
{xmlelement, _Name, _Attrs, Els} = SubEl,
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
F = fun() ->
|
||||
lists:foreach(fun(El) -> process_item_set_t(LUser, El) end, Els)
|
||||
lists:foreach(fun(El) ->
|
||||
process_item_set_t(LUser, LServer, El)
|
||||
end, Els)
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
process_item_set_t(LUser, {xmlelement, _Name, Attrs, Els}) ->
|
||||
process_item_set_t(LUser, LServer, {xmlelement, _Name, Attrs, Els}) ->
|
||||
JID1 = jlib:string_to_jid(xml:get_attr_s("jid", Attrs)),
|
||||
case JID1 of
|
||||
error ->
|
||||
@ -553,19 +573,19 @@ process_item_set_t(LUser, {xmlelement, _Name, Attrs, Els}) ->
|
||||
_ ->
|
||||
JID = {JID1#jid.user, JID1#jid.server, JID1#jid.resource},
|
||||
LJID = {JID1#jid.luser, JID1#jid.lserver, JID1#jid.lresource},
|
||||
Item = #roster{uj = {LUser, LJID},
|
||||
user = LUser,
|
||||
Item = #roster{usj = {LUser, LServer, LJID},
|
||||
us = {LUser, LServer},
|
||||
jid = JID},
|
||||
Item1 = process_item_attrs_ws(Item, Attrs),
|
||||
Item2 = process_item_els(Item1, Els),
|
||||
case Item2#roster.subscription of
|
||||
remove ->
|
||||
mnesia:delete({roster, {LUser, LJID}});
|
||||
mnesia:delete({roster, {LUser, LServer, LJID}});
|
||||
_ ->
|
||||
mnesia:write(Item2)
|
||||
end
|
||||
end;
|
||||
process_item_set_t(_LUser, _) ->
|
||||
process_item_set_t(_LUser, _LServer, _) ->
|
||||
ok.
|
||||
|
||||
process_item_attrs_ws(Item, [{Attr, Val} | Attrs]) ->
|
||||
@ -613,10 +633,11 @@ process_item_attrs_ws(Item, []) ->
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
get_jid_info(_, User, JID) ->
|
||||
get_jid_info(_, User, Server, JID) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LJID = jlib:jid_tolower(JID),
|
||||
case catch mnesia:dirty_read(roster, {LUser, LJID}) of
|
||||
LServer = jlib:nameprep(Server),
|
||||
case catch mnesia:dirty_read(roster, {LUser, LServer, LJID}) of
|
||||
[#roster{subscription = Subscription, groups = Groups}] ->
|
||||
{Subscription, Groups};
|
||||
_ ->
|
||||
@ -625,7 +646,8 @@ get_jid_info(_, User, JID) ->
|
||||
LRJID == LJID ->
|
||||
{none, []};
|
||||
true ->
|
||||
case catch mnesia:dirty_read(roster, {LUser, LRJID}) of
|
||||
case catch mnesia:dirty_read(
|
||||
roster, {LUser, LServer, LRJID}) of
|
||||
[#roster{subscription = Subscription,
|
||||
groups = Groups}] ->
|
||||
{Subscription, Groups};
|
||||
@ -635,4 +657,51 @@ get_jid_info(_, User, JID) ->
|
||||
end
|
||||
end.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
|
||||
update_table() ->
|
||||
Fields = record_info(fields, roster),
|
||||
case mnesia:table_info(roster, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[uj, user, jid, name, subscription, ask, groups, xattrs, xs] ->
|
||||
?INFO_MSG("Converting roster table from "
|
||||
"{uj, user, jid, name, subscription, ask, groups, xattrs, xs} format", []),
|
||||
Host = ?MYNAME,
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_roster_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, roster},
|
||||
{attributes, record_info(fields, roster)}]),
|
||||
mnesia:del_table_index(roster, user),
|
||||
mnesia:transform_table(roster, ignore, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_roster_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#roster{usj = {U, JID}, us = U} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_roster_tmp_table,
|
||||
R#roster{usj = {U, Host, JID},
|
||||
us = {U, Host}})
|
||||
end, ok, roster)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(roster),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(roster),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, mod_roster_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(mod_roster_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating roster table", []),
|
||||
mnesia:transform_table(roster, ignore, Fields)
|
||||
end.
|
||||
|
||||
|
||||
|
18
src/mod_roster.hrl
Normal file
18
src/mod_roster.hrl
Normal file
@ -0,0 +1,18 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : mod_roster.hrl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Roster management
|
||||
%%% Created : 5 Mar 2005 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id: mod_roster.hrl 11 2005-03-06 22:36:15Z alexey $
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(roster, {usj,
|
||||
us,
|
||||
jid,
|
||||
name = "",
|
||||
subscription = none,
|
||||
ask = none,
|
||||
groups = [],
|
||||
xattrs = [],
|
||||
xs = []}).
|
||||
|
@ -333,7 +333,7 @@ push_item(User, From, Item) ->
|
||||
Item#roster.subscription}]}),
|
||||
lists:foreach(fun(Resource) ->
|
||||
push_item(User, Resource, From, Item)
|
||||
end, ejabberd_sm:get_user_resources(User)).
|
||||
end, ejabberd_sm:get_user_resources(User, 'TODO')).
|
||||
|
||||
% TODO: don't push to those who not load roster
|
||||
-ifdef(PSI_ROSTER_WORKAROUND).
|
||||
|
291
src/mod_shared_roster.erl
Normal file
291
src/mod_shared_roster.erl
Normal file
@ -0,0 +1,291 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : mod_shared_roster.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Shared roster management
|
||||
%%% Created : 5 Mar 2005 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id: mod_shared_roster.erl 24 2005-04-14 01:15:31Z alexey $
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(mod_shared_roster).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision: 24 $ ').
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/1, stop/0,
|
||||
get_user_roster/2,
|
||||
get_subscription_lists/3,
|
||||
get_jid_info/4,
|
||||
in_subscription/5,
|
||||
out_subscription/4,
|
||||
list_groups/1,
|
||||
create_group/2,
|
||||
create_group/3,
|
||||
delete_group/2,
|
||||
get_group_opts/2,
|
||||
set_group_opts/3,
|
||||
get_group_users/2,
|
||||
add_user_to_group/3,
|
||||
remove_user_from_group/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
-include("mod_roster.hrl").
|
||||
|
||||
-record(sr_group, {group_host, opts}).
|
||||
-record(sr_user, {us, group_host}).
|
||||
|
||||
start(_Opts) ->
|
||||
mnesia:create_table(sr_group,
|
||||
[{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, sr_group)}]),
|
||||
mnesia:create_table(sr_user,
|
||||
[{disc_copies, [node()]},
|
||||
{type, bag},
|
||||
{attributes, record_info(fields, sr_user)}]),
|
||||
mnesia:add_table_index(sr_user, group_host),
|
||||
ejabberd_hooks:add(roster_get,
|
||||
?MODULE, get_user_roster, 70),
|
||||
ejabberd_hooks:add(roster_in_subscription,
|
||||
?MODULE, in_subscription, 30),
|
||||
ejabberd_hooks:add(roster_out_subscription,
|
||||
?MODULE, out_subscription, 30),
|
||||
ejabberd_hooks:add(roster_get_subscription_lists,
|
||||
?MODULE, get_subscription_lists, 70),
|
||||
ejabberd_hooks:add(roster_get_jid_info,
|
||||
?MODULE, get_jid_info, 70).
|
||||
%ejabberd_hooks:add(remove_user,
|
||||
% ?MODULE, remove_user, 50),
|
||||
|
||||
stop() ->
|
||||
ejabberd_hooks:delete(roster_get,
|
||||
?MODULE, get_user_roster, 70),
|
||||
ejabberd_hooks:delete(roster_in_subscription,
|
||||
?MODULE, in_subscription, 30),
|
||||
ejabberd_hooks:delete(roster_out_subscription,
|
||||
?MODULE, out_subscription, 30),
|
||||
ejabberd_hooks:delete(roster_get_subscription_lists,
|
||||
?MODULE, get_subscription_lists, 70),
|
||||
ejabberd_hooks:delete(roster_get_jid_info,
|
||||
?MODULE, get_jid_info, 70).
|
||||
%ejabberd_hooks:delete(remove_user,
|
||||
% ?MODULE, remove_user, 50),
|
||||
|
||||
|
||||
get_user_roster(Items, US) ->
|
||||
{U, S} = US,
|
||||
DisplayedGroups = get_user_displayed_groups(US),
|
||||
SRUsers =
|
||||
lists:foldl(
|
||||
fun(Group, Acc1) ->
|
||||
lists:foldl(
|
||||
fun(User, Acc2) ->
|
||||
dict:append(User, get_group_name(S, Group), Acc2)
|
||||
end, Acc1, get_group_users(S, Group))
|
||||
end, dict:new(), DisplayedGroups),
|
||||
{NewItems1, SRUsersRest} =
|
||||
lists:mapfoldl(
|
||||
fun(Item, SRUsers1) ->
|
||||
{_, _, {U1, S1, _}} = Item#roster.usj,
|
||||
US1 = {U1, S1},
|
||||
case dict:find(US1, SRUsers1) of
|
||||
{ok, _GroupNames} ->
|
||||
{Item#roster{subscription = both, ask = none},
|
||||
dict:erase(US1, SRUsers1)};
|
||||
error ->
|
||||
{Item, SRUsers1}
|
||||
end
|
||||
end, SRUsers, Items),
|
||||
SRItems = [#roster{usj = {U, S, {U1, S1, ""}},
|
||||
us = US,
|
||||
jid = {U1, S1, ""},
|
||||
name = "",
|
||||
subscription = both,
|
||||
ask = none,
|
||||
groups = GroupNames} ||
|
||||
{{U1, S1}, GroupNames} <- dict:to_list(SRUsersRest)],
|
||||
SRItems ++ NewItems1.
|
||||
|
||||
get_subscription_lists({F, T}, User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
DisplayedGroups = get_user_displayed_groups(US),
|
||||
SRUsers =
|
||||
lists:usort(
|
||||
lists:flatmap(
|
||||
fun(Group) ->
|
||||
get_group_users(LServer, Group)
|
||||
end, DisplayedGroups)),
|
||||
SRJIDs = [{U1, S1, ""} || {U1, S1} <- SRUsers],
|
||||
{lists:usort(SRJIDs ++ F), lists:usort(SRJIDs ++ T)}.
|
||||
|
||||
get_jid_info({Subscription, Groups}, User, Server, JID) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
{U1, S1, _} = jlib:jid_tolower(JID),
|
||||
US1 = {U1, S1},
|
||||
DisplayedGroups = get_user_displayed_groups(US),
|
||||
SRUsers =
|
||||
lists:foldl(
|
||||
fun(Group, Acc1) ->
|
||||
lists:foldl(
|
||||
fun(User1, Acc2) ->
|
||||
dict:append(
|
||||
User1, get_group_name(LServer, Group), Acc2)
|
||||
end, Acc1, get_group_users(LServer, Group))
|
||||
end, dict:new(), DisplayedGroups),
|
||||
case dict:find(US1, SRUsers) of
|
||||
{ok, GroupNames} ->
|
||||
NewGroups = if
|
||||
Groups == [] -> GroupNames;
|
||||
true -> Groups
|
||||
end,
|
||||
{both, NewGroups};
|
||||
error ->
|
||||
{Subscription, Groups}
|
||||
end.
|
||||
|
||||
in_subscription(Acc, User, Server, JID, Type) ->
|
||||
process_subscription(in, User, Server, JID, Type, Acc).
|
||||
|
||||
out_subscription(User, Server, JID, Type) ->
|
||||
process_subscription(out, User, Server, JID, Type, false).
|
||||
|
||||
process_subscription(Direction, User, Server, JID, _Type, Acc) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
{U1, S1, _} = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
|
||||
US1 = {U1, S1},
|
||||
DisplayedGroups = get_user_displayed_groups(US),
|
||||
SRUsers =
|
||||
lists:usort(
|
||||
lists:flatmap(
|
||||
fun(Group) ->
|
||||
get_group_users(LServer, Group)
|
||||
end, DisplayedGroups)),
|
||||
case lists:member(US1, SRUsers) of
|
||||
true ->
|
||||
case Direction of
|
||||
in ->
|
||||
{stop, false};
|
||||
out ->
|
||||
stop
|
||||
end;
|
||||
false ->
|
||||
Acc
|
||||
end.
|
||||
|
||||
list_groups(Host) ->
|
||||
mnesia:dirty_select(
|
||||
sr_group,
|
||||
[{#sr_group{group_host = {'$1', '$2'},
|
||||
_ = '_'},
|
||||
[{'==', '$2', Host}],
|
||||
['$1']}]).
|
||||
|
||||
create_group(Host, Group) ->
|
||||
create_group(Host, Group, []).
|
||||
|
||||
create_group(Host, Group, Opts) ->
|
||||
R = #sr_group{group_host = {Group, Host}, opts = Opts},
|
||||
F = fun() ->
|
||||
mnesia:write(R)
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
delete_group(Host, Group) ->
|
||||
F = fun() ->
|
||||
mnesia:delete({sr_group, {Group, Host}})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
get_group_opts(Host, Group) ->
|
||||
case catch mnesia:dirty_read(sr_group, {Group, Host}) of
|
||||
[#sr_group{opts = Opts}] ->
|
||||
Opts;
|
||||
_ ->
|
||||
error
|
||||
end.
|
||||
|
||||
set_group_opts(Host, Group, Opts) ->
|
||||
R = #sr_group{group_host = {Group, Host}, opts = Opts},
|
||||
F = fun() ->
|
||||
mnesia:write(R)
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
|
||||
|
||||
get_user_groups(US) ->
|
||||
Host = element(2, US),
|
||||
case catch mnesia:dirty_read(sr_user, US) of
|
||||
Rs when is_list(Rs) ->
|
||||
[Group || #sr_user{group_host = {Group, H}} <- Rs, H == Host];
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
|
||||
is_group_enabled(Host, Group) ->
|
||||
case catch mnesia:dirty_read(sr_group, {Group, Host}) of
|
||||
[#sr_group{opts = Opts}] ->
|
||||
not lists:member(disabled, Opts);
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
get_group_opt(Host, Group, Opt, Default) ->
|
||||
case catch mnesia:dirty_read(sr_group, {Group, Host}) of
|
||||
[#sr_group{opts = Opts}] ->
|
||||
case lists:keysearch(Opt, 1, Opts) of
|
||||
{value, {_, Val}} ->
|
||||
Val;
|
||||
false ->
|
||||
Default
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
get_group_users(Host, Group) ->
|
||||
case catch mnesia:dirty_index_read(
|
||||
sr_user, {Group, Host}, #sr_user.group_host) of
|
||||
Rs when is_list(Rs) ->
|
||||
[R#sr_user.us || R <- Rs];
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
|
||||
get_group_name(Host, Group) ->
|
||||
get_group_opt(Host, Group, name, Group).
|
||||
|
||||
get_user_displayed_groups(US) ->
|
||||
Host = element(2, US),
|
||||
DisplayedGroups1 =
|
||||
lists:usort(
|
||||
lists:flatmap(
|
||||
fun(Group) ->
|
||||
case is_group_enabled(Host, Group) of
|
||||
true ->
|
||||
get_group_opt(Host, Group, displayed_groups, []);
|
||||
false ->
|
||||
[]
|
||||
end
|
||||
end, get_user_groups(US))),
|
||||
[Group || Group <- DisplayedGroups1, is_group_enabled(Host, Group)].
|
||||
|
||||
add_user_to_group(Host, US, Group) ->
|
||||
R = #sr_user{us = US, group_host = {Group, Host}},
|
||||
F = fun() ->
|
||||
mnesia:write(R)
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
remove_user_from_group(Host, US, Group) ->
|
||||
R = #sr_user{us = US, group_host = {Group, Host}},
|
||||
F = fun() ->
|
||||
mnesia:delete_object(R)
|
||||
end,
|
||||
mnesia:transaction(F).
|
@ -38,7 +38,7 @@ process_local_iq(From, To, #iq{id = ID, type = Type,
|
||||
Node = string:tokens(xml:get_tag_attr_s("node", SubEl), "/"),
|
||||
Names = get_names(Els, []),
|
||||
|
||||
case get_local_stats(Node, Names) of
|
||||
case get_local_stats(To#jid.server, Node, Names) of
|
||||
{result, Res} ->
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
@ -66,16 +66,20 @@ get_names([_ | Els], Res) ->
|
||||
|
||||
-define(STAT(Name), {xmlelement, "stat", [{"name", Name}], []}).
|
||||
|
||||
get_local_stats([], []) ->
|
||||
get_local_stats(_Server, [], []) ->
|
||||
{result,
|
||||
[?STAT("users/online"),
|
||||
?STAT("users/total")
|
||||
?STAT("users/total"),
|
||||
?STAT("users/all-hosts/online"),
|
||||
?STAT("users/all-hosts/total")
|
||||
]};
|
||||
|
||||
get_local_stats([], Names) ->
|
||||
{result, lists:map(fun(Name) -> get_local_stat([], Name) end, Names)};
|
||||
get_local_stats(Server, [], Names) ->
|
||||
{result, lists:map(fun(Name) ->
|
||||
get_local_stat(Server, [], Name)
|
||||
end, Names)};
|
||||
|
||||
get_local_stats(["running nodes", _], []) ->
|
||||
get_local_stats(_Server, ["running nodes", _], []) ->
|
||||
{result,
|
||||
[?STAT("time/uptime"),
|
||||
?STAT("time/cputime"),
|
||||
@ -86,7 +90,7 @@ get_local_stats(["running nodes", _], []) ->
|
||||
?STAT("transactions/logged")
|
||||
]};
|
||||
|
||||
get_local_stats(["running nodes", ENode], Names) ->
|
||||
get_local_stats(_Server, ["running nodes", ENode], Names) ->
|
||||
case search_running_node(ENode) of
|
||||
false ->
|
||||
{error, ?ERR_ITEM_NOT_FOUND};
|
||||
@ -95,7 +99,7 @@ get_local_stats(["running nodes", ENode], Names) ->
|
||||
lists:map(fun(Name) -> get_node_stat(Node, Name) end, Names)}
|
||||
end;
|
||||
|
||||
get_local_stats(_, _) ->
|
||||
get_local_stats(_Server, _, _) ->
|
||||
{error, ?ERR_FEATURE_NOT_IMPLEMENTED}.
|
||||
|
||||
|
||||
@ -115,27 +119,40 @@ get_local_stats(_, _) ->
|
||||
[{xmlcdata, Desc}]}]}).
|
||||
|
||||
|
||||
%get_local_stat([], Name) when Name == "time/uptime" ->
|
||||
% ?STATVAL(io_lib:format("~.3f", [element(1, statistics(wall_clock))/1000]),
|
||||
% "seconds");
|
||||
%get_local_stat([], Name) when Name == "time/cputime" ->
|
||||
% ?STATVAL(io_lib:format("~.3f", [element(1, statistics(runtime))/1000]),
|
||||
% "seconds");
|
||||
get_local_stat([], Name) when Name == "users/online" ->
|
||||
get_local_stat(Server, [], Name) when Name == "users/online" ->
|
||||
case catch ejabberd_sm:get_vh_session_list(Server) of
|
||||
{'EXIT', Reason} ->
|
||||
?STATERR("500", "Internal Server Error");
|
||||
Users ->
|
||||
?STATVAL(integer_to_list(length(Users)), "users")
|
||||
end;
|
||||
|
||||
get_local_stat(Server, [], Name) when Name == "users/total" ->
|
||||
LServer = jlib:nameprep(Server),
|
||||
case catch ejabberd_auth:get_vh_registered_users(Server) of
|
||||
{'EXIT', Reason} ->
|
||||
?STATERR("500", "Internal Server Error");
|
||||
Users ->
|
||||
?STATVAL(integer_to_list(length(Users)), "users")
|
||||
end;
|
||||
|
||||
get_local_stat(_Server, [], Name) when Name == "users/all-hosts/online" ->
|
||||
case catch mnesia:table_info(session, size) of
|
||||
{'EXIT', Reason} ->
|
||||
?STATERR("500", "Internal Server Error");
|
||||
Users ->
|
||||
?STATVAL(integer_to_list(Users), "users")
|
||||
end;
|
||||
get_local_stat([], Name) when Name == "users/total" ->
|
||||
|
||||
get_local_stat(_Server, [], Name) when Name == "users/all-hosts/total" ->
|
||||
case catch mnesia:table_info(passwd, size) of
|
||||
{'EXIT', Reason} ->
|
||||
?STATERR("500", "Internal Server Error");
|
||||
Users ->
|
||||
?STATVAL(integer_to_list(Users), "users")
|
||||
end;
|
||||
get_local_stat(_, Name) ->
|
||||
|
||||
get_local_stat(_Server, _, Name) ->
|
||||
?STATERR("404", "Not Found").
|
||||
|
||||
|
||||
|
@ -16,16 +16,16 @@
|
||||
process_local_iq/3,
|
||||
process_sm_iq/3,
|
||||
reindex_vcards/0,
|
||||
remove_user/1]).
|
||||
remove_user/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
|
||||
%-define(JUD_ALLOW_RETURN_ALL, true).
|
||||
-define(JUD_MATCHES, 30).
|
||||
|
||||
-record(vcard_search, {user, luser,
|
||||
-record(vcard_search, {us,
|
||||
user, luser,
|
||||
fn, lfn,
|
||||
family, lfamily,
|
||||
given, lgiven,
|
||||
@ -38,7 +38,7 @@
|
||||
orgname, lorgname,
|
||||
orgunit, lorgunit
|
||||
}).
|
||||
-record(vcard, {user, vcard}).
|
||||
-record(vcard, {us, vcard}).
|
||||
|
||||
|
||||
start(Opts) ->
|
||||
@ -47,6 +47,7 @@ start(Opts) ->
|
||||
mnesia:create_table(vcard_search,
|
||||
[{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, vcard_search)}]),
|
||||
update_tables(),
|
||||
mnesia:add_table_index(vcard_search, luser),
|
||||
mnesia:add_table_index(vcard_search, lfn),
|
||||
mnesia:add_table_index(vcard_search, lfamily),
|
||||
@ -68,21 +69,21 @@ start(Opts) ->
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_VCARD,
|
||||
?MODULE, process_sm_iq, IQDisc),
|
||||
catch mod_disco:register_sm_feature(?NS_VCARD),
|
||||
Host = gen_mod:get_opt(host, Opts, "vjud." ++ ?MYNAME),
|
||||
Hosts = gen_mod:get_hosts(Opts, "vjud."),
|
||||
Search = gen_mod:get_opt(search, Opts, true),
|
||||
register(ejabberd_mod_vcard, spawn(?MODULE, init, [Host, Search])).
|
||||
register(ejabberd_mod_vcard, spawn(?MODULE, init, [Hosts, Search])).
|
||||
|
||||
|
||||
init(Host, Search) ->
|
||||
init(Hosts, Search) ->
|
||||
case Search of
|
||||
false ->
|
||||
loop(Host);
|
||||
loop(Hosts);
|
||||
_ ->
|
||||
ejabberd_router:register_route(Host),
|
||||
loop(Host)
|
||||
ejabberd_router:register_routes(Hosts),
|
||||
loop(Hosts)
|
||||
end.
|
||||
|
||||
loop(Host) ->
|
||||
loop(Hosts) ->
|
||||
receive
|
||||
{route, From, To, Packet} ->
|
||||
case catch do_route(From, To, Packet) of
|
||||
@ -91,12 +92,12 @@ loop(Host) ->
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
loop(Host);
|
||||
loop(Hosts);
|
||||
stop ->
|
||||
catch ejabberd_router:unregister_route(Host),
|
||||
catch ejabberd_router:unregister_routes(Hosts),
|
||||
ok;
|
||||
_ ->
|
||||
loop(Host)
|
||||
loop(Hosts)
|
||||
end.
|
||||
|
||||
stop() ->
|
||||
@ -137,17 +138,18 @@ process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||
case Type of
|
||||
set ->
|
||||
#jid{user = User, lserver = LServer, luser = LUser} = From,
|
||||
case ?MYNAME of
|
||||
LServer ->
|
||||
set_vcard(User, SubEl),
|
||||
case lists:member(LServer, ?MYHOSTS) of
|
||||
true ->
|
||||
set_vcard(User, LServer, SubEl),
|
||||
IQ#iq{type = result, sub_el = []};
|
||||
_ ->
|
||||
false ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
|
||||
end;
|
||||
get ->
|
||||
#jid{luser = LUser} = To,
|
||||
#jid{luser = LUser, lserver = LServer} = To,
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
mnesia:read({vcard, LUser})
|
||||
mnesia:read({vcard, US})
|
||||
end,
|
||||
Els = case mnesia:transaction(F) of
|
||||
{atomic, Rs} ->
|
||||
@ -160,7 +162,7 @@ process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||
IQ#iq{type = result, sub_el = Els}
|
||||
end.
|
||||
|
||||
set_vcard(User, VCARD) ->
|
||||
set_vcard(User, LServer, VCARD) ->
|
||||
FN = xml:get_path_s(VCARD, [{elem, "FN"}, cdata]),
|
||||
Family = xml:get_path_s(VCARD, [{elem, "N"}, {elem, "FAMILY"}, cdata]),
|
||||
Given = xml:get_path_s(VCARD, [{elem, "N"}, {elem, "GIVEN"}, cdata]),
|
||||
@ -193,6 +195,8 @@ set_vcard(User, VCARD) ->
|
||||
LOrgName = stringprep:tolower(OrgName),
|
||||
LOrgUnit = stringprep:tolower(OrgUnit),
|
||||
|
||||
US = {LUser, LServer},
|
||||
|
||||
if
|
||||
(LUser == error) or
|
||||
(LFN == error) or
|
||||
@ -209,9 +213,11 @@ set_vcard(User, VCARD) ->
|
||||
{error, badarg};
|
||||
true ->
|
||||
F = fun() ->
|
||||
mnesia:write(#vcard{user = LUser, vcard = VCARD}),
|
||||
mnesia:write(#vcard{us = US, vcard = VCARD}),
|
||||
mnesia:write(
|
||||
#vcard_search{user = User, luser = LUser,
|
||||
#vcard_search{us = US,
|
||||
user = {User, LServer},
|
||||
luser = LUser,
|
||||
fn = FN, lfn = LFN,
|
||||
family = Family, lfamily = LFamily,
|
||||
given = Given, lgiven = LGiven,
|
||||
@ -428,7 +434,7 @@ search_result(Lang, JID, Data) ->
|
||||
?LFIELD("email", "email"),
|
||||
?LFIELD("Organization Name", "orgname"),
|
||||
?LFIELD("Organization Unit", "orgunit")
|
||||
]}] ++ lists:map(fun record_to_item/1, search(Data)).
|
||||
]}] ++ lists:map(fun record_to_item/1, search(JID#jid.lserver, Data)).
|
||||
|
||||
-define(FIELD(Var, Val),
|
||||
{xmlelement, "field", [{"var", Var}],
|
||||
@ -436,9 +442,10 @@ search_result(Lang, JID, Data) ->
|
||||
[{xmlcdata, Val}]}]}).
|
||||
|
||||
record_to_item(R) ->
|
||||
{xmlelement, "item", [],
|
||||
[
|
||||
?FIELD("jid", R#vcard_search.user ++ "@" ++ ?MYNAME),
|
||||
{User, Server} = R#vcard_search.user,
|
||||
{xmlelement, "item", [],
|
||||
[
|
||||
?FIELD("jid", User ++ "@" ++ Server),
|
||||
?FIELD("fn", R#vcard_search.fn),
|
||||
?FIELD("family", R#vcard_search.family),
|
||||
?FIELD("given", R#vcard_search.given),
|
||||
@ -453,34 +460,12 @@ record_to_item(R) ->
|
||||
]
|
||||
}.
|
||||
|
||||
-ifdef(JUD_ALLOW_RETURN_ALL).
|
||||
|
||||
search(Data) ->
|
||||
MatchSpec = make_matchspec(Data),
|
||||
case catch mnesia:dirty_select(vcard_search, [{MatchSpec, [], ['$_']}]) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p", [Reason]),
|
||||
[];
|
||||
Rs ->
|
||||
case gen_mod:get_module_opt(?MODULE, matches, 30) of
|
||||
infinity ->
|
||||
Rs;
|
||||
Val when is_integer(Val) and Val > 0 ->
|
||||
lists:sublist(Rs, Val);
|
||||
Val ->
|
||||
?ERROR_MSG("Illegal option value ~p. "
|
||||
"Default value ~p substituted.",
|
||||
[{matches, Val}, ?JUD_MATCHES]),
|
||||
lists:sublist(Rs, ?JUD_MATCHES)
|
||||
end
|
||||
end.
|
||||
|
||||
-else.
|
||||
|
||||
search(Data) ->
|
||||
MatchSpec = make_matchspec(Data),
|
||||
search(LServer, Data) ->
|
||||
MatchSpec = make_matchspec(LServer, Data),
|
||||
AllowReturnAll = gen_mod:get_module_opt(?MODULE, allow_return_all, false),
|
||||
if
|
||||
MatchSpec == #vcard_search{_ = '_'} ->
|
||||
(MatchSpec == #vcard_search{_ = '_'}) and (not AllowReturnAll) ->
|
||||
[];
|
||||
true ->
|
||||
case catch mnesia:dirty_select(vcard_search,
|
||||
@ -503,33 +488,27 @@ search(Data) ->
|
||||
end
|
||||
end.
|
||||
|
||||
-endif.
|
||||
|
||||
|
||||
% TODO: remove
|
||||
% F = fun() ->
|
||||
% mnesia:select(vcard_search, [{MatchSpec, [], ['$_']}])
|
||||
% end,
|
||||
% case mnesia:transaction(F) of
|
||||
% {atomic, Rs} ->
|
||||
% Rs;
|
||||
% _ ->
|
||||
% []
|
||||
% end.
|
||||
|
||||
|
||||
make_matchspec(Data) ->
|
||||
make_matchspec(LServer, Data) ->
|
||||
GlobMatch = #vcard_search{_ = '_'},
|
||||
Match = filter_fields(Data, GlobMatch),
|
||||
Match = filter_fields(Data, GlobMatch, LServer),
|
||||
Match.
|
||||
|
||||
filter_fields([], Match) ->
|
||||
filter_fields([], Match, _LServer) ->
|
||||
Match;
|
||||
filter_fields([{SVar, [Val]} | Ds], Match)
|
||||
filter_fields([{SVar, [Val]} | Ds], Match, LServer)
|
||||
when is_list(Val) and (Val /= "") ->
|
||||
LVal = stringprep:tolower(Val),
|
||||
NewMatch = case SVar of
|
||||
"user" -> Match#vcard_search{luser = make_val(LVal)};
|
||||
"user" ->
|
||||
case gen_mod:get_module_opt(
|
||||
?MODULE, search_all_hosts, true) of
|
||||
true ->
|
||||
Match#vcard_search{luser = make_val(LVal)};
|
||||
false ->
|
||||
Host = find_my_host(LServer),
|
||||
Match#vcard_search{us = {make_val(LVal), Host}}
|
||||
end;
|
||||
"fn" -> Match#vcard_search{lfn = make_val(LVal)};
|
||||
"family" -> Match#vcard_search{lfamily = make_val(LVal)};
|
||||
"given" -> Match#vcard_search{lgiven = make_val(LVal)};
|
||||
@ -543,9 +522,9 @@ filter_fields([{SVar, [Val]} | Ds], Match)
|
||||
"orgunit" -> Match#vcard_search{lorgunit = make_val(LVal)};
|
||||
_ -> Match
|
||||
end,
|
||||
filter_fields(Ds, NewMatch);
|
||||
filter_fields([_ | Ds], Match) ->
|
||||
filter_fields(Ds, Match).
|
||||
filter_fields(Ds, NewMatch, LServer);
|
||||
filter_fields([_ | Ds], Match, LServer) ->
|
||||
filter_fields(Ds, Match, LServer).
|
||||
|
||||
make_val(Val) ->
|
||||
case lists:suffix("*", Val) of
|
||||
@ -555,10 +534,32 @@ make_val(Val) ->
|
||||
Val
|
||||
end.
|
||||
|
||||
find_my_host(LServer) ->
|
||||
Parts = string:tokens(LServer, "."),
|
||||
find_my_host(Parts, ?MYHOSTS).
|
||||
|
||||
find_my_host([], _Hosts) ->
|
||||
?MYNAME;
|
||||
find_my_host([_ | Tail] = Parts, Hosts) ->
|
||||
Domain = parts_to_string(Parts),
|
||||
case lists:member(Domain, Hosts) of
|
||||
true ->
|
||||
Domain;
|
||||
false ->
|
||||
find_my_host(Tail, Hosts)
|
||||
end.
|
||||
|
||||
parts_to_string(Parts) ->
|
||||
string:strip(lists:flatten(lists:map(fun(S) -> [S, $.] end, Parts)),
|
||||
right, $.).
|
||||
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
set_vcard_t(R, _) ->
|
||||
User = R#vcard.user,
|
||||
US = R#vcard.us,
|
||||
User = US,
|
||||
VCARD = R#vcard.vcard,
|
||||
|
||||
FN = xml:get_path_s(VCARD, [{elem, "FN"}, cdata]),
|
||||
@ -573,7 +574,7 @@ set_vcard_t(R, _) ->
|
||||
OrgName = xml:get_path_s(VCARD, [{elem, "ORG"}, {elem, "ORGNAME"}, cdata]),
|
||||
OrgUnit = xml:get_path_s(VCARD, [{elem, "ORG"}, {elem, "ORGUNIT"}, cdata]),
|
||||
|
||||
LUser = jlib:nodeprep(User),
|
||||
{LUser, _LServer} = US,
|
||||
LFN = stringprep:tolower(FN),
|
||||
LFamily = stringprep:tolower(Family),
|
||||
LGiven = stringprep:tolower(Given),
|
||||
@ -602,7 +603,8 @@ set_vcard_t(R, _) ->
|
||||
{error, badarg};
|
||||
true ->
|
||||
mnesia:write(
|
||||
#vcard_search{user = User, luser = LUser,
|
||||
#vcard_search{us = US,
|
||||
user = User, luser = LUser,
|
||||
fn = FN, lfn = LFN,
|
||||
family = Family, lfamily = LFamily,
|
||||
given = Given, lgiven = LGiven,
|
||||
@ -625,16 +627,159 @@ reindex_vcards() ->
|
||||
mnesia:transaction(F).
|
||||
|
||||
|
||||
remove_user(User) ->
|
||||
remove_user(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
mnesia:delete({vcard, LUser}),
|
||||
lists:foreach(fun(R) ->
|
||||
mnesia:delete_object(R)
|
||||
end,
|
||||
mnesia:index_read(vcard_search,
|
||||
LUser,
|
||||
#vcard_search.luser))
|
||||
mnesia:delete({vcard, US}),
|
||||
mnesia:delete({vcard_search, US})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
|
||||
update_tables() ->
|
||||
update_vcard_table(),
|
||||
update_vcard_search_table().
|
||||
|
||||
update_vcard_table() ->
|
||||
Fields = record_info(fields, vcard),
|
||||
case mnesia:table_info(vcard, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[user, vcard] ->
|
||||
?INFO_MSG("Converting vcard table from "
|
||||
"{user, vcard} format", []),
|
||||
Host = ?MYNAME,
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_vcard_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, vcard},
|
||||
{attributes, record_info(fields, vcard)}]),
|
||||
mnesia:transform_table(vcard, ignore, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_vcard_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#vcard{us = U} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_vcard_tmp_table,
|
||||
R#vcard{us = {U, Host}})
|
||||
end, ok, vcard)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(vcard),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(vcard),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, mod_vcard_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(mod_vcard_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating vcard table", []),
|
||||
mnesia:transform_table(vcard, ignore, Fields)
|
||||
end.
|
||||
|
||||
|
||||
update_vcard_search_table() ->
|
||||
Fields = record_info(fields, vcard_search),
|
||||
case mnesia:table_info(vcard_search, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[user, luser,
|
||||
fn, lfn,
|
||||
family, lfamily,
|
||||
given, lgiven,
|
||||
middle, lmiddle,
|
||||
nickname, lnickname,
|
||||
bday, lbday,
|
||||
ctry, lctry,
|
||||
locality, llocality,
|
||||
email, lemail,
|
||||
orgname, lorgname,
|
||||
orgunit, lorgunit] ->
|
||||
?INFO_MSG("Converting vcard_search table from "
|
||||
"{user, luser, fn, lfn, family, lfamily, given, lgiven, middle, lmiddle, nickname, lnickname, bday, lbday, ctry, lctry, locality, llocality, email, lemail, orgname, lorgname, orgunit, lorgunit} format", []),
|
||||
Host = ?MYNAME,
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_vcard_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, vcard_search},
|
||||
{attributes, record_info(fields, vcard_search)}]),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_vcard_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun({vcard_search,
|
||||
User, LUser,
|
||||
FN, LFN,
|
||||
Family, LFamily,
|
||||
Given, LGiven,
|
||||
Middle, LMiddle,
|
||||
Nickname, LNickname,
|
||||
BDay, LBDay,
|
||||
CTRY, LCTRY,
|
||||
Locality, LLocality,
|
||||
EMail, LEMail,
|
||||
OrgName, LOrgName,
|
||||
OrgUnit, LOrgUnit
|
||||
}, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_vcard_tmp_table,
|
||||
#vcard_search{
|
||||
us = {LUser, Host},
|
||||
user = {User, Host},
|
||||
luser = LUser,
|
||||
fn = FN, lfn = LFN,
|
||||
family = Family, lfamily = LFamily,
|
||||
given = Given, lgiven = LGiven,
|
||||
middle = Middle, lmiddle = LMiddle,
|
||||
nickname = Nickname, lnickname = LNickname,
|
||||
bday = BDay, lbday = LBDay,
|
||||
ctry = CTRY, lctry = LCTRY,
|
||||
locality = Locality, llocality = LLocality,
|
||||
email = EMail, lemail = LEMail,
|
||||
orgname = OrgName, lorgname = LOrgName,
|
||||
orgunit = OrgUnit, lorgunit = LOrgUnit
|
||||
})
|
||||
end, ok, vcard_search)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
lists:foreach(fun(I) ->
|
||||
mnesia:del_table_index(
|
||||
vcard_search,
|
||||
element(I, {vcard_search,
|
||||
user, luser,
|
||||
fn, lfn,
|
||||
family, lfamily,
|
||||
given, lgiven,
|
||||
middle, lmiddle,
|
||||
nickname, lnickname,
|
||||
bday, lbday,
|
||||
ctry, lctry,
|
||||
locality, llocality,
|
||||
email, lemail,
|
||||
orgname, lorgname,
|
||||
orgunit, lorgunit}))
|
||||
end, mnesia:table_info(vcard_search, index)),
|
||||
mnesia:clear_table(vcard_search),
|
||||
mnesia:transform_table(vcard_search, ignore, Fields),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(vcard_search),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, mod_vcard_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(mod_vcard_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating vcard_search table", []),
|
||||
mnesia:transform_table(vcard_search, ignore, Fields)
|
||||
end.
|
||||
|
||||
|
@ -195,18 +195,23 @@ process_request(#state{request_method = 'GET',
|
||||
request_lang = Lang,
|
||||
use_http_poll = UseHTTPPoll,
|
||||
use_web_admin = UseWebAdmin}) ->
|
||||
User = case Auth of
|
||||
{U, P} ->
|
||||
case ejabberd_auth:check_password(U, P) of
|
||||
true ->
|
||||
U;
|
||||
false ->
|
||||
unauthorized
|
||||
end;
|
||||
_ ->
|
||||
undefined
|
||||
end,
|
||||
case User of
|
||||
US = case Auth of
|
||||
{SJID, P} ->
|
||||
case jlib:string_to_jid(SJID) of
|
||||
error ->
|
||||
unauthorized;
|
||||
#jid{user = U, server = S} ->
|
||||
case ejabberd_auth:check_password(U, S, P) of
|
||||
true ->
|
||||
{U, S};
|
||||
false ->
|
||||
unauthorized
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
undefined
|
||||
end,
|
||||
case US of
|
||||
unauthorized ->
|
||||
make_xhtml_output(
|
||||
401,
|
||||
@ -228,7 +233,7 @@ process_request(#state{request_method = 'GET',
|
||||
Request = #request{method = 'GET',
|
||||
path = LPath,
|
||||
q = LQuery,
|
||||
user = User,
|
||||
us = US,
|
||||
lang = Lang},
|
||||
case ejabberd_web:process_get({UseHTTPPoll, UseWebAdmin},
|
||||
Request) of
|
||||
@ -256,18 +261,23 @@ process_request(#state{request_method = 'POST',
|
||||
use_http_poll = UseHTTPPoll,
|
||||
use_web_admin = UseWebAdmin} = State)
|
||||
when is_integer(Len) ->
|
||||
User = case Auth of
|
||||
{U, P} ->
|
||||
case ejabberd_auth:check_password(U, P) of
|
||||
true ->
|
||||
U;
|
||||
false ->
|
||||
unauthorized
|
||||
end;
|
||||
_ ->
|
||||
undefined
|
||||
end,
|
||||
case User of
|
||||
US = case Auth of
|
||||
{SJID, P} ->
|
||||
case jlib:string_to_jid(SJID) of
|
||||
error ->
|
||||
unauthorized;
|
||||
#jid{user = U, server = S} ->
|
||||
case ejabberd_auth:check_password(U, S, P) of
|
||||
true ->
|
||||
{U, S};
|
||||
false ->
|
||||
unauthorized
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
undefined
|
||||
end,
|
||||
case US of
|
||||
unauthorized ->
|
||||
make_xhtml_output(
|
||||
401,
|
||||
@ -297,7 +307,7 @@ process_request(#state{request_method = 'POST',
|
||||
Request = #request{method = 'POST',
|
||||
path = LPath,
|
||||
q = LQuery,
|
||||
user = User,
|
||||
us = US,
|
||||
data = Data,
|
||||
lang = Lang},
|
||||
case ejabberd_web:process_get({UseHTTPPoll, UseWebAdmin},
|
||||
|
@ -9,7 +9,7 @@
|
||||
-record(request, {method,
|
||||
path,
|
||||
q = [],
|
||||
user,
|
||||
us,
|
||||
lang = "",
|
||||
data = ""
|
||||
}).
|
||||
|
404
src/web/ejabberd_http1.erl
Normal file
404
src/web/ejabberd_http1.erl
Normal file
@ -0,0 +1,404 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% 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$ ').
|
||||
|
||||
-behaviour(gen_fsm).
|
||||
|
||||
%% External exports
|
||||
-export([start/2,
|
||||
start_link/2]).
|
||||
|
||||
%% gen_fsm callbacks
|
||||
-export([init/1,
|
||||
wait_for_headers/2,
|
||||
handle_event/3,
|
||||
handle_sync_event/4,
|
||||
code_change/4,
|
||||
handle_info/3,
|
||||
terminate/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-define(DICT, dict).
|
||||
|
||||
-record(state, {socket,
|
||||
request_method,
|
||||
request_path,
|
||||
request_auth
|
||||
}).
|
||||
|
||||
|
||||
-define(DBGFSM, true).
|
||||
|
||||
-ifdef(DBGFSM).
|
||||
-define(FSMOPTS, [{debug, [trace]}]).
|
||||
-else.
|
||||
-define(FSMOPTS, []).
|
||||
-endif.
|
||||
|
||||
-define(XHTML_DOCTYPE,
|
||||
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" "
|
||||
"\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n").
|
||||
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start(SockData, Opts) ->
|
||||
supervisor:start_child(ejabberd_http_sup, [SockData, Opts]).
|
||||
|
||||
start_link(SockData, Opts) ->
|
||||
gen_fsm:start_link(ejabberd_http, [SockData, Opts], ?FSMOPTS).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Callback functions from gen_fsm
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: init/1
|
||||
%% Returns: {ok, StateName, StateData} |
|
||||
%% {ok, StateName, StateData, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, StopReason}
|
||||
%%----------------------------------------------------------------------
|
||||
init([{SockMod, Socket}, Opts]) ->
|
||||
?INFO_MSG("started: ~p", [{SockMod, Socket}]),
|
||||
case SockMod of
|
||||
gen_tcp ->
|
||||
inet:setopts(Socket, [{packet, http}, {active, true}, {recbuf, 0}]);
|
||||
ssl ->
|
||||
ssl:setopts(Socket, [{packet, http}, {active, true}])
|
||||
end,
|
||||
{ok, wait_for_headers, #state{socket = Socket}}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/2
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
wait_for_headers(_, StateData) ->
|
||||
{next_state, wait_for_headers, StateData}.
|
||||
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {reply, Reply, NextStateName, NextStateData} |
|
||||
%% {reply, Reply, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData} |
|
||||
%% {stop, Reason, Reply, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
%state_name(Event, From, StateData) ->
|
||||
% Reply = ok,
|
||||
% {reply, Reply, state_name, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_event/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_event(_Event, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_sync_event/4
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {reply, Reply, NextStateName, NextStateData} |
|
||||
%% {reply, Reply, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData} |
|
||||
%% {stop, Reason, Reply, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_sync_event(_Event, _From, StateName, StateData) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, StateName, StateData}.
|
||||
|
||||
code_change(_OldVsn, StateName, StateData, _Extra) ->
|
||||
{ok, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_info/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_info({http_request, _Socket, Method, Path, _Version},
|
||||
StateName, StateData) ->
|
||||
{next_state, StateName, StateData#state{request_method = Method,
|
||||
request_path = Path}};
|
||||
handle_info({http_header, _Socket, _, 'Authorization', _, Auth},
|
||||
StateName, StateData) ->
|
||||
{next_state, StateName,
|
||||
StateData#state{request_auth = parse_auth(Auth)}};
|
||||
handle_info({http_eoh, _Socket}, StateName, StateData) ->
|
||||
process_request(StateData),
|
||||
{stop, normal, StateData};
|
||||
|
||||
handle_info({tcp_closed, _Socket}, StateName, StateData) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
handle_info({tcp_error, _Socket, _Reason}, StateName, StateData) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
handle_info(_, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: terminate/3
|
||||
%% Purpose: Shutdown the fsm
|
||||
%% Returns: any
|
||||
%%----------------------------------------------------------------------
|
||||
terminate(Reason, _StateName, StateData) ->
|
||||
?INFO_MSG("terminated: ~p", [Reason]),
|
||||
gen_tcp:close(StateData#state.socket),
|
||||
ok.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
send_text(State, Text) ->
|
||||
gen_tcp:send(State#state.socket, Text).
|
||||
|
||||
|
||||
process_request(#state{request_method = 'GET',
|
||||
request_path = {abs_path, Path},
|
||||
request_auth = undefined} = State) ->
|
||||
Out = make_xhtml_output(
|
||||
401,
|
||||
[{"WWW-Authenticate", "basic realm=\"ejabberd\""}],
|
||||
make_xhtml([{xmlelement, "h1", [],
|
||||
[{xmlcdata, "403 Unauthorized"}]}])),
|
||||
send_text(State, Out),
|
||||
ok;
|
||||
|
||||
process_request(#state{request_method = 'GET',
|
||||
request_path = {abs_path, Path},
|
||||
request_auth = {User, Pass}} = State) ->
|
||||
Out = make_xhtml_output(
|
||||
200,
|
||||
[],
|
||||
make_xhtml([{xmlelement, "h1", [],
|
||||
[{xmlcdata, "Welcome " ++ User}]}])),
|
||||
send_text(State, Out),
|
||||
ok;
|
||||
|
||||
process_request(State) ->
|
||||
todo.
|
||||
|
||||
|
||||
|
||||
make_xhtml(Els) ->
|
||||
{xmlelement, "html", [{"xmlns", "http://www.w3.org/1999/xhtml"},
|
||||
{"xml:lang", "en"},
|
||||
{"lang", "en"}],
|
||||
[{xmlelement, "head", [],
|
||||
[{xmlelement, "meta", [{"http-equiv", "Content-Type"},
|
||||
{"content", "text/html; charset=utf-8"}], []}]},
|
||||
{xmlelement, "body", [], Els}
|
||||
]}.
|
||||
|
||||
|
||||
make_xhtml_output(Status, Headers, XHTML) ->
|
||||
Data = list_to_binary([?XHTML_DOCTYPE, xml:element_to_string(XHTML)]),
|
||||
Headers1 = [{"Content-Type", "text/html; charset=utf-8"},
|
||||
{"Content-Length", integer_to_list(size(Data))} | Headers],
|
||||
H = lists:map(fun({Attr, Val}) ->
|
||||
[Attr, ": ", Val, "\r\n"]
|
||||
end, Headers1),
|
||||
SL = ["HTTP/1.1 ", integer_to_list(Status), " ",
|
||||
code_to_phrase(Status), "\r\n"],
|
||||
[SL, H, "\r\n", Data].
|
||||
|
||||
|
||||
|
||||
% Code below is taken (with some modifications) from the yaws webserver, which
|
||||
% is distributed under the folowing license:
|
||||
%
|
||||
%This software (the yaws webserver) is free software.
|
||||
%Parts of this software is Copyright (c) Claes Wikstrom <klacke@hyber.org>
|
||||
%Any use or misuse of the source code is hereby freely allowed.
|
||||
%
|
||||
%1. Redistributions of source code must retain the above copyright
|
||||
% notice as well as this list of conditions.
|
||||
%
|
||||
%2. Redistributions in binary form must reproduce the above copyright
|
||||
% notice as well as this list of conditions.
|
||||
|
||||
|
||||
%% url decode the path and return {Path, QueryPart}
|
||||
|
||||
url_decode_q_split(Path) ->
|
||||
url_decode_q_split(Path, []).
|
||||
|
||||
url_decode_q_split([$%, $C, $2, $%, Hi, Lo | Tail], Ack) ->
|
||||
Hex = hex_to_integer([Hi, Lo]),
|
||||
url_decode_q_split(Tail, [Hex|Ack]);
|
||||
url_decode_q_split([$%, $C, $3, $%, Hi, Lo | Tail], Ack) when Hi > $9 ->
|
||||
Hex = hex_to_integer([Hi+4, Lo]),
|
||||
url_decode_q_split(Tail, [Hex|Ack]);
|
||||
url_decode_q_split([$%, $C, $3, $%, Hi, Lo | Tail], Ack) when Hi < $A ->
|
||||
Hex = hex_to_integer([Hi+4+7, Lo]),
|
||||
url_decode_q_split(Tail, [Hex|Ack]);
|
||||
url_decode_q_split([$%, Hi, Lo | Tail], Ack) ->
|
||||
Hex = hex_to_integer([Hi, Lo]),
|
||||
url_decode_q_split(Tail, [Hex|Ack]);
|
||||
url_decode_q_split([$?|T], Ack) ->
|
||||
%% Don't decode the query string here, that is parsed separately.
|
||||
{path_norm_reverse(Ack), T};
|
||||
url_decode_q_split([H|T], Ack) ->
|
||||
url_decode_q_split(T, [H|Ack]);
|
||||
url_decode_q_split([], Ack) ->
|
||||
{path_norm_reverse(Ack), []}.
|
||||
|
||||
path_norm_reverse("/" ++ T) -> start_dir(0, "/", T);
|
||||
path_norm_reverse( T) -> start_dir(0, "", T).
|
||||
|
||||
start_dir(N, Path, ".." ) -> rest_dir(N, Path, "");
|
||||
start_dir(N, Path, "/" ++ T ) -> start_dir(N , Path, T);
|
||||
start_dir(N, Path, "./" ++ T ) -> start_dir(N , Path, T);
|
||||
start_dir(N, Path, "../" ++ T ) -> start_dir(N + 1, Path, T);
|
||||
start_dir(N, Path, T ) -> rest_dir (N , Path, T).
|
||||
|
||||
rest_dir (_N, Path, [] ) -> case Path of
|
||||
[] -> "/";
|
||||
_ -> Path
|
||||
end;
|
||||
rest_dir (0, Path, [ $/ | T ] ) -> start_dir(0 , [ $/ | Path ], T);
|
||||
rest_dir (N, Path, [ $/ | T ] ) -> start_dir(N - 1, Path , T);
|
||||
rest_dir (0, Path, [ H | T ] ) -> rest_dir (0 , [ H | Path ], T);
|
||||
rest_dir (N, Path, [ _H | T ] ) -> rest_dir (N , Path , T).
|
||||
|
||||
|
||||
%% hex_to_integer
|
||||
|
||||
|
||||
hex_to_integer(Hex) ->
|
||||
case catch erlang:list_to_integer(Hex, 16) of
|
||||
{'EXIT', _} ->
|
||||
old_hex_to_integer(Hex);
|
||||
X ->
|
||||
X
|
||||
end.
|
||||
|
||||
|
||||
old_hex_to_integer(Hex) ->
|
||||
DEHEX = fun (H) when H >= $a, H =< $f -> H - $a + 10;
|
||||
(H) when H >= $A, H =< $F -> H - $A + 10;
|
||||
(H) when H >= $0, H =< $9 -> H - $0
|
||||
end,
|
||||
lists:foldl(fun(E, Acc) -> Acc*16+DEHEX(E) end, 0, Hex).
|
||||
|
||||
code_to_phrase(100) -> "Continue";
|
||||
code_to_phrase(101) -> "Switching Protocols ";
|
||||
code_to_phrase(200) -> "OK";
|
||||
code_to_phrase(201) -> "Created";
|
||||
code_to_phrase(202) -> "Accepted";
|
||||
code_to_phrase(203) -> "Non-Authoritative Information";
|
||||
code_to_phrase(204) -> "No Content";
|
||||
code_to_phrase(205) -> "Reset Content";
|
||||
code_to_phrase(206) -> "Partial Content";
|
||||
code_to_phrase(300) -> "Multiple Choices";
|
||||
code_to_phrase(301) -> "Moved Permanently";
|
||||
code_to_phrase(302) -> "Found";
|
||||
code_to_phrase(303) -> "See Other";
|
||||
code_to_phrase(304) -> "Not Modified";
|
||||
code_to_phrase(305) -> "Use Proxy";
|
||||
code_to_phrase(306) -> "(Unused)";
|
||||
code_to_phrase(307) -> "Temporary Redirect";
|
||||
code_to_phrase(400) -> "Bad Request";
|
||||
code_to_phrase(401) -> "Unauthorized";
|
||||
code_to_phrase(402) -> "Payment Required";
|
||||
code_to_phrase(403) -> "Forbidden";
|
||||
code_to_phrase(404) -> "Not Found";
|
||||
code_to_phrase(405) -> "Method Not Allowed";
|
||||
code_to_phrase(406) -> "Not Acceptable";
|
||||
code_to_phrase(407) -> "Proxy Authentication Required";
|
||||
code_to_phrase(408) -> "Request Timeout";
|
||||
code_to_phrase(409) -> "Conflict";
|
||||
code_to_phrase(410) -> "Gone";
|
||||
code_to_phrase(411) -> "Length Required";
|
||||
code_to_phrase(412) -> "Precondition Failed";
|
||||
code_to_phrase(413) -> "Request Entity Too Large";
|
||||
code_to_phrase(414) -> "Request-URI Too Long";
|
||||
code_to_phrase(415) -> "Unsupported Media Type";
|
||||
code_to_phrase(416) -> "Requested Range Not Satisfiable";
|
||||
code_to_phrase(417) -> "Expectation Failed";
|
||||
code_to_phrase(500) -> "Internal Server Error";
|
||||
code_to_phrase(501) -> "Not Implemented";
|
||||
code_to_phrase(502) -> "Bad Gateway";
|
||||
code_to_phrase(503) -> "Service Unavailable";
|
||||
code_to_phrase(504) -> "Gateway Timeout";
|
||||
code_to_phrase(505) -> "HTTP Version Not Supported".
|
||||
|
||||
|
||||
parse_auth(Orig = "Basic " ++ Auth64) ->
|
||||
case decode_base64(Auth64) of
|
||||
{error, _Err} ->
|
||||
undefined;
|
||||
Auth ->
|
||||
case string:tokens(Auth, ":") of
|
||||
[User, Pass] ->
|
||||
{User, Pass};
|
||||
_ ->
|
||||
undefined
|
||||
end
|
||||
end;
|
||||
parse_auth(_) ->
|
||||
undefined.
|
||||
|
||||
|
||||
|
||||
decode_base64([]) ->
|
||||
[];
|
||||
decode_base64([Sextet1,Sextet2,$=,$=|Rest]) ->
|
||||
Bits2x6=
|
||||
(d(Sextet1) bsl 18) bor
|
||||
(d(Sextet2) bsl 12),
|
||||
Octet1=Bits2x6 bsr 16,
|
||||
[Octet1|decode_base64(Rest)];
|
||||
decode_base64([Sextet1,Sextet2,Sextet3,$=|Rest]) ->
|
||||
Bits3x6=
|
||||
(d(Sextet1) bsl 18) bor
|
||||
(d(Sextet2) bsl 12) bor
|
||||
(d(Sextet3) bsl 6),
|
||||
Octet1=Bits3x6 bsr 16,
|
||||
Octet2=(Bits3x6 bsr 8) band 16#ff,
|
||||
[Octet1,Octet2|decode_base64(Rest)];
|
||||
decode_base64([Sextet1,Sextet2,Sextet3,Sextet4|Rest]) ->
|
||||
Bits4x6=
|
||||
(d(Sextet1) bsl 18) bor
|
||||
(d(Sextet2) bsl 12) bor
|
||||
(d(Sextet3) bsl 6) bor
|
||||
d(Sextet4),
|
||||
Octet1=Bits4x6 bsr 16,
|
||||
Octet2=(Bits4x6 bsr 8) band 16#ff,
|
||||
Octet3=Bits4x6 band 16#ff,
|
||||
[Octet1,Octet2,Octet3|decode_base64(Rest)];
|
||||
decode_base64(_CatchAll) ->
|
||||
{error, bad_base64}.
|
||||
|
||||
d(X) when X >= $A, X =<$Z ->
|
||||
X-65;
|
||||
d(X) when X >= $a, X =<$z ->
|
||||
X-71;
|
||||
d(X) when X >= $0, X =<$9 ->
|
||||
X+4;
|
||||
d($+) -> 62;
|
||||
d($/) -> 63;
|
||||
d(_) -> 63.
|
||||
|
328
src/web/ejabberd_wcs.erl
Normal file
328
src/web/ejabberd_wcs.erl
Normal file
@ -0,0 +1,328 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_wcs.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Web Client Service
|
||||
%%% Created : 13 Jul 2004 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_wcs).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-behaviour(gen_fsm).
|
||||
|
||||
%% External exports
|
||||
-export([start_link/2,
|
||||
init/1,
|
||||
handle_event/3,
|
||||
handle_sync_event/4,
|
||||
code_change/4,
|
||||
handle_info/3,
|
||||
terminate/3,
|
||||
send/2,
|
||||
recv/3,
|
||||
close/1,
|
||||
process_request/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
-include("ejabberd_http.hrl").
|
||||
|
||||
-record(http_poll, {id, pid}).
|
||||
|
||||
-record(state, {id,
|
||||
key,
|
||||
output = "",
|
||||
input = "",
|
||||
waiting_input = false,
|
||||
timer}).
|
||||
|
||||
%-define(DBGFSM, true).
|
||||
|
||||
-ifdef(DBGFSM).
|
||||
-define(FSMOPTS, [{debug, [trace]}]).
|
||||
-else.
|
||||
-define(FSMOPTS, []).
|
||||
-endif.
|
||||
|
||||
-define(HTTP_POLL_TIMEOUT, 300000).
|
||||
-define(CT, {"Content-Type", "text/xml; charset=utf-8"}).
|
||||
-define(BAD_REQUEST, [?CT, {"Set-Cookie", "ID=-3:0; expires=-1"}]).
|
||||
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start(ID, Key) ->
|
||||
mnesia:create_table(http_poll,
|
||||
[{ram_copies, [node()]},
|
||||
{attributes, record_info(fields, http_poll)}]),
|
||||
supervisor:start_child(ejabberd_http_poll_sup, [ID, Key]).
|
||||
|
||||
start_link(ID, Key) ->
|
||||
gen_fsm:start_link(?MODULE, [ID, Key], ?FSMOPTS).
|
||||
|
||||
send({http_poll, FsmRef}, Packet) ->
|
||||
gen_fsm:sync_send_all_state_event(FsmRef, {send, Packet}).
|
||||
|
||||
recv({http_poll, FsmRef}, _Length, Timeout) ->
|
||||
gen_fsm:sync_send_all_state_event(FsmRef, recv, Timeout).
|
||||
|
||||
close({http_poll, FsmRef}) ->
|
||||
catch gen_fsm:sync_send_all_state_event(FsmRef, close).
|
||||
|
||||
|
||||
process_request(#request{path = [],
|
||||
data = Data} = Request) ->
|
||||
case catch parse_request(Data) of
|
||||
{ok, ID1, Key, NewKey, Packet} ->
|
||||
ID = if
|
||||
(ID1 == "0") or (ID1 == "mobile") ->
|
||||
NewID = sha:sha(term_to_binary({now(), make_ref()})),
|
||||
{ok, Pid} = start(NewID, ""),
|
||||
mnesia:transaction(
|
||||
fun() ->
|
||||
mnesia:write(#http_poll{id = NewID,
|
||||
pid = Pid})
|
||||
end),
|
||||
NewID;
|
||||
true ->
|
||||
ID1
|
||||
end,
|
||||
case http_put(ID, Key, NewKey, Packet) of
|
||||
{error, not_exists} ->
|
||||
{200, ?BAD_REQUEST, ""};
|
||||
{error, bad_key} ->
|
||||
{200, ?BAD_REQUEST, ""};
|
||||
ok ->
|
||||
receive
|
||||
after 100 -> ok
|
||||
end,
|
||||
case http_get(ID) of
|
||||
{error, not_exists} ->
|
||||
{200, [?BAD_REQUEST], ""};
|
||||
{ok, OutPacket} ->
|
||||
if
|
||||
ID == ID1 ->
|
||||
{200, [?CT], OutPacket};
|
||||
ID1 == "mobile" ->
|
||||
{200, [?CT], [ID, $\n, OutPacket]};
|
||||
true ->
|
||||
Cookie = "ID=" ++ ID ++ "; expires=-1",
|
||||
{200, [?CT, {"Set-Cookie", Cookie}],
|
||||
OutPacket}
|
||||
end
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
{200, [?CT, {"Set-Cookie", "ID=-2:0; expires=-1"}], ""}
|
||||
end.
|
||||
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Callback functions from gen_fsm
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: init/1
|
||||
%% Returns: {ok, StateName, StateData} |
|
||||
%% {ok, StateName, StateData, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, StopReason}
|
||||
%%----------------------------------------------------------------------
|
||||
init([ID, Key]) ->
|
||||
?INFO_MSG("started: ~p", [{ID, Key}]),
|
||||
Opts = [], % TODO
|
||||
ejabberd_c2s:start({?MODULE, {http_poll, self()}}, Opts),
|
||||
Timer = erlang:start_timer(?HTTP_POLL_TIMEOUT, self(), []),
|
||||
{ok, loop, #state{id = ID,
|
||||
key = Key,
|
||||
timer = Timer}}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/2
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {reply, Reply, NextStateName, NextStateData} |
|
||||
%% {reply, Reply, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData} |
|
||||
%% {stop, Reason, Reply, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
%state_name(Event, From, StateData) ->
|
||||
% Reply = ok,
|
||||
% {reply, Reply, state_name, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_event/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_event(Event, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_sync_event/4
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {reply, Reply, NextStateName, NextStateData} |
|
||||
%% {reply, Reply, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData} |
|
||||
%% {stop, Reason, Reply, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_sync_event({send, Packet}, From, StateName, StateData) ->
|
||||
Output = [StateData#state.output | Packet],
|
||||
Reply = ok,
|
||||
{reply, Reply, StateName, StateData#state{output = Output}};
|
||||
|
||||
handle_sync_event(recv, From, StateName, StateData) ->
|
||||
case StateData#state.input of
|
||||
"" ->
|
||||
{next_state, StateName, StateData#state{waiting_input = From}};
|
||||
Input ->
|
||||
Reply = {ok, list_to_binary(Input)},
|
||||
{reply, Reply, StateName, StateData#state{input = "",
|
||||
waiting_input = false}}
|
||||
end;
|
||||
|
||||
handle_sync_event(stop, From, StateName, StateData) ->
|
||||
Reply = ok,
|
||||
{stop, normal, Reply, StateData};
|
||||
|
||||
handle_sync_event({http_put, Key, NewKey, Packet},
|
||||
From, StateName, StateData) ->
|
||||
Allow = case StateData#state.key of
|
||||
"" ->
|
||||
true;
|
||||
OldKey ->
|
||||
NextKey = jlib:encode_base64(
|
||||
binary_to_list(crypto:sha(Key))),
|
||||
if
|
||||
OldKey == NextKey ->
|
||||
true;
|
||||
true ->
|
||||
false
|
||||
end
|
||||
end,
|
||||
if
|
||||
Allow ->
|
||||
case StateData#state.waiting_input of
|
||||
false ->
|
||||
Input = [StateData#state.input | Packet],
|
||||
Reply = ok,
|
||||
{reply, Reply, StateName, StateData#state{input = Input,
|
||||
key = NewKey}};
|
||||
Receiver ->
|
||||
gen_fsm:reply(Receiver, {ok, list_to_binary(Packet)}),
|
||||
cancel_timer(StateData#state.timer),
|
||||
Timer = erlang:start_timer(?HTTP_POLL_TIMEOUT, self(), []),
|
||||
Reply = ok,
|
||||
{reply, Reply, StateName,
|
||||
StateData#state{waiting_input = false,
|
||||
key = NewKey,
|
||||
timer = Timer}}
|
||||
end;
|
||||
true ->
|
||||
Reply = {error, bad_key},
|
||||
{reply, Reply, StateName, StateData}
|
||||
end;
|
||||
|
||||
handle_sync_event(http_get, From, StateName, StateData) ->
|
||||
Reply = {ok, StateData#state.output},
|
||||
{reply, Reply, StateName, StateData#state{output = ""}};
|
||||
|
||||
handle_sync_event(Event, From, StateName, StateData) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, StateName, StateData}.
|
||||
|
||||
code_change(OldVsn, StateName, StateData, Extra) ->
|
||||
{ok, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_info/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_info({timeout, Timer, _}, StateName,
|
||||
#state{timer = Timer} = StateData) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
handle_info(_, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: terminate/3
|
||||
%% Purpose: Shutdown the fsm
|
||||
%% Returns: any
|
||||
%%----------------------------------------------------------------------
|
||||
terminate(Reason, StateName, StateData) ->
|
||||
mnesia:transaction(
|
||||
fun() ->
|
||||
mnesia:delete({http_poll, StateData#state.id})
|
||||
end),
|
||||
case StateData#state.waiting_input of
|
||||
false ->
|
||||
ok;
|
||||
Receiver ->
|
||||
gen_fsm:reply(Receiver, {error, closed})
|
||||
end,
|
||||
ok.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
|
||||
http_put(ID, Key, NewKey, Packet) ->
|
||||
case mnesia:dirty_read({http_poll, ID}) of
|
||||
[] ->
|
||||
{error, not_exists};
|
||||
[#http_poll{pid = FsmRef}] ->
|
||||
gen_fsm:sync_send_all_state_event(
|
||||
FsmRef, {http_put, Key, NewKey, Packet})
|
||||
end.
|
||||
|
||||
http_get(ID) ->
|
||||
case mnesia:dirty_read({http_poll, ID}) of
|
||||
[] ->
|
||||
{error, not_exists};
|
||||
[#http_poll{pid = FsmRef}] ->
|
||||
gen_fsm:sync_send_all_state_event(FsmRef, http_get)
|
||||
end.
|
||||
|
||||
|
||||
parse_request(Data) ->
|
||||
Comma = string:chr(Data, $,),
|
||||
Header = lists:sublist(Data, Comma - 1),
|
||||
Packet = lists:nthtail(Comma, Data),
|
||||
{ID, Key, NewKey} =
|
||||
case string:tokens(Header, ";") of
|
||||
[ID1] ->
|
||||
{ID1, "", ""};
|
||||
[ID1, Key1] ->
|
||||
{ID1, Key1, Key1};
|
||||
[ID1, Key1, NewKey1] ->
|
||||
{ID1, Key1, NewKey1}
|
||||
end,
|
||||
{ok, ID, Key, NewKey, Packet}.
|
||||
|
||||
|
||||
cancel_timer(Timer) ->
|
||||
erlang:cancel_timer(Timer),
|
||||
receive
|
||||
{timeout, Timer, _} ->
|
||||
ok
|
||||
after 0 ->
|
||||
ok
|
||||
end.
|
||||
|
@ -50,20 +50,20 @@ make_xhtml(Els) ->
|
||||
|
||||
|
||||
process_get({_, true},
|
||||
#request{user = User,
|
||||
#request{us = US,
|
||||
path = ["admin" | RPath],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
if
|
||||
User /= undefined ->
|
||||
case acl:match_rule(configure, jlib:make_jid(User, ?MYNAME, "")) of
|
||||
case US of
|
||||
{User, Server} ->
|
||||
case acl:match_rule(configure, jlib:make_jid(User, Server, "")) of
|
||||
deny ->
|
||||
{401, [], make_xhtml([?XC("h1", "Not Allowed")])};
|
||||
allow ->
|
||||
ejabberd_web_admin:process_admin(
|
||||
Request#request{path = RPath})
|
||||
end;
|
||||
true ->
|
||||
undefined ->
|
||||
{401,
|
||||
[{"WWW-Authenticate", "basic realm=\"ejabberd\""}],
|
||||
ejabberd_web:make_xhtml([{xmlelement, "h1", [],
|
||||
@ -71,10 +71,10 @@ process_get({_, true},
|
||||
end;
|
||||
|
||||
process_get({true, _},
|
||||
#request{user = User,
|
||||
#request{us = _US,
|
||||
path = ["http-poll" | RPath],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
q = _Query,
|
||||
lang = _Lang} = Request) ->
|
||||
ejabberd_http_poll:process_request(Request#request{path = RPath});
|
||||
|
||||
process_get(_, _Request) ->
|
||||
|
@ -5,8 +5,8 @@
|
||||
%%% Created : 9 Apr 2004 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Copyright (c) 2004 Alexey Shchepin
|
||||
%%% Copyright (c) 2004 Process One
|
||||
%%% Copyright (c) 2004-2005 Alexey Shchepin
|
||||
%%% Copyright (c) 2004-2005 Process One
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_web_admin).
|
||||
@ -119,7 +119,7 @@ make_xhtml(Els, Lang) ->
|
||||
[?XE("tbody",
|
||||
[?XE("tr",
|
||||
[?XCT("td",
|
||||
"ejabberd (c) 2002-2005 Alexey Shchepin, 2004 Process One")
|
||||
"ejabberd (c) 2002-2005 Alexey Shchepin, 2004-2005 Process One")
|
||||
])])
|
||||
])])])])])])
|
||||
]}}.
|
||||
@ -304,13 +304,13 @@ input[type=submit] {
|
||||
}
|
||||
|
||||
textarea {
|
||||
border: 1px solid #93a6c7;
|
||||
color: #556655;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #d6760e;
|
||||
color: #723202;
|
||||
background-color: #fff2e8;
|
||||
vertical-align: middle;
|
||||
margin-top: 7px;
|
||||
margin-left: 7px;
|
||||
margin-right: 7px;
|
||||
/*margin-left: 7px;
|
||||
margin-right: 7px;*/
|
||||
margin-bottom: 5px;
|
||||
padding: 0.1em;
|
||||
}
|
||||
@ -494,10 +494,10 @@ empty() ->
|
||||
jlib:decode_base64(
|
||||
"R0lGODlhAQABAIAAAP///////yH+FUNyZWF0ZWQgd2l0aCBUaGUgR0lNUAAh+QQBCgABACwAAAAAAQABAAACAkwBADs=").
|
||||
|
||||
process_admin(#request{user = User,
|
||||
path = [],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
process_admin(#request{us = US,
|
||||
path = [],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
make_xhtml([?XCT("h1", "ejabberd administration"),
|
||||
?XE("ul",
|
||||
[?LI([?ACT("acls/", "Access Control Lists"), ?C(" "),
|
||||
@ -511,31 +511,31 @@ process_admin(#request{user = User,
|
||||
])
|
||||
], Lang);
|
||||
|
||||
process_admin(#request{user = User,
|
||||
process_admin(#request{us = US,
|
||||
path = ["style.css"],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
{200, [{"Content-Type", "text/css"}], css()};
|
||||
|
||||
process_admin(#request{user = User,
|
||||
process_admin(#request{us = US,
|
||||
path = ["logo.png"],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
{200, [{"Content-Type", "image/png"}], logo()};
|
||||
|
||||
process_admin(#request{user = User,
|
||||
process_admin(#request{us = US,
|
||||
path = ["logo-fill.png"],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
{200, [{"Content-Type", "image/png"}], logo_fill()};
|
||||
|
||||
process_admin(#request{user = User,
|
||||
process_admin(#request{us = US,
|
||||
path = ["1x1tr.gif"],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
{200, [{"Content-Type", "image/gif"}], empty()};
|
||||
|
||||
process_admin(#request{user = User,
|
||||
process_admin(#request{us = US,
|
||||
path = ["acls-raw"],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
@ -578,7 +578,7 @@ process_admin(#request{user = User,
|
||||
], Lang);
|
||||
|
||||
process_admin(#request{method = Method,
|
||||
user = User,
|
||||
us = US,
|
||||
path = ["acls"],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
@ -618,7 +618,7 @@ process_admin(#request{method = Method,
|
||||
])
|
||||
], Lang);
|
||||
|
||||
process_admin(#request{user = User,
|
||||
process_admin(#request{us = US,
|
||||
path = ["access-raw"],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
@ -686,7 +686,7 @@ process_admin(#request{user = User,
|
||||
], Lang);
|
||||
|
||||
process_admin(#request{method = Method,
|
||||
user = User,
|
||||
us = US,
|
||||
path = ["access"],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
@ -722,7 +722,7 @@ process_admin(#request{method = Method,
|
||||
], Lang);
|
||||
|
||||
process_admin(#request{method = Method,
|
||||
user = User,
|
||||
us = US,
|
||||
path = ["access", SName],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
@ -761,63 +761,63 @@ process_admin(#request{method = Method,
|
||||
])
|
||||
], Lang);
|
||||
|
||||
process_admin(#request{user = User,
|
||||
process_admin(#request{us = US,
|
||||
path = ["users"],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
Res = list_users(Query, Lang),
|
||||
make_xhtml([?XCT("h1", "ejabberd users")] ++ Res, Lang);
|
||||
|
||||
process_admin(#request{user = User,
|
||||
process_admin(#request{us = US,
|
||||
path = ["users", Diap],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
Res = list_users_in_diapason(Diap, Lang),
|
||||
make_xhtml([?XCT("h1", "ejabberd users")] ++ Res, Lang);
|
||||
|
||||
process_admin(#request{user = User,
|
||||
process_admin(#request{us = US,
|
||||
path = ["online-users"],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
Res = list_online_users(Lang),
|
||||
make_xhtml([?XCT("h1", "ejabberd users")] ++ Res, Lang);
|
||||
|
||||
process_admin(#request{user = User,
|
||||
process_admin(#request{us = US,
|
||||
path = ["stats"],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
Res = get_stats(Lang),
|
||||
make_xhtml([?XCT("h1", "ejabberd stats")] ++ Res, Lang);
|
||||
|
||||
process_admin(#request{user = User,
|
||||
process_admin(#request{us = US,
|
||||
path = ["user", U],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
Res = user_info(U, Query, Lang),
|
||||
make_xhtml(Res, Lang);
|
||||
|
||||
process_admin(#request{user = User,
|
||||
process_admin(#request{us = US,
|
||||
path = ["user", U, "queue"],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
Res = user_queue(U, Query, Lang),
|
||||
make_xhtml(Res, Lang);
|
||||
|
||||
process_admin(#request{user = User,
|
||||
process_admin(#request{us = US,
|
||||
path = ["user", U, "roster"],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
Res = user_roster(U, Query, Lang, true),
|
||||
make_xhtml(Res, Lang);
|
||||
|
||||
process_admin(#request{user = User,
|
||||
process_admin(#request{us = US,
|
||||
path = ["nodes"],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
Res = get_nodes(Lang),
|
||||
make_xhtml(Res, Lang);
|
||||
|
||||
process_admin(#request{user = User,
|
||||
process_admin(#request{us = US,
|
||||
path = ["node", SNode | NPath],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
@ -829,6 +829,20 @@ process_admin(#request{user = User,
|
||||
make_xhtml(Res, Lang)
|
||||
end;
|
||||
|
||||
process_admin(#request{us = US,
|
||||
path = ["shared-roster"],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
Res = list_shared_roster_groups(Query, Lang),
|
||||
make_xhtml(Res, Lang);
|
||||
|
||||
process_admin(#request{us = US,
|
||||
path = ["shared-roster", Group],
|
||||
q = Query,
|
||||
lang = Lang} = Request) ->
|
||||
Res = shared_roster_group(Group, Query, Lang),
|
||||
make_xhtml(Res, Lang);
|
||||
|
||||
process_admin(#request{lang = Lang}) ->
|
||||
setelement(1, make_xhtml([?XC("h1", "Not found")], Lang), 404).
|
||||
|
||||
@ -1065,7 +1079,7 @@ parse_access_rule(Text) ->
|
||||
list_users(Query, Lang) ->
|
||||
Res = list_users_parse_query(Query),
|
||||
Users = ejabberd_auth:dirty_get_registered_users(),
|
||||
SUsers = lists:sort(Users),
|
||||
SUsers = lists:sort([{S, U} || {U, S} <- Users]),
|
||||
FUsers =
|
||||
case length(SUsers) of
|
||||
N when N =< 100 ->
|
||||
@ -1077,11 +1091,12 @@ list_users(Query, Lang) ->
|
||||
fun(K) ->
|
||||
L = K + M - 1,
|
||||
Node = integer_to_list(K) ++ "-" ++ integer_to_list(L),
|
||||
Last = if L < N -> lists:nth(L, SUsers);
|
||||
true -> lists:last(SUsers)
|
||||
Last = if L < N -> su_to_list(lists:nth(L, SUsers));
|
||||
true -> su_to_list(lists:last(SUsers))
|
||||
end,
|
||||
Name =
|
||||
lists:nth(K, SUsers) ++ [$\s, 226, 128, 148, $\s] ++
|
||||
su_to_list(lists:nth(K, SUsers)) ++
|
||||
[$\s, 226, 128, 148, $\s] ++
|
||||
Last,
|
||||
[?AC(Node ++ "/", Name), ?BR]
|
||||
end, lists:seq(1, N, M))
|
||||
@ -1131,7 +1146,7 @@ list_users_parse_query(Query) ->
|
||||
|
||||
list_users_in_diapason(Diap, Lang) ->
|
||||
Users = ejabberd_auth:dirty_get_registered_users(),
|
||||
SUsers = lists:sort(Users),
|
||||
SUsers = lists:sort([{S, U} || {U, S} <- Users]),
|
||||
{ok, [S1, S2]} = regexp:split(Diap, "-"),
|
||||
N1 = list_to_integer(S1),
|
||||
N2 = list_to_integer(S2),
|
||||
@ -1147,14 +1162,15 @@ list_given_users(Users, Prefix, Lang) ->
|
||||
?XCT("td", "Last Activity")])]),
|
||||
?XE("tbody",
|
||||
lists:map(
|
||||
fun(User) ->
|
||||
QueueLen = length(mnesia:dirty_read({offline_msg, User})),
|
||||
fun(SU = {Server, User}) ->
|
||||
US = {User, Server},
|
||||
QueueLen = length(mnesia:dirty_read({offline_msg, US})),
|
||||
FQueueLen = [?AC(Prefix ++ "user/" ++ User ++ "/queue/",
|
||||
integer_to_list(QueueLen))],
|
||||
FLast =
|
||||
case ejabberd_sm:get_user_resources(User) of
|
||||
case ejabberd_sm:get_user_resources(User, Server) of
|
||||
[] ->
|
||||
case mnesia:dirty_read({last_activity, User}) of
|
||||
case mnesia:dirty_read({last_activity, US}) of
|
||||
[] ->
|
||||
?T("Never");
|
||||
[E] ->
|
||||
@ -1173,13 +1189,20 @@ list_given_users(Users, Prefix, Lang) ->
|
||||
?T("Online")
|
||||
end,
|
||||
?XE("tr",
|
||||
[?XE("td", [?AC(Prefix ++ "user/" ++ User ++ "/",
|
||||
User)]),
|
||||
[?XE("td", [?AC(Prefix ++ "user/" ++
|
||||
us_to_list(US) ++ "/",
|
||||
us_to_list(US))]),
|
||||
?XE("td", FQueueLen),
|
||||
?XC("td", FLast)])
|
||||
end, Users)
|
||||
)]).
|
||||
|
||||
us_to_list({User, Server}) ->
|
||||
jlib:jid_to_string({User, Server, ""}).
|
||||
|
||||
su_to_list({Server, User}) ->
|
||||
jlib:jid_to_string({User, Server, ""}).
|
||||
|
||||
|
||||
get_stats(Lang) ->
|
||||
OnlineUsers = mnesia:table_info(presence, size),
|
||||
@ -1206,17 +1229,20 @@ get_stats(Lang) ->
|
||||
|
||||
|
||||
list_online_users(_Lang) ->
|
||||
Users = lists:map(fun({U, R}) -> U end,
|
||||
ejabberd_sm:dirty_get_sessions_list()),
|
||||
Users = [{S, U} || {U, S, R} <- ejabberd_sm:dirty_get_sessions_list()],
|
||||
SUsers = lists:usort(Users),
|
||||
lists:flatmap(
|
||||
fun(U) ->
|
||||
[?AC("../user/" ++ U ++ "/", U), ?BR]
|
||||
fun(SU) ->
|
||||
[?AC("../user/" ++ su_to_list(SU) ++ "/", su_to_list(SU)), ?BR]
|
||||
end, SUsers).
|
||||
|
||||
user_info(User, Query, Lang) ->
|
||||
Res = user_parse_query(User, Query),
|
||||
Resources = ejabberd_sm:get_user_resources(User),
|
||||
user_info(SUser, Query, Lang) ->
|
||||
UJID = jlib:string_to_jid(SUser),
|
||||
User = UJID#jid.user,
|
||||
Server = UJID#jid.server,
|
||||
US = {UJID#jid.luser, UJID#jid.lserver},
|
||||
Res = user_parse_query(User, Server, Query),
|
||||
Resources = ejabberd_sm:get_user_resources(User, Server),
|
||||
FResources =
|
||||
case Resources of
|
||||
[] ->
|
||||
@ -1227,13 +1253,13 @@ user_info(User, Query, Lang) ->
|
||||
?LI([?C(R)])
|
||||
end, lists:sort(Resources)))]
|
||||
end,
|
||||
Password = ejabberd_auth:get_password_s(User),
|
||||
Password = ejabberd_auth:get_password_s(User, Server),
|
||||
FPassword = [?INPUT("password", "password", Password), ?C(" "),
|
||||
?INPUTT("submit", "chpassword", "Change Password")],
|
||||
QueueLen = length(mnesia:dirty_read({offline_msg, User})),
|
||||
QueueLen = length(mnesia:dirty_read({offline_msg, US})),
|
||||
FQueueLen = [?AC("queue/",
|
||||
integer_to_list(QueueLen))],
|
||||
[?XC("h1", ?T("User ") ++ User)] ++
|
||||
[?XC("h1", ?T("User ") ++ us_to_list(US))] ++
|
||||
case Res of
|
||||
ok -> [?CT("submitted"), ?P];
|
||||
error -> [?CT("bad format"), ?P];
|
||||
@ -1247,14 +1273,14 @@ user_info(User, Query, Lang) ->
|
||||
[?BR, ?INPUTT("submit", "removeuser", "Remove User")])].
|
||||
|
||||
|
||||
user_parse_query(User, Query) ->
|
||||
user_parse_query(User, Server, Query) ->
|
||||
case lists:keysearch("chpassword", 1, Query) of
|
||||
{value, _} ->
|
||||
case lists:keysearch("password", 1, Query) of
|
||||
{value, {_, undefined}} ->
|
||||
error;
|
||||
{value, {_, Password}} ->
|
||||
ejabberd_auth:set_password(User, Password),
|
||||
ejabberd_auth:set_password(User, Server, Password),
|
||||
ok;
|
||||
_ ->
|
||||
error
|
||||
@ -1262,7 +1288,7 @@ user_parse_query(User, Query) ->
|
||||
_ ->
|
||||
case lists:keysearch("removeuser", 1, Query) of
|
||||
{value, _} ->
|
||||
ejabberd_auth:remove_user(User),
|
||||
ejabberd_auth:remove_user(User, Server),
|
||||
ok;
|
||||
false ->
|
||||
nothing
|
||||
@ -1270,12 +1296,16 @@ user_parse_query(User, Query) ->
|
||||
end.
|
||||
|
||||
|
||||
user_queue(User, Query, Lang) ->
|
||||
Res = user_queue_parse_query(User, Query),
|
||||
Msgs = lists:keysort(3, mnesia:dirty_read({offline_msg, User})),
|
||||
user_queue(SUser, Query, Lang) ->
|
||||
UJID = jlib:string_to_jid(SUser),
|
||||
User = UJID#jid.user,
|
||||
Server = UJID#jid.server,
|
||||
US = {UJID#jid.luser, UJID#jid.lserver},
|
||||
Res = user_queue_parse_query(US, Query),
|
||||
Msgs = lists:keysort(3, mnesia:dirty_read({offline_msg, US})),
|
||||
FMsgs =
|
||||
lists:map(
|
||||
fun({offline_msg, _User, TimeStamp, _Expire, From, To,
|
||||
fun({offline_msg, _US, TimeStamp, _Expire, From, To,
|
||||
{xmlelement, Name, Attrs, Els}} = Msg) ->
|
||||
ID = jlib:encode_base64(binary_to_list(term_to_binary(Msg))),
|
||||
{{Year, Month, Day}, {Hour, Minute, Second}} =
|
||||
@ -1298,7 +1328,8 @@ user_queue(User, Query, Lang) ->
|
||||
?XAE("td", [{"class", "valign"}], [?XC("pre", FPacket)])]
|
||||
)
|
||||
end, Msgs),
|
||||
[?XC("h1", io_lib:format(?T("~s offline messages queue"), [User]))] ++
|
||||
[?XC("h1", io_lib:format(?T("~s offline messages queue"),
|
||||
[us_to_list(US)]))] ++
|
||||
case Res of
|
||||
ok -> [?CT("submitted"), ?P];
|
||||
error -> [?CT("bad format"), ?P];
|
||||
@ -1319,10 +1350,10 @@ user_queue(User, Query, Lang) ->
|
||||
?INPUTT("submit", "delete", "Delete Selected")
|
||||
])].
|
||||
|
||||
user_queue_parse_query(User, Query) ->
|
||||
user_queue_parse_query(US, Query) ->
|
||||
case lists:keysearch("delete", 1, Query) of
|
||||
{value, _} ->
|
||||
Msgs = lists:keysort(3, mnesia:dirty_read({offline_msg, User})),
|
||||
Msgs = lists:keysort(3, mnesia:dirty_read({offline_msg, US})),
|
||||
F = fun() ->
|
||||
lists:foreach(
|
||||
fun(Msg) ->
|
||||
@ -1344,8 +1375,8 @@ user_queue_parse_query(User, Query) ->
|
||||
|
||||
|
||||
|
||||
-record(roster, {uj,
|
||||
user,
|
||||
-record(roster, {usj,
|
||||
us,
|
||||
jid,
|
||||
name = "",
|
||||
subscription = none,
|
||||
@ -1359,11 +1390,14 @@ ask_to_pending(unsubscribe) -> none;
|
||||
ask_to_pending(Ask) -> Ask.
|
||||
|
||||
|
||||
user_roster(User, Query, Lang, Admin) ->
|
||||
LUser = jlib:nameprep(User),
|
||||
Items1 = mnesia:dirty_index_read(roster, LUser, #roster.user),
|
||||
Res = user_roster_parse_query(User, Items1, Query, Admin),
|
||||
Items = mnesia:dirty_index_read(roster, LUser, #roster.user),
|
||||
user_roster(SUser, Query, Lang, Admin) ->
|
||||
UJID = jlib:string_to_jid(SUser),
|
||||
User = UJID#jid.user,
|
||||
Server = UJID#jid.server,
|
||||
US = {UJID#jid.luser, UJID#jid.lserver},
|
||||
Items1 = mnesia:dirty_index_read(roster, US, #roster.us),
|
||||
Res = user_roster_parse_query(User, Server, Items1, Query, Admin),
|
||||
Items = mnesia:dirty_index_read(roster, US, #roster.us),
|
||||
SItems = lists:sort(Items),
|
||||
FItems =
|
||||
case SItems of
|
||||
@ -1415,7 +1449,7 @@ user_roster(User, Query, Lang, Admin) ->
|
||||
"Remove")])])
|
||||
end, SItems))])]
|
||||
end,
|
||||
[?XC("h1", ?T("Roster of ") ++ User)] ++
|
||||
[?XC("h1", ?T("Roster of ") ++ us_to_list(US))] ++
|
||||
case Res of
|
||||
ok -> [?CT("submitted"), ?P];
|
||||
error -> [?CT("bad format"), ?P];
|
||||
@ -1428,7 +1462,7 @@ user_roster(User, Query, Lang, Admin) ->
|
||||
?INPUTT("submit", "addjid", "Add JID")
|
||||
])].
|
||||
|
||||
user_roster_parse_query(User, Items, Query, Admin) ->
|
||||
user_roster_parse_query(User, Server, Items, Query, Admin) ->
|
||||
case lists:keysearch("addjid", 1, Query) of
|
||||
{value, _} ->
|
||||
case lists:keysearch("newjid", 1, Query) of
|
||||
@ -1437,7 +1471,7 @@ user_roster_parse_query(User, Items, Query, Admin) ->
|
||||
{value, {_, SJID}} ->
|
||||
case jlib:string_to_jid(SJID) of
|
||||
JID when is_record(JID, jid) ->
|
||||
user_roster_subscribe_jid(User, JID),
|
||||
user_roster_subscribe_jid(User, Server, JID),
|
||||
ok;
|
||||
error ->
|
||||
error
|
||||
@ -1446,69 +1480,25 @@ user_roster_parse_query(User, Items, Query, Admin) ->
|
||||
error
|
||||
end;
|
||||
false ->
|
||||
case lists:keysearch("adduser", 1, Query) of
|
||||
{value, _} ->
|
||||
case lists:keysearch("newuser", 1, Query) of
|
||||
{value, {_, undefined}} ->
|
||||
error;
|
||||
{value, {_, U}} ->
|
||||
if
|
||||
Admin ->
|
||||
user_roster_subscribe_users(User, U);
|
||||
true ->
|
||||
case jlib:make_jid(U, ?MYNAME, "") of
|
||||
JID when is_record(JID, jid) ->
|
||||
user_roster_subscribe_jid(
|
||||
User, JID),
|
||||
ok;
|
||||
false ->
|
||||
error
|
||||
end
|
||||
end;
|
||||
false ->
|
||||
error
|
||||
end;
|
||||
false ->
|
||||
case catch user_roster_item_parse_query(
|
||||
User, Items, Query) of
|
||||
submitted ->
|
||||
ok;
|
||||
{'EXIT', _Reason} ->
|
||||
error;
|
||||
_ ->
|
||||
nothing
|
||||
end
|
||||
case catch user_roster_item_parse_query(
|
||||
User, Server, Items, Query) of
|
||||
submitted ->
|
||||
ok;
|
||||
{'EXIT', _Reason} ->
|
||||
error;
|
||||
_ ->
|
||||
nothing
|
||||
end
|
||||
end.
|
||||
|
||||
user_roster_subscribe_users(User1, User2) ->
|
||||
case jlib:make_jid(User1, ?MYNAME, "") of
|
||||
JID1 when is_record(JID1, jid) ->
|
||||
case jlib:make_jid(User2, ?MYNAME, "") of
|
||||
JID2 when is_record(JID2, jid) ->
|
||||
mod_roster:out_subscription(User1, JID2, subscribe),
|
||||
mod_roster:in_subscription(User2, JID1, subscribe),
|
||||
mod_roster:out_subscription(User2, JID1, subscribe),
|
||||
mod_roster:in_subscription(User1, JID2, subscribe),
|
||||
mod_roster:out_subscription(User1, JID2, subscribed),
|
||||
mod_roster:in_subscription(User2, JID1, subscribed),
|
||||
mod_roster:out_subscription(User2, JID1, subscribed),
|
||||
mod_roster:in_subscription(User1, JID2, subscribed),
|
||||
ok;
|
||||
false ->
|
||||
error
|
||||
end;
|
||||
false ->
|
||||
error
|
||||
end.
|
||||
|
||||
user_roster_subscribe_jid(User, JID) ->
|
||||
mod_roster:out_subscription(User, JID, subscribe),
|
||||
UJID = jlib:make_jid(User, ?MYNAME, ""),
|
||||
user_roster_subscribe_jid(User, Server, JID) ->
|
||||
mod_roster:out_subscription(User, Server, JID, subscribe),
|
||||
UJID = jlib:make_jid(User, Server, ""),
|
||||
ejabberd_router:route(
|
||||
UJID, JID, {xmlelement, "presence", [{"type", "subscribe"}], []}).
|
||||
|
||||
user_roster_item_parse_query(User, Items, Query) ->
|
||||
user_roster_item_parse_query(User, Server, Items, Query) ->
|
||||
lists:foreach(
|
||||
fun(R) ->
|
||||
JID = R#roster.jid,
|
||||
@ -1516,8 +1506,9 @@ user_roster_item_parse_query(User, Items, Query) ->
|
||||
"validate" ++ term_to_id(JID), 1, Query) of
|
||||
{value, _} ->
|
||||
JID1 = jlib:make_jid(JID),
|
||||
mod_roster:out_subscription(User, JID1, subscribed),
|
||||
UJID = jlib:make_jid(User, ?MYNAME, ""),
|
||||
mod_roster:out_subscription(
|
||||
User, Server, JID1, subscribed),
|
||||
UJID = jlib:make_jid(User, Server, ""),
|
||||
ejabberd_router:route(
|
||||
UJID, JID1, {xmlelement, "presence",
|
||||
[{"type", "subscribed"}], []}),
|
||||
@ -1526,7 +1517,7 @@ user_roster_item_parse_query(User, Items, Query) ->
|
||||
case lists:keysearch(
|
||||
"remove" ++ term_to_id(JID), 1, Query) of
|
||||
{value, _} ->
|
||||
UJID = jlib:make_jid(User, ?MYNAME, ""),
|
||||
UJID = jlib:make_jid(User, Server, ""),
|
||||
mod_roster:process_iq(
|
||||
UJID, UJID,
|
||||
#iq{type = set,
|
||||
@ -2038,3 +2029,200 @@ pretty_print({xmlelement, Name, Attrs, Els}, Prefix) ->
|
||||
end
|
||||
end].
|
||||
|
||||
|
||||
list_shared_roster_groups(Query, Lang) ->
|
||||
Res = list_sr_groups_parse_query(Query),
|
||||
SRGroups = mod_shared_roster:list_groups(?MYNAME),
|
||||
FGroups =
|
||||
?XAE("table", [],
|
||||
[?XE("tbody",
|
||||
lists:map(
|
||||
fun(Group) ->
|
||||
?XE("tr",
|
||||
[?XE("td", [?INPUT("checkbox", "selected",
|
||||
Group)]),
|
||||
?XE("td", [?AC(Group ++ "/", Group)])
|
||||
]
|
||||
)
|
||||
end, lists:sort(SRGroups)) ++
|
||||
[?XE("tr",
|
||||
[?X("td"),
|
||||
?XE("td", [?INPUT("text", "namenew", "")]),
|
||||
?XE("td", [?INPUTT("submit", "addnew", "Add New")])
|
||||
]
|
||||
)]
|
||||
)]),
|
||||
[?XC("h1", ?T("Shared roster groups"))] ++
|
||||
case Res of
|
||||
ok -> [?CT("submitted"), ?P];
|
||||
error -> [?CT("bad format"), ?P];
|
||||
nothing -> []
|
||||
end ++
|
||||
[?XAE("form", [{"method", "post"}],
|
||||
[FGroups,
|
||||
?BR,
|
||||
?INPUTT("submit", "delete", "Delete Selected")
|
||||
])
|
||||
].
|
||||
|
||||
list_sr_groups_parse_query(Query) ->
|
||||
case lists:keysearch("addnew", 1, Query) of
|
||||
{value, _} ->
|
||||
list_sr_groups_parse_addnew(Query);
|
||||
_ ->
|
||||
case lists:keysearch("delete", 1, Query) of
|
||||
{value, _} ->
|
||||
list_sr_groups_parse_delete(Query);
|
||||
_ ->
|
||||
nothing
|
||||
end
|
||||
end.
|
||||
|
||||
list_sr_groups_parse_addnew(Query) ->
|
||||
case lists:keysearch("namenew", 1, Query) of
|
||||
{value, {_, Group}} when Group /= "" ->
|
||||
mod_shared_roster:create_group(?MYNAME, Group),
|
||||
ok;
|
||||
_ ->
|
||||
error
|
||||
end.
|
||||
|
||||
list_sr_groups_parse_delete(Query) ->
|
||||
SRGroups = mod_shared_roster:list_groups(?MYNAME),
|
||||
lists:foreach(
|
||||
fun(Group) ->
|
||||
case lists:member({"selected", Group}, Query) of
|
||||
true ->
|
||||
mod_shared_roster:delete_group(?MYNAME, Group);
|
||||
_ ->
|
||||
ok
|
||||
end
|
||||
end, SRGroups),
|
||||
ok.
|
||||
|
||||
|
||||
shared_roster_group(Group, Query, Lang) ->
|
||||
Res = shared_roster_group_parse_query(?MYNAME, Group, Query),
|
||||
GroupOpts = mod_shared_roster:get_group_opts(?MYNAME, Group),
|
||||
Name = get_opt(GroupOpts, name, ""),
|
||||
Description = get_opt(GroupOpts, description, ""),
|
||||
Members = mod_shared_roster:get_group_users(?MYNAME, Group),
|
||||
Disabled = false,
|
||||
DisplayedGroups = get_opt(GroupOpts, displayed_groups, []),
|
||||
FMembers = [[us_to_list(Member), $\n] || Member <- Members],
|
||||
FDisplayedGroups = [[DG, $\n] || DG <- DisplayedGroups],
|
||||
FGroup =
|
||||
?XAE("table", [],
|
||||
[?XE("tbody",
|
||||
[?XE("tr",
|
||||
[?XCT("td", "Name:"),
|
||||
?XE("td", [?INPUT("text", "name", Name)])
|
||||
]
|
||||
),
|
||||
?XE("tr",
|
||||
[?XCT("td", "Description:"),
|
||||
?XE("td", [?XAC("textarea", [{"name", "description"},
|
||||
{"rows", "3"},
|
||||
{"cols", "20"}],
|
||||
Description)])
|
||||
]
|
||||
),
|
||||
?XE("tr",
|
||||
[?XCT("td", "Members:"),
|
||||
?XE("td", [?XAC("textarea", [{"name", "members"},
|
||||
{"rows", "3"},
|
||||
{"cols", "20"}],
|
||||
FMembers)])
|
||||
]
|
||||
),
|
||||
?XE("tr",
|
||||
[?XCT("td", "Displayed Groups:"),
|
||||
?XE("td", [?XAC("textarea", [{"name", "dispgroups"},
|
||||
{"rows", "3"},
|
||||
{"cols", "20"}],
|
||||
FDisplayedGroups)])
|
||||
]
|
||||
)]
|
||||
)]),
|
||||
[?XC("h1", ?T("Shared roster group " ++ Group))] ++
|
||||
case Res of
|
||||
ok -> [?CT("submitted"), ?P];
|
||||
error -> [?CT("bad format"), ?P];
|
||||
nothing -> []
|
||||
end ++
|
||||
[?XAE("form", [{"method", "post"}],
|
||||
[FGroup,
|
||||
?BR,
|
||||
?INPUTT("submit", "submit", "Submit")
|
||||
])
|
||||
].
|
||||
|
||||
shared_roster_group_parse_query(Host, Group, Query) ->
|
||||
case lists:keysearch("submit", 1, Query) of
|
||||
{value, _} ->
|
||||
{value, {_, Name}} = lists:keysearch("name", 1, Query),
|
||||
{value, {_, Description}} = lists:keysearch("description", 1, Query),
|
||||
{value, {_, SMembers}} = lists:keysearch("members", 1, Query),
|
||||
{value, {_, SDispGroups}} = lists:keysearch("dispgroups", 1, Query),
|
||||
NameOpt =
|
||||
if
|
||||
Name == "" -> [];
|
||||
true -> [{name, Name}]
|
||||
end,
|
||||
DescriptionOpt =
|
||||
if
|
||||
Description == "" -> [];
|
||||
true -> [{description, Description}]
|
||||
end,
|
||||
DispGroups = string:tokens(SDispGroups, "\r\n"),
|
||||
DispGroupsOpt =
|
||||
if
|
||||
DispGroups == [] -> [];
|
||||
true -> [{displayed_groups, DispGroups}]
|
||||
end,
|
||||
mod_shared_roster:set_group_opts(
|
||||
?MYNAME, Group, NameOpt ++ DispGroupsOpt ++ DescriptionOpt),
|
||||
|
||||
OldMembers = mod_shared_roster:get_group_users(?MYNAME, Group),
|
||||
NewMembers =
|
||||
lists:foldl(
|
||||
fun(_SJID, error) -> error;
|
||||
(SJID, USs) ->
|
||||
case jlib:string_to_jid(SJID) of
|
||||
JID when is_record(JID, jid) ->
|
||||
[{JID#jid.luser, JID#jid.lserver} | USs];
|
||||
error ->
|
||||
error
|
||||
end
|
||||
end, [], string:tokens(SMembers, "\r\n")),
|
||||
if
|
||||
NewMembers == error -> error;
|
||||
true ->
|
||||
AddedMembers = NewMembers -- OldMembers,
|
||||
RemovedMembers = OldMembers -- NewMembers,
|
||||
lists:foreach(
|
||||
fun(US) ->
|
||||
mod_shared_roster:remove_user_from_group(
|
||||
Host, US, Group)
|
||||
end, RemovedMembers),
|
||||
lists:foreach(
|
||||
fun(US) ->
|
||||
mod_shared_roster:add_user_to_group(
|
||||
Host, US, Group)
|
||||
end, AddedMembers),
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
nothing
|
||||
end.
|
||||
|
||||
|
||||
|
||||
get_opt(Opts, Opt, Default) ->
|
||||
case lists:keysearch(Opt, 1, Opts) of
|
||||
{value, {_, Val}} ->
|
||||
Val;
|
||||
false ->
|
||||
Default
|
||||
end.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user