aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cyrsasl.erl18
-rw-r--r--src/cyrsasl_digest.erl33
-rw-r--r--src/cyrsasl_plain.erl13
-rw-r--r--src/ejabberd.hrl5
-rw-r--r--src/ejabberd_auth.erl56
-rw-r--r--src/ejabberd_auth_external.erl40
-rw-r--r--src/ejabberd_auth_internal.erl158
-rw-r--r--src/ejabberd_auth_ldap.erl40
-rw-r--r--src/ejabberd_auth_odbc.erl37
-rw-r--r--src/ejabberd_c2s.erl243
-rw-r--r--src/ejabberd_config.erl4
-rw-r--r--src/ejabberd_ctl.erl2
-rw-r--r--src/ejabberd_local.erl5
-rw-r--r--src/ejabberd_router.erl67
-rw-r--r--src/ejabberd_sm.erl141
-rw-r--r--src/ejabberd_sup.erl16
-rw-r--r--src/eldap/ELDAPv3.erl2972
-rw-r--r--src/eldap/ELDAPv3.hrl74
-rw-r--r--src/gen_mod.erl21
-rw-r--r--src/jd2ejd.erl313
-rw-r--r--src/mod_announce.erl180
-rw-r--r--src/mod_configure.erl33
-rw-r--r--src/mod_disco.erl135
-rw-r--r--src/mod_irc/mod_irc.erl123
-rw-r--r--src/mod_irc/mod_irc_connection.erl53
-rw-r--r--src/mod_last.erl68
-rw-r--r--src/mod_last_odbc.erl2
-rw-r--r--src/mod_muc/mod_muc.erl250
-rw-r--r--src/mod_muc/mod_muc_room.erl21
-rw-r--r--src/mod_offline.erl100
-rw-r--r--src/mod_privacy.erl117
-rw-r--r--src/mod_private.erl92
-rw-r--r--src/mod_pubsub/mod_pubsub.erl180
-rw-r--r--src/mod_register.erl19
-rw-r--r--src/mod_roster.erl197
-rw-r--r--src/mod_roster.hrl18
-rw-r--r--src/mod_roster_odbc.erl2
-rw-r--r--src/mod_shared_roster.erl291
-rw-r--r--src/mod_stats.erl51
-rw-r--r--src/mod_vcard.erl317
-rw-r--r--src/web/ejabberd_http.erl62
-rw-r--r--src/web/ejabberd_http.hrl2
-rw-r--r--src/web/ejabberd_http1.erl404
-rw-r--r--src/web/ejabberd_wcs.erl328
-rw-r--r--src/web/ejabberd_web.erl16
-rw-r--r--src/web/ejabberd_web_admin.erl448
46 files changed, 6467 insertions, 1300 deletions
diff --git a/src/cyrsasl.erl b/src/cyrsasl.erl
index 9376b25d2..2f17cffbc 100644
--- a/src/cyrsasl.erl
+++ b/src/cyrsasl.erl
@@ -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);
diff --git a/src/cyrsasl_digest.erl b/src/cyrsasl_digest.erl
index 2e5e94e5a..c87ffdc08 100644
--- a/src/cyrsasl_digest.erl
+++ b/src/cyrsasl_digest.erl
@@ -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))),
diff --git a/src/cyrsasl_plain.erl b/src/cyrsasl_plain.erl
index 71e4a6baf..0c4b66ff2 100644
--- a/src/cyrsasl_plain.erl
+++ b/src/cyrsasl_plain.erl
@@ -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}]};
_ ->
diff --git a/src/ejabberd.hrl b/src/ejabberd.hrl
index 249818d60..012bda7c8 100644
--- a/src/ejabberd.hrl
+++ b/src/ejabberd.hrl
@@ -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").
diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl
index 8abd2ac6b..022173534 100644
--- a/src/ejabberd_auth.erl
+++ b/src/ejabberd_auth.erl
@@ -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
diff --git a/src/ejabberd_auth_external.erl b/src/ejabberd_auth_external.erl
index e379fb9df..383ce15a8 100644
--- a/src/ejabberd_auth_external.erl
+++ b/src/ejabberd_auth_external.erl
@@ -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.
diff --git a/src/ejabberd_auth_internal.erl b/src/ejabberd_auth_internal.erl
index 1743c8478..8dbd58818 100644
--- a/src/ejabberd_auth_internal.erl
+++ b/src/ejabberd_auth_internal.erl
@@ -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.
+
+
+
diff --git a/src/ejabberd_auth_ldap.erl b/src/ejabberd_auth_ldap.erl
index 63c23c701..355fd3b60 100644
--- a/src/ejabberd_auth_ldap.erl
+++ b/src/ejabberd_auth_ldap.erl
@@ -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.
diff --git a/src/ejabberd_auth_odbc.erl b/src/ejabberd_auth_odbc.erl
index 35d1f92d0..1cb5ca4e4 100644
--- a/src/ejabberd_auth_odbc.erl
+++ b/src/ejabberd_auth_odbc.erl
@@ -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;
diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl
index 09b98af3e..3873ac171 100644
--- a/src/ejabberd_c2s.erl
+++ b/src/ejabberd_c2s.erl
@@ -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
diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl
index a2822a388..e1aa9c2df 100644
--- a/src/ejabberd_config.erl
+++ b/src/ejabberd_config.erl
@@ -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;
diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl
index 55c8008a7..1c1d33877 100644
--- a/src/ejabberd_ctl.erl
+++ b/src/ejabberd_ctl.erl
@@ -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",
diff --git a/src/ejabberd_local.erl b/src/ejabberd_local.erl
index 40b598e02..3e7eaf1b9 100644
--- a/src/ejabberd_local.erl
+++ b/src/ejabberd_local.erl
@@ -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),
diff --git a/src/ejabberd_router.erl b/src/ejabberd_router.erl
index ac5583696..31d355542 100644
--- a/src/ejabberd_router.erl
+++ b/src/ejabberd_router.erl
@@ -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)).
diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl
index cfefa0c95..c7f78ca71 100644
--- a/src/ejabberd_sm.erl
+++ b/src/ejabberd_sm.erl
@@ -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
diff --git a/src/ejabberd_sup.erl b/src/ejabberd_sup.erl
index 9a2cc74d5..d58ec422f 100644
--- a/src/ejabberd_sup.erl
+++ b/src/ejabberd_sup.erl
@@ -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,
diff --git a/src/eldap/ELDAPv3.erl b/src/eldap/ELDAPv3.erl
new file mode 100644
index 000000000..44657eed6
--- /dev/null
+++ b/src/eldap/ELDAPv3.erl
@@ -0,0 +1,2972 @@
+%% Generated by the Erlang ASN.1 BER-compiler version, utilizing bit-syntax:1.3.2
+%% Purpose: encoder and decoder to the types in mod ELDAPv3
+
+-module('ELDAPv3').
+-include("ELDAPv3.hrl").
+-define('RT_BER',asn1rt_ber_bin).
+-export([encoding_rule/0]).
+-export([
+'enc_LDAPMessage'/2,
+'enc_MessageID'/2,
+'enc_LDAPString'/2,
+'enc_LDAPOID'/2,
+'enc_LDAPDN'/2,
+'enc_RelativeLDAPDN'/2,
+'enc_AttributeType'/2,
+'enc_AttributeDescription'/2,
+'enc_AttributeDescriptionList'/2,
+'enc_AttributeValue'/2,
+'enc_AttributeValueAssertion'/2,
+'enc_AssertionValue'/2,
+'enc_Attribute'/2,
+'enc_MatchingRuleId'/2,
+'enc_LDAPResult'/2,
+'enc_Referral'/2,
+'enc_LDAPURL'/2,
+'enc_Controls'/2,
+'enc_Control'/2,
+'enc_BindRequest'/2,
+'enc_AuthenticationChoice'/2,
+'enc_SaslCredentials'/2,
+'enc_BindResponse'/2,
+'enc_UnbindRequest'/2,
+'enc_SearchRequest'/2,
+'enc_Filter'/2,
+'enc_SubstringFilter'/2,
+'enc_MatchingRuleAssertion'/2,
+'enc_SearchResultEntry'/2,
+'enc_PartialAttributeList'/2,
+'enc_SearchResultReference'/2,
+'enc_SearchResultDone'/2,
+'enc_ModifyRequest'/2,
+'enc_AttributeTypeAndValues'/2,
+'enc_ModifyResponse'/2,
+'enc_AddRequest'/2,
+'enc_AttributeList'/2,
+'enc_AddResponse'/2,
+'enc_DelRequest'/2,
+'enc_DelResponse'/2,
+'enc_ModifyDNRequest'/2,
+'enc_ModifyDNResponse'/2,
+'enc_CompareRequest'/2,
+'enc_CompareResponse'/2,
+'enc_AbandonRequest'/2,
+'enc_ExtendedRequest'/2,
+'enc_ExtendedResponse'/2
+]).
+
+-export([
+'dec_LDAPMessage'/2,
+'dec_MessageID'/2,
+'dec_LDAPString'/2,
+'dec_LDAPOID'/2,
+'dec_LDAPDN'/2,
+'dec_RelativeLDAPDN'/2,
+'dec_AttributeType'/2,
+'dec_AttributeDescription'/2,
+'dec_AttributeDescriptionList'/2,
+'dec_AttributeValue'/2,
+'dec_AttributeValueAssertion'/2,
+'dec_AssertionValue'/2,
+'dec_Attribute'/2,
+'dec_MatchingRuleId'/2,
+'dec_LDAPResult'/2,
+'dec_Referral'/2,
+'dec_LDAPURL'/2,
+'dec_Controls'/2,
+'dec_Control'/2,
+'dec_BindRequest'/2,
+'dec_AuthenticationChoice'/2,
+'dec_SaslCredentials'/2,
+'dec_BindResponse'/2,
+'dec_UnbindRequest'/2,
+'dec_SearchRequest'/2,
+'dec_Filter'/2,
+'dec_SubstringFilter'/2,
+'dec_MatchingRuleAssertion'/2,
+'dec_SearchResultEntry'/2,
+'dec_PartialAttributeList'/2,
+'dec_SearchResultReference'/2,
+'dec_SearchResultDone'/2,
+'dec_ModifyRequest'/2,
+'dec_AttributeTypeAndValues'/2,
+'dec_ModifyResponse'/2,
+'dec_AddRequest'/2,
+'dec_AttributeList'/2,
+'dec_AddResponse'/2,
+'dec_DelRequest'/2,
+'dec_DelResponse'/2,
+'dec_ModifyDNRequest'/2,
+'dec_ModifyDNResponse'/2,
+'dec_CompareRequest'/2,
+'dec_CompareResponse'/2,
+'dec_AbandonRequest'/2,
+'dec_ExtendedRequest'/2,
+'dec_ExtendedResponse'/2
+]).
+
+-export([
+'dec_LDAPMessage'/3,
+'dec_MessageID'/3,
+'dec_LDAPString'/3,
+'dec_LDAPOID'/3,
+'dec_LDAPDN'/3,
+'dec_RelativeLDAPDN'/3,
+'dec_AttributeType'/3,
+'dec_AttributeDescription'/3,
+'dec_AttributeDescriptionList'/3,
+'dec_AttributeValue'/3,
+'dec_AttributeValueAssertion'/3,
+'dec_AssertionValue'/3,
+'dec_Attribute'/3,
+'dec_MatchingRuleId'/3,
+'dec_LDAPResult'/3,
+'dec_Referral'/3,
+'dec_LDAPURL'/3,
+'dec_Controls'/3,
+'dec_Control'/3,
+'dec_BindRequest'/3,
+'dec_AuthenticationChoice'/3,
+'dec_SaslCredentials'/3,
+'dec_BindResponse'/3,
+'dec_UnbindRequest'/3,
+'dec_SearchRequest'/3,
+'dec_Filter'/3,
+'dec_SubstringFilter'/3,
+'dec_MatchingRuleAssertion'/3,
+'dec_SearchResultEntry'/3,
+'dec_PartialAttributeList'/3,
+'dec_SearchResultReference'/3,
+'dec_SearchResultDone'/3,
+'dec_ModifyRequest'/3,
+'dec_AttributeTypeAndValues'/3,
+'dec_ModifyResponse'/3,
+'dec_AddRequest'/3,
+'dec_AttributeList'/3,
+'dec_AddResponse'/3,
+'dec_DelRequest'/3,
+'dec_DelResponse'/3,
+'dec_ModifyDNRequest'/3,
+'dec_ModifyDNResponse'/3,
+'dec_CompareRequest'/3,
+'dec_CompareResponse'/3,
+'dec_AbandonRequest'/3,
+'dec_ExtendedRequest'/3,
+'dec_ExtendedResponse'/3
+]).
+
+-export([
+'maxInt'/0
+]).
+
+
+
+-export([encode/2,decode/2,encode_disp/2,decode_disp/2]).
+
+encoding_rule() ->
+ ber_bin.
+
+encode(Type,Data) ->
+case catch encode_disp(Type,Data) of
+ {'EXIT',{error,Reason}} ->
+ {error,Reason};
+ {'EXIT',Reason} ->
+ {error,{asn1,Reason}};
+ {Bytes,Len} ->
+ {ok,Bytes};
+ X ->
+ {ok,X}
+end.
+
+decode(Type,Data) ->
+case catch decode_disp(Type,Data) of
+ {'EXIT',{error,Reason}} ->
+ {error,Reason};
+ {'EXIT',Reason} ->
+ {error,{asn1,Reason}};
+ {X,_Rest} ->
+ {ok,X};
+ {X,_Rest,_Len} ->
+ {ok,X}
+end.
+
+encode_disp('LDAPMessage',Data) -> 'enc_LDAPMessage'(Data,[]);
+encode_disp('MessageID',Data) -> 'enc_MessageID'(Data,[]);
+encode_disp('LDAPString',Data) -> 'enc_LDAPString'(Data,[]);
+encode_disp('LDAPOID',Data) -> 'enc_LDAPOID'(Data,[]);
+encode_disp('LDAPDN',Data) -> 'enc_LDAPDN'(Data,[]);
+encode_disp('RelativeLDAPDN',Data) -> 'enc_RelativeLDAPDN'(Data,[]);
+encode_disp('AttributeType',Data) -> 'enc_AttributeType'(Data,[]);
+encode_disp('AttributeDescription',Data) -> 'enc_AttributeDescription'(Data,[]);
+encode_disp('AttributeDescriptionList',Data) -> 'enc_AttributeDescriptionList'(Data,[]);
+encode_disp('AttributeValue',Data) -> 'enc_AttributeValue'(Data,[]);
+encode_disp('AttributeValueAssertion',Data) -> 'enc_AttributeValueAssertion'(Data,[]);
+encode_disp('AssertionValue',Data) -> 'enc_AssertionValue'(Data,[]);
+encode_disp('Attribute',Data) -> 'enc_Attribute'(Data,[]);
+encode_disp('MatchingRuleId',Data) -> 'enc_MatchingRuleId'(Data,[]);
+encode_disp('LDAPResult',Data) -> 'enc_LDAPResult'(Data,[]);
+encode_disp('Referral',Data) -> 'enc_Referral'(Data,[]);
+encode_disp('LDAPURL',Data) -> 'enc_LDAPURL'(Data,[]);
+encode_disp('Controls',Data) -> 'enc_Controls'(Data,[]);
+encode_disp('Control',Data) -> 'enc_Control'(Data,[]);
+encode_disp('BindRequest',Data) -> 'enc_BindRequest'(Data,[]);
+encode_disp('AuthenticationChoice',Data) -> 'enc_AuthenticationChoice'(Data,[]);
+encode_disp('SaslCredentials',Data) -> 'enc_SaslCredentials'(Data,[]);
+encode_disp('BindResponse',Data) -> 'enc_BindResponse'(Data,[]);
+encode_disp('UnbindRequest',Data) -> 'enc_UnbindRequest'(Data,[]);
+encode_disp('SearchRequest',Data) -> 'enc_SearchRequest'(Data,[]);
+encode_disp('Filter',Data) -> 'enc_Filter'(Data,[]);
+encode_disp('SubstringFilter',Data) -> 'enc_SubstringFilter'(Data,[]);
+encode_disp('MatchingRuleAssertion',Data) -> 'enc_MatchingRuleAssertion'(Data,[]);
+encode_disp('SearchResultEntry',Data) -> 'enc_SearchResultEntry'(Data,[]);
+encode_disp('PartialAttributeList',Data) -> 'enc_PartialAttributeList'(Data,[]);
+encode_disp('SearchResultReference',Data) -> 'enc_SearchResultReference'(Data,[]);
+encode_disp('SearchResultDone',Data) -> 'enc_SearchResultDone'(Data,[]);
+encode_disp('ModifyRequest',Data) -> 'enc_ModifyRequest'(Data,[]);
+encode_disp('AttributeTypeAndValues',Data) -> 'enc_AttributeTypeAndValues'(Data,[]);
+encode_disp('ModifyResponse',Data) -> 'enc_ModifyResponse'(Data,[]);
+encode_disp('AddRequest',Data) -> 'enc_AddRequest'(Data,[]);
+encode_disp('AttributeList',Data) -> 'enc_AttributeList'(Data,[]);
+encode_disp('AddResponse',Data) -> 'enc_AddResponse'(Data,[]);
+encode_disp('DelRequest',Data) -> 'enc_DelRequest'(Data,[]);
+encode_disp('DelResponse',Data) -> 'enc_DelResponse'(Data,[]);
+encode_disp('ModifyDNRequest',Data) -> 'enc_ModifyDNRequest'(Data,[]);
+encode_disp('ModifyDNResponse',Data) -> 'enc_ModifyDNResponse'(Data,[]);
+encode_disp('CompareRequest',Data) -> 'enc_CompareRequest'(Data,[]);
+encode_disp('CompareResponse',Data) -> 'enc_CompareResponse'(Data,[]);
+encode_disp('AbandonRequest',Data) -> 'enc_AbandonRequest'(Data,[]);
+encode_disp('ExtendedRequest',Data) -> 'enc_ExtendedRequest'(Data,[]);
+encode_disp('ExtendedResponse',Data) -> 'enc_ExtendedResponse'(Data,[]);
+encode_disp(Type,Data) -> exit({error,{asn1,{undefined_type,Type}}}).
+
+
+decode_disp('LDAPMessage',Data) -> 'dec_LDAPMessage'(Data,mandatory);
+decode_disp('MessageID',Data) -> 'dec_MessageID'(Data,mandatory);
+decode_disp('LDAPString',Data) -> 'dec_LDAPString'(Data,mandatory);
+decode_disp('LDAPOID',Data) -> 'dec_LDAPOID'(Data,mandatory);
+decode_disp('LDAPDN',Data) -> 'dec_LDAPDN'(Data,mandatory);
+decode_disp('RelativeLDAPDN',Data) -> 'dec_RelativeLDAPDN'(Data,mandatory);
+decode_disp('AttributeType',Data) -> 'dec_AttributeType'(Data,mandatory);
+decode_disp('AttributeDescription',Data) -> 'dec_AttributeDescription'(Data,mandatory);
+decode_disp('AttributeDescriptionList',Data) -> 'dec_AttributeDescriptionList'(Data,mandatory);
+decode_disp('AttributeValue',Data) -> 'dec_AttributeValue'(Data,mandatory);
+decode_disp('AttributeValueAssertion',Data) -> 'dec_AttributeValueAssertion'(Data,mandatory);
+decode_disp('AssertionValue',Data) -> 'dec_AssertionValue'(Data,mandatory);
+decode_disp('Attribute',Data) -> 'dec_Attribute'(Data,mandatory);
+decode_disp('MatchingRuleId',Data) -> 'dec_MatchingRuleId'(Data,mandatory);
+decode_disp('LDAPResult',Data) -> 'dec_LDAPResult'(Data,mandatory);
+decode_disp('Referral',Data) -> 'dec_Referral'(Data,mandatory);
+decode_disp('LDAPURL',Data) -> 'dec_LDAPURL'(Data,mandatory);
+decode_disp('Controls',Data) -> 'dec_Controls'(Data,mandatory);
+decode_disp('Control',Data) -> 'dec_Control'(Data,mandatory);
+decode_disp('BindRequest',Data) -> 'dec_BindRequest'(Data,mandatory);
+decode_disp('AuthenticationChoice',Data) -> 'dec_AuthenticationChoice'(Data,mandatory);
+decode_disp('SaslCredentials',Data) -> 'dec_SaslCredentials'(Data,mandatory);
+decode_disp('BindResponse',Data) -> 'dec_BindResponse'(Data,mandatory);
+decode_disp('UnbindRequest',Data) -> 'dec_UnbindRequest'(Data,mandatory);
+decode_disp('SearchRequest',Data) -> 'dec_SearchRequest'(Data,mandatory);
+decode_disp('Filter',Data) -> 'dec_Filter'(Data,mandatory);
+decode_disp('SubstringFilter',Data) -> 'dec_SubstringFilter'(Data,mandatory);
+decode_disp('MatchingRuleAssertion',Data) -> 'dec_MatchingRuleAssertion'(Data,mandatory);
+decode_disp('SearchResultEntry',Data) -> 'dec_SearchResultEntry'(Data,mandatory);
+decode_disp('PartialAttributeList',Data) -> 'dec_PartialAttributeList'(Data,mandatory);
+decode_disp('SearchResultReference',Data) -> 'dec_SearchResultReference'(Data,mandatory);
+decode_disp('SearchResultDone',Data) -> 'dec_SearchResultDone'(Data,mandatory);
+decode_disp('ModifyRequest',Data) -> 'dec_ModifyRequest'(Data,mandatory);
+decode_disp('AttributeTypeAndValues',Data) -> 'dec_AttributeTypeAndValues'(Data,mandatory);
+decode_disp('ModifyResponse',Data) -> 'dec_ModifyResponse'(Data,mandatory);
+decode_disp('AddRequest',Data) -> 'dec_AddRequest'(Data,mandatory);
+decode_disp('AttributeList',Data) -> 'dec_AttributeList'(Data,mandatory);
+decode_disp('AddResponse',Data) -> 'dec_AddResponse'(Data,mandatory);
+decode_disp('DelRequest',Data) -> 'dec_DelRequest'(Data,mandatory);
+decode_disp('DelResponse',Data) -> 'dec_DelResponse'(Data,mandatory);
+decode_disp('ModifyDNRequest',Data) -> 'dec_ModifyDNRequest'(Data,mandatory);
+decode_disp('ModifyDNResponse',Data) -> 'dec_ModifyDNResponse'(Data,mandatory);
+decode_disp('CompareRequest',Data) -> 'dec_CompareRequest'(Data,mandatory);
+decode_disp('CompareResponse',Data) -> 'dec_CompareResponse'(Data,mandatory);
+decode_disp('AbandonRequest',Data) -> 'dec_AbandonRequest'(Data,mandatory);
+decode_disp('ExtendedRequest',Data) -> 'dec_ExtendedRequest'(Data,mandatory);
+decode_disp('ExtendedResponse',Data) -> 'dec_ExtendedResponse'(Data,mandatory);
+decode_disp(Type,Data) -> exit({error,{asn1,{undefined_type,Type}}}).
+
+
+
+
+
+
+
+%%================================
+%% LDAPMessage
+%%================================
+'enc_LDAPMessage'(Val, TagIn) ->
+
+%%-------------------------------------------------
+%% attribute number 1 with type INTEGER
+%%-------------------------------------------------
+ {EncBytes1,EncLen1} = ?RT_BER:encode_integer([], ?RT_BER:cindex(2,Val,messageID), []),
+
+%%-------------------------------------------------
+%% attribute number 2 with type CHOICE
+%%-------------------------------------------------
+ {EncBytes2,EncLen2} = 'enc_LDAPMessage_protocolOp'(?RT_BER:cindex(3,Val,protocolOp), []),
+
+%%-------------------------------------------------
+%% attribute number 3 External ELDAPv3:Controls OPTIONAL
+%%-------------------------------------------------
+ {EncBytes3,EncLen3} = case ?RT_BER:cindex(4,Val,controls) of
+ asn1_NOVALUE -> {<<>>,0};
+ _ ->
+ 'enc_Controls'(?RT_BER:cindex(4,Val,controls), [{tag,128,0,'IMPLICIT',32}])
+ end,
+
+ BytesSoFar = [EncBytes1, EncBytes2, EncBytes3],
+LenSoFar = EncLen1 + EncLen2 + EncLen3,
+{TagBytes,Len} = ?RT_BER:encode_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], BytesSoFar, LenSoFar).
+
+
+
+%%================================
+%% LDAPMessage_protocolOp
+%%================================
+
+'enc_LDAPMessage_protocolOp'({'LDAPMessage_protocolOp',Val}, TagIn) ->
+ 'enc_LDAPMessage_protocolOp'(Val, TagIn);
+
+'enc_LDAPMessage_protocolOp'(Val, TagIn) ->
+ {EncBytes,EncLen} = case element(1,Val) of
+ bindRequest ->
+ 'enc_BindRequest'(element(2,Val), []);
+ bindResponse ->
+ 'enc_BindResponse'(element(2,Val), []);
+ unbindRequest ->
+ ?RT_BER:encode_null(element(2,Val), [{tag,64,2,'IMPLICIT',32}]);
+ searchRequest ->
+ 'enc_SearchRequest'(element(2,Val), []);
+ searchResEntry ->
+ 'enc_SearchResultEntry'(element(2,Val), []);
+ searchResDone ->
+ 'enc_SearchResultDone'(element(2,Val), []);
+ searchResRef ->
+ 'enc_SearchResultReference'(element(2,Val), []);
+ modifyRequest ->
+ 'enc_ModifyRequest'(element(2,Val), []);
+ modifyResponse ->
+ 'enc_ModifyResponse'(element(2,Val), []);
+ addRequest ->
+ 'enc_AddRequest'(element(2,Val), []);
+ addResponse ->
+ 'enc_AddResponse'(element(2,Val), []);
+ delRequest ->
+ 'enc_DelRequest'(element(2,Val), []);
+ delResponse ->
+ 'enc_DelResponse'(element(2,Val), []);
+ modDNRequest ->
+ 'enc_ModifyDNRequest'(element(2,Val), []);
+ modDNResponse ->
+ 'enc_ModifyDNResponse'(element(2,Val), []);
+ compareRequest ->
+ 'enc_CompareRequest'(element(2,Val), []);
+ compareResponse ->
+ 'enc_CompareResponse'(element(2,Val), []);
+ abandonRequest ->
+ 'enc_AbandonRequest'(element(2,Val), []);
+ extendedReq ->
+ 'enc_ExtendedRequest'(element(2,Val), []);
+ extendedResp ->
+ 'enc_ExtendedResponse'(element(2,Val), []);
+ Else ->
+ exit({error,{asn1,{invalid_choice_type,Else}}})
+ end,
+
+?RT_BER:encode_tags(TagIn ++[], EncBytes, EncLen).
+
+
+'dec_LDAPMessage_protocolOp'(Bytes, OptOrMand, TagIn) ->
+ {FormLen,Bytes1, RbExp} = ?RT_BER:check_tags(TagIn++[], Bytes, OptOrMand),
+ case Bytes1 of
+
+%% 'bindRequest'
+ <<1:2,_:1,0:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_BindRequest'(Bytes1, mandatory, []),
+ {{bindRequest, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'bindResponse'
+ <<1:2,_:1,1:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_BindResponse'(Bytes1, mandatory, []),
+ {{bindResponse, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'unbindRequest'
+ <<1:2,_:1,2:5,_/binary>> ->
+ {Dec, Rest, RbCho} = ?RT_BER:decode_null(Bytes1,[{tag,64,2,'IMPLICIT',32}], mandatory),
+ {{unbindRequest, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'searchRequest'
+ <<1:2,_:1,3:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_SearchRequest'(Bytes1, mandatory, []),
+ {{searchRequest, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'searchResEntry'
+ <<1:2,_:1,4:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_SearchResultEntry'(Bytes1, mandatory, []),
+ {{searchResEntry, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'searchResDone'
+ <<1:2,_:1,5:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_SearchResultDone'(Bytes1, mandatory, []),
+ {{searchResDone, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'searchResRef'
+ <<1:2,_:1,19:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_SearchResultReference'(Bytes1, mandatory, []),
+ {{searchResRef, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'modifyRequest'
+ <<1:2,_:1,6:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_ModifyRequest'(Bytes1, mandatory, []),
+ {{modifyRequest, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'modifyResponse'
+ <<1:2,_:1,7:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_ModifyResponse'(Bytes1, mandatory, []),
+ {{modifyResponse, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'addRequest'
+ <<1:2,_:1,8:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_AddRequest'(Bytes1, mandatory, []),
+ {{addRequest, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'addResponse'
+ <<1:2,_:1,9:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_AddResponse'(Bytes1, mandatory, []),
+ {{addResponse, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'delRequest'
+ <<1:2,_:1,10:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_DelRequest'(Bytes1, mandatory, []),
+ {{delRequest, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'delResponse'
+ <<1:2,_:1,11:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_DelResponse'(Bytes1, mandatory, []),
+ {{delResponse, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'modDNRequest'
+ <<1:2,_:1,12:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_ModifyDNRequest'(Bytes1, mandatory, []),
+ {{modDNRequest, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'modDNResponse'
+ <<1:2,_:1,13:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_ModifyDNResponse'(Bytes1, mandatory, []),
+ {{modDNResponse, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'compareRequest'
+ <<1:2,_:1,14:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_CompareRequest'(Bytes1, mandatory, []),
+ {{compareRequest, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'compareResponse'
+ <<1:2,_:1,15:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_CompareResponse'(Bytes1, mandatory, []),
+ {{compareResponse, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'abandonRequest'
+ <<1:2,_:1,16:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_AbandonRequest'(Bytes1, mandatory, []),
+ {{abandonRequest, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'extendedReq'
+ <<1:2,_:1,23:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_ExtendedRequest'(Bytes1, mandatory, []),
+ {{extendedReq, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'extendedResp'
+ <<1:2,_:1,24:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_ExtendedResponse'(Bytes1, mandatory, []),
+ {{extendedResp, Dec}, Rest, RbExp + RbCho};
+
+ Else ->
+ case OptOrMand of
+ mandatory ->exit({error,{asn1,{invalid_choice_tag,Else}}});
+ _ ->exit({error,{asn1,{no_optional_tag,Else}}})
+ end
+ end.
+
+
+'dec_LDAPMessage'(Bytes, OptOrMand) ->
+ 'dec_LDAPMessage'(Bytes, OptOrMand, []).
+
+'dec_LDAPMessage'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+{Bytes2,RemBytes} = ?RT_BER:split_list(Bytes1,Len),
+
+%%-------------------------------------------------
+%% attribute number 1 with type INTEGER
+%%-------------------------------------------------
+ {Term1,Bytes3,Rb2} = ?RT_BER:decode_integer(Bytes2,{0,2147483647},[], mandatory),
+
+%%-------------------------------------------------
+%% attribute number 2 with type CHOICE
+%%-------------------------------------------------
+ {Term2,Bytes4,Rb3} = 'dec_LDAPMessage_protocolOp'(Bytes3, mandatory, []),
+
+%%-------------------------------------------------
+%% attribute number 3 External ELDAPv3:Controls OPTIONAL
+%%-------------------------------------------------
+ {Term3,Bytes5,Rb4} = case Bytes4 of
+<<2:2,_:1,0:5,_/binary>> ->
+'dec_Controls'(Bytes4, opt_or_default, [{tag,128,0,'IMPLICIT',32}]);
+_ ->
+{ asn1_NOVALUE, Bytes4, 0 }
+end,
+
+{Bytes6,Rb5} = ?RT_BER:restbytes2(RemBytes, Bytes5,noext),
+ {{'LDAPMessage', Term1, Term2, Term3}, Bytes6, Rb1+Rb2+Rb3+Rb4+Rb5}.
+
+
+%%================================
+%% MessageID
+%%================================
+
+'enc_MessageID'({'MessageID',Val}, TagIn) ->
+ 'enc_MessageID'(Val, TagIn);
+
+'enc_MessageID'(Val, TagIn) ->
+?RT_BER:encode_integer([], Val, TagIn ++ []).
+
+
+'dec_MessageID'(Bytes, OptOrMand) ->
+ 'dec_MessageID'(Bytes, OptOrMand, []).
+
+'dec_MessageID'(Bytes, OptOrMand, TagIn) ->
+?RT_BER:decode_integer(Bytes,{0,2147483647},TagIn++[], OptOrMand).
+
+
+
+%%================================
+%% LDAPString
+%%================================
+
+'enc_LDAPString'({'LDAPString',Val}, TagIn) ->
+ 'enc_LDAPString'(Val, TagIn);
+
+'enc_LDAPString'(Val, TagIn) ->
+?RT_BER:encode_octet_string([], Val, TagIn ++ []).
+
+
+'dec_LDAPString'(Bytes, OptOrMand) ->
+ 'dec_LDAPString'(Bytes, OptOrMand, []).
+
+'dec_LDAPString'(Bytes, OptOrMand, TagIn) ->
+?RT_BER:decode_octet_string(Bytes,[],TagIn++[], no_length, OptOrMand).
+
+
+
+%%================================
+%% LDAPOID
+%%================================
+
+'enc_LDAPOID'({'LDAPOID',Val}, TagIn) ->
+ 'enc_LDAPOID'(Val, TagIn);
+
+'enc_LDAPOID'(Val, TagIn) ->
+?RT_BER:encode_octet_string([], Val, TagIn ++ []).
+
+
+'dec_LDAPOID'(Bytes, OptOrMand) ->
+ 'dec_LDAPOID'(Bytes, OptOrMand, []).
+
+'dec_LDAPOID'(Bytes, OptOrMand, TagIn) ->
+?RT_BER:decode_octet_string(Bytes,[],TagIn++[], no_length, OptOrMand).
+
+
+
+%%================================
+%% LDAPDN
+%%================================
+
+'enc_LDAPDN'({'LDAPDN',Val}, TagIn) ->
+ 'enc_LDAPDN'(Val, TagIn);
+
+'enc_LDAPDN'(Val, TagIn) ->
+?RT_BER:encode_octet_string([], Val, TagIn ++ []).
+
+
+'dec_LDAPDN'(Bytes, OptOrMand) ->
+ 'dec_LDAPDN'(Bytes, OptOrMand, []).
+
+'dec_LDAPDN'(Bytes, OptOrMand, TagIn) ->
+?RT_BER:decode_octet_string(Bytes,[],TagIn++[], no_length, OptOrMand).
+
+
+
+%%================================
+%% RelativeLDAPDN
+%%================================
+
+'enc_RelativeLDAPDN'({'RelativeLDAPDN',Val}, TagIn) ->
+ 'enc_RelativeLDAPDN'(Val, TagIn);
+
+'enc_RelativeLDAPDN'(Val, TagIn) ->
+?RT_BER:encode_octet_string([], Val, TagIn ++ []).
+
+
+'dec_RelativeLDAPDN'(Bytes, OptOrMand) ->
+ 'dec_RelativeLDAPDN'(Bytes, OptOrMand, []).
+
+'dec_RelativeLDAPDN'(Bytes, OptOrMand, TagIn) ->
+?RT_BER:decode_octet_string(Bytes,[],TagIn++[], no_length, OptOrMand).
+
+
+
+%%================================
+%% AttributeType
+%%================================
+
+'enc_AttributeType'({'AttributeType',Val}, TagIn) ->
+ 'enc_AttributeType'(Val, TagIn);
+
+'enc_AttributeType'(Val, TagIn) ->
+?RT_BER:encode_octet_string([], Val, TagIn ++ []).
+
+
+'dec_AttributeType'(Bytes, OptOrMand) ->
+ 'dec_AttributeType'(Bytes, OptOrMand, []).
+
+'dec_AttributeType'(Bytes, OptOrMand, TagIn) ->
+?RT_BER:decode_octet_string(Bytes,[],TagIn++[], no_length, OptOrMand).
+
+
+
+%%================================
+%% AttributeDescription
+%%================================
+
+'enc_AttributeDescription'({'AttributeDescription',Val}, TagIn) ->
+ 'enc_AttributeDescription'(Val, TagIn);
+
+'enc_AttributeDescription'(Val, TagIn) ->
+?RT_BER:encode_octet_string([], Val, TagIn ++ []).
+
+
+'dec_AttributeDescription'(Bytes, OptOrMand) ->
+ 'dec_AttributeDescription'(Bytes, OptOrMand, []).
+
+'dec_AttributeDescription'(Bytes, OptOrMand, TagIn) ->
+?RT_BER:decode_octet_string(Bytes,[],TagIn++[], no_length, OptOrMand).
+
+
+
+%%================================
+%% AttributeDescriptionList
+%%================================
+
+'enc_AttributeDescriptionList'({'AttributeDescriptionList',Val}, TagIn) ->
+ 'enc_AttributeDescriptionList'(Val, TagIn);
+
+'enc_AttributeDescriptionList'(Val, TagIn) ->
+ {EncBytes,EncLen} = 'enc_AttributeDescriptionList_components'(Val,[],0),
+ ?RT_BER:encode_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], EncBytes, EncLen).
+
+'enc_AttributeDescriptionList_components'([], AccBytes, AccLen) ->
+ {lists:reverse(AccBytes),AccLen};
+
+'enc_AttributeDescriptionList_components'([H|T],AccBytes, AccLen) ->
+ {EncBytes,EncLen} = ?RT_BER:encode_octet_string([], H, []),
+ 'enc_AttributeDescriptionList_components'(T,[EncBytes|AccBytes], AccLen + EncLen).
+
+
+
+'dec_AttributeDescriptionList'(Bytes, OptOrMand) ->
+ 'dec_AttributeDescriptionList'(Bytes, OptOrMand, []).
+
+'dec_AttributeDescriptionList'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+ ?RT_BER:decode_components(Rb1, Len, Bytes1, fun(FBytes,_,_)->
+?RT_BER:decode_octet_string(FBytes,[],[], no_length, mandatory)
+end, [], []).
+
+
+
+
+%%================================
+%% AttributeValue
+%%================================
+
+'enc_AttributeValue'({'AttributeValue',Val}, TagIn) ->
+ 'enc_AttributeValue'(Val, TagIn);
+
+'enc_AttributeValue'(Val, TagIn) ->
+?RT_BER:encode_octet_string([], Val, TagIn ++ []).
+
+
+'dec_AttributeValue'(Bytes, OptOrMand) ->
+ 'dec_AttributeValue'(Bytes, OptOrMand, []).
+
+'dec_AttributeValue'(Bytes, OptOrMand, TagIn) ->
+?RT_BER:decode_octet_string(Bytes,[],TagIn++[], no_length, OptOrMand).
+
+
+
+%%================================
+%% AttributeValueAssertion
+%%================================
+'enc_AttributeValueAssertion'(Val, TagIn) ->
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {EncBytes1,EncLen1} = ?RT_BER:encode_octet_string([], ?RT_BER:cindex(2,Val,attributeDesc), []),
+
+%%-------------------------------------------------
+%% attribute number 2 with type OCTET STRING
+%%-------------------------------------------------
+ {EncBytes2,EncLen2} = ?RT_BER:encode_octet_string([], ?RT_BER:cindex(3,Val,assertionValue), []),
+
+ BytesSoFar = [EncBytes1, EncBytes2],
+LenSoFar = EncLen1 + EncLen2,
+{TagBytes,Len} = ?RT_BER:encode_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], BytesSoFar, LenSoFar).
+
+
+'dec_AttributeValueAssertion'(Bytes, OptOrMand) ->
+ 'dec_AttributeValueAssertion'(Bytes, OptOrMand, []).
+
+'dec_AttributeValueAssertion'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+{Bytes2,RemBytes} = ?RT_BER:split_list(Bytes1,Len),
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {Term1,Bytes3,Rb2} = ?RT_BER:decode_octet_string(Bytes2,[],[], no_length, mandatory),
+
+%%-------------------------------------------------
+%% attribute number 2 with type OCTET STRING
+%%-------------------------------------------------
+ {Term2,Bytes4,Rb3} = ?RT_BER:decode_octet_string(Bytes3,[],[], no_length, mandatory),
+
+{Bytes5,Rb4} = ?RT_BER:restbytes2(RemBytes, Bytes4,noext),
+ {{'AttributeValueAssertion', Term1, Term2}, Bytes5, Rb1+Rb2+Rb3+Rb4}.
+
+
+%%================================
+%% AssertionValue
+%%================================
+
+'enc_AssertionValue'({'AssertionValue',Val}, TagIn) ->
+ 'enc_AssertionValue'(Val, TagIn);
+
+'enc_AssertionValue'(Val, TagIn) ->
+?RT_BER:encode_octet_string([], Val, TagIn ++ []).
+
+
+'dec_AssertionValue'(Bytes, OptOrMand) ->
+ 'dec_AssertionValue'(Bytes, OptOrMand, []).
+
+'dec_AssertionValue'(Bytes, OptOrMand, TagIn) ->
+?RT_BER:decode_octet_string(Bytes,[],TagIn++[], no_length, OptOrMand).
+
+
+
+%%================================
+%% Attribute
+%%================================
+'enc_Attribute'(Val, TagIn) ->
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {EncBytes1,EncLen1} = ?RT_BER:encode_octet_string([], ?RT_BER:cindex(2,Val,type), []),
+
+%%-------------------------------------------------
+%% attribute number 2 with type SET OF
+%%-------------------------------------------------
+ {EncBytes2,EncLen2} = 'enc_Attribute_vals'(?RT_BER:cindex(3,Val,vals), []),
+
+ BytesSoFar = [EncBytes1, EncBytes2],
+LenSoFar = EncLen1 + EncLen2,
+{TagBytes,Len} = ?RT_BER:encode_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], BytesSoFar, LenSoFar).
+
+
+
+%%================================
+%% Attribute_vals
+%%================================
+
+'enc_Attribute_vals'({'Attribute_vals',Val}, TagIn) ->
+ 'enc_Attribute_vals'(Val, TagIn);
+
+'enc_Attribute_vals'(Val, TagIn) ->
+ {EncBytes,EncLen} = 'enc_Attribute_vals_components'(Val,[],0),
+ ?RT_BER:encode_tags(TagIn ++ [{tag,0,17,'IMPLICIT',32}], EncBytes, EncLen).
+
+'enc_Attribute_vals_components'([], AccBytes, AccLen) ->
+ {lists:reverse(AccBytes),AccLen};
+
+'enc_Attribute_vals_components'([H|T],AccBytes, AccLen) ->
+ {EncBytes,EncLen} = ?RT_BER:encode_octet_string([], H, []),
+ 'enc_Attribute_vals_components'(T,[EncBytes|AccBytes], AccLen + EncLen).
+
+'dec_Attribute_vals'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,0,17,'IMPLICIT',32}], Bytes, OptOrMand),
+ ?RT_BER:decode_components(Rb1, Len, Bytes1, fun(FBytes,_,_)->
+?RT_BER:decode_octet_string(FBytes,[],[], no_length, mandatory)
+end, [], []).
+
+
+
+
+'dec_Attribute'(Bytes, OptOrMand) ->
+ 'dec_Attribute'(Bytes, OptOrMand, []).
+
+'dec_Attribute'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+{Bytes2,RemBytes} = ?RT_BER:split_list(Bytes1,Len),
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {Term1,Bytes3,Rb2} = ?RT_BER:decode_octet_string(Bytes2,[],[], no_length, mandatory),
+
+%%-------------------------------------------------
+%% attribute number 2 with type SET OF
+%%-------------------------------------------------
+ {Term2,Bytes4,Rb3} = 'dec_Attribute_vals'(Bytes3, mandatory, []),
+
+{Bytes5,Rb4} = ?RT_BER:restbytes2(RemBytes, Bytes4,noext),
+ {{'Attribute', Term1, Term2}, Bytes5, Rb1+Rb2+Rb3+Rb4}.
+
+
+%%================================
+%% MatchingRuleId
+%%================================
+
+'enc_MatchingRuleId'({'MatchingRuleId',Val}, TagIn) ->
+ 'enc_MatchingRuleId'(Val, TagIn);
+
+'enc_MatchingRuleId'(Val, TagIn) ->
+?RT_BER:encode_octet_string([], Val, TagIn ++ []).
+
+
+'dec_MatchingRuleId'(Bytes, OptOrMand) ->
+ 'dec_MatchingRuleId'(Bytes, OptOrMand, []).
+
+'dec_MatchingRuleId'(Bytes, OptOrMand, TagIn) ->
+?RT_BER:decode_octet_string(Bytes,[],TagIn++[], no_length, OptOrMand).
+
+
+
+%%================================
+%% LDAPResult
+%%================================
+'enc_LDAPResult'(Val, TagIn) ->
+
+%%-------------------------------------------------
+%% attribute number 1 with type ENUMERATED
+%%-------------------------------------------------
+ {EncBytes1,EncLen1} = case (case ?RT_BER:cindex(2,Val,resultCode) of {_,_}->element(2,?RT_BER:cindex(2,Val,resultCode));_->?RT_BER:cindex(2,Val,resultCode) end) of
+success -> ?RT_BER:encode_enumerated(0,[]);
+operationsError -> ?RT_BER:encode_enumerated(1,[]);
+protocolError -> ?RT_BER:encode_enumerated(2,[]);
+timeLimitExceeded -> ?RT_BER:encode_enumerated(3,[]);
+sizeLimitExceeded -> ?RT_BER:encode_enumerated(4,[]);
+compareFalse -> ?RT_BER:encode_enumerated(5,[]);
+compareTrue -> ?RT_BER:encode_enumerated(6,[]);
+authMethodNotSupported -> ?RT_BER:encode_enumerated(7,[]);
+strongAuthRequired -> ?RT_BER:encode_enumerated(8,[]);
+referral -> ?RT_BER:encode_enumerated(10,[]);
+adminLimitExceeded -> ?RT_BER:encode_enumerated(11,[]);
+unavailableCriticalExtension -> ?RT_BER:encode_enumerated(12,[]);
+confidentialityRequired -> ?RT_BER:encode_enumerated(13,[]);
+saslBindInProgress -> ?RT_BER:encode_enumerated(14,[]);
+noSuchAttribute -> ?RT_BER:encode_enumerated(16,[]);
+undefinedAttributeType -> ?RT_BER:encode_enumerated(17,[]);
+inappropriateMatching -> ?RT_BER:encode_enumerated(18,[]);
+constraintViolation -> ?RT_BER:encode_enumerated(19,[]);
+attributeOrValueExists -> ?RT_BER:encode_enumerated(20,[]);
+invalidAttributeSyntax -> ?RT_BER:encode_enumerated(21,[]);
+noSuchObject -> ?RT_BER:encode_enumerated(32,[]);
+aliasProblem -> ?RT_BER:encode_enumerated(33,[]);
+invalidDNSyntax -> ?RT_BER:encode_enumerated(34,[]);
+aliasDereferencingProblem -> ?RT_BER:encode_enumerated(36,[]);
+inappropriateAuthentication -> ?RT_BER:encode_enumerated(48,[]);
+invalidCredentials -> ?RT_BER:encode_enumerated(49,[]);
+insufficientAccessRights -> ?RT_BER:encode_enumerated(50,[]);
+busy -> ?RT_BER:encode_enumerated(51,[]);
+unavailable -> ?RT_BER:encode_enumerated(52,[]);
+unwillingToPerform -> ?RT_BER:encode_enumerated(53,[]);
+loopDetect -> ?RT_BER:encode_enumerated(54,[]);
+namingViolation -> ?RT_BER:encode_enumerated(64,[]);
+objectClassViolation -> ?RT_BER:encode_enumerated(65,[]);
+notAllowedOnNonLeaf -> ?RT_BER:encode_enumerated(66,[]);
+notAllowedOnRDN -> ?RT_BER:encode_enumerated(67,[]);
+entryAlreadyExists -> ?RT_BER:encode_enumerated(68,[]);
+objectClassModsProhibited -> ?RT_BER:encode_enumerated(69,[]);
+affectsMultipleDSAs -> ?RT_BER:encode_enumerated(71,[]);
+other -> ?RT_BER:encode_enumerated(80,[]);
+Enumval1 -> exit({error,{asn1, {enumerated_not_in_range,Enumval1}}})
+end,
+
+%%-------------------------------------------------
+%% attribute number 2 with type OCTET STRING
+%%-------------------------------------------------
+ {EncBytes2,EncLen2} = ?RT_BER:encode_octet_string([], ?RT_BER:cindex(3,Val,matchedDN), []),
+
+%%-------------------------------------------------
+%% attribute number 3 with type OCTET STRING
+%%-------------------------------------------------
+ {EncBytes3,EncLen3} = ?RT_BER:encode_octet_string([], ?RT_BER:cindex(4,Val,errorMessage), []),
+
+%%-------------------------------------------------
+%% attribute number 4 External ELDAPv3:Referral OPTIONAL
+%%-------------------------------------------------
+ {EncBytes4,EncLen4} = case ?RT_BER:cindex(5,Val,referral) of
+ asn1_NOVALUE -> {<<>>,0};
+ _ ->
+ 'enc_Referral'(?RT_BER:cindex(5,Val,referral), [{tag,128,3,'IMPLICIT',32}])
+ end,
+
+ BytesSoFar = [EncBytes1, EncBytes2, EncBytes3, EncBytes4],
+LenSoFar = EncLen1 + EncLen2 + EncLen3 + EncLen4,
+{TagBytes,Len} = ?RT_BER:encode_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], BytesSoFar, LenSoFar).
+
+
+'dec_LDAPResult'(Bytes, OptOrMand) ->
+ 'dec_LDAPResult'(Bytes, OptOrMand, []).
+
+'dec_LDAPResult'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+{Bytes2,RemBytes} = ?RT_BER:split_list(Bytes1,Len),
+
+%%-------------------------------------------------
+%% attribute number 1 with type ENUMERATED
+%%-------------------------------------------------
+ {Term1,Bytes3,Rb2} = ?RT_BER:decode_enumerated(Bytes2,[],[{success,0},{operationsError,1},{protocolError,2},{timeLimitExceeded,3},{sizeLimitExceeded,4},{compareFalse,5},{compareTrue,6},{authMethodNotSupported,7},{strongAuthRequired,8},{referral,10},{adminLimitExceeded,11},{unavailableCriticalExtension,12},{confidentialityRequired,13},{saslBindInProgress,14},{noSuchAttribute,16},{undefinedAttributeType,17},{inappropriateMatching,18},{constraintViolation,19},{attributeOrValueExists,20},{invalidAttributeSyntax,21},{noSuchObject,32},{aliasProblem,33},{invalidDNSyntax,34},{aliasDereferencingProblem,36},{inappropriateAuthentication,48},{invalidCredentials,49},{insufficientAccessRights,50},{busy,51},{unavailable,52},{unwillingToPerform,53},{loopDetect,54},{namingViolation,64},{objectClassViolation,65},{notAllowedOnNonLeaf,66},{notAllowedOnRDN,67},{entryAlreadyExists,68},{objectClassModsProhibited,69},{affectsMultipleDSAs,71},{other,80}],[], mandatory),
+
+%%-------------------------------------------------
+%% attribute number 2 with type OCTET STRING
+%%-------------------------------------------------
+ {Term2,Bytes4,Rb3} = ?RT_BER:decode_octet_string(Bytes3,[],[], no_length, mandatory),
+
+%%-------------------------------------------------
+%% attribute number 3 with type OCTET STRING
+%%-------------------------------------------------
+ {Term3,Bytes5,Rb4} = ?RT_BER:decode_octet_string(Bytes4,[],[], no_length, mandatory),
+
+%%-------------------------------------------------
+%% attribute number 4 External ELDAPv3:Referral OPTIONAL
+%%-------------------------------------------------
+ {Term4,Bytes6,Rb5} = case Bytes5 of
+<<2:2,_:1,3:5,_/binary>> ->
+'dec_Referral'(Bytes5, opt_or_default, [{tag,128,3,'IMPLICIT',32}]);
+_ ->
+{ asn1_NOVALUE, Bytes5, 0 }
+end,
+
+{Bytes7,Rb6} = ?RT_BER:restbytes2(RemBytes, Bytes6,noext),
+ {{'LDAPResult', Term1, Term2, Term3, Term4}, Bytes7, Rb1+Rb2+Rb3+Rb4+Rb5+Rb6}.
+
+
+%%================================
+%% Referral
+%%================================
+
+'enc_Referral'({'Referral',Val}, TagIn) ->
+ 'enc_Referral'(Val, TagIn);
+
+'enc_Referral'(Val, TagIn) ->
+ {EncBytes,EncLen} = 'enc_Referral_components'(Val,[],0),
+ ?RT_BER:encode_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], EncBytes, EncLen).
+
+'enc_Referral_components'([], AccBytes, AccLen) ->
+ {lists:reverse(AccBytes),AccLen};
+
+'enc_Referral_components'([H|T],AccBytes, AccLen) ->
+ {EncBytes,EncLen} = 'enc_LDAPURL'(H, []),
+ 'enc_Referral_components'(T,[EncBytes|AccBytes], AccLen + EncLen).
+
+
+
+'dec_Referral'(Bytes, OptOrMand) ->
+ 'dec_Referral'(Bytes, OptOrMand, []).
+
+'dec_Referral'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+ ?RT_BER:decode_components(Rb1, Len, Bytes1, fun 'dec_LDAPURL'/3, [], []).
+
+
+
+
+%%================================
+%% LDAPURL
+%%================================
+
+'enc_LDAPURL'({'LDAPURL',Val}, TagIn) ->
+ 'enc_LDAPURL'(Val, TagIn);
+
+'enc_LDAPURL'(Val, TagIn) ->
+?RT_BER:encode_octet_string([], Val, TagIn ++ []).
+
+
+'dec_LDAPURL'(Bytes, OptOrMand) ->
+ 'dec_LDAPURL'(Bytes, OptOrMand, []).
+
+'dec_LDAPURL'(Bytes, OptOrMand, TagIn) ->
+?RT_BER:decode_octet_string(Bytes,[],TagIn++[], no_length, OptOrMand).
+
+
+
+%%================================
+%% Controls
+%%================================
+
+'enc_Controls'({'Controls',Val}, TagIn) ->
+ 'enc_Controls'(Val, TagIn);
+
+'enc_Controls'(Val, TagIn) ->
+ {EncBytes,EncLen} = 'enc_Controls_components'(Val,[],0),
+ ?RT_BER:encode_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], EncBytes, EncLen).
+
+'enc_Controls_components'([], AccBytes, AccLen) ->
+ {lists:reverse(AccBytes),AccLen};
+
+'enc_Controls_components'([H|T],AccBytes, AccLen) ->
+ {EncBytes,EncLen} = 'enc_Control'(H, []),
+ 'enc_Controls_components'(T,[EncBytes|AccBytes], AccLen + EncLen).
+
+
+
+'dec_Controls'(Bytes, OptOrMand) ->
+ 'dec_Controls'(Bytes, OptOrMand, []).
+
+'dec_Controls'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+ ?RT_BER:decode_components(Rb1, Len, Bytes1, fun 'dec_Control'/3, [], []).
+
+
+
+
+%%================================
+%% Control
+%%================================
+'enc_Control'(Val, TagIn) ->
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {EncBytes1,EncLen1} = ?RT_BER:encode_octet_string([], ?RT_BER:cindex(2,Val,controlType), []),
+
+%%-------------------------------------------------
+%% attribute number 2 with type BOOLEAN DEFAULT = false
+%%-------------------------------------------------
+ {EncBytes2,EncLen2} = case ?RT_BER:cindex(3,Val,criticality) of
+ asn1_DEFAULT -> {<<>>,0};
+ false -> {<<>>,0};
+ _ ->
+ ?RT_BER:encode_boolean(?RT_BER:cindex(3,Val,criticality), [])
+ end,
+
+%%-------------------------------------------------
+%% attribute number 3 with type OCTET STRING OPTIONAL
+%%-------------------------------------------------
+ {EncBytes3,EncLen3} = case ?RT_BER:cindex(4,Val,controlValue) of
+ asn1_NOVALUE -> {<<>>,0};
+ _ ->
+ ?RT_BER:encode_octet_string([], ?RT_BER:cindex(4,Val,controlValue), [])
+ end,
+
+ BytesSoFar = [EncBytes1, EncBytes2, EncBytes3],
+LenSoFar = EncLen1 + EncLen2 + EncLen3,
+{TagBytes,Len} = ?RT_BER:encode_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], BytesSoFar, LenSoFar).
+
+
+'dec_Control'(Bytes, OptOrMand) ->
+ 'dec_Control'(Bytes, OptOrMand, []).
+
+'dec_Control'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+{Bytes2,RemBytes} = ?RT_BER:split_list(Bytes1,Len),
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {Term1,Bytes3,Rb2} = ?RT_BER:decode_octet_string(Bytes2,[],[], no_length, mandatory),
+
+%%-------------------------------------------------
+%% attribute number 2 with type BOOLEAN DEFAULT = false
+%%-------------------------------------------------
+ {Term2,Bytes4,Rb3} = case Bytes3 of
+<<0:2,_:1,1:5,_/binary>> ->
+?RT_BER:decode_boolean(Bytes3,[], mandatory);
+_ ->
+{false,Bytes3, 0 }
+end,
+
+%%-------------------------------------------------
+%% attribute number 3 with type OCTET STRING OPTIONAL
+%%-------------------------------------------------
+ {Term3,Bytes5,Rb4} = case Bytes4 of
+<<0:2,_:1,4:5,_/binary>> ->
+?RT_BER:decode_octet_string(Bytes4,[],[], no_length, mandatory);
+_ ->
+{ asn1_NOVALUE, Bytes4, 0 }
+end,
+
+{Bytes6,Rb5} = ?RT_BER:restbytes2(RemBytes, Bytes5,noext),
+ {{'Control', Term1, Term2, Term3}, Bytes6, Rb1+Rb2+Rb3+Rb4+Rb5}.
+
+
+%%================================
+%% BindRequest
+%%================================
+'enc_BindRequest'(Val, TagIn) ->
+
+%%-------------------------------------------------
+%% attribute number 1 with type INTEGER
+%%-------------------------------------------------
+ {EncBytes1,EncLen1} = ?RT_BER:encode_integer([], ?RT_BER:cindex(2,Val,version), []),
+
+%%-------------------------------------------------
+%% attribute number 2 with type OCTET STRING
+%%-------------------------------------------------
+ {EncBytes2,EncLen2} = ?RT_BER:encode_octet_string([], ?RT_BER:cindex(3,Val,name), []),
+
+%%-------------------------------------------------
+%% attribute number 3 External ELDAPv3:AuthenticationChoice
+%%-------------------------------------------------
+ {EncBytes3,EncLen3} = 'enc_AuthenticationChoice'(?RT_BER:cindex(4,Val,authentication), []),
+
+ BytesSoFar = [EncBytes1, EncBytes2, EncBytes3],
+LenSoFar = EncLen1 + EncLen2 + EncLen3,
+{TagBytes,Len} = ?RT_BER:encode_tags(TagIn ++ [{tag,64,0,'IMPLICIT',32},{tag,0,16,'IMPLICIT',32}], BytesSoFar, LenSoFar).
+
+
+'dec_BindRequest'(Bytes, OptOrMand) ->
+ 'dec_BindRequest'(Bytes, OptOrMand, []).
+
+'dec_BindRequest'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,64,0,'IMPLICIT',32},{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+{Bytes2,RemBytes} = ?RT_BER:split_list(Bytes1,Len),
+
+%%-------------------------------------------------
+%% attribute number 1 with type INTEGER
+%%-------------------------------------------------
+ {Term1,Bytes3,Rb2} = ?RT_BER:decode_integer(Bytes2,{1,127},[], mandatory),
+
+%%-------------------------------------------------
+%% attribute number 2 with type OCTET STRING
+%%-------------------------------------------------
+ {Term2,Bytes4,Rb3} = ?RT_BER:decode_octet_string(Bytes3,[],[], no_length, mandatory),
+
+%%-------------------------------------------------
+%% attribute number 3 External ELDAPv3:AuthenticationChoice
+%%-------------------------------------------------
+ {Term3,Bytes5,Rb4} = 'dec_AuthenticationChoice'(Bytes4, mandatory, []),
+
+{Bytes6,Rb5} = ?RT_BER:restbytes2(RemBytes, Bytes5,noext),
+ {{'BindRequest', Term1, Term2, Term3}, Bytes6, Rb1+Rb2+Rb3+Rb4+Rb5}.
+
+
+%%================================
+%% AuthenticationChoice
+%%================================
+
+'enc_AuthenticationChoice'({'AuthenticationChoice',Val}, TagIn) ->
+ 'enc_AuthenticationChoice'(Val, TagIn);
+
+'enc_AuthenticationChoice'(Val, TagIn) ->
+ {EncBytes,EncLen} = case element(1,Val) of
+ simple ->
+ ?RT_BER:encode_octet_string([], element(2,Val), [{tag,128,0,'IMPLICIT',32}]);
+ sasl ->
+ 'enc_SaslCredentials'(element(2,Val), [{tag,128,3,'IMPLICIT',32}]);
+ Else ->
+ exit({error,{asn1,{invalid_choice_type,Else}}})
+ end,
+
+?RT_BER:encode_tags(TagIn ++[], EncBytes, EncLen).
+
+
+
+
+'dec_AuthenticationChoice'(Bytes, OptOrMand) ->
+ 'dec_AuthenticationChoice'(Bytes, OptOrMand, []).
+
+'dec_AuthenticationChoice'(Bytes, OptOrMand, TagIn) ->
+ {FormLen,Bytes1, RbExp} = ?RT_BER:check_tags(TagIn++[], Bytes, OptOrMand),
+ case Bytes1 of
+
+%% 'simple'
+ <<2:2,_:1,0:5,_/binary>> ->
+ {Dec, Rest, RbCho} = ?RT_BER:decode_octet_string(Bytes1,[],[{tag,128,0,'IMPLICIT',32}], no_length, mandatory),
+ {{simple, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'sasl'
+ <<2:2,_:1,3:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_SaslCredentials'(Bytes1, mandatory, [{tag,128,3,'IMPLICIT',32}]),
+ {{sasl, Dec}, Rest, RbExp + RbCho};
+
+ Else ->
+ case OptOrMand of
+ mandatory ->exit({error,{asn1,{invalid_choice_tag,Else}}});
+ _ ->exit({error,{asn1,{no_optional_tag,Else}}})
+ end
+ end.
+
+
+%%================================
+%% SaslCredentials
+%%================================
+'enc_SaslCredentials'(Val, TagIn) ->
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {EncBytes1,EncLen1} = ?RT_BER:encode_octet_string([], ?RT_BER:cindex(2,Val,mechanism), []),
+
+%%-------------------------------------------------
+%% attribute number 2 with type OCTET STRING OPTIONAL
+%%-------------------------------------------------
+ {EncBytes2,EncLen2} = case ?RT_BER:cindex(3,Val,credentials) of
+ asn1_NOVALUE -> {<<>>,0};
+ _ ->
+ ?RT_BER:encode_octet_string([], ?RT_BER:cindex(3,Val,credentials), [])
+ end,
+
+ BytesSoFar = [EncBytes1, EncBytes2],
+LenSoFar = EncLen1 + EncLen2,
+{TagBytes,Len} = ?RT_BER:encode_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], BytesSoFar, LenSoFar).
+
+
+'dec_SaslCredentials'(Bytes, OptOrMand) ->
+ 'dec_SaslCredentials'(Bytes, OptOrMand, []).
+
+'dec_SaslCredentials'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+{Bytes2,RemBytes} = ?RT_BER:split_list(Bytes1,Len),
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {Term1,Bytes3,Rb2} = ?RT_BER:decode_octet_string(Bytes2,[],[], no_length, mandatory),
+
+%%-------------------------------------------------
+%% attribute number 2 with type OCTET STRING OPTIONAL
+%%-------------------------------------------------
+ {Term2,Bytes4,Rb3} = case Bytes3 of
+<<0:2,_:1,4:5,_/binary>> ->
+?RT_BER:decode_octet_string(Bytes3,[],[], no_length, mandatory);
+_ ->
+{ asn1_NOVALUE, Bytes3, 0 }
+end,
+
+{Bytes5,Rb4} = ?RT_BER:restbytes2(RemBytes, Bytes4,noext),
+ {{'SaslCredentials', Term1, Term2}, Bytes5, Rb1+Rb2+Rb3+Rb4}.
+
+
+%%================================
+%% BindResponse
+%%================================
+'enc_BindResponse'(Val, TagIn) ->
+
+%%-------------------------------------------------
+%% attribute number 1 with type ENUMERATED
+%%-------------------------------------------------
+ {EncBytes1,EncLen1} = case (case ?RT_BER:cindex(2,Val,resultCode) of {_,_}->element(2,?RT_BER:cindex(2,Val,resultCode));_->?RT_BER:cindex(2,Val,resultCode) end) of
+success -> ?RT_BER:encode_enumerated(0,[]);
+operationsError -> ?RT_BER:encode_enumerated(1,[]);
+protocolError -> ?RT_BER:encode_enumerated(2,[]);
+timeLimitExceeded -> ?RT_BER:encode_enumerated(3,[]);
+sizeLimitExceeded -> ?RT_BER:encode_enumerated(4,[]);
+compareFalse -> ?RT_BER:encode_enumerated(5,[]);
+compareTrue -> ?RT_BER:encode_enumerated(6,[]);
+authMethodNotSupported -> ?RT_BER:encode_enumerated(7,[]);
+strongAuthRequired -> ?RT_BER:encode_enumerated(8,[]);
+referral -> ?RT_BER:encode_enumerated(10,[]);
+adminLimitExceeded -> ?RT_BER:encode_enumerated(11,[]);
+unavailableCriticalExtension -> ?RT_BER:encode_enumerated(12,[]);
+confidentialityRequired -> ?RT_BER:encode_enumerated(13,[]);
+saslBindInProgress -> ?RT_BER:encode_enumerated(14,[]);
+noSuchAttribute -> ?RT_BER:encode_enumerated(16,[]);
+undefinedAttributeType -> ?RT_BER:encode_enumerated(17,[]);
+inappropriateMatching -> ?RT_BER:encode_enumerated(18,[]);
+constraintViolation -> ?RT_BER:encode_enumerated(19,[]);
+attributeOrValueExists -> ?RT_BER:encode_enumerated(20,[]);
+invalidAttributeSyntax -> ?RT_BER:encode_enumerated(21,[]);
+noSuchObject -> ?RT_BER:encode_enumerated(32,[]);
+aliasProblem -> ?RT_BER:encode_enumerated(33,[]);
+invalidDNSyntax -> ?RT_BER:encode_enumerated(34,[]);
+aliasDereferencingProblem -> ?RT_BER:encode_enumerated(36,[]);
+inappropriateAuthentication -> ?RT_BER:encode_enumerated(48,[]);
+invalidCredentials -> ?RT_BER:encode_enumerated(49,[]);
+insufficientAccessRights -> ?RT_BER:encode_enumerated(50,[]);
+busy -> ?RT_BER:encode_enumerated(51,[]);
+unavailable -> ?RT_BER:encode_enumerated(52,[]);
+unwillingToPerform -> ?RT_BER:encode_enumerated(53,[]);
+loopDetect -> ?RT_BER:encode_enumerated(54,[]);
+namingViolation -> ?RT_BER:encode_enumerated(64,[]);
+objectClassViolation -> ?RT_BER:encode_enumerated(65,[]);
+notAllowedOnNonLeaf -> ?RT_BER:encode_enumerated(66,[]);
+notAllowedOnRDN -> ?RT_BER:encode_enumerated(67,[]);
+entryAlreadyExists -> ?RT_BER:encode_enumerated(68,[]);
+objectClassModsProhibited -> ?RT_BER:encode_enumerated(69,[]);
+affectsMultipleDSAs -> ?RT_BER:encode_enumerated(71,[]);
+other -> ?RT_BER:encode_enumerated(80,[]);
+Enumval1 -> exit({error,{asn1, {enumerated_not_in_range,Enumval1}}})
+end,
+
+%%-------------------------------------------------
+%% attribute number 2 with type OCTET STRING
+%%-------------------------------------------------
+ {EncBytes2,EncLen2} = ?RT_BER:encode_octet_string([], ?RT_BER:cindex(3,Val,matchedDN), []),
+
+%%-------------------------------------------------
+%% attribute number 3 with type OCTET STRING
+%%-------------------------------------------------
+ {EncBytes3,EncLen3} = ?RT_BER:encode_octet_string([], ?RT_BER:cindex(4,Val,errorMessage), []),
+
+%%-------------------------------------------------
+%% attribute number 4 External ELDAPv3:Referral OPTIONAL
+%%-------------------------------------------------
+ {EncBytes4,EncLen4} = case ?RT_BER:cindex(5,Val,referral) of
+ asn1_NOVALUE -> {<<>>,0};
+ _ ->
+ 'enc_Referral'(?RT_BER:cindex(5,Val,referral), [{tag,128,3,'IMPLICIT',32}])
+ end,
+
+%%-------------------------------------------------
+%% attribute number 5 with type OCTET STRING OPTIONAL
+%%-------------------------------------------------
+ {EncBytes5,EncLen5} = case ?RT_BER:cindex(6,Val,serverSaslCreds) of
+ asn1_NOVALUE -> {<<>>,0};
+ _ ->
+ ?RT_BER:encode_octet_string([], ?RT_BER:cindex(6,Val,serverSaslCreds), [{tag,128,7,'IMPLICIT',32}])
+ end,
+
+ BytesSoFar = [EncBytes1, EncBytes2, EncBytes3, EncBytes4, EncBytes5],
+LenSoFar = EncLen1 + EncLen2 + EncLen3 + EncLen4 + EncLen5,
+{TagBytes,Len} = ?RT_BER:encode_tags(TagIn ++ [{tag,64,1,'IMPLICIT',32},{tag,0,16,'IMPLICIT',32}], BytesSoFar, LenSoFar).
+
+
+'dec_BindResponse'(Bytes, OptOrMand) ->
+ 'dec_BindResponse'(Bytes, OptOrMand, []).
+
+'dec_BindResponse'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,64,1,'IMPLICIT',32},{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+{Bytes2,RemBytes} = ?RT_BER:split_list(Bytes1,Len),
+
+%%-------------------------------------------------
+%% attribute number 1 with type ENUMERATED
+%%-------------------------------------------------
+ {Term1,Bytes3,Rb2} = ?RT_BER:decode_enumerated(Bytes2,[],[{success,0},{operationsError,1},{protocolError,2},{timeLimitExceeded,3},{sizeLimitExceeded,4},{compareFalse,5},{compareTrue,6},{authMethodNotSupported,7},{strongAuthRequired,8},{referral,10},{adminLimitExceeded,11},{unavailableCriticalExtension,12},{confidentialityRequired,13},{saslBindInProgress,14},{noSuchAttribute,16},{undefinedAttributeType,17},{inappropriateMatching,18},{constraintViolation,19},{attributeOrValueExists,20},{invalidAttributeSyntax,21},{noSuchObject,32},{aliasProblem,33},{invalidDNSyntax,34},{aliasDereferencingProblem,36},{inappropriateAuthentication,48},{invalidCredentials,49},{insufficientAccessRights,50},{busy,51},{unavailable,52},{unwillingToPerform,53},{loopDetect,54},{namingViolation,64},{objectClassViolation,65},{notAllowedOnNonLeaf,66},{notAllowedOnRDN,67},{entryAlreadyExists,68},{objectClassModsProhibited,69},{affectsMultipleDSAs,71},{other,80}],[], mandatory),
+
+%%-------------------------------------------------
+%% attribute number 2 with type OCTET STRING
+%%-------------------------------------------------
+ {Term2,Bytes4,Rb3} = ?RT_BER:decode_octet_string(Bytes3,[],[], no_length, mandatory),
+
+%%-------------------------------------------------
+%% attribute number 3 with type OCTET STRING
+%%-------------------------------------------------
+ {Term3,Bytes5,Rb4} = ?RT_BER:decode_octet_string(Bytes4,[],[], no_length, mandatory),
+
+%%-------------------------------------------------
+%% attribute number 4 External ELDAPv3:Referral OPTIONAL
+%%-------------------------------------------------
+ {Term4,Bytes6,Rb5} = case Bytes5 of
+<<2:2,_:1,3:5,_/binary>> ->
+'dec_Referral'(Bytes5, opt_or_default, [{tag,128,3,'IMPLICIT',32}]);
+_ ->
+{ asn1_NOVALUE, Bytes5, 0 }
+end,
+
+%%-------------------------------------------------
+%% attribute number 5 with type OCTET STRING OPTIONAL
+%%-------------------------------------------------
+ {Term5,Bytes7,Rb6} = case Bytes6 of
+<<2:2,_:1,7:5,_/binary>> ->
+?RT_BER:decode_octet_string(Bytes6,[],[{tag,128,7,'IMPLICIT',32}], no_length, mandatory);
+_ ->
+{ asn1_NOVALUE, Bytes6, 0 }
+end,
+
+{Bytes8,Rb7} = ?RT_BER:restbytes2(RemBytes, Bytes7,noext),
+ {{'BindResponse', Term1, Term2, Term3, Term4, Term5}, Bytes8, Rb1+Rb2+Rb3+Rb4+Rb5+Rb6+Rb7}.
+
+
+%%================================
+%% UnbindRequest
+%%================================
+
+'enc_UnbindRequest'({'UnbindRequest',Val}, TagIn) ->
+ 'enc_UnbindRequest'(Val, TagIn);
+
+'enc_UnbindRequest'(Val, TagIn) ->
+?RT_BER:encode_null(Val, TagIn ++ [{tag,64,2,'IMPLICIT',32}]).
+
+
+'dec_UnbindRequest'(Bytes, OptOrMand) ->
+ 'dec_UnbindRequest'(Bytes, OptOrMand, []).
+
+'dec_UnbindRequest'(Bytes, OptOrMand, TagIn) ->
+?RT_BER:decode_null(Bytes,TagIn++[{tag,64,2,'IMPLICIT',32}], OptOrMand).
+
+
+
+%%================================
+%% SearchRequest
+%%================================
+'enc_SearchRequest'(Val, TagIn) ->
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {EncBytes1,EncLen1} = ?RT_BER:encode_octet_string([], ?RT_BER:cindex(2,Val,baseObject), []),
+
+%%-------------------------------------------------
+%% attribute number 2 with type ENUMERATED
+%%-------------------------------------------------
+ {EncBytes2,EncLen2} = case (case ?RT_BER:cindex(3,Val,scope) of {_,_}->element(2,?RT_BER:cindex(3,Val,scope));_->?RT_BER:cindex(3,Val,scope) end) of
+baseObject -> ?RT_BER:encode_enumerated(0,[]);
+singleLevel -> ?RT_BER:encode_enumerated(1,[]);
+wholeSubtree -> ?RT_BER:encode_enumerated(2,[]);
+Enumval2 -> exit({error,{asn1, {enumerated_not_in_range,Enumval2}}})
+end,
+
+%%-------------------------------------------------
+%% attribute number 3 with type ENUMERATED
+%%-------------------------------------------------
+ {EncBytes3,EncLen3} = case (case ?RT_BER:cindex(4,Val,derefAliases) of {_,_}->element(2,?RT_BER:cindex(4,Val,derefAliases));_->?RT_BER:cindex(4,Val,derefAliases) end) of
+neverDerefAliases -> ?RT_BER:encode_enumerated(0,[]);
+derefInSearching -> ?RT_BER:encode_enumerated(1,[]);
+derefFindingBaseObj -> ?RT_BER:encode_enumerated(2,[]);
+derefAlways -> ?RT_BER:encode_enumerated(3,[]);
+Enumval3 -> exit({error,{asn1, {enumerated_not_in_range,Enumval3}}})
+end,
+
+%%-------------------------------------------------
+%% attribute number 4 with type INTEGER
+%%-------------------------------------------------
+ {EncBytes4,EncLen4} = ?RT_BER:encode_integer([], ?RT_BER:cindex(5,Val,sizeLimit), []),
+
+%%-------------------------------------------------
+%% attribute number 5 with type INTEGER
+%%-------------------------------------------------
+ {EncBytes5,EncLen5} = ?RT_BER:encode_integer([], ?RT_BER:cindex(6,Val,timeLimit), []),
+
+%%-------------------------------------------------
+%% attribute number 6 with type BOOLEAN
+%%-------------------------------------------------
+ {EncBytes6,EncLen6} = ?RT_BER:encode_boolean(?RT_BER:cindex(7,Val,typesOnly), []),
+
+%%-------------------------------------------------
+%% attribute number 7 External ELDAPv3:Filter
+%%-------------------------------------------------
+ {EncBytes7,EncLen7} = 'enc_Filter'(?RT_BER:cindex(8,Val,filter), []),
+
+%%-------------------------------------------------
+%% attribute number 8 External ELDAPv3:AttributeDescriptionList
+%%-------------------------------------------------
+ {EncBytes8,EncLen8} = 'enc_AttributeDescriptionList'(?RT_BER:cindex(9,Val,attributes), []),
+
+ BytesSoFar = [EncBytes1, EncBytes2, EncBytes3, EncBytes4, EncBytes5, EncBytes6, EncBytes7, EncBytes8],
+LenSoFar = EncLen1 + EncLen2 + EncLen3 + EncLen4 + EncLen5 + EncLen6 + EncLen7 + EncLen8,
+{TagBytes,Len} = ?RT_BER:encode_tags(TagIn ++ [{tag,64,3,'IMPLICIT',32},{tag,0,16,'IMPLICIT',32}], BytesSoFar, LenSoFar).
+
+
+'dec_SearchRequest'(Bytes, OptOrMand) ->
+ 'dec_SearchRequest'(Bytes, OptOrMand, []).
+
+'dec_SearchRequest'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,64,3,'IMPLICIT',32},{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+{Bytes2,RemBytes} = ?RT_BER:split_list(Bytes1,Len),
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {Term1,Bytes3,Rb2} = ?RT_BER:decode_octet_string(Bytes2,[],[], no_length, mandatory),
+
+%%-------------------------------------------------
+%% attribute number 2 with type ENUMERATED
+%%-------------------------------------------------
+ {Term2,Bytes4,Rb3} = ?RT_BER:decode_enumerated(Bytes3,[],[{baseObject,0},{singleLevel,1},{wholeSubtree,2}],[], mandatory),
+
+%%-------------------------------------------------
+%% attribute number 3 with type ENUMERATED
+%%-------------------------------------------------
+ {Term3,Bytes5,Rb4} = ?RT_BER:decode_enumerated(Bytes4,[],[{neverDerefAliases,0},{derefInSearching,1},{derefFindingBaseObj,2},{derefAlways,3}],[], mandatory),
+
+%%-------------------------------------------------
+%% attribute number 4 with type INTEGER
+%%-------------------------------------------------
+ {Term4,Bytes6,Rb5} = ?RT_BER:decode_integer(Bytes5,{0,2147483647},[], mandatory),
+
+%%-------------------------------------------------
+%% attribute number 5 with type INTEGER
+%%-------------------------------------------------
+ {Term5,Bytes7,Rb6} = ?RT_BER:decode_integer(Bytes6,{0,2147483647},[], mandatory),
+
+%%-------------------------------------------------
+%% attribute number 6 with type BOOLEAN
+%%-------------------------------------------------
+ {Term6,Bytes8,Rb7} = ?RT_BER:decode_boolean(Bytes7,[], mandatory),
+
+%%-------------------------------------------------
+%% attribute number 7 External ELDAPv3:Filter
+%%-------------------------------------------------
+ {Term7,Bytes9,Rb8} = 'dec_Filter'(Bytes8, mandatory, []),
+
+%%-------------------------------------------------
+%% attribute number 8 External ELDAPv3:AttributeDescriptionList
+%%-------------------------------------------------
+ {Term8,Bytes10,Rb9} = 'dec_AttributeDescriptionList'(Bytes9, mandatory, []),
+
+{Bytes11,Rb10} = ?RT_BER:restbytes2(RemBytes, Bytes10,noext),
+ {{'SearchRequest', Term1, Term2, Term3, Term4, Term5, Term6, Term7, Term8}, Bytes11, Rb1+Rb2+Rb3+Rb4+Rb5+Rb6+Rb7+Rb8+Rb9+Rb10}.
+
+
+%%================================
+%% Filter
+%%================================
+
+'enc_Filter'({'Filter',Val}, TagIn) ->
+ 'enc_Filter'(Val, TagIn);
+
+'enc_Filter'(Val, TagIn) ->
+ {EncBytes,EncLen} = case element(1,Val) of
+ 'and' ->
+ 'enc_Filter_and'(element(2,Val), [{tag,128,0,'IMPLICIT',32}]);
+ 'or' ->
+ 'enc_Filter_or'(element(2,Val), [{tag,128,1,'IMPLICIT',32}]);
+ 'not' ->
+ 'enc_Filter'(element(2,Val), [{tag,128,2,'EXPLICIT',32}]);
+ equalityMatch ->
+ 'enc_AttributeValueAssertion'(element(2,Val), [{tag,128,3,'IMPLICIT',32}]);
+ substrings ->
+ 'enc_SubstringFilter'(element(2,Val), [{tag,128,4,'IMPLICIT',32}]);
+ greaterOrEqual ->
+ 'enc_AttributeValueAssertion'(element(2,Val), [{tag,128,5,'IMPLICIT',32}]);
+ lessOrEqual ->
+ 'enc_AttributeValueAssertion'(element(2,Val), [{tag,128,6,'IMPLICIT',32}]);
+ present ->
+ ?RT_BER:encode_octet_string([], element(2,Val), [{tag,128,7,'IMPLICIT',32}]);
+ approxMatch ->
+ 'enc_AttributeValueAssertion'(element(2,Val), [{tag,128,8,'IMPLICIT',32}]);
+ extensibleMatch ->
+ 'enc_MatchingRuleAssertion'(element(2,Val), [{tag,128,9,'IMPLICIT',32}]);
+ Else ->
+ exit({error,{asn1,{invalid_choice_type,Else}}})
+ end,
+
+?RT_BER:encode_tags(TagIn ++[], EncBytes, EncLen).
+
+
+
+
+
+%%================================
+%% Filter_and
+%%================================
+
+'enc_Filter_and'({'Filter_and',Val}, TagIn) ->
+ 'enc_Filter_and'(Val, TagIn);
+
+'enc_Filter_and'(Val, TagIn) ->
+ {EncBytes,EncLen} = 'enc_Filter_and_components'(Val,[],0),
+ ?RT_BER:encode_tags(TagIn ++ [{tag,0,17,'IMPLICIT',32}], EncBytes, EncLen).
+
+'enc_Filter_and_components'([], AccBytes, AccLen) ->
+ {lists:reverse(AccBytes),AccLen};
+
+'enc_Filter_and_components'([H|T],AccBytes, AccLen) ->
+ {EncBytes,EncLen} = 'enc_Filter'(H, []),
+ 'enc_Filter_and_components'(T,[EncBytes|AccBytes], AccLen + EncLen).
+
+'dec_Filter_and'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,0,17,'IMPLICIT',32}], Bytes, OptOrMand),
+ ?RT_BER:decode_components(Rb1, Len, Bytes1, fun 'dec_Filter'/3, [], []).
+
+
+
+
+
+%%================================
+%% Filter_or
+%%================================
+
+'enc_Filter_or'({'Filter_or',Val}, TagIn) ->
+ 'enc_Filter_or'(Val, TagIn);
+
+'enc_Filter_or'(Val, TagIn) ->
+ {EncBytes,EncLen} = 'enc_Filter_or_components'(Val,[],0),
+ ?RT_BER:encode_tags(TagIn ++ [{tag,0,17,'IMPLICIT',32}], EncBytes, EncLen).
+
+'enc_Filter_or_components'([], AccBytes, AccLen) ->
+ {lists:reverse(AccBytes),AccLen};
+
+'enc_Filter_or_components'([H|T],AccBytes, AccLen) ->
+ {EncBytes,EncLen} = 'enc_Filter'(H, []),
+ 'enc_Filter_or_components'(T,[EncBytes|AccBytes], AccLen + EncLen).
+
+'dec_Filter_or'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,0,17,'IMPLICIT',32}], Bytes, OptOrMand),
+ ?RT_BER:decode_components(Rb1, Len, Bytes1, fun 'dec_Filter'/3, [], []).
+
+
+
+
+'dec_Filter'(Bytes, OptOrMand) ->
+ 'dec_Filter'(Bytes, OptOrMand, []).
+
+'dec_Filter'(Bytes, OptOrMand, TagIn) ->
+ {FormLen,Bytes1, RbExp} = ?RT_BER:check_tags(TagIn++[], Bytes, OptOrMand),
+ case Bytes1 of
+
+%% 'and'
+ <<2:2,_:1,0:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_Filter_and'(Bytes1, mandatory, [{tag,128,0,'IMPLICIT',32}]),
+ {{'and', Dec}, Rest, RbExp + RbCho};
+
+
+%% 'or'
+ <<2:2,_:1,1:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_Filter_or'(Bytes1, mandatory, [{tag,128,1,'IMPLICIT',32}]),
+ {{'or', Dec}, Rest, RbExp + RbCho};
+
+
+%% 'not'
+ <<2:2,_:1,2:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_Filter'(Bytes1, mandatory, [{tag,128,2,'EXPLICIT',32}]),
+ {{'not', Dec}, Rest, RbExp + RbCho};
+
+
+%% 'equalityMatch'
+ <<2:2,_:1,3:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_AttributeValueAssertion'(Bytes1, mandatory, [{tag,128,3,'IMPLICIT',32}]),
+ {{equalityMatch, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'substrings'
+ <<2:2,_:1,4:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_SubstringFilter'(Bytes1, mandatory, [{tag,128,4,'IMPLICIT',32}]),
+ {{substrings, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'greaterOrEqual'
+ <<2:2,_:1,5:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_AttributeValueAssertion'(Bytes1, mandatory, [{tag,128,5,'IMPLICIT',32}]),
+ {{greaterOrEqual, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'lessOrEqual'
+ <<2:2,_:1,6:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_AttributeValueAssertion'(Bytes1, mandatory, [{tag,128,6,'IMPLICIT',32}]),
+ {{lessOrEqual, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'present'
+ <<2:2,_:1,7:5,_/binary>> ->
+ {Dec, Rest, RbCho} = ?RT_BER:decode_octet_string(Bytes1,[],[{tag,128,7,'IMPLICIT',32}], no_length, mandatory),
+ {{present, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'approxMatch'
+ <<2:2,_:1,8:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_AttributeValueAssertion'(Bytes1, mandatory, [{tag,128,8,'IMPLICIT',32}]),
+ {{approxMatch, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'extensibleMatch'
+ <<2:2,_:1,9:5,_/binary>> ->
+ {Dec, Rest, RbCho} = 'dec_MatchingRuleAssertion'(Bytes1, mandatory, [{tag,128,9,'IMPLICIT',32}]),
+ {{extensibleMatch, Dec}, Rest, RbExp + RbCho};
+
+ Else ->
+ case OptOrMand of
+ mandatory ->exit({error,{asn1,{invalid_choice_tag,Else}}});
+ _ ->exit({error,{asn1,{no_optional_tag,Else}}})
+ end
+ end.
+
+
+%%================================
+%% SubstringFilter
+%%================================
+'enc_SubstringFilter'(Val, TagIn) ->
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {EncBytes1,EncLen1} = ?RT_BER:encode_octet_string([], ?RT_BER:cindex(2,Val,type), []),
+
+%%-------------------------------------------------
+%% attribute number 2 with type SEQUENCE OF
+%%-------------------------------------------------
+ {EncBytes2,EncLen2} = 'enc_SubstringFilter_substrings'(?RT_BER:cindex(3,Val,substrings), []),
+
+ BytesSoFar = [EncBytes1, EncBytes2],
+LenSoFar = EncLen1 + EncLen2,
+{TagBytes,Len} = ?RT_BER:encode_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], BytesSoFar, LenSoFar).
+
+
+
+%%================================
+%% SubstringFilter_substrings
+%%================================
+
+'enc_SubstringFilter_substrings'({'SubstringFilter_substrings',Val}, TagIn) ->
+ 'enc_SubstringFilter_substrings'(Val, TagIn);
+
+'enc_SubstringFilter_substrings'(Val, TagIn) ->
+ {EncBytes,EncLen} = 'enc_SubstringFilter_substrings_components'(Val,[],0),
+ ?RT_BER:encode_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], EncBytes, EncLen).
+
+'enc_SubstringFilter_substrings_components'([], AccBytes, AccLen) ->
+ {lists:reverse(AccBytes),AccLen};
+
+'enc_SubstringFilter_substrings_components'([H|T],AccBytes, AccLen) ->
+ {EncBytes,EncLen} = 'enc_SubstringFilter_substrings_SEQOF'(H, []),
+ 'enc_SubstringFilter_substrings_components'(T,[EncBytes|AccBytes], AccLen + EncLen).
+
+
+
+
+%%================================
+%% SubstringFilter_substrings_SEQOF
+%%================================
+
+'enc_SubstringFilter_substrings_SEQOF'({'SubstringFilter_substrings_SEQOF',Val}, TagIn) ->
+ 'enc_SubstringFilter_substrings_SEQOF'(Val, TagIn);
+
+'enc_SubstringFilter_substrings_SEQOF'(Val, TagIn) ->
+ {EncBytes,EncLen} = case element(1,Val) of
+ initial ->
+ ?RT_BER:encode_octet_string([], element(2,Val), [{tag,128,0,'IMPLICIT',32}]);
+ any ->
+ ?RT_BER:encode_octet_string([], element(2,Val), [{tag,128,1,'IMPLICIT',32}]);
+ final ->
+ ?RT_BER:encode_octet_string([], element(2,Val), [{tag,128,2,'IMPLICIT',32}]);
+ Else ->
+ exit({error,{asn1,{invalid_choice_type,Else}}})
+ end,
+
+?RT_BER:encode_tags(TagIn ++[], EncBytes, EncLen).
+
+
+'dec_SubstringFilter_substrings_SEQOF'(Bytes, OptOrMand, TagIn) ->
+ {FormLen,Bytes1, RbExp} = ?RT_BER:check_tags(TagIn++[], Bytes, OptOrMand),
+ case Bytes1 of
+
+%% 'initial'
+ <<2:2,_:1,0:5,_/binary>> ->
+ {Dec, Rest, RbCho} = ?RT_BER:decode_octet_string(Bytes1,[],[{tag,128,0,'IMPLICIT',32}], no_length, mandatory),
+ {{initial, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'any'
+ <<2:2,_:1,1:5,_/binary>> ->
+ {Dec, Rest, RbCho} = ?RT_BER:decode_octet_string(Bytes1,[],[{tag,128,1,'IMPLICIT',32}], no_length, mandatory),
+ {{any, Dec}, Rest, RbExp + RbCho};
+
+
+%% 'final'
+ <<2:2,_:1,2:5,_/binary>> ->
+ {Dec, Rest, RbCho} = ?RT_BER:decode_octet_string(Bytes1,[],[{tag,128,2,'IMPLICIT',32}], no_length, mandatory),
+ {{final, Dec}, Rest, RbExp + RbCho};
+
+ Else ->
+ case OptOrMand of
+ mandatory ->exit({error,{asn1,{invalid_choice_tag,Else}}});
+ _ ->exit({error,{asn1,{no_optional_tag,Else}}})
+ end
+ end.
+'dec_SubstringFilter_substrings'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+ ?RT_BER:decode_components(Rb1, Len, Bytes1, fun 'dec_SubstringFilter_substrings_SEQOF'/3, [], []).
+
+
+
+
+'dec_SubstringFilter'(Bytes, OptOrMand) ->
+ 'dec_SubstringFilter'(Bytes, OptOrMand, []).
+
+'dec_SubstringFilter'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+{Bytes2,RemBytes} = ?RT_BER:split_list(Bytes1,Len),
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {Term1,Bytes3,Rb2} = ?RT_BER:decode_octet_string(Bytes2,[],[], no_length, mandatory),
+
+%%-------------------------------------------------
+%% attribute number 2 with type SEQUENCE OF
+%%-------------------------------------------------
+ {Term2,Bytes4,Rb3} = 'dec_SubstringFilter_substrings'(Bytes3, mandatory, []),
+
+{Bytes5,Rb4} = ?RT_BER:restbytes2(RemBytes, Bytes4,noext),
+ {{'SubstringFilter', Term1, Term2}, Bytes5, Rb1+Rb2+Rb3+Rb4}.
+
+
+%%================================
+%% MatchingRuleAssertion
+%%================================
+'enc_MatchingRuleAssertion'(Val, TagIn) ->
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING OPTIONAL
+%%-------------------------------------------------
+ {EncBytes1,EncLen1} = case ?RT_BER:cindex(2,Val,matchingRule) of
+ asn1_NOVALUE -> {<<>>,0};
+ _ ->
+ ?RT_BER:encode_octet_string([], ?RT_BER:cindex(2,Val,matchingRule), [{tag,128,1,'IMPLICIT',32}])
+ end,
+
+%%-------------------------------------------------
+%% attribute number 2 with type OCTET STRING OPTIONAL
+%%-------------------------------------------------
+ {EncBytes2,EncLen2} = case ?RT_BER:cindex(3,Val,type) of
+ asn1_NOVALUE -> {<<>>,0};
+ _ ->
+ ?RT_BER:encode_octet_string([], ?RT_BER:cindex(3,Val,type), [{tag,128,2,'IMPLICIT',32}])
+ end,
+
+%%-------------------------------------------------
+%% attribute number 3 with type OCTET STRING
+%%-------------------------------------------------
+ {EncBytes3,EncLen3} = ?RT_BER:encode_octet_string([], ?RT_BER:cindex(4,Val,matchValue), [{tag,128,3,'IMPLICIT',32}]),
+
+%%-------------------------------------------------
+%% attribute number 4 with type BOOLEAN DEFAULT = false
+%%-------------------------------------------------
+ {EncBytes4,EncLen4} = case ?RT_BER:cindex(5,Val,dnAttributes) of
+ asn1_DEFAULT -> {<<>>,0};
+ false -> {<<>>,0};
+ _ ->
+ ?RT_BER:encode_boolean(?RT_BER:cindex(5,Val,dnAttributes), [{tag,128,4,'IMPLICIT',32}])
+ end,
+
+ BytesSoFar = [EncBytes1, EncBytes2, EncBytes3, EncBytes4],
+LenSoFar = EncLen1 + EncLen2 + EncLen3 + EncLen4,
+{TagBytes,Len} = ?RT_BER:encode_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], BytesSoFar, LenSoFar).
+
+
+'dec_MatchingRuleAssertion'(Bytes, OptOrMand) ->
+ 'dec_MatchingRuleAssertion'(Bytes, OptOrMand, []).
+
+'dec_MatchingRuleAssertion'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+{Bytes2,RemBytes} = ?RT_BER:split_list(Bytes1,Len),
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING OPTIONAL
+%%-------------------------------------------------
+ {Term1,Bytes3,Rb2} = case Bytes2 of
+<<2:2,_:1,1:5,_/binary>> ->
+?RT_BER:decode_octet_string(Bytes2,[],[{tag,128,1,'IMPLICIT',32}], no_length, mandatory);
+_ ->
+{ asn1_NOVALUE, Bytes2, 0 }
+end,
+
+%%-------------------------------------------------
+%% attribute number 2 with type OCTET STRING OPTIONAL
+%%-------------------------------------------------
+ {Term2,Bytes4,Rb3} = case Bytes3 of
+<<2:2,_:1,2:5,_/binary>> ->
+?RT_BER:decode_octet_string(Bytes3,[],[{tag,128,2,'IMPLICIT',32}], no_length, mandatory);
+_ ->
+{ asn1_NOVALUE, Bytes3, 0 }
+end,
+
+%%-------------------------------------------------
+%% attribute number 3 with type OCTET STRING
+%%-------------------------------------------------
+ {Term3,Bytes5,Rb4} = ?RT_BER:decode_octet_string(Bytes4,[],[{tag,128,3,'IMPLICIT',32}], no_length, mandatory),
+
+%%-------------------------------------------------
+%% attribute number 4 with type BOOLEAN DEFAULT = false
+%%-------------------------------------------------
+ {Term4,Bytes6,Rb5} = case Bytes5 of
+<<2:2,_:1,4:5,_/binary>> ->
+?RT_BER:decode_boolean(Bytes5,[{tag,128,4,'IMPLICIT',32}], mandatory);
+_ ->
+{false,Bytes5, 0 }
+end,
+
+{Bytes7,Rb6} = ?RT_BER:restbytes2(RemBytes, Bytes6,noext),
+ {{'MatchingRuleAssertion', Term1, Term2, Term3, Term4}, Bytes7, Rb1+Rb2+Rb3+Rb4+Rb5+Rb6}.
+
+
+%%================================
+%% SearchResultEntry
+%%================================
+'enc_SearchResultEntry'(Val, TagIn) ->
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {EncBytes1,EncLen1} = ?RT_BER:encode_octet_string([], ?RT_BER:cindex(2,Val,objectName), []),
+
+%%-------------------------------------------------
+%% attribute number 2 External ELDAPv3:PartialAttributeList
+%%-------------------------------------------------
+ {EncBytes2,EncLen2} = 'enc_PartialAttributeList'(?RT_BER:cindex(3,Val,attributes), []),
+
+ BytesSoFar = [EncBytes1, EncBytes2],
+LenSoFar = EncLen1 + EncLen2,
+{TagBytes,Len} = ?RT_BER:encode_tags(TagIn ++ [{tag,64,4,'IMPLICIT',32},{tag,0,16,'IMPLICIT',32}], BytesSoFar, LenSoFar).
+
+
+'dec_SearchResultEntry'(Bytes, OptOrMand) ->
+ 'dec_SearchResultEntry'(Bytes, OptOrMand, []).
+
+'dec_SearchResultEntry'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,64,4,'IMPLICIT',32},{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+{Bytes2,RemBytes} = ?RT_BER:split_list(Bytes1,Len),
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {Term1,Bytes3,Rb2} = ?RT_BER:decode_octet_string(Bytes2,[],[], no_length, mandatory),
+
+%%-------------------------------------------------
+%% attribute number 2 External ELDAPv3:PartialAttributeList
+%%-------------------------------------------------
+ {Term2,Bytes4,Rb3} = 'dec_PartialAttributeList'(Bytes3, mandatory, []),
+
+{Bytes5,Rb4} = ?RT_BER:restbytes2(RemBytes, Bytes4,noext),
+ {{'SearchResultEntry', Term1, Term2}, Bytes5, Rb1+Rb2+Rb3+Rb4}.
+
+
+%%================================
+%% PartialAttributeList
+%%================================
+
+'enc_PartialAttributeList'({'PartialAttributeList',Val}, TagIn) ->
+ 'enc_PartialAttributeList'(Val, TagIn);
+
+'enc_PartialAttributeList'(Val, TagIn) ->
+ {EncBytes,EncLen} = 'enc_PartialAttributeList_components'(Val,[],0),
+ ?RT_BER:encode_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], EncBytes, EncLen).
+
+'enc_PartialAttributeList_components'([], AccBytes, AccLen) ->
+ {lists:reverse(AccBytes),AccLen};
+
+'enc_PartialAttributeList_components'([H|T],AccBytes, AccLen) ->
+ {EncBytes,EncLen} = 'enc_PartialAttributeList_SEQOF'(H, []),
+ 'enc_PartialAttributeList_components'(T,[EncBytes|AccBytes], AccLen + EncLen).
+
+
+
+
+%%================================
+%% PartialAttributeList_SEQOF
+%%================================
+'enc_PartialAttributeList_SEQOF'(Val, TagIn) ->
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {EncBytes1,EncLen1} = ?RT_BER:encode_octet_string([], ?RT_BER:cindex(2,Val,type), []),
+
+%%-------------------------------------------------
+%% attribute number 2 with type SET OF
+%%-------------------------------------------------
+ {EncBytes2,EncLen2} = 'enc_PartialAttributeList_SEQOF_vals'(?RT_BER:cindex(3,Val,vals), []),
+
+ BytesSoFar = [EncBytes1, EncBytes2],
+LenSoFar = EncLen1 + EncLen2,
+{TagBytes,Len} = ?RT_BER:encode_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], BytesSoFar, LenSoFar).
+
+
+
+%%================================
+%% PartialAttributeList_SEQOF_vals
+%%================================
+
+'enc_PartialAttributeList_SEQOF_vals'({'PartialAttributeList_SEQOF_vals',Val}, TagIn) ->
+ 'enc_PartialAttributeList_SEQOF_vals'(Val, TagIn);
+
+'enc_PartialAttributeList_SEQOF_vals'(Val, TagIn) ->
+ {EncBytes,EncLen} = 'enc_PartialAttributeList_SEQOF_vals_components'(Val,[],0),
+ ?RT_BER:encode_tags(TagIn ++ [{tag,0,17,'IMPLICIT',32}], EncBytes, EncLen).
+
+'enc_PartialAttributeList_SEQOF_vals_components'([], AccBytes, AccLen) ->
+ {lists:reverse(AccBytes),AccLen};
+
+'enc_PartialAttributeList_SEQOF_vals_components'([H|T],AccBytes, AccLen) ->
+ {EncBytes,EncLen} = ?RT_BER:encode_octet_string([], H, []),
+ 'enc_PartialAttributeList_SEQOF_vals_components'(T,[EncBytes|AccBytes], AccLen + EncLen).
+
+'dec_PartialAttributeList_SEQOF_vals'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,0,17,'IMPLICIT',32}], Bytes, OptOrMand),
+ ?RT_BER:decode_components(Rb1, Len, Bytes1, fun(FBytes,_,_)->
+?RT_BER:decode_octet_string(FBytes,[],[], no_length, mandatory)
+end, [], []).
+
+
+'dec_PartialAttributeList_SEQOF'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+{Bytes2,RemBytes} = ?RT_BER:split_list(Bytes1,Len),
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {Term1,Bytes3,Rb2} = ?RT_BER:decode_octet_string(Bytes2,[],[], no_length, mandatory),
+
+%%-------------------------------------------------
+%% attribute number 2 with type SET OF
+%%-------------------------------------------------
+ {Term2,Bytes4,Rb3} = 'dec_PartialAttributeList_SEQOF_vals'(Bytes3, mandatory, []),
+
+{Bytes5,Rb4} = ?RT_BER:restbytes2(RemBytes, Bytes4,noext),
+ {{'PartialAttributeList_SEQOF', Term1, Term2}, Bytes5, Rb1+Rb2+Rb3+Rb4}.
+
+
+'dec_PartialAttributeList'(Bytes, OptOrMand) ->
+ 'dec_PartialAttributeList'(Bytes, OptOrMand, []).
+
+'dec_PartialAttributeList'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+ ?RT_BER:decode_components(Rb1, Len, Bytes1, fun 'dec_PartialAttributeList_SEQOF'/3, [], []).
+
+
+
+
+%%================================
+%% SearchResultReference
+%%================================
+
+'enc_SearchResultReference'({'SearchResultReference',Val}, TagIn) ->
+ 'enc_SearchResultReference'(Val, TagIn);
+
+'enc_SearchResultReference'(Val, TagIn) ->
+ {EncBytes,EncLen} = 'enc_SearchResultReference_components'(Val,[],0),
+ ?RT_BER:encode_tags(TagIn ++ [{tag,64,19,'IMPLICIT',32},{tag,0,16,'IMPLICIT',32}], EncBytes, EncLen).
+
+'enc_SearchResultReference_components'([], AccBytes, AccLen) ->
+ {lists:reverse(AccBytes),AccLen};
+
+'enc_SearchResultReference_components'([H|T],AccBytes, AccLen) ->
+ {EncBytes,EncLen} = ?RT_BER:encode_octet_string([], H, []),
+ 'enc_SearchResultReference_components'(T,[EncBytes|AccBytes], AccLen + EncLen).
+
+
+
+'dec_SearchResultReference'(Bytes, OptOrMand) ->
+ 'dec_SearchResultReference'(Bytes, OptOrMand, []).
+
+'dec_SearchResultReference'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,64,19,'IMPLICIT',32},{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+ ?RT_BER:decode_components(Rb1, Len, Bytes1, fun(FBytes,_,_)->
+?RT_BER:decode_octet_string(FBytes,[],[], no_length, mandatory)
+end, [], []).
+
+
+
+
+%%================================
+%% SearchResultDone
+%%================================
+
+'enc_SearchResultDone'({'SearchResultDone',Val}, TagIn) ->
+ 'enc_SearchResultDone'(Val, TagIn);
+
+'enc_SearchResultDone'(Val, TagIn) ->
+ 'enc_LDAPResult'(Val, TagIn ++ [{tag,64,5,'IMPLICIT',32}]).
+
+
+'dec_SearchResultDone'(Bytes, OptOrMand) ->
+ 'dec_SearchResultDone'(Bytes, OptOrMand, []).
+
+'dec_SearchResultDone'(Bytes, OptOrMand, TagIn) ->
+'dec_LDAPResult'(Bytes, OptOrMand, TagIn++[{tag,64,5,'IMPLICIT',32}]).
+
+
+
+%%================================
+%% ModifyRequest
+%%================================
+'enc_ModifyRequest'(Val, TagIn) ->
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {EncBytes1,EncLen1} = ?RT_BER:encode_octet_string([], ?RT_BER:cindex(2,Val,object), []),
+
+%%-------------------------------------------------
+%% attribute number 2 with type SEQUENCE OF
+%%-------------------------------------------------
+ {EncBytes2,EncLen2} = 'enc_ModifyRequest_modification'(?RT_BER:cindex(3,Val,modification), []),
+
+ BytesSoFar = [EncBytes1, EncBytes2],
+LenSoFar = EncLen1 + EncLen2,
+{TagBytes,Len} = ?RT_BER:encode_tags(TagIn ++ [{tag,64,6,'IMPLICIT',32},{tag,0,16,'IMPLICIT',32}], BytesSoFar, LenSoFar).
+
+
+
+%%================================
+%% ModifyRequest_modification
+%%================================
+
+'enc_ModifyRequest_modification'({'ModifyRequest_modification',Val}, TagIn) ->
+ 'enc_ModifyRequest_modification'(Val, TagIn);
+
+'enc_ModifyRequest_modification'(Val, TagIn) ->
+ {EncBytes,EncLen} = 'enc_ModifyRequest_modification_components'(Val,[],0),
+ ?RT_BER:encode_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], EncBytes, EncLen).
+
+'enc_ModifyRequest_modification_components'([], AccBytes, AccLen) ->
+ {lists:reverse(AccBytes),AccLen};
+
+'enc_ModifyRequest_modification_components'([H|T],AccBytes, AccLen) ->
+ {EncBytes,EncLen} = 'enc_ModifyRequest_modification_SEQOF'(H, []),
+ 'enc_ModifyRequest_modification_components'(T,[EncBytes|AccBytes], AccLen + EncLen).
+
+
+
+
+%%================================
+%% ModifyRequest_modification_SEQOF
+%%================================
+'enc_ModifyRequest_modification_SEQOF'(Val, TagIn) ->
+
+%%-------------------------------------------------
+%% attribute number 1 with type ENUMERATED
+%%-------------------------------------------------
+ {EncBytes1,EncLen1} = case (case ?RT_BER:cindex(2,Val,operation) of {_,_}->element(2,?RT_BER:cindex(2,Val,operation));_->?RT_BER:cindex(2,Val,operation) end) of
+add -> ?RT_BER:encode_enumerated(0,[]);
+delete -> ?RT_BER:encode_enumerated(1,[]);
+replace -> ?RT_BER:encode_enumerated(2,[]);
+Enumval1 -> exit({error,{asn1, {enumerated_not_in_range,Enumval1}}})
+end,
+
+%%-------------------------------------------------
+%% attribute number 2 External ELDAPv3:AttributeTypeAndValues
+%%-------------------------------------------------
+ {EncBytes2,EncLen2} = 'enc_AttributeTypeAndValues'(?RT_BER:cindex(3,Val,modification), []),
+
+ BytesSoFar = [EncBytes1, EncBytes2],
+LenSoFar = EncLen1 + EncLen2,
+{TagBytes,Len} = ?RT_BER:encode_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], BytesSoFar, LenSoFar).
+'dec_ModifyRequest_modification_SEQOF'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+{Bytes2,RemBytes} = ?RT_BER:split_list(Bytes1,Len),
+
+%%-------------------------------------------------
+%% attribute number 1 with type ENUMERATED
+%%-------------------------------------------------
+ {Term1,Bytes3,Rb2} = ?RT_BER:decode_enumerated(Bytes2,[],[{add,0},{delete,1},{replace,2}],[], mandatory),
+
+%%-------------------------------------------------
+%% attribute number 2 External ELDAPv3:AttributeTypeAndValues
+%%-------------------------------------------------
+ {Term2,Bytes4,Rb3} = 'dec_AttributeTypeAndValues'(Bytes3, mandatory, []),
+
+{Bytes5,Rb4} = ?RT_BER:restbytes2(RemBytes, Bytes4,noext),
+ {{'ModifyRequest_modification_SEQOF', Term1, Term2}, Bytes5, Rb1+Rb2+Rb3+Rb4}.
+'dec_ModifyRequest_modification'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+ ?RT_BER:decode_components(Rb1, Len, Bytes1, fun 'dec_ModifyRequest_modification_SEQOF'/3, [], []).
+
+
+
+
+'dec_ModifyRequest'(Bytes, OptOrMand) ->
+ 'dec_ModifyRequest'(Bytes, OptOrMand, []).
+
+'dec_ModifyRequest'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,64,6,'IMPLICIT',32},{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+{Bytes2,RemBytes} = ?RT_BER:split_list(Bytes1,Len),
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {Term1,Bytes3,Rb2} = ?RT_BER:decode_octet_string(Bytes2,[],[], no_length, mandatory),
+
+%%-------------------------------------------------
+%% attribute number 2 with type SEQUENCE OF
+%%-------------------------------------------------
+ {Term2,Bytes4,Rb3} = 'dec_ModifyRequest_modification'(Bytes3, mandatory, []),
+
+{Bytes5,Rb4} = ?RT_BER:restbytes2(RemBytes, Bytes4,noext),
+ {{'ModifyRequest', Term1, Term2}, Bytes5, Rb1+Rb2+Rb3+Rb4}.
+
+
+%%================================
+%% AttributeTypeAndValues
+%%================================
+'enc_AttributeTypeAndValues'(Val, TagIn) ->
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {EncBytes1,EncLen1} = ?RT_BER:encode_octet_string([], ?RT_BER:cindex(2,Val,type), []),
+
+%%-------------------------------------------------
+%% attribute number 2 with type SET OF
+%%-------------------------------------------------
+ {EncBytes2,EncLen2} = 'enc_AttributeTypeAndValues_vals'(?RT_BER:cindex(3,Val,vals), []),
+
+ BytesSoFar = [EncBytes1, EncBytes2],
+LenSoFar = EncLen1 + EncLen2,
+{TagBytes,Len} = ?RT_BER:encode_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], BytesSoFar, LenSoFar).
+
+
+
+%%================================
+%% AttributeTypeAndValues_vals
+%%================================
+
+'enc_AttributeTypeAndValues_vals'({'AttributeTypeAndValues_vals',Val}, TagIn) ->
+ 'enc_AttributeTypeAndValues_vals'(Val, TagIn);
+
+'enc_AttributeTypeAndValues_vals'(Val, TagIn) ->
+ {EncBytes,EncLen} = 'enc_AttributeTypeAndValues_vals_components'(Val,[],0),
+ ?RT_BER:encode_tags(TagIn ++ [{tag,0,17,'IMPLICIT',32}], EncBytes, EncLen).
+
+'enc_AttributeTypeAndValues_vals_components'([], AccBytes, AccLen) ->
+ {lists:reverse(AccBytes),AccLen};
+
+'enc_AttributeTypeAndValues_vals_components'([H|T],AccBytes, AccLen) ->
+ {EncBytes,EncLen} = ?RT_BER:encode_octet_string([], H, []),
+ 'enc_AttributeTypeAndValues_vals_components'(T,[EncBytes|AccBytes], AccLen + EncLen).
+
+'dec_AttributeTypeAndValues_vals'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,0,17,'IMPLICIT',32}], Bytes, OptOrMand),
+ ?RT_BER:decode_components(Rb1, Len, Bytes1, fun(FBytes,_,_)->
+?RT_BER:decode_octet_string(FBytes,[],[], no_length, mandatory)
+end, [], []).
+
+
+
+
+'dec_AttributeTypeAndValues'(Bytes, OptOrMand) ->
+ 'dec_AttributeTypeAndValues'(Bytes, OptOrMand, []).
+
+'dec_AttributeTypeAndValues'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+{Bytes2,RemBytes} = ?RT_BER:split_list(Bytes1,Len),
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {Term1,Bytes3,Rb2} = ?RT_BER:decode_octet_string(Bytes2,[],[], no_length, mandatory),
+
+%%-------------------------------------------------
+%% attribute number 2 with type SET OF
+%%-------------------------------------------------
+ {Term2,Bytes4,Rb3} = 'dec_AttributeTypeAndValues_vals'(Bytes3, mandatory, []),
+
+{Bytes5,Rb4} = ?RT_BER:restbytes2(RemBytes, Bytes4,noext),
+ {{'AttributeTypeAndValues', Term1, Term2}, Bytes5, Rb1+Rb2+Rb3+Rb4}.
+
+
+%%================================
+%% ModifyResponse
+%%================================
+
+'enc_ModifyResponse'({'ModifyResponse',Val}, TagIn) ->
+ 'enc_ModifyResponse'(Val, TagIn);
+
+'enc_ModifyResponse'(Val, TagIn) ->
+ 'enc_LDAPResult'(Val, TagIn ++ [{tag,64,7,'IMPLICIT',32}]).
+
+
+'dec_ModifyResponse'(Bytes, OptOrMand) ->
+ 'dec_ModifyResponse'(Bytes, OptOrMand, []).
+
+'dec_ModifyResponse'(Bytes, OptOrMand, TagIn) ->
+'dec_LDAPResult'(Bytes, OptOrMand, TagIn++[{tag,64,7,'IMPLICIT',32}]).
+
+
+
+%%================================
+%% AddRequest
+%%================================
+'enc_AddRequest'(Val, TagIn) ->
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {EncBytes1,EncLen1} = ?RT_BER:encode_octet_string([], ?RT_BER:cindex(2,Val,entry), []),
+
+%%-------------------------------------------------
+%% attribute number 2 External ELDAPv3:AttributeList
+%%-------------------------------------------------
+ {EncBytes2,EncLen2} = 'enc_AttributeList'(?RT_BER:cindex(3,Val,attributes), []),
+
+ BytesSoFar = [EncBytes1, EncBytes2],
+LenSoFar = EncLen1 + EncLen2,
+{TagBytes,Len} = ?RT_BER:encode_tags(TagIn ++ [{tag,64,8,'IMPLICIT',32},{tag,0,16,'IMPLICIT',32}], BytesSoFar, LenSoFar).
+
+
+'dec_AddRequest'(Bytes, OptOrMand) ->
+ 'dec_AddRequest'(Bytes, OptOrMand, []).
+
+'dec_AddRequest'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,64,8,'IMPLICIT',32},{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+{Bytes2,RemBytes} = ?RT_BER:split_list(Bytes1,Len),
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {Term1,Bytes3,Rb2} = ?RT_BER:decode_octet_string(Bytes2,[],[], no_length, mandatory),
+
+%%-------------------------------------------------
+%% attribute number 2 External ELDAPv3:AttributeList
+%%-------------------------------------------------
+ {Term2,Bytes4,Rb3} = 'dec_AttributeList'(Bytes3, mandatory, []),
+
+{Bytes5,Rb4} = ?RT_BER:restbytes2(RemBytes, Bytes4,noext),
+ {{'AddRequest', Term1, Term2}, Bytes5, Rb1+Rb2+Rb3+Rb4}.
+
+
+%%================================
+%% AttributeList
+%%================================
+
+'enc_AttributeList'({'AttributeList',Val}, TagIn) ->
+ 'enc_AttributeList'(Val, TagIn);
+
+'enc_AttributeList'(Val, TagIn) ->
+ {EncBytes,EncLen} = 'enc_AttributeList_components'(Val,[],0),
+ ?RT_BER:encode_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], EncBytes, EncLen).
+
+'enc_AttributeList_components'([], AccBytes, AccLen) ->
+ {lists:reverse(AccBytes),AccLen};
+
+'enc_AttributeList_components'([H|T],AccBytes, AccLen) ->
+ {EncBytes,EncLen} = 'enc_AttributeList_SEQOF'(H, []),
+ 'enc_AttributeList_components'(T,[EncBytes|AccBytes], AccLen + EncLen).
+
+
+
+
+%%================================
+%% AttributeList_SEQOF
+%%================================
+'enc_AttributeList_SEQOF'(Val, TagIn) ->
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {EncBytes1,EncLen1} = ?RT_BER:encode_octet_string([], ?RT_BER:cindex(2,Val,type), []),
+
+%%-------------------------------------------------
+%% attribute number 2 with type SET OF
+%%-------------------------------------------------
+ {EncBytes2,EncLen2} = 'enc_AttributeList_SEQOF_vals'(?RT_BER:cindex(3,Val,vals), []),
+
+ BytesSoFar = [EncBytes1, EncBytes2],
+LenSoFar = EncLen1 + EncLen2,
+{TagBytes,Len} = ?RT_BER:encode_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], BytesSoFar, LenSoFar).
+
+
+
+%%================================
+%% AttributeList_SEQOF_vals
+%%================================
+
+'enc_AttributeList_SEQOF_vals'({'AttributeList_SEQOF_vals',Val}, TagIn) ->
+ 'enc_AttributeList_SEQOF_vals'(Val, TagIn);
+
+'enc_AttributeList_SEQOF_vals'(Val, TagIn) ->
+ {EncBytes,EncLen} = 'enc_AttributeList_SEQOF_vals_components'(Val,[],0),
+ ?RT_BER:encode_tags(TagIn ++ [{tag,0,17,'IMPLICIT',32}], EncBytes, EncLen).
+
+'enc_AttributeList_SEQOF_vals_components'([], AccBytes, AccLen) ->
+ {lists:reverse(AccBytes),AccLen};
+
+'enc_AttributeList_SEQOF_vals_components'([H|T],AccBytes, AccLen) ->
+ {EncBytes,EncLen} = ?RT_BER:encode_octet_string([], H, []),
+ 'enc_AttributeList_SEQOF_vals_components'(T,[EncBytes|AccBytes], AccLen + EncLen).
+
+'dec_AttributeList_SEQOF_vals'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,0,17,'IMPLICIT',32}], Bytes, OptOrMand),
+ ?RT_BER:decode_components(Rb1, Len, Bytes1, fun(FBytes,_,_)->
+?RT_BER:decode_octet_string(FBytes,[],[], no_length, mandatory)
+end, [], []).
+
+
+'dec_AttributeList_SEQOF'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+{Bytes2,RemBytes} = ?RT_BER:split_list(Bytes1,Len),
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {Term1,Bytes3,Rb2} = ?RT_BER:decode_octet_string(Bytes2,[],[], no_length, mandatory),
+
+%%-------------------------------------------------
+%% attribute number 2 with type SET OF
+%%-------------------------------------------------
+ {Term2,Bytes4,Rb3} = 'dec_AttributeList_SEQOF_vals'(Bytes3, mandatory, []),
+
+{Bytes5,Rb4} = ?RT_BER:restbytes2(RemBytes, Bytes4,noext),
+ {{'AttributeList_SEQOF', Term1, Term2}, Bytes5, Rb1+Rb2+Rb3+Rb4}.
+
+
+'dec_AttributeList'(Bytes, OptOrMand) ->
+ 'dec_AttributeList'(Bytes, OptOrMand, []).
+
+'dec_AttributeList'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+ ?RT_BER:decode_components(Rb1, Len, Bytes1, fun 'dec_AttributeList_SEQOF'/3, [], []).
+
+
+
+
+%%================================
+%% AddResponse
+%%================================
+
+'enc_AddResponse'({'AddResponse',Val}, TagIn) ->
+ 'enc_AddResponse'(Val, TagIn);
+
+'enc_AddResponse'(Val, TagIn) ->
+ 'enc_LDAPResult'(Val, TagIn ++ [{tag,64,9,'IMPLICIT',32}]).
+
+
+'dec_AddResponse'(Bytes, OptOrMand) ->
+ 'dec_AddResponse'(Bytes, OptOrMand, []).
+
+'dec_AddResponse'(Bytes, OptOrMand, TagIn) ->
+'dec_LDAPResult'(Bytes, OptOrMand, TagIn++[{tag,64,9,'IMPLICIT',32}]).
+
+
+
+%%================================
+%% DelRequest
+%%================================
+
+'enc_DelRequest'({'DelRequest',Val}, TagIn) ->
+ 'enc_DelRequest'(Val, TagIn);
+
+'enc_DelRequest'(Val, TagIn) ->
+?RT_BER:encode_octet_string([], Val, TagIn ++ [{tag,64,10,'IMPLICIT',32}]).
+
+
+'dec_DelRequest'(Bytes, OptOrMand) ->
+ 'dec_DelRequest'(Bytes, OptOrMand, []).
+
+'dec_DelRequest'(Bytes, OptOrMand, TagIn) ->
+?RT_BER:decode_octet_string(Bytes,[],TagIn++[{tag,64,10,'IMPLICIT',32}], no_length, OptOrMand).
+
+
+
+%%================================
+%% DelResponse
+%%================================
+
+'enc_DelResponse'({'DelResponse',Val}, TagIn) ->
+ 'enc_DelResponse'(Val, TagIn);
+
+'enc_DelResponse'(Val, TagIn) ->
+ 'enc_LDAPResult'(Val, TagIn ++ [{tag,64,11,'IMPLICIT',32}]).
+
+
+'dec_DelResponse'(Bytes, OptOrMand) ->
+ 'dec_DelResponse'(Bytes, OptOrMand, []).
+
+'dec_DelResponse'(Bytes, OptOrMand, TagIn) ->
+'dec_LDAPResult'(Bytes, OptOrMand, TagIn++[{tag,64,11,'IMPLICIT',32}]).
+
+
+
+%%================================
+%% ModifyDNRequest
+%%================================
+'enc_ModifyDNRequest'(Val, TagIn) ->
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {EncBytes1,EncLen1} = ?RT_BER:encode_octet_string([], ?RT_BER:cindex(2,Val,entry), []),
+
+%%-------------------------------------------------
+%% attribute number 2 with type OCTET STRING
+%%-------------------------------------------------
+ {EncBytes2,EncLen2} = ?RT_BER:encode_octet_string([], ?RT_BER:cindex(3,Val,newrdn), []),
+
+%%-------------------------------------------------
+%% attribute number 3 with type BOOLEAN
+%%-------------------------------------------------
+ {EncBytes3,EncLen3} = ?RT_BER:encode_boolean(?RT_BER:cindex(4,Val,deleteoldrdn), []),
+
+%%-------------------------------------------------
+%% attribute number 4 with type OCTET STRING OPTIONAL
+%%-------------------------------------------------
+ {EncBytes4,EncLen4} = case ?RT_BER:cindex(5,Val,newSuperior) of
+ asn1_NOVALUE -> {<<>>,0};
+ _ ->
+ ?RT_BER:encode_octet_string([], ?RT_BER:cindex(5,Val,newSuperior), [{tag,128,0,'IMPLICIT',32}])
+ end,
+
+ BytesSoFar = [EncBytes1, EncBytes2, EncBytes3, EncBytes4],
+LenSoFar = EncLen1 + EncLen2 + EncLen3 + EncLen4,
+{TagBytes,Len} = ?RT_BER:encode_tags(TagIn ++ [{tag,64,12,'IMPLICIT',32},{tag,0,16,'IMPLICIT',32}], BytesSoFar, LenSoFar).
+
+
+'dec_ModifyDNRequest'(Bytes, OptOrMand) ->
+ 'dec_ModifyDNRequest'(Bytes, OptOrMand, []).
+
+'dec_ModifyDNRequest'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,64,12,'IMPLICIT',32},{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+{Bytes2,RemBytes} = ?RT_BER:split_list(Bytes1,Len),
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {Term1,Bytes3,Rb2} = ?RT_BER:decode_octet_string(Bytes2,[],[], no_length, mandatory),
+
+%%-------------------------------------------------
+%% attribute number 2 with type OCTET STRING
+%%-------------------------------------------------
+ {Term2,Bytes4,Rb3} = ?RT_BER:decode_octet_string(Bytes3,[],[], no_length, mandatory),
+
+%%-------------------------------------------------
+%% attribute number 3 with type BOOLEAN
+%%-------------------------------------------------
+ {Term3,Bytes5,Rb4} = ?RT_BER:decode_boolean(Bytes4,[], mandatory),
+
+%%-------------------------------------------------
+%% attribute number 4 with type OCTET STRING OPTIONAL
+%%-------------------------------------------------
+ {Term4,Bytes6,Rb5} = case Bytes5 of
+<<2:2,_:1,0:5,_/binary>> ->
+?RT_BER:decode_octet_string(Bytes5,[],[{tag,128,0,'IMPLICIT',32}], no_length, mandatory);
+_ ->
+{ asn1_NOVALUE, Bytes5, 0 }
+end,
+
+{Bytes7,Rb6} = ?RT_BER:restbytes2(RemBytes, Bytes6,noext),
+ {{'ModifyDNRequest', Term1, Term2, Term3, Term4}, Bytes7, Rb1+Rb2+Rb3+Rb4+Rb5+Rb6}.
+
+
+%%================================
+%% ModifyDNResponse
+%%================================
+
+'enc_ModifyDNResponse'({'ModifyDNResponse',Val}, TagIn) ->
+ 'enc_ModifyDNResponse'(Val, TagIn);
+
+'enc_ModifyDNResponse'(Val, TagIn) ->
+ 'enc_LDAPResult'(Val, TagIn ++ [{tag,64,13,'IMPLICIT',32}]).
+
+
+'dec_ModifyDNResponse'(Bytes, OptOrMand) ->
+ 'dec_ModifyDNResponse'(Bytes, OptOrMand, []).
+
+'dec_ModifyDNResponse'(Bytes, OptOrMand, TagIn) ->
+'dec_LDAPResult'(Bytes, OptOrMand, TagIn++[{tag,64,13,'IMPLICIT',32}]).
+
+
+
+%%================================
+%% CompareRequest
+%%================================
+'enc_CompareRequest'(Val, TagIn) ->
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {EncBytes1,EncLen1} = ?RT_BER:encode_octet_string([], ?RT_BER:cindex(2,Val,entry), []),
+
+%%-------------------------------------------------
+%% attribute number 2 External ELDAPv3:AttributeValueAssertion
+%%-------------------------------------------------
+ {EncBytes2,EncLen2} = 'enc_AttributeValueAssertion'(?RT_BER:cindex(3,Val,ava), []),
+
+ BytesSoFar = [EncBytes1, EncBytes2],
+LenSoFar = EncLen1 + EncLen2,
+{TagBytes,Len} = ?RT_BER:encode_tags(TagIn ++ [{tag,64,14,'IMPLICIT',32},{tag,0,16,'IMPLICIT',32}], BytesSoFar, LenSoFar).
+
+
+'dec_CompareRequest'(Bytes, OptOrMand) ->
+ 'dec_CompareRequest'(Bytes, OptOrMand, []).
+
+'dec_CompareRequest'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,64,14,'IMPLICIT',32},{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+{Bytes2,RemBytes} = ?RT_BER:split_list(Bytes1,Len),
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {Term1,Bytes3,Rb2} = ?RT_BER:decode_octet_string(Bytes2,[],[], no_length, mandatory),
+
+%%-------------------------------------------------
+%% attribute number 2 External ELDAPv3:AttributeValueAssertion
+%%-------------------------------------------------
+ {Term2,Bytes4,Rb3} = 'dec_AttributeValueAssertion'(Bytes3, mandatory, []),
+
+{Bytes5,Rb4} = ?RT_BER:restbytes2(RemBytes, Bytes4,noext),
+ {{'CompareRequest', Term1, Term2}, Bytes5, Rb1+Rb2+Rb3+Rb4}.
+
+
+%%================================
+%% CompareResponse
+%%================================
+
+'enc_CompareResponse'({'CompareResponse',Val}, TagIn) ->
+ 'enc_CompareResponse'(Val, TagIn);
+
+'enc_CompareResponse'(Val, TagIn) ->
+ 'enc_LDAPResult'(Val, TagIn ++ [{tag,64,15,'IMPLICIT',32}]).
+
+
+'dec_CompareResponse'(Bytes, OptOrMand) ->
+ 'dec_CompareResponse'(Bytes, OptOrMand, []).
+
+'dec_CompareResponse'(Bytes, OptOrMand, TagIn) ->
+'dec_LDAPResult'(Bytes, OptOrMand, TagIn++[{tag,64,15,'IMPLICIT',32}]).
+
+
+
+%%================================
+%% AbandonRequest
+%%================================
+
+'enc_AbandonRequest'({'AbandonRequest',Val}, TagIn) ->
+ 'enc_AbandonRequest'(Val, TagIn);
+
+'enc_AbandonRequest'(Val, TagIn) ->
+?RT_BER:encode_integer([], Val, TagIn ++ [{tag,64,16,'IMPLICIT',32}]).
+
+
+'dec_AbandonRequest'(Bytes, OptOrMand) ->
+ 'dec_AbandonRequest'(Bytes, OptOrMand, []).
+
+'dec_AbandonRequest'(Bytes, OptOrMand, TagIn) ->
+?RT_BER:decode_integer(Bytes,{0,2147483647},TagIn++[{tag,64,16,'IMPLICIT',32}], OptOrMand).
+
+
+
+%%================================
+%% ExtendedRequest
+%%================================
+'enc_ExtendedRequest'(Val, TagIn) ->
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {EncBytes1,EncLen1} = ?RT_BER:encode_octet_string([], ?RT_BER:cindex(2,Val,requestName), [{tag,128,0,'IMPLICIT',32}]),
+
+%%-------------------------------------------------
+%% attribute number 2 with type OCTET STRING OPTIONAL
+%%-------------------------------------------------
+ {EncBytes2,EncLen2} = case ?RT_BER:cindex(3,Val,requestValue) of
+ asn1_NOVALUE -> {<<>>,0};
+ _ ->
+ ?RT_BER:encode_octet_string([], ?RT_BER:cindex(3,Val,requestValue), [{tag,128,1,'IMPLICIT',32}])
+ end,
+
+ BytesSoFar = [EncBytes1, EncBytes2],
+LenSoFar = EncLen1 + EncLen2,
+{TagBytes,Len} = ?RT_BER:encode_tags(TagIn ++ [{tag,64,23,'IMPLICIT',32},{tag,0,16,'IMPLICIT',32}], BytesSoFar, LenSoFar).
+
+
+'dec_ExtendedRequest'(Bytes, OptOrMand) ->
+ 'dec_ExtendedRequest'(Bytes, OptOrMand, []).
+
+'dec_ExtendedRequest'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,64,23,'IMPLICIT',32},{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+{Bytes2,RemBytes} = ?RT_BER:split_list(Bytes1,Len),
+
+%%-------------------------------------------------
+%% attribute number 1 with type OCTET STRING
+%%-------------------------------------------------
+ {Term1,Bytes3,Rb2} = ?RT_BER:decode_octet_string(Bytes2,[],[{tag,128,0,'IMPLICIT',32}], no_length, mandatory),
+
+%%-------------------------------------------------
+%% attribute number 2 with type OCTET STRING OPTIONAL
+%%-------------------------------------------------
+ {Term2,Bytes4,Rb3} = case Bytes3 of
+<<2:2,_:1,1:5,_/binary>> ->
+?RT_BER:decode_octet_string(Bytes3,[],[{tag,128,1,'IMPLICIT',32}], no_length, mandatory);
+_ ->
+{ asn1_NOVALUE, Bytes3, 0 }
+end,
+
+{Bytes5,Rb4} = ?RT_BER:restbytes2(RemBytes, Bytes4,noext),
+ {{'ExtendedRequest', Term1, Term2}, Bytes5, Rb1+Rb2+Rb3+Rb4}.
+
+
+%%================================
+%% ExtendedResponse
+%%================================
+'enc_ExtendedResponse'(Val, TagIn) ->
+
+%%-------------------------------------------------
+%% attribute number 1 with type ENUMERATED
+%%-------------------------------------------------
+ {EncBytes1,EncLen1} = case (case ?RT_BER:cindex(2,Val,resultCode) of {_,_}->element(2,?RT_BER:cindex(2,Val,resultCode));_->?RT_BER:cindex(2,Val,resultCode) end) of
+success -> ?RT_BER:encode_enumerated(0,[]);
+operationsError -> ?RT_BER:encode_enumerated(1,[]);
+protocolError -> ?RT_BER:encode_enumerated(2,[]);
+timeLimitExceeded -> ?RT_BER:encode_enumerated(3,[]);
+sizeLimitExceeded -> ?RT_BER:encode_enumerated(4,[]);
+compareFalse -> ?RT_BER:encode_enumerated(5,[]);
+compareTrue -> ?RT_BER:encode_enumerated(6,[]);
+authMethodNotSupported -> ?RT_BER:encode_enumerated(7,[]);
+strongAuthRequired -> ?RT_BER:encode_enumerated(8,[]);
+referral -> ?RT_BER:encode_enumerated(10,[]);
+adminLimitExceeded -> ?RT_BER:encode_enumerated(11,[]);
+unavailableCriticalExtension -> ?RT_BER:encode_enumerated(12,[]);
+confidentialityRequired -> ?RT_BER:encode_enumerated(13,[]);
+saslBindInProgress -> ?RT_BER:encode_enumerated(14,[]);
+noSuchAttribute -> ?RT_BER:encode_enumerated(16,[]);
+undefinedAttributeType -> ?RT_BER:encode_enumerated(17,[]);
+inappropriateMatching -> ?RT_BER:encode_enumerated(18,[]);
+constraintViolation -> ?RT_BER:encode_enumerated(19,[]);
+attributeOrValueExists -> ?RT_BER:encode_enumerated(20,[]);
+invalidAttributeSyntax -> ?RT_BER:encode_enumerated(21,[]);
+noSuchObject -> ?RT_BER:encode_enumerated(32,[]);
+aliasProblem -> ?RT_BER:encode_enumerated(33,[]);
+invalidDNSyntax -> ?RT_BER:encode_enumerated(34,[]);
+aliasDereferencingProblem -> ?RT_BER:encode_enumerated(36,[]);
+inappropriateAuthentication -> ?RT_BER:encode_enumerated(48,[]);
+invalidCredentials -> ?RT_BER:encode_enumerated(49,[]);
+insufficientAccessRights -> ?RT_BER:encode_enumerated(50,[]);
+busy -> ?RT_BER:encode_enumerated(51,[]);
+unavailable -> ?RT_BER:encode_enumerated(52,[]);
+unwillingToPerform -> ?RT_BER:encode_enumerated(53,[]);
+loopDetect -> ?RT_BER:encode_enumerated(54,[]);
+namingViolation -> ?RT_BER:encode_enumerated(64,[]);
+objectClassViolation -> ?RT_BER:encode_enumerated(65,[]);
+notAllowedOnNonLeaf -> ?RT_BER:encode_enumerated(66,[]);
+notAllowedOnRDN -> ?RT_BER:encode_enumerated(67,[]);
+entryAlreadyExists -> ?RT_BER:encode_enumerated(68,[]);
+objectClassModsProhibited -> ?RT_BER:encode_enumerated(69,[]);
+affectsMultipleDSAs -> ?RT_BER:encode_enumerated(71,[]);
+other -> ?RT_BER:encode_enumerated(80,[]);
+Enumval1 -> exit({error,{asn1, {enumerated_not_in_range,Enumval1}}})
+end,
+
+%%-------------------------------------------------
+%% attribute number 2 with type OCTET STRING
+%%-------------------------------------------------
+ {EncBytes2,EncLen2} = ?RT_BER:encode_octet_string([], ?RT_BER:cindex(3,Val,matchedDN), []),
+
+%%-------------------------------------------------
+%% attribute number 3 with type OCTET STRING
+%%-------------------------------------------------
+ {EncBytes3,EncLen3} = ?RT_BER:encode_octet_string([], ?RT_BER:cindex(4,Val,errorMessage), []),
+
+%%-------------------------------------------------
+%% attribute number 4 External ELDAPv3:Referral OPTIONAL
+%%-------------------------------------------------
+ {EncBytes4,EncLen4} = case ?RT_BER:cindex(5,Val,referral) of
+ asn1_NOVALUE -> {<<>>,0};
+ _ ->
+ 'enc_Referral'(?RT_BER:cindex(5,Val,referral), [{tag,128,3,'IMPLICIT',32}])
+ end,
+
+%%-------------------------------------------------
+%% attribute number 5 with type OCTET STRING OPTIONAL
+%%-------------------------------------------------
+ {EncBytes5,EncLen5} = case ?RT_BER:cindex(6,Val,responseName) of
+ asn1_NOVALUE -> {<<>>,0};
+ _ ->
+ ?RT_BER:encode_octet_string([], ?RT_BER:cindex(6,Val,responseName), [{tag,128,10,'IMPLICIT',32}])
+ end,
+
+%%-------------------------------------------------
+%% attribute number 6 with type OCTET STRING OPTIONAL
+%%-------------------------------------------------
+ {EncBytes6,EncLen6} = case ?RT_BER:cindex(7,Val,response) of
+ asn1_NOVALUE -> {<<>>,0};
+ _ ->
+ ?RT_BER:encode_octet_string([], ?RT_BER:cindex(7,Val,response), [{tag,128,11,'IMPLICIT',32}])
+ end,
+
+ BytesSoFar = [EncBytes1, EncBytes2, EncBytes3, EncBytes4, EncBytes5, EncBytes6],
+LenSoFar = EncLen1 + EncLen2 + EncLen3 + EncLen4 + EncLen5 + EncLen6,
+{TagBytes,Len} = ?RT_BER:encode_tags(TagIn ++ [{tag,64,24,'IMPLICIT',32},{tag,0,16,'IMPLICIT',32}], BytesSoFar, LenSoFar).
+
+
+'dec_ExtendedResponse'(Bytes, OptOrMand) ->
+ 'dec_ExtendedResponse'(Bytes, OptOrMand, []).
+
+'dec_ExtendedResponse'(Bytes, OptOrMand, TagIn) ->
+ %%-------------------------------------------------
+ %% decode tag and length
+ %%-------------------------------------------------
+ {{_,Len},Bytes1,Rb1} = ?RT_BER:check_tags(TagIn ++ [{tag,64,24,'IMPLICIT',32},{tag,0,16,'IMPLICIT',32}], Bytes, OptOrMand),
+{Bytes2,RemBytes} = ?RT_BER:split_list(Bytes1,Len),
+
+%%-------------------------------------------------
+%% attribute number 1 with type ENUMERATED
+%%-------------------------------------------------
+ {Term1,Bytes3,Rb2} = ?RT_BER:decode_enumerated(Bytes2,[],[{success,0},{operationsError,1},{protocolError,2},{timeLimitExceeded,3},{sizeLimitExceeded,4},{compareFalse,5},{compareTrue,6},{authMethodNotSupported,7},{strongAuthRequired,8},{referral,10},{adminLimitExceeded,11},{unavailableCriticalExtension,12},{confidentialityRequired,13},{saslBindInProgress,14},{noSuchAttribute,16},{undefinedAttributeType,17},{inappropriateMatching,18},{constraintViolation,19},{attributeOrValueExists,20},{invalidAttributeSyntax,21},{noSuchObject,32},{aliasProblem,33},{invalidDNSyntax,34},{aliasDereferencingProblem,36},{inappropriateAuthentication,48},{invalidCredentials,49},{insufficientAccessRights,50},{busy,51},{unavailable,52},{unwillingToPerform,53},{loopDetect,54},{namingViolation,64},{objectClassViolation,65},{notAllowedOnNonLeaf,66},{notAllowedOnRDN,67},{entryAlreadyExists,68},{objectClassModsProhibited,69},{affectsMultipleDSAs,71},{other,80}],[], mandatory),
+
+%%-------------------------------------------------
+%% attribute number 2 with type OCTET STRING
+%%-------------------------------------------------
+ {Term2,Bytes4,Rb3} = ?RT_BER:decode_octet_string(Bytes3,[],[], no_length, mandatory),
+
+%%-------------------------------------------------
+%% attribute number 3 with type OCTET STRING
+%%-------------------------------------------------
+ {Term3,Bytes5,Rb4} = ?RT_BER:decode_octet_string(Bytes4,[],[], no_length, mandatory),
+
+%%-------------------------------------------------
+%% attribute number 4 External ELDAPv3:Referral OPTIONAL
+%%-------------------------------------------------
+ {Term4,Bytes6,Rb5} = case Bytes5 of
+<<2:2,_:1,3:5,_/binary>> ->
+'dec_Referral'(Bytes5, opt_or_default, [{tag,128,3,'IMPLICIT',32}]);
+_ ->
+{ asn1_NOVALUE, Bytes5, 0 }
+end,
+
+%%-------------------------------------------------
+%% attribute number 5 with type OCTET STRING OPTIONAL
+%%-------------------------------------------------
+ {Term5,Bytes7,Rb6} = case Bytes6 of
+<<2:2,_:1,10:5,_/binary>> ->
+?RT_BER:decode_octet_string(Bytes6,[],[{tag,128,10,'IMPLICIT',32}], no_length, mandatory);
+_ ->
+{ asn1_NOVALUE, Bytes6, 0 }
+end,
+
+%%-------------------------------------------------
+%% attribute number 6 with type OCTET STRING OPTIONAL
+%%-------------------------------------------------
+ {Term6,Bytes8,Rb7} = case Bytes7 of
+<<2:2,_:1,11:5,_/binary>> ->
+?RT_BER:decode_octet_string(Bytes7,[],[{tag,128,11,'IMPLICIT',32}], no_length, mandatory);
+_ ->
+{ asn1_NOVALUE, Bytes7, 0 }
+end,
+
+{Bytes9,Rb8} = ?RT_BER:restbytes2(RemBytes, Bytes8,noext),
+ {{'ExtendedResponse', Term1, Term2, Term3, Term4, Term5, Term6}, Bytes9, Rb1+Rb2+Rb3+Rb4+Rb5+Rb6+Rb7+Rb8}.
+'maxInt'() ->
+2147483647.
+
diff --git a/src/eldap/ELDAPv3.hrl b/src/eldap/ELDAPv3.hrl
new file mode 100644
index 000000000..e87bddadb
--- /dev/null
+++ b/src/eldap/ELDAPv3.hrl
@@ -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).
diff --git a/src/gen_mod.erl b/src/gen_mod.erl
index 945754fbf..205c843c8 100644
--- a/src/gen_mod.erl
+++ b/src/gen_mod.erl
@@ -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.
diff --git a/src/jd2ejd.erl b/src/jd2ejd.erl
index 1fff78149..974b80540 100644
--- a/src/jd2ejd.erl
+++ b/src/jd2ejd.erl
@@ -10,222 +10,127 @@
-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) ->
+
+import_file(File) ->
User = filename:rootname(filename:basename(File)),
- start(File, User).
+ 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.
-start(File, User) ->
- gen_fsm:start(?MODULE, [File, User, self()], ?FSMOPTS).
+
+import_dir(Dir) ->
+ {ok, Files} = file:list_dir(Dir),
+ MsgFiles = lists:filter(
+ fun(FN) ->
+ case string:len(FN) > 4 of
+ true ->
+ string:substr(FN,
+ string:len(FN) - 3) == ".xml";
+ _ ->
+ false
+ end
+ end, Files),
+ lists:foreach(
+ fun(FN) ->
+ import_file(filename:join([Dir, FN]))
+ end, MsgFiles),
+ ok.
%%%----------------------------------------------------------------------
-%%% Callback functions from gen_fsm
+%%% Internal functions
%%%----------------------------------------------------------------------
-%%----------------------------------------------------------------------
-%% 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) ->
+process_xdb(User, Server, {xmlelement, Name, _Attrs, Els}) ->
case Name of
"xdb" ->
- {next_state, xdb_data, StateData};
+ lists:foreach(
+ fun(El) ->
+ xdb_data(User, Server, El)
+ end, Els);
_ ->
- {stop, normal, StateData}
- end;
-
-wait_for_xdb(closed, StateData) ->
- {stop, normal, StateData}.
+ ok
+ end.
-xdb_data({xmlstreamelement, El}, StateData) ->
+xdb_data(User, Server, El) ->
{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.
+ 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.
-%%%----------------------------------------------------------------------
-%%% 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:make_jid("", ?MYNAME, "");
_ ->
jlib:string_to_jid(FromS)
end,
@@ -237,39 +142,3 @@ process_offline(To, {xmlelement, _, _, Els}) ->
end
end, Els).
-
-import_file(File) ->
- clear_queue(),
- start(File),
- receive
- {jd2ejd, Result} -> Result
- %after 4000 -> timeout
- end.
-
-clear_queue() ->
- receive
- {jd2ejd, _Result} -> clear_queue
- after 0 -> ok
- end.
-
-
-import_dir(Dir) ->
- {ok, Files} = file:list_dir(Dir),
- MsgFiles = lists:filter(
- fun(FN) ->
- case string:len(FN) > 4 of
- true ->
- string:substr(FN,
- string:len(FN) - 3) == ".xml";
- _ ->
- false
- end
- end, Files),
- lists:foreach(
- fun(FN) ->
- import_file(filename:join([Dir, FN]))
- end, MsgFiles),
- ok.
-
-
-
diff --git a/src/mod_announce.erl b/src/mod_announce.erl
index b8164aab0..495eac260 100644
--- a/src/mod_announce.erl
+++ b/src/mod_announce.erl
@@ -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.
diff --git a/src/mod_configure.erl b/src/mod_configure.erl
index ae72a1933..6a896ffb1 100644
--- a/src/mod_configure.erl
+++ b/src/mod_configure.erl
@@ -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}) ->
diff --git a/src/mod_disco.erl b/src/mod_disco.erl
index 3a7578ddf..084b39da8 100644
--- a/src/mod_disco.erl
+++ b/src/mod_disco.erl
@@ -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},
diff --git a/src/mod_irc/mod_irc.erl b/src/mod_irc/mod_irc.erl
index 1a9d78ce2..337d9761b 100644
--- a/src/mod_irc/mod_irc.erl
+++ b/src/mod_irc/mod_irc.erl
@@ -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.
diff --git a/src/mod_irc/mod_irc_connection.erl b/src/mod_irc/mod_irc_connection.erl
index fff4fedf8..11a172914 100644
--- a/src/mod_irc/mod_irc_connection.erl
+++ b/src/mod_irc/mod_irc_connection.erl
@@ -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"}],
diff --git a/src/mod_last.erl b/src/mod_last.erl
index c20b83b0a..c46f82890 100644
--- a/src/mod_last.erl
+++ b/src/mod_last.erl
@@ -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)
diff --git a/src/mod_last_odbc.erl b/src/mod_last_odbc.erl
index 1a670afc1..6edd10a2d 100644
--- a/src/mod_last_odbc.erl
+++ b/src/mod_last_odbc.erl
@@ -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
diff --git a/src/mod_muc/mod_muc.erl b/src/mod_muc/mod_muc.erl
index b7f5862b8..e7ffe42db 100644
--- a/src/mod_muc/mod_muc.erl
+++ b/src/mod_muc/mod_muc.erl
@@ -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.
diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl
index 298fe0765..0a90faf3c 100644
--- a/src/mod_muc/mod_muc_room.erl
+++ b/src/mod_muc/mod_muc_room.erl
@@ -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,
diff --git a/src/mod_offline.erl b/src/mod_offline.erl
index b59987e4b..3d7bd0e06 100644
--- a/src/mod_offline.erl
+++ b/src/mod_offline.erl
@@ -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.
diff --git a/src/mod_privacy.erl b/src/mod_privacy.erl
index 166a11c40..f13f145f5 100644
--- a/src/mod_privacy.erl
+++ b/src/mod_privacy.erl
@@ -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.
+
diff --git a/src/mod_private.erl b/src/mod_private.erl
index 487386fd9..109817244 100644
--- a/src/mod_private.erl
+++ b/src/mod_private.erl
@@ -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.
+
+
diff --git a/src/mod_pubsub/mod_pubsub.erl b/src/mod_pubsub/mod_pubsub.erl
index e73a8d015..8404d212c 100644
--- a/src/mod_pubsub/mod_pubsub.erl
+++ b/src/mod_pubsub/mod_pubsub.erl
@@ -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).
-
-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(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.
+
+
+
diff --git a/src/mod_register.erl b/src/mod_register.erl
index 3099af0b1..68ead661a 100644
--- a/src/mod_register.erl
+++ b/src/mod_register.erl
@@ -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),
diff --git a/src/mod_roster.erl b/src/mod_roster.erl
index 724b8d682..88db5d192 100644
--- a/src/mod_roster.erl
+++ b/src/mod_roster.erl
@@ -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.
+
diff --git a/src/mod_roster.hrl b/src/mod_roster.hrl
new file mode 100644
index 000000000..5cab7e414
--- /dev/null
+++ b/src/mod_roster.hrl
@@ -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 = []}).
+
diff --git a/src/mod_roster_odbc.erl b/src/mod_roster_odbc.erl
index bc6a8e324..390689a43 100644
--- a/src/mod_roster_odbc.erl
+++ b/src/mod_roster_odbc.erl
@@ -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).
diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl
new file mode 100644
index 000000000..3566da245
--- /dev/null
+++ b/src/mod_shared_roster.erl
@@ -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).
diff --git a/src/mod_stats.erl b/src/mod_stats.erl
index ca4b57335..41d0b303c 100644
--- a/src/mod_stats.erl
+++ b/src/mod_stats.erl
@@ -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").
diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl
index db380cb83..7feb5b397 100644
--- a/src/mod_vcard.erl
+++ b/src/mod_vcard.erl
@@ -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.
+
diff --git a/src/web/ejabberd_http.erl b/src/web/ejabberd_http.erl
index 99041da61..edfd8aca4 100644
--- a/src/web/ejabberd_http.erl
+++ b/src/web/ejabberd_http.erl
@@ -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},
diff --git a/src/web/ejabberd_http.hrl b/src/web/ejabberd_http.hrl
index f5af2e9af..dab67dae4 100644
--- a/src/web/ejabberd_http.hrl
+++ b/src/web/ejabberd_http.hrl
@@ -9,7 +9,7 @@
-record(request, {method,
path,
q = [],
- user,
+ us,
lang = "",
data = ""
}).
diff --git a/src/web/ejabberd_http1.erl b/src/web/ejabberd_http1.erl
new file mode 100644
index 000000000..24e1ad7fc
--- /dev/null
+++ b/src/web/ejabberd_http1.erl
@@ -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.
+
diff --git a/src/web/ejabberd_wcs.erl b/src/web/ejabberd_wcs.erl
new file mode 100644
index 000000000..0859b8914
--- /dev/null
+++ b/src/web/ejabberd_wcs.erl
@@ -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.
+
diff --git a/src/web/ejabberd_web.erl b/src/web/ejabberd_web.erl
index 4ef511e52..3f2867507 100644
--- a/src/web/ejabberd_web.erl
+++ b/src/web/ejabberd_web.erl
@@ -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) ->
diff --git a/src/web/ejabberd_web_admin.erl b/src/web/ejabberd_web_admin.erl
index 7e396b6ac..314b98380 100644
--- a/src/web/ejabberd_web_admin.erl
+++ b/src/web/ejabberd_web_admin.erl
@@ -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.
+