From 374446f8471747c878cdaf760c4bb37d17493ab7 Mon Sep 17 00:00:00 2001 From: tmallard Date: Sun, 17 Apr 2005 18:08:34 +0000 Subject: Merged the Process One contributions ( Virtual Hosting ) SVN Revision: 307 --- src/cyrsasl.erl | 18 +- src/cyrsasl_digest.erl | 33 +- src/cyrsasl_plain.erl | 13 +- src/ejabberd.hrl | 5 +- src/ejabberd_auth.erl | 56 +- src/ejabberd_auth_external.erl | 40 +- src/ejabberd_auth_internal.erl | 158 +- src/ejabberd_auth_ldap.erl | 40 +- src/ejabberd_auth_odbc.erl | 37 +- src/ejabberd_c2s.erl | 243 +-- src/ejabberd_config.erl | 4 +- src/ejabberd_ctl.erl | 2 +- src/ejabberd_local.erl | 5 +- src/ejabberd_router.erl | 67 +- src/ejabberd_sm.erl | 141 +- src/ejabberd_sup.erl | 16 +- src/eldap/ELDAPv3.erl | 2972 ++++++++++++++++++++++++++++++++++++ src/eldap/ELDAPv3.hrl | 74 + src/gen_mod.erl | 21 +- src/jd2ejd.erl | 313 ++-- src/mod_announce.erl | 180 ++- src/mod_configure.erl | 33 +- src/mod_disco.erl | 135 +- src/mod_irc/mod_irc.erl | 123 +- src/mod_irc/mod_irc_connection.erl | 53 +- src/mod_last.erl | 68 +- src/mod_last_odbc.erl | 2 +- src/mod_muc/mod_muc.erl | 250 ++- src/mod_muc/mod_muc_room.erl | 21 +- src/mod_offline.erl | 100 +- src/mod_privacy.erl | 117 +- src/mod_private.erl | 92 +- src/mod_pubsub/mod_pubsub.erl | 180 ++- src/mod_register.erl | 19 +- src/mod_roster.erl | 197 ++- src/mod_roster.hrl | 18 + src/mod_roster_odbc.erl | 2 +- src/mod_shared_roster.erl | 291 ++++ src/mod_stats.erl | 51 +- src/mod_vcard.erl | 317 ++-- src/web/ejabberd_http.erl | 62 +- src/web/ejabberd_http.hrl | 2 +- src/web/ejabberd_http1.erl | 404 +++++ src/web/ejabberd_wcs.erl | 328 ++++ src/web/ejabberd_web.erl | 16 +- src/web/ejabberd_web_admin.erl | 448 ++++-- 46 files changed, 6467 insertions(+), 1300 deletions(-) create mode 100644 src/eldap/ELDAPv3.erl create mode 100644 src/eldap/ELDAPv3.hrl create mode 100644 src/mod_roster.hrl create mode 100644 src/mod_shared_roster.erl create mode 100644 src/web/ejabberd_http1.erl create mode 100644 src/web/ejabberd_wcs.erl (limited to 'src') 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 +%%% Purpose : Roster management +%%% Created : 5 Mar 2005 by Alexey Shchepin +%%% 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 +%%% Purpose : Shared roster management +%%% Created : 5 Mar 2005 by Alexey Shchepin +%%% 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 +%%% Purpose : +%%% Created : 27 Feb 2004 by Alexey Shchepin +%%% 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, + "\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 +%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 +%%% Purpose : Web Client Service +%%% Created : 13 Jul 2004 by Alexey Shchepin +%%% 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 %%% 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. + -- cgit v1.2.3