]> granicus.if.org Git - ejabberd/commitdiff
Merged the Process One contributions ( Virtual Hosting )
authortmallard <tmallard@null>
Sun, 17 Apr 2005 18:08:34 +0000 (18:08 +0000)
committertmallard <tmallard@null>
Sun, 17 Apr 2005 18:08:34 +0000 (18:08 +0000)
SVN Revision: 307

46 files changed:
src/cyrsasl.erl
src/cyrsasl_digest.erl
src/cyrsasl_plain.erl
src/ejabberd.hrl
src/ejabberd_auth.erl
src/ejabberd_auth_external.erl
src/ejabberd_auth_internal.erl
src/ejabberd_auth_ldap.erl
src/ejabberd_auth_odbc.erl
src/ejabberd_c2s.erl
src/ejabberd_config.erl
src/ejabberd_ctl.erl
src/ejabberd_local.erl
src/ejabberd_router.erl
src/ejabberd_sm.erl
src/ejabberd_sup.erl
src/eldap/ELDAPv3.erl [new file with mode: 0644]
src/eldap/ELDAPv3.hrl [new file with mode: 0644]
src/gen_mod.erl
src/jd2ejd.erl
src/mod_announce.erl
src/mod_configure.erl
src/mod_disco.erl
src/mod_irc/mod_irc.erl
src/mod_irc/mod_irc_connection.erl
src/mod_last.erl
src/mod_last_odbc.erl
src/mod_muc/mod_muc.erl
src/mod_muc/mod_muc_room.erl
src/mod_offline.erl
src/mod_privacy.erl
src/mod_private.erl
src/mod_pubsub/mod_pubsub.erl
src/mod_register.erl
src/mod_roster.erl
src/mod_roster.hrl [new file with mode: 0644]
src/mod_roster_odbc.erl
src/mod_shared_roster.erl [new file with mode: 0644]
src/mod_stats.erl
src/mod_vcard.erl
src/web/ejabberd_http.erl
src/web/ejabberd_http.hrl
src/web/ejabberd_http1.erl [new file with mode: 0644]
src/web/ejabberd_wcs.erl [new file with mode: 0644]
src/web/ejabberd_web.erl
src/web/ejabberd_web_admin.erl

index 9376b25d2733ca0989f4e43056be8ea2f6d93df6..2f17cffbcaa85578c08bd6d0aaad3fd63b4810c4 100644 (file)
 -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);
index 2e5e94e5ac342129ca45007519353ee0933f6f74..c87ffdc083546d045c21e0d14b571e3fc300ef84 100644 (file)
 
 -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))),
index 71e4a6bafb158a947d4254e34701822f3b7f6a5c..0c4b66ff297aeeb6a2ac496a8bfd8e7c237b02c2 100644 (file)
 -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}]};
                _ ->
index 249818d60a9a5039ceb7bf2f02cc047fa7fb8c88..012bda7c88ab6f6d2d393c16da124ba0484396d2 100644 (file)
                              [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").
index 8abd2ac6b33fa672f0e3e609685dfd091ebcda95..0221735343b42e97eab3bbcc36760a5329636c3d 100644 (file)
 
 %% 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
index e379fb9df8e1b876079f2a3d574545a3e37de95a..383ce15a821f0f0524a3d17fe05ff3394a018e37 100644 (file)
 
 %% 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.
 
index 1743c84782ed20f733eae392ec6ca7a63efec242..8dbd5881817328ab0637ec7e7386497edd225f82 100644 (file)
 
 %% 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.
+
+
+
index 63c23c701b42e5192bac9305ce378f32cc0e88ab..355fd3b603b68dbf6805201f3b5772fd9ffbcd37 100644 (file)
 
 %% 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.
 
 
index 35d1f92d01253c0afb3b1e5855510d78a6b6cca7..1cb5ca4e41df33b372f7df2c029824f2027aeedf 100644 (file)
 
 %% 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;
index 09b98af3e28755f108cb468c5ddd4d7147f8909f..3873ac1715e42a49e2be14f8797abe00786a5a3d 100644 (file)
@@ -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
index a2822a388227acfc234be62da7c9a312ff79ca04..e1aa9c2df8f7a5b2a00294e64c952d8d64ddf6dd 100644 (file)
@@ -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;
index 55c8008a7804286fd3769057b66d793e4a306061..1c1d338771c3798655af31b174da9845da7302a6 100644 (file)
@@ -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",
index 40b598e0240af815f92ab16d1d65df75753e3bf0..3e7eaf1b92c46ffc32bf7c1eeccb4c200cf0727e 100644 (file)
@@ -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),
index ac5583696a1710e9537eb32a089a689f00935f31..31d355542908697e756c02753641affc880f0bdb 100644 (file)
@@ -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)).
index cfefa0c9548648f18dd2472c0cfbf01dd984e70e..c7f78ca7119a9c3af9485e8da0920bb998484b8f 100644 (file)
 
 -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
index 9a2cc74d55c31a5baac87b81e139c42410a2d659..d58ec422ff128510b06d48312af72d8056f7d65f 100644 (file)
@@ -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 (file)
index 0000000..44657ee
--- /dev/null
@@ -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 (file)
index 0000000..e87bdda
--- /dev/null
@@ -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).
index 945754fbf796b0d5b1bf57c2f8fa1c110ba13a61..205c843c80609d46eccc0ca7764758266c16522e 100644 (file)
@@ -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.
index 1fff78149bc3046f1ef65b9935031bb942a5a7da..974b80540bc9a7a2d569e22d25c0f43d96cad548 100644 (file)
 -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.
-
-
-
index b8164aab089ab1c8b131bd202b06618314384b12..495eac260831e53ae48a48dda2e0b729c9673bc9 100644 (file)
@@ -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.
index ae72a19331e2aa7c1aa2c51c390370f05da043cb..6a896ffb174c38a4c40dbed78c1873f008e20daf 100644 (file)
@@ -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}) ->
index 3a7578ddf0dfc229ad6629e1f35ed1ccb2bb2eb2..084b39da86046cdbe7683b9ae96415ddf3320071 100644 (file)
@@ -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},
index 1a9d78ce2d7f2fab7042f0d0035ec393a936f961..337d9761bb2129b9890756be892ed9860a28450d 100644 (file)
 
 -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.
index fff4fedf8f4404ee06db7dfcb9a56595330a0f18..11a172914efc04dcac7c0999630ead15acdb5c1e 100644 (file)
@@ -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"}],
index c20b83b0a6a68b30e9c66ceabe19068d482bcc4d..c46f82890ae87a4dcbfec9e14e3fa3d1ca42076f 100644 (file)
         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)
index 1a670afc1c12754457429077e2e52d67a6972af3..6edd10a2d467df57fc5ce0e6121abcb7481d226f 100644 (file)
@@ -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
index b7f5862b84aa960c3267acc32e00cec68c53d2d9..e7ffe42db5da60ae2ca54cdc02b8624aa4ca0940 100644 (file)
 -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.
index 298fe0765149a1570629db84d8d197f1d40b05ce..0a90faf3c1661376d41010589e0fce4b2492f04e 100644 (file)
@@ -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,
index b59987e4bfed570ece214795c6ce228d4c6eeba9..3d7bd0e060c277b9d864a193bf79ea61e10b5465 100644 (file)
         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.
 
index 166a11c4012e54e34cc88a8bb5ba8268718e6d4b..f13f145f579e76547b451e1a36c4e75f0b73d5a7 100644 (file)
         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.
+
 
index 487386fd9d66ce15b28ba7e3559699a751fbd4a8..1098172449d6bd240f3f93af43f776638b97c671 100644 (file)
 -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.
+
+
index e73a8d01508330c6d67521823fddaff8bc74cf27..8404d212c5246af658c7e485487f9c1bbf4dcd2b 100644 (file)
@@ -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.
+
+
+
index 3099af0b19c8c319000a54c4ebe4402b845194c7..68ead661aeb0dc96af1090ce7d56b6213b4d4991 100644 (file)
@@ -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),
index 724b8d682e451dc3c2bccf670d4a2ae1de9f9016..88db5d192ef6fb6af6e06c16892b7cf133878ea1 100644 (file)
 -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 (file)
index 0000000..5cab7e4
--- /dev/null
@@ -0,0 +1,18 @@
+%%%----------------------------------------------------------------------
+%%% File    : mod_roster.hrl
+%%% Author  : Alexey Shchepin <alexey@sevcom.net>
+%%% Purpose : Roster management
+%%% Created :  5 Mar 2005 by Alexey Shchepin <alexey@sevcom.net>
+%%% Id      : $Id: mod_roster.hrl 11 2005-03-06 22:36:15Z alexey $
+%%%----------------------------------------------------------------------
+
+-record(roster, {usj,
+                us,
+                jid,
+                name = "",
+                subscription = none,
+                ask = none,
+                groups = [],
+                xattrs = [],
+                xs = []}).
+
index bc6a8e3240df1c7e3f525bd3bf91b20750a11adf..390689a43b6de38c8821bddb13bf642c31108ea1 100644 (file)
@@ -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 (file)
index 0000000..3566da2
--- /dev/null
@@ -0,0 +1,291 @@
+%%%----------------------------------------------------------------------
+%%% File    : mod_shared_roster.erl
+%%% Author  : Alexey Shchepin <alexey@sevcom.net>
+%%% Purpose : Shared roster management
+%%% Created :  5 Mar 2005 by Alexey Shchepin <alexey@sevcom.net>
+%%% Id      : $Id: mod_shared_roster.erl 24 2005-04-14 01:15:31Z alexey $
+%%%----------------------------------------------------------------------
+
+-module(mod_shared_roster).
+-author('alexey@sevcom.net').
+-vsn('$Revision: 24 $ ').
+
+-behaviour(gen_mod).
+
+-export([start/1, stop/0,
+        get_user_roster/2,
+        get_subscription_lists/3,
+        get_jid_info/4,
+        in_subscription/5,
+        out_subscription/4,
+        list_groups/1,
+        create_group/2,
+        create_group/3,
+        delete_group/2,
+        get_group_opts/2,
+        set_group_opts/3,
+        get_group_users/2,
+        add_user_to_group/3,
+        remove_user_from_group/3]).
+
+-include("ejabberd.hrl").
+-include("jlib.hrl").
+-include("mod_roster.hrl").
+
+-record(sr_group, {group_host, opts}).
+-record(sr_user, {us, group_host}).
+
+start(_Opts) ->
+    mnesia:create_table(sr_group,
+                       [{disc_copies, [node()]},
+                        {attributes, record_info(fields, sr_group)}]),
+    mnesia:create_table(sr_user,
+                       [{disc_copies, [node()]},
+                        {type, bag},
+                        {attributes, record_info(fields, sr_user)}]),
+    mnesia:add_table_index(sr_user, group_host),
+    ejabberd_hooks:add(roster_get,
+                      ?MODULE, get_user_roster, 70),
+    ejabberd_hooks:add(roster_in_subscription,
+                      ?MODULE, in_subscription, 30),
+    ejabberd_hooks:add(roster_out_subscription,
+                      ?MODULE, out_subscription, 30),
+    ejabberd_hooks:add(roster_get_subscription_lists,
+                      ?MODULE, get_subscription_lists, 70),
+    ejabberd_hooks:add(roster_get_jid_info,
+                      ?MODULE, get_jid_info, 70).
+    %ejabberd_hooks:add(remove_user,
+    %                 ?MODULE, remove_user, 50),
+
+stop() ->
+    ejabberd_hooks:delete(roster_get,
+                         ?MODULE, get_user_roster, 70),
+    ejabberd_hooks:delete(roster_in_subscription,
+                         ?MODULE, in_subscription, 30),
+    ejabberd_hooks:delete(roster_out_subscription,
+                         ?MODULE, out_subscription, 30),
+    ejabberd_hooks:delete(roster_get_subscription_lists,
+                         ?MODULE, get_subscription_lists, 70),
+    ejabberd_hooks:delete(roster_get_jid_info,
+                         ?MODULE, get_jid_info, 70).
+    %ejabberd_hooks:delete(remove_user,
+    %                    ?MODULE, remove_user, 50),
+
+
+get_user_roster(Items, US) ->
+    {U, S} = US,
+    DisplayedGroups = get_user_displayed_groups(US),
+    SRUsers = 
+       lists:foldl(
+         fun(Group, Acc1) ->
+                 lists:foldl(
+                   fun(User, Acc2) ->
+                           dict:append(User, get_group_name(S, Group), Acc2)
+                   end, Acc1, get_group_users(S, Group))
+         end, dict:new(), DisplayedGroups),
+    {NewItems1, SRUsersRest} =
+       lists:mapfoldl(
+         fun(Item, SRUsers1) ->
+                 {_, _, {U1, S1, _}} = Item#roster.usj,
+                 US1 = {U1, S1},
+                 case dict:find(US1, SRUsers1) of
+                     {ok, _GroupNames} ->
+                         {Item#roster{subscription = both, ask = none},
+                          dict:erase(US1, SRUsers1)};
+                     error ->
+                         {Item, SRUsers1}
+                 end
+         end, SRUsers, Items),
+    SRItems = [#roster{usj = {U, S, {U1, S1, ""}},
+                      us = US,
+                      jid = {U1, S1, ""},
+                      name = "",
+                      subscription = both,
+                      ask = none,
+                      groups = GroupNames} ||
+                 {{U1, S1}, GroupNames} <- dict:to_list(SRUsersRest)],
+    SRItems ++ NewItems1.
+
+get_subscription_lists({F, T}, User, Server) ->
+    LUser = jlib:nodeprep(User),
+    LServer = jlib:nameprep(Server),
+    US = {LUser, LServer},
+    DisplayedGroups = get_user_displayed_groups(US),
+    SRUsers =
+       lists:usort(
+         lists:flatmap(
+           fun(Group) ->
+                   get_group_users(LServer, Group)
+           end, DisplayedGroups)),
+    SRJIDs = [{U1, S1, ""} || {U1, S1} <- SRUsers],
+    {lists:usort(SRJIDs ++ F), lists:usort(SRJIDs ++ T)}.
+
+get_jid_info({Subscription, Groups}, User, Server, JID) ->
+    LUser = jlib:nodeprep(User),
+    LServer = jlib:nameprep(Server),
+    US = {LUser, LServer},
+    {U1, S1, _} = jlib:jid_tolower(JID),
+    US1 = {U1, S1},
+    DisplayedGroups = get_user_displayed_groups(US),
+    SRUsers = 
+       lists:foldl(
+         fun(Group, Acc1) ->
+                 lists:foldl(
+                   fun(User1, Acc2) ->
+                           dict:append(
+                             User1, get_group_name(LServer, Group), Acc2)
+                   end, Acc1, get_group_users(LServer, Group))
+         end, dict:new(), DisplayedGroups),
+    case dict:find(US1, SRUsers) of
+       {ok, GroupNames} ->
+           NewGroups = if
+                           Groups == [] -> GroupNames;
+                           true -> Groups
+                       end,
+           {both, NewGroups};
+       error ->
+           {Subscription, Groups}
+    end.
+
+in_subscription(Acc, User, Server, JID, Type) ->
+    process_subscription(in, User, Server, JID, Type, Acc).
+
+out_subscription(User, Server, JID, Type) ->
+    process_subscription(out, User, Server, JID, Type, false).
+
+process_subscription(Direction, User, Server, JID, _Type, Acc) ->
+    LUser = jlib:nodeprep(User),
+    LServer = jlib:nameprep(Server),
+    US = {LUser, LServer},
+    {U1, S1, _} = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
+    US1 = {U1, S1},
+    DisplayedGroups = get_user_displayed_groups(US),
+    SRUsers =
+       lists:usort(
+         lists:flatmap(
+           fun(Group) ->
+                   get_group_users(LServer, Group)
+           end, DisplayedGroups)),
+    case lists:member(US1, SRUsers) of
+       true ->
+           case Direction of
+               in ->
+                   {stop, false};
+               out ->
+                   stop
+           end;
+       false ->
+           Acc
+    end.
+
+list_groups(Host) ->
+    mnesia:dirty_select(
+      sr_group,
+      [{#sr_group{group_host = {'$1', '$2'},
+                 _ = '_'},
+       [{'==', '$2', Host}],
+       ['$1']}]).
+
+create_group(Host, Group) ->
+    create_group(Host, Group, []).
+
+create_group(Host, Group, Opts) ->
+    R = #sr_group{group_host = {Group, Host}, opts = Opts},
+    F = fun() ->
+               mnesia:write(R)
+       end,
+    mnesia:transaction(F).
+
+delete_group(Host, Group) ->
+    F = fun() ->
+               mnesia:delete({sr_group, {Group, Host}})
+       end,
+    mnesia:transaction(F).
+
+get_group_opts(Host, Group) ->
+    case catch mnesia:dirty_read(sr_group, {Group, Host}) of
+       [#sr_group{opts = Opts}] ->
+           Opts;
+       _ ->
+           error
+    end.
+
+set_group_opts(Host, Group, Opts) ->
+    R = #sr_group{group_host = {Group, Host}, opts = Opts},
+    F = fun() ->
+               mnesia:write(R)
+       end,
+    mnesia:transaction(F).
+
+
+
+get_user_groups(US) ->
+    Host = element(2, US),
+    case catch mnesia:dirty_read(sr_user, US) of
+       Rs when is_list(Rs) ->
+           [Group || #sr_user{group_host = {Group, H}} <- Rs, H == Host];
+       _ ->
+           []
+    end.
+
+is_group_enabled(Host, Group) ->
+    case catch mnesia:dirty_read(sr_group, {Group, Host}) of
+       [#sr_group{opts = Opts}] ->
+           not lists:member(disabled, Opts);
+       _ ->
+           false
+    end.
+
+get_group_opt(Host, Group, Opt, Default) ->
+    case catch mnesia:dirty_read(sr_group, {Group, Host}) of
+       [#sr_group{opts = Opts}] ->
+           case lists:keysearch(Opt, 1, Opts) of
+               {value, {_, Val}} ->
+                   Val;
+               false ->
+                   Default
+           end;
+       _ ->
+           false
+    end.
+
+get_group_users(Host, Group) ->
+    case catch mnesia:dirty_index_read(
+                sr_user, {Group, Host}, #sr_user.group_host) of
+       Rs when is_list(Rs) ->
+           [R#sr_user.us || R <- Rs];
+       _ ->
+           []
+    end.
+
+get_group_name(Host, Group) ->
+    get_group_opt(Host, Group, name, Group).
+
+get_user_displayed_groups(US) ->
+    Host = element(2, US),
+    DisplayedGroups1 =
+       lists:usort(
+         lists:flatmap(
+           fun(Group) ->
+                   case is_group_enabled(Host, Group) of
+                       true ->
+                           get_group_opt(Host, Group, displayed_groups, []);
+                       false ->
+                           []
+                   end
+           end, get_user_groups(US))),
+    [Group || Group <- DisplayedGroups1, is_group_enabled(Host, Group)].
+
+add_user_to_group(Host, US, Group) ->
+    R = #sr_user{us = US, group_host = {Group, Host}},
+    F = fun() ->
+               mnesia:write(R)
+       end,
+    mnesia:transaction(F).
+
+remove_user_from_group(Host, US, Group) ->
+    R = #sr_user{us = US, group_host = {Group, Host}},
+    F = fun() ->
+               mnesia:delete_object(R)
+       end,
+    mnesia:transaction(F).
index ca4b573357bf21fe85dbc6b2f69ced188aa38601..41d0b303cce52b9af264d5802471900ac41ae06f 100644 (file)
@@ -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").
 
 
index db380cb831eb325fa8115a2549c18554064fd8c8..7feb5b397c98e15aa82750e4642d27ed039e1cd2 100644 (file)
         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.
+
index 99041da61ec650584c238c7be2a8ec9b31e148e6..edfd8aca44b0b480ea4160222b60a25e48db565d 100644 (file)
@@ -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},
index f5af2e9af89e1085fa3438a10baa0f39e331e6d6..dab67dae44f51f18d10fd2654834b43267ab3b2d 100644 (file)
@@ -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 (file)
index 0000000..24e1ad7
--- /dev/null
@@ -0,0 +1,404 @@
+%%%----------------------------------------------------------------------
+%%% File    : ejabberd_http.erl
+%%% Author  : Alexey Shchepin <alexey@sevcom.net>
+%%% Purpose : 
+%%% Created : 27 Feb 2004 by Alexey Shchepin <alexey@sevcom.net>
+%%% Id      : $Id$
+%%%----------------------------------------------------------------------
+
+-module(ejabberd_http).
+-author('alexey@sevcom.net').
+-vsn('$Revision$ ').
+
+-behaviour(gen_fsm).
+
+%% External exports
+-export([start/2,
+        start_link/2]).
+
+%% gen_fsm callbacks
+-export([init/1,
+        wait_for_headers/2,
+        handle_event/3,
+        handle_sync_event/4,
+        code_change/4,
+        handle_info/3,
+        terminate/3]).
+
+-include("ejabberd.hrl").
+-include("jlib.hrl").
+
+-define(DICT, dict).
+
+-record(state, {socket,
+               request_method,
+               request_path,
+               request_auth
+              }).
+
+
+-define(DBGFSM, true).
+
+-ifdef(DBGFSM).
+-define(FSMOPTS, [{debug, [trace]}]).
+-else.
+-define(FSMOPTS, []).
+-endif.
+
+-define(XHTML_DOCTYPE,
+       "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" "
+       "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n").
+
+
+%%%----------------------------------------------------------------------
+%%% API
+%%%----------------------------------------------------------------------
+start(SockData, Opts) ->
+    supervisor:start_child(ejabberd_http_sup, [SockData, Opts]).
+
+start_link(SockData, Opts) ->
+    gen_fsm:start_link(ejabberd_http, [SockData, Opts], ?FSMOPTS).
+
+%%%----------------------------------------------------------------------
+%%% Callback functions from gen_fsm
+%%%----------------------------------------------------------------------
+
+%%----------------------------------------------------------------------
+%% Func: init/1
+%% Returns: {ok, StateName, StateData}          |
+%%          {ok, StateName, StateData, Timeout} |
+%%          ignore                              |
+%%          {stop, StopReason}                   
+%%----------------------------------------------------------------------
+init([{SockMod, Socket}, Opts]) ->
+    ?INFO_MSG("started: ~p", [{SockMod, Socket}]),
+    case SockMod of
+       gen_tcp ->
+           inet:setopts(Socket, [{packet, http}, {active, true}, {recbuf, 0}]);
+       ssl ->
+           ssl:setopts(Socket, [{packet, http}, {active, true}])
+    end,
+    {ok, wait_for_headers, #state{socket = Socket}}.
+
+%%----------------------------------------------------------------------
+%% Func: StateName/2
+%% Returns: {next_state, NextStateName, NextStateData}          |
+%%          {next_state, NextStateName, NextStateData, Timeout} |
+%%          {stop, Reason, NewStateData}                         
+%%----------------------------------------------------------------------
+
+wait_for_headers(_, StateData) ->
+    {next_state, wait_for_headers, StateData}.
+
+
+%%----------------------------------------------------------------------
+%% Func: StateName/3
+%% Returns: {next_state, NextStateName, NextStateData}            |
+%%          {next_state, NextStateName, NextStateData, Timeout}   |
+%%          {reply, Reply, NextStateName, NextStateData}          |
+%%          {reply, Reply, NextStateName, NextStateData, Timeout} |
+%%          {stop, Reason, NewStateData}                          |
+%%          {stop, Reason, Reply, NewStateData}                    
+%%----------------------------------------------------------------------
+%state_name(Event, From, StateData) ->
+%    Reply = ok,
+%    {reply, Reply, state_name, StateData}.
+
+%%----------------------------------------------------------------------
+%% Func: handle_event/3
+%% Returns: {next_state, NextStateName, NextStateData}          |
+%%          {next_state, NextStateName, NextStateData, Timeout} |
+%%          {stop, Reason, NewStateData}                         
+%%----------------------------------------------------------------------
+handle_event(_Event, StateName, StateData) ->
+    {next_state, StateName, StateData}.
+
+%%----------------------------------------------------------------------
+%% Func: handle_sync_event/4
+%% Returns: {next_state, NextStateName, NextStateData}            |
+%%          {next_state, NextStateName, NextStateData, Timeout}   |
+%%          {reply, Reply, NextStateName, NextStateData}          |
+%%          {reply, Reply, NextStateName, NextStateData, Timeout} |
+%%          {stop, Reason, NewStateData}                          |
+%%          {stop, Reason, Reply, NewStateData}                    
+%%----------------------------------------------------------------------
+handle_sync_event(_Event, _From, StateName, StateData) ->
+    Reply = ok,
+    {reply, Reply, StateName, StateData}.
+
+code_change(_OldVsn, StateName, StateData, _Extra) ->
+    {ok, StateName, StateData}.
+
+%%----------------------------------------------------------------------
+%% Func: handle_info/3
+%% Returns: {next_state, NextStateName, NextStateData}          |
+%%          {next_state, NextStateName, NextStateData, Timeout} |
+%%          {stop, Reason, NewStateData}                         
+%%----------------------------------------------------------------------
+handle_info({http_request, _Socket, Method, Path, _Version},
+           StateName, StateData) ->
+    {next_state, StateName, StateData#state{request_method = Method,
+                                           request_path = Path}};
+handle_info({http_header, _Socket, _, 'Authorization', _, Auth},
+           StateName, StateData) ->
+    {next_state, StateName,
+     StateData#state{request_auth = parse_auth(Auth)}};
+handle_info({http_eoh, _Socket}, StateName, StateData) ->
+    process_request(StateData),
+    {stop, normal, StateData};
+
+handle_info({tcp_closed, _Socket}, StateName, StateData) ->
+    {stop, normal, StateData};
+
+handle_info({tcp_error, _Socket, _Reason}, StateName, StateData) ->
+    {stop, normal, StateData};
+
+handle_info(_, StateName, StateData) ->
+    {next_state, StateName, StateData}.
+
+
+%%----------------------------------------------------------------------
+%% Func: terminate/3
+%% Purpose: Shutdown the fsm
+%% Returns: any
+%%----------------------------------------------------------------------
+terminate(Reason, _StateName, StateData) ->
+    ?INFO_MSG("terminated: ~p", [Reason]),
+    gen_tcp:close(StateData#state.socket),
+    ok.
+
+%%%----------------------------------------------------------------------
+%%% Internal functions
+%%%----------------------------------------------------------------------
+
+send_text(State, Text) ->
+    gen_tcp:send(State#state.socket, Text).
+
+
+process_request(#state{request_method = 'GET',
+                      request_path = {abs_path, Path},
+                      request_auth = undefined} = State) ->
+    Out = make_xhtml_output(
+           401,
+           [{"WWW-Authenticate", "basic realm=\"ejabberd\""}],
+           make_xhtml([{xmlelement, "h1", [],
+                        [{xmlcdata, "403 Unauthorized"}]}])),
+    send_text(State, Out),
+    ok;
+
+process_request(#state{request_method = 'GET',
+                      request_path = {abs_path, Path},
+                      request_auth = {User, Pass}} = State) ->
+    Out = make_xhtml_output(
+           200,
+           [],
+           make_xhtml([{xmlelement, "h1", [],
+                        [{xmlcdata, "Welcome " ++ User}]}])),
+    send_text(State, Out),
+    ok;
+
+process_request(State) ->
+    todo.
+
+
+
+make_xhtml(Els) ->
+    {xmlelement, "html", [{"xmlns", "http://www.w3.org/1999/xhtml"},
+                         {"xml:lang", "en"},
+                         {"lang", "en"}],
+     [{xmlelement, "head", [],
+       [{xmlelement, "meta", [{"http-equiv", "Content-Type"},
+                             {"content", "text/html; charset=utf-8"}], []}]},
+      {xmlelement, "body", [], Els}
+     ]}.
+
+
+make_xhtml_output(Status, Headers, XHTML) ->
+    Data = list_to_binary([?XHTML_DOCTYPE, xml:element_to_string(XHTML)]),
+    Headers1 = [{"Content-Type", "text/html; charset=utf-8"},
+               {"Content-Length", integer_to_list(size(Data))} | Headers],
+    H = lists:map(fun({Attr, Val}) ->
+                         [Attr, ": ", Val, "\r\n"]
+                 end, Headers1),
+    SL = ["HTTP/1.1 ", integer_to_list(Status), " ",
+         code_to_phrase(Status), "\r\n"],
+    [SL, H, "\r\n", Data].
+    
+
+
+% Code below is taken (with some modifications) from the yaws webserver, which
+% is distributed under the folowing license:
+%
+%This software (the yaws webserver) is free software.
+%Parts of this software is Copyright (c) Claes Wikstrom <klacke@hyber.org>
+%Any use or misuse of the source code is hereby freely allowed.
+%
+%1. Redistributions of source code must retain the above copyright
+%   notice as well as this list of conditions.
+%
+%2. Redistributions in binary form must reproduce the above copyright
+%   notice as well as this list of conditions.
+
+
+%% url decode the path and return {Path, QueryPart}
+
+url_decode_q_split(Path) ->
+    url_decode_q_split(Path, []).
+
+url_decode_q_split([$%, $C, $2, $%, Hi, Lo | Tail], Ack) ->
+    Hex = hex_to_integer([Hi, Lo]),
+    url_decode_q_split(Tail, [Hex|Ack]);
+url_decode_q_split([$%, $C, $3, $%, Hi, Lo | Tail], Ack) when Hi > $9 ->
+    Hex = hex_to_integer([Hi+4, Lo]),
+    url_decode_q_split(Tail, [Hex|Ack]);
+url_decode_q_split([$%, $C, $3, $%, Hi, Lo | Tail], Ack) when Hi < $A ->
+    Hex = hex_to_integer([Hi+4+7, Lo]),
+    url_decode_q_split(Tail, [Hex|Ack]);
+url_decode_q_split([$%, Hi, Lo | Tail], Ack) ->
+    Hex = hex_to_integer([Hi, Lo]),
+    url_decode_q_split(Tail, [Hex|Ack]);
+url_decode_q_split([$?|T], Ack) ->
+    %% Don't decode the query string here, that is parsed separately.
+    {path_norm_reverse(Ack), T};
+url_decode_q_split([H|T], Ack) ->
+    url_decode_q_split(T, [H|Ack]);
+url_decode_q_split([], Ack) ->
+    {path_norm_reverse(Ack), []}.
+
+path_norm_reverse("/" ++ T) -> start_dir(0, "/", T);
+path_norm_reverse(       T) -> start_dir(0,  "", T).
+
+start_dir(N, Path, ".."       ) -> rest_dir(N, Path, "");
+start_dir(N, Path, "/"   ++ T ) -> start_dir(N    , Path, T);
+start_dir(N, Path, "./"  ++ T ) -> start_dir(N    , Path, T);
+start_dir(N, Path, "../" ++ T ) -> start_dir(N + 1, Path, T);
+start_dir(N, Path,          T ) -> rest_dir (N    , Path, T).
+
+rest_dir (_N, Path, []         ) -> case Path of 
+                                      [] -> "/";
+                                      _  -> Path
+                                  end;
+rest_dir (0, Path, [ $/ | T ] ) -> start_dir(0    , [ $/ | Path ], T);
+rest_dir (N, Path, [ $/ | T ] ) -> start_dir(N - 1,        Path  , T);
+rest_dir (0, Path, [  H | T ] ) -> rest_dir (0    , [  H | Path ], T);
+rest_dir (N, Path, [ _H | T ] ) -> rest_dir (N    ,        Path  , T).
+
+
+%% hex_to_integer
+
+
+hex_to_integer(Hex) ->
+    case catch erlang:list_to_integer(Hex, 16) of
+       {'EXIT', _} ->
+           old_hex_to_integer(Hex);
+       X ->
+           X
+    end.
+
+
+old_hex_to_integer(Hex) ->
+    DEHEX = fun (H) when H >= $a, H =< $f -> H - $a + 10;
+               (H) when H >= $A, H =< $F -> H - $A + 10;
+               (H) when H >= $0, H =< $9 -> H - $0
+           end,
+    lists:foldl(fun(E, Acc) -> Acc*16+DEHEX(E) end, 0, Hex).
+
+code_to_phrase(100) -> "Continue";
+code_to_phrase(101) -> "Switching Protocols ";
+code_to_phrase(200) -> "OK";
+code_to_phrase(201) -> "Created";
+code_to_phrase(202) -> "Accepted";
+code_to_phrase(203) -> "Non-Authoritative Information";
+code_to_phrase(204) -> "No Content";
+code_to_phrase(205) -> "Reset Content";
+code_to_phrase(206) -> "Partial Content";
+code_to_phrase(300) -> "Multiple Choices";
+code_to_phrase(301) -> "Moved Permanently";
+code_to_phrase(302) -> "Found";
+code_to_phrase(303) -> "See Other";
+code_to_phrase(304) -> "Not Modified";
+code_to_phrase(305) -> "Use Proxy";
+code_to_phrase(306) -> "(Unused)";
+code_to_phrase(307) -> "Temporary Redirect";
+code_to_phrase(400) -> "Bad Request";
+code_to_phrase(401) -> "Unauthorized";
+code_to_phrase(402) -> "Payment Required";
+code_to_phrase(403) -> "Forbidden";
+code_to_phrase(404) -> "Not Found";
+code_to_phrase(405) -> "Method Not Allowed";
+code_to_phrase(406) -> "Not Acceptable";
+code_to_phrase(407) -> "Proxy Authentication Required";
+code_to_phrase(408) -> "Request Timeout";
+code_to_phrase(409) -> "Conflict";
+code_to_phrase(410) -> "Gone";
+code_to_phrase(411) -> "Length Required";
+code_to_phrase(412) -> "Precondition Failed";
+code_to_phrase(413) -> "Request Entity Too Large";
+code_to_phrase(414) -> "Request-URI Too Long";
+code_to_phrase(415) -> "Unsupported Media Type";
+code_to_phrase(416) -> "Requested Range Not Satisfiable";
+code_to_phrase(417) -> "Expectation Failed";
+code_to_phrase(500) -> "Internal Server Error";
+code_to_phrase(501) -> "Not Implemented";
+code_to_phrase(502) -> "Bad Gateway";
+code_to_phrase(503) -> "Service Unavailable";
+code_to_phrase(504) -> "Gateway Timeout";
+code_to_phrase(505) -> "HTTP Version Not Supported".
+
+
+parse_auth(Orig = "Basic " ++ Auth64) ->
+    case decode_base64(Auth64) of
+       {error, _Err} ->
+           undefined;
+       Auth ->
+           case string:tokens(Auth, ":") of
+               [User, Pass] ->
+                   {User, Pass};
+               _ ->
+                   undefined
+           end
+    end;
+parse_auth(_) ->
+    undefined.
+
+
+
+decode_base64([]) ->
+  [];
+decode_base64([Sextet1,Sextet2,$=,$=|Rest]) ->
+  Bits2x6=
+    (d(Sextet1) bsl 18) bor
+    (d(Sextet2) bsl 12),
+  Octet1=Bits2x6 bsr 16,
+  [Octet1|decode_base64(Rest)];
+decode_base64([Sextet1,Sextet2,Sextet3,$=|Rest]) ->
+  Bits3x6=
+    (d(Sextet1) bsl 18) bor
+    (d(Sextet2) bsl 12) bor
+    (d(Sextet3) bsl 6),
+  Octet1=Bits3x6 bsr 16,
+  Octet2=(Bits3x6 bsr 8) band 16#ff,
+  [Octet1,Octet2|decode_base64(Rest)];
+decode_base64([Sextet1,Sextet2,Sextet3,Sextet4|Rest]) ->
+  Bits4x6=
+    (d(Sextet1) bsl 18) bor
+    (d(Sextet2) bsl 12) bor
+    (d(Sextet3) bsl 6) bor
+    d(Sextet4),
+  Octet1=Bits4x6 bsr 16,
+  Octet2=(Bits4x6 bsr 8) band 16#ff,
+  Octet3=Bits4x6 band 16#ff,
+  [Octet1,Octet2,Octet3|decode_base64(Rest)];
+decode_base64(_CatchAll) ->
+  {error, bad_base64}.
+
+d(X) when X >= $A, X =<$Z ->
+    X-65;
+d(X) when X >= $a, X =<$z ->
+    X-71;
+d(X) when X >= $0, X =<$9 ->
+    X+4;
+d($+) -> 62;
+d($/) -> 63;
+d(_) -> 63.
+
diff --git a/src/web/ejabberd_wcs.erl b/src/web/ejabberd_wcs.erl
new file mode 100644 (file)
index 0000000..0859b89
--- /dev/null
@@ -0,0 +1,328 @@
+%%%----------------------------------------------------------------------
+%%% File    : ejabberd_wcs.erl
+%%% Author  : Alexey Shchepin <alexey@sevcom.net>
+%%% Purpose : Web Client Service
+%%% Created : 13 Jul 2004 by Alexey Shchepin <alexey@sevcom.net>
+%%% Id      : $Id$
+%%%----------------------------------------------------------------------
+
+-module(ejabberd_wcs).
+-author('alexey@sevcom.net').
+-vsn('$Revision$ ').
+
+-behaviour(gen_fsm).
+
+%% External exports
+-export([start_link/2,
+        init/1,
+        handle_event/3,
+        handle_sync_event/4,
+        code_change/4,
+        handle_info/3,
+        terminate/3,
+        send/2,
+        recv/3,
+        close/1,
+        process_request/1]).
+
+-include("ejabberd.hrl").
+-include("jlib.hrl").
+-include("ejabberd_http.hrl").
+
+-record(http_poll, {id, pid}).
+
+-record(state, {id,
+               key,
+               output = "",
+               input = "",
+               waiting_input = false,
+               timer}).
+
+%-define(DBGFSM, true).
+
+-ifdef(DBGFSM).
+-define(FSMOPTS, [{debug, [trace]}]).
+-else.
+-define(FSMOPTS, []).
+-endif.
+
+-define(HTTP_POLL_TIMEOUT, 300000).
+-define(CT, {"Content-Type", "text/xml; charset=utf-8"}).
+-define(BAD_REQUEST, [?CT, {"Set-Cookie", "ID=-3:0; expires=-1"}]).
+
+
+%%%----------------------------------------------------------------------
+%%% API
+%%%----------------------------------------------------------------------
+start(ID, Key) ->
+    mnesia:create_table(http_poll,
+                       [{ram_copies, [node()]},
+                        {attributes, record_info(fields, http_poll)}]),
+    supervisor:start_child(ejabberd_http_poll_sup, [ID, Key]).
+
+start_link(ID, Key) ->
+    gen_fsm:start_link(?MODULE, [ID, Key], ?FSMOPTS).
+
+send({http_poll, FsmRef}, Packet) ->
+    gen_fsm:sync_send_all_state_event(FsmRef, {send, Packet}).
+
+recv({http_poll, FsmRef}, _Length, Timeout) ->
+    gen_fsm:sync_send_all_state_event(FsmRef, recv, Timeout).
+
+close({http_poll, FsmRef}) ->
+    catch gen_fsm:sync_send_all_state_event(FsmRef, close).
+
+
+process_request(#request{path = [],
+                        data = Data} = Request) ->
+    case catch parse_request(Data) of
+       {ok, ID1, Key, NewKey, Packet} ->
+           ID = if
+                    (ID1 == "0") or (ID1 == "mobile") ->
+                        NewID = sha:sha(term_to_binary({now(), make_ref()})),
+                        {ok, Pid} = start(NewID, ""),
+                        mnesia:transaction(
+                          fun() ->
+                                  mnesia:write(#http_poll{id = NewID,
+                                                          pid = Pid})
+                          end),
+                        NewID;
+                    true ->
+                        ID1
+                end,
+           case http_put(ID, Key, NewKey, Packet) of
+               {error, not_exists} ->
+                   {200, ?BAD_REQUEST, ""};
+               {error, bad_key} ->
+                   {200, ?BAD_REQUEST, ""};
+               ok ->
+                   receive
+                   after 100 -> ok
+                   end,
+                   case http_get(ID) of
+                       {error, not_exists} ->
+                           {200, [?BAD_REQUEST], ""};
+                       {ok, OutPacket} ->
+                           if
+                               ID == ID1 ->
+                                   {200, [?CT], OutPacket};
+                               ID1 == "mobile" ->
+                                   {200, [?CT], [ID, $\n, OutPacket]};
+                               true ->
+                                   Cookie = "ID=" ++ ID ++ "; expires=-1",
+                                   {200, [?CT, {"Set-Cookie", Cookie}],
+                                    OutPacket}
+                           end
+                   end
+           end;
+       _ ->
+           {200, [?CT, {"Set-Cookie", "ID=-2:0; expires=-1"}], ""}
+    end.
+
+
+%%%----------------------------------------------------------------------
+%%% Callback functions from gen_fsm
+%%%----------------------------------------------------------------------
+
+%%----------------------------------------------------------------------
+%% Func: init/1
+%% Returns: {ok, StateName, StateData}          |
+%%          {ok, StateName, StateData, Timeout} |
+%%          ignore                              |
+%%          {stop, StopReason}                   
+%%----------------------------------------------------------------------
+init([ID, Key]) ->
+    ?INFO_MSG("started: ~p", [{ID, Key}]),
+    Opts = [], % TODO
+    ejabberd_c2s:start({?MODULE, {http_poll, self()}}, Opts),
+    Timer = erlang:start_timer(?HTTP_POLL_TIMEOUT, self(), []),
+    {ok, loop, #state{id = ID,
+                     key = Key,
+                     timer = Timer}}.
+
+%%----------------------------------------------------------------------
+%% Func: StateName/2
+%% Returns: {next_state, NextStateName, NextStateData}          |
+%%          {next_state, NextStateName, NextStateData, Timeout} |
+%%          {stop, Reason, NewStateData}                         
+%%----------------------------------------------------------------------
+
+
+%%----------------------------------------------------------------------
+%% Func: StateName/3
+%% Returns: {next_state, NextStateName, NextStateData}            |
+%%          {next_state, NextStateName, NextStateData, Timeout}   |
+%%          {reply, Reply, NextStateName, NextStateData}          |
+%%          {reply, Reply, NextStateName, NextStateData, Timeout} |
+%%          {stop, Reason, NewStateData}                          |
+%%          {stop, Reason, Reply, NewStateData}                    
+%%----------------------------------------------------------------------
+%state_name(Event, From, StateData) ->
+%    Reply = ok,
+%    {reply, Reply, state_name, StateData}.
+
+%%----------------------------------------------------------------------
+%% Func: handle_event/3
+%% Returns: {next_state, NextStateName, NextStateData}          |
+%%          {next_state, NextStateName, NextStateData, Timeout} |
+%%          {stop, Reason, NewStateData}                         
+%%----------------------------------------------------------------------
+handle_event(Event, StateName, StateData) ->
+    {next_state, StateName, StateData}.
+
+%%----------------------------------------------------------------------
+%% Func: handle_sync_event/4
+%% Returns: {next_state, NextStateName, NextStateData}            |
+%%          {next_state, NextStateName, NextStateData, Timeout}   |
+%%          {reply, Reply, NextStateName, NextStateData}          |
+%%          {reply, Reply, NextStateName, NextStateData, Timeout} |
+%%          {stop, Reason, NewStateData}                          |
+%%          {stop, Reason, Reply, NewStateData}                    
+%%----------------------------------------------------------------------
+handle_sync_event({send, Packet}, From, StateName, StateData) ->
+    Output = [StateData#state.output | Packet],
+    Reply = ok,
+    {reply, Reply, StateName, StateData#state{output = Output}};
+
+handle_sync_event(recv, From, StateName, StateData) ->
+    case StateData#state.input of
+       "" ->
+           {next_state, StateName, StateData#state{waiting_input = From}};
+       Input ->
+           Reply = {ok, list_to_binary(Input)},
+           {reply, Reply, StateName, StateData#state{input = "",
+                                                     waiting_input = false}}
+    end;
+
+handle_sync_event(stop, From, StateName, StateData) ->
+    Reply = ok,
+    {stop, normal, Reply, StateData};
+
+handle_sync_event({http_put, Key, NewKey, Packet},
+                 From, StateName, StateData) ->
+    Allow = case StateData#state.key of
+               "" ->
+                   true;
+               OldKey ->
+                   NextKey = jlib:encode_base64(
+                               binary_to_list(crypto:sha(Key))),
+                   if
+                       OldKey == NextKey ->
+                           true;
+                       true ->
+                           false
+                   end
+           end,
+    if
+       Allow ->
+           case StateData#state.waiting_input of
+               false ->
+                   Input = [StateData#state.input | Packet],
+                   Reply = ok,
+                   {reply, Reply, StateName, StateData#state{input = Input,
+                                                             key = NewKey}};
+               Receiver ->
+                   gen_fsm:reply(Receiver, {ok, list_to_binary(Packet)}),
+                   cancel_timer(StateData#state.timer),
+                   Timer = erlang:start_timer(?HTTP_POLL_TIMEOUT, self(), []),
+                   Reply = ok,
+                   {reply, Reply, StateName,
+                    StateData#state{waiting_input = false,
+                                    key = NewKey,
+                                    timer = Timer}}
+           end;
+       true ->
+           Reply = {error, bad_key},
+           {reply, Reply, StateName, StateData}
+    end;
+
+handle_sync_event(http_get, From, StateName, StateData) ->
+    Reply = {ok, StateData#state.output},
+    {reply, Reply, StateName, StateData#state{output = ""}};
+
+handle_sync_event(Event, From, StateName, StateData) ->
+    Reply = ok,
+    {reply, Reply, StateName, StateData}.
+
+code_change(OldVsn, StateName, StateData, Extra) ->
+    {ok, StateName, StateData}.
+
+%%----------------------------------------------------------------------
+%% Func: handle_info/3
+%% Returns: {next_state, NextStateName, NextStateData}          |
+%%          {next_state, NextStateName, NextStateData, Timeout} |
+%%          {stop, Reason, NewStateData}                         
+%%----------------------------------------------------------------------
+handle_info({timeout, Timer, _}, StateName,
+           #state{timer = Timer} = StateData) ->
+    {stop, normal, StateData};
+
+handle_info(_, StateName, StateData) ->
+    {next_state, StateName, StateData}.
+
+%%----------------------------------------------------------------------
+%% Func: terminate/3
+%% Purpose: Shutdown the fsm
+%% Returns: any
+%%----------------------------------------------------------------------
+terminate(Reason, StateName, StateData) ->
+    mnesia:transaction(
+      fun() ->
+             mnesia:delete({http_poll, StateData#state.id})
+      end),
+    case StateData#state.waiting_input of
+       false ->
+           ok;
+       Receiver ->
+           gen_fsm:reply(Receiver, {error, closed})
+    end,
+    ok.
+
+%%%----------------------------------------------------------------------
+%%% Internal functions
+%%%----------------------------------------------------------------------
+
+
+http_put(ID, Key, NewKey, Packet) ->
+    case mnesia:dirty_read({http_poll, ID}) of
+       [] ->
+           {error, not_exists};
+       [#http_poll{pid = FsmRef}] ->
+           gen_fsm:sync_send_all_state_event(
+             FsmRef, {http_put, Key, NewKey, Packet})
+    end.
+
+http_get(ID) ->
+    case mnesia:dirty_read({http_poll, ID}) of
+       [] ->
+           {error, not_exists};
+       [#http_poll{pid = FsmRef}] ->
+           gen_fsm:sync_send_all_state_event(FsmRef, http_get)
+    end.
+
+
+parse_request(Data) ->
+    Comma = string:chr(Data, $,),
+    Header = lists:sublist(Data, Comma - 1),
+    Packet = lists:nthtail(Comma, Data),
+    {ID, Key, NewKey} =
+       case string:tokens(Header, ";") of
+           [ID1] ->
+               {ID1, "", ""};
+           [ID1, Key1] ->
+               {ID1, Key1, Key1};
+           [ID1, Key1, NewKey1] ->
+               {ID1, Key1, NewKey1}
+       end,
+    {ok, ID, Key, NewKey, Packet}.
+
+
+cancel_timer(Timer) ->
+    erlang:cancel_timer(Timer),
+    receive
+       {timeout, Timer, _} ->
+           ok
+    after 0 ->
+           ok
+    end.
+
index 4ef511e522a3cacae4c2b4ca62a922ffd3d278ea..3f28675072f1fadb57cb552260ef10d17b53a330 100644 (file)
@@ -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) ->
index 7e396b6ac8b7f7276fcc33a7297c89275410b6bb..314b9838044c3ed00965468e12f2019c0ea9c932 100644 (file)
@@ -5,8 +5,8 @@
 %%% Created :  9 Apr 2004 by Alexey Shchepin <alexey@sevcom.net>
 %%% Id      : $Id$
 %%%----------------------------------------------------------------------
-%%% Copyright (c) 2004 Alexey Shchepin
-%%% Copyright (c) 2004 Process One
+%%% Copyright (c) 2004-2005 Alexey Shchepin
+%%% Copyright (c) 2004-2005 Process One
 %%%----------------------------------------------------------------------
 
 -module(ejabberd_web_admin).
@@ -119,7 +119,7 @@ make_xhtml(Els, Lang) ->
                                      [?XE("tbody",
                                           [?XE("tr",
                                                [?XCT("td",
-                                                     "ejabberd (c) 2002-2005 Alexey Shchepin, 2004 Process One")
+                                                     "ejabberd (c) 2002-2005 Alexey Shchepin, 2004-2005 Process One")
                                                ])])
                                      ])])])])])])
       ]}}.
@@ -304,13 +304,13 @@ input[type=submit] {
 }
 
 textarea {
-    border: 1px solid #93a6c7;  
-    color: #556655;
-    background-color: #ffffff;
+    border: 1px solid #d6760e;
+    color: #723202;
+    background-color: #fff2e8;
     vertical-align: middle;
     margin-top: 7px;
-    margin-left: 7px;
-    margin-right: 7px;
+    /*margin-left: 7px;
+    margin-right: 7px;*/
     margin-bottom: 5px;
     padding: 0.1em;
 }
@@ -494,10 +494,10 @@ empty() ->
     jlib:decode_base64(
       "R0lGODlhAQABAIAAAP///////yH+FUNyZWF0ZWQgd2l0aCBUaGUgR0lNUAAh+QQBCgABACwAAAAAAQABAAACAkwBADs=").
 
-process_admin(#request{user = User,
-                       path = [],
-                       q = Query,
-                       lang = Lang} = Request) ->
+process_admin(#request{us = US,
+                      path = [],
+                      q = Query,
+                      lang = Lang} = Request) ->
     make_xhtml([?XCT("h1", "ejabberd administration"),
                ?XE("ul",
                    [?LI([?ACT("acls/", "Access Control Lists"), ?C(" "),
@@ -511,31 +511,31 @@ process_admin(#request{user = User,
                    ])
               ], Lang);
 
-process_admin(#request{user = User,
+process_admin(#request{us = US,
                       path = ["style.css"],
                       q = Query,
                       lang = Lang} = Request) ->
     {200, [{"Content-Type", "text/css"}], css()};
 
-process_admin(#request{user = User,
+process_admin(#request{us = US,
                       path = ["logo.png"],
                       q = Query,
                       lang = Lang} = Request) ->
     {200, [{"Content-Type", "image/png"}], logo()};
 
-process_admin(#request{user = User,
+process_admin(#request{us = US,
                       path = ["logo-fill.png"],
                       q = Query,
                       lang = Lang} = Request) ->
     {200, [{"Content-Type", "image/png"}], logo_fill()};
 
-process_admin(#request{user = User,
+process_admin(#request{us = US,
                       path = ["1x1tr.gif"],
                       q = Query,
                       lang = Lang} = Request) ->
     {200, [{"Content-Type", "image/gif"}], empty()};
 
-process_admin(#request{user = User,
+process_admin(#request{us = US,
                       path = ["acls-raw"],
                       q = Query,
                       lang = Lang} = Request) ->
@@ -578,7 +578,7 @@ process_admin(#request{user = User,
               ], Lang);
 
 process_admin(#request{method = Method,
-                       user = User,
+                       us = US,
                        path = ["acls"],
                        q = Query,
                        lang = Lang} = Request) ->
@@ -618,7 +618,7 @@ process_admin(#request{method = Method,
                     ])
               ], Lang);
 
-process_admin(#request{user = User,
+process_admin(#request{us = US,
                        path = ["access-raw"],
                        q = Query,
                        lang = Lang} = Request) ->
@@ -686,7 +686,7 @@ process_admin(#request{user = User,
               ], Lang);
 
 process_admin(#request{method = Method,
-                      user = User,
+                      us = US,
                       path = ["access"],
                       q = Query,
                       lang = Lang} = Request) ->
@@ -722,7 +722,7 @@ process_admin(#request{method = Method,
               ], Lang);
 
 process_admin(#request{method = Method,
-                      user = User,
+                      us = US,
                       path = ["access", SName],
                       q = Query,
                       lang = Lang} = Request) ->
@@ -761,63 +761,63 @@ process_admin(#request{method = Method,
                     ])
               ], Lang);
 
-process_admin(#request{user = User,
+process_admin(#request{us = US,
                        path = ["users"],
                        q = Query,
                        lang = Lang} = Request) ->
     Res = list_users(Query, Lang),
     make_xhtml([?XCT("h1", "ejabberd users")] ++ Res, Lang);
 
-process_admin(#request{user = User,
+process_admin(#request{us = US,
                       path = ["users", Diap],
                       q = Query,
                       lang = Lang} = Request) ->
     Res = list_users_in_diapason(Diap, Lang),
     make_xhtml([?XCT("h1", "ejabberd users")] ++ Res, Lang);
 
-process_admin(#request{user = User,
+process_admin(#request{us = US,
                        path = ["online-users"],
                        q = Query,
                        lang = Lang} = Request) ->
     Res = list_online_users(Lang),
     make_xhtml([?XCT("h1", "ejabberd users")] ++ Res, Lang);
 
-process_admin(#request{user = User,
+process_admin(#request{us = US,
                       path = ["stats"],
                       q = Query,
                       lang = Lang} = Request) ->
     Res = get_stats(Lang),
     make_xhtml([?XCT("h1", "ejabberd stats")] ++ Res, Lang);
 
-process_admin(#request{user = User,
+process_admin(#request{us = US,
                       path = ["user", U],
                       q = Query,
                       lang = Lang} = Request) ->
     Res = user_info(U, Query, Lang),
     make_xhtml(Res, Lang);
 
-process_admin(#request{user = User,
+process_admin(#request{us = US,
                       path = ["user", U, "queue"],
                       q = Query,
                       lang = Lang} = Request) ->
     Res = user_queue(U, Query, Lang),
     make_xhtml(Res, Lang);
 
-process_admin(#request{user = User,
+process_admin(#request{us = US,
                       path = ["user", U, "roster"],
                       q = Query,
                       lang = Lang} = Request) ->
     Res = user_roster(U, Query, Lang, true),
     make_xhtml(Res, Lang);
 
-process_admin(#request{user = User,
+process_admin(#request{us = US,
                       path = ["nodes"],
                       q = Query,
                       lang = Lang} = Request) ->
     Res = get_nodes(Lang),
     make_xhtml(Res, Lang);
 
-process_admin(#request{user = User,
+process_admin(#request{us = US,
                       path = ["node", SNode | NPath],
                       q = Query,
                       lang = Lang} = Request) ->
@@ -829,6 +829,20 @@ process_admin(#request{user = User,
            make_xhtml(Res, Lang)
     end;
 
+process_admin(#request{us = US,
+                       path = ["shared-roster"],
+                       q = Query,
+                       lang = Lang} = Request) ->
+    Res = list_shared_roster_groups(Query, Lang),
+    make_xhtml(Res, Lang);
+
+process_admin(#request{us = US,
+                       path = ["shared-roster", Group],
+                       q = Query,
+                       lang = Lang} = Request) ->
+    Res = shared_roster_group(Group, Query, Lang),
+    make_xhtml(Res, Lang);
+
 process_admin(#request{lang = Lang}) ->
     setelement(1, make_xhtml([?XC("h1", "Not found")], Lang), 404).
 
@@ -1065,7 +1079,7 @@ parse_access_rule(Text) ->
 list_users(Query, Lang) ->
     Res = list_users_parse_query(Query),
     Users = ejabberd_auth:dirty_get_registered_users(),
-    SUsers = lists:sort(Users),
+    SUsers = lists:sort([{S, U} || {U, S} <- Users]),
     FUsers =
        case length(SUsers) of
            N when N =< 100 ->
@@ -1077,11 +1091,12 @@ list_users(Query, Lang) ->
                  fun(K) ->
                          L = K + M - 1,
                          Node = integer_to_list(K) ++ "-" ++ integer_to_list(L),
-                         Last = if L < N -> lists:nth(L, SUsers);
-                                   true -> lists:last(SUsers)
+                         Last = if L < N -> su_to_list(lists:nth(L, SUsers));
+                                   true -> su_to_list(lists:last(SUsers))
                                 end,
                          Name = 
-                             lists:nth(K, SUsers) ++ [$\s, 226, 128, 148, $\s] ++
+                             su_to_list(lists:nth(K, SUsers)) ++
+                             [$\s, 226, 128, 148, $\s] ++
                              Last,
                          [?AC(Node ++ "/", Name), ?BR]
                  end, lists:seq(1, N, M))
@@ -1131,7 +1146,7 @@ list_users_parse_query(Query) ->
 
 list_users_in_diapason(Diap, Lang) ->
     Users = ejabberd_auth:dirty_get_registered_users(),
-    SUsers = lists:sort(Users),
+    SUsers = lists:sort([{S, U} || {U, S} <- Users]),
     {ok, [S1, S2]} = regexp:split(Diap, "-"),
     N1 = list_to_integer(S1),
     N2 = list_to_integer(S2),
@@ -1147,14 +1162,15 @@ list_given_users(Users, Prefix, Lang) ->
                   ?XCT("td", "Last Activity")])]),
         ?XE("tbody",
             lists:map(
-              fun(User) ->
-                      QueueLen = length(mnesia:dirty_read({offline_msg, User})),
+              fun(SU = {Server, User}) ->
+                      US = {User, Server},
+                      QueueLen = length(mnesia:dirty_read({offline_msg, US})),
                       FQueueLen = [?AC(Prefix ++ "user/" ++ User ++ "/queue/",
                                        integer_to_list(QueueLen))],
                       FLast =
-                          case ejabberd_sm:get_user_resources(User) of
+                          case ejabberd_sm:get_user_resources(User, Server) of
                               [] ->
-                                  case mnesia:dirty_read({last_activity, User}) of
+                                  case mnesia:dirty_read({last_activity, US}) of
                                       [] ->
                                           ?T("Never");
                                       [E] ->
@@ -1173,13 +1189,20 @@ list_given_users(Users, Prefix, Lang) ->
                                   ?T("Online")
                           end,
                       ?XE("tr",
-                          [?XE("td", [?AC(Prefix ++ "user/" ++ User ++ "/",
-                                          User)]),
+                          [?XE("td", [?AC(Prefix ++ "user/" ++
+                                          us_to_list(US) ++ "/",
+                                          us_to_list(US))]),
                            ?XE("td", FQueueLen),
                            ?XC("td", FLast)])
               end, Users)
            )]).
 
+us_to_list({User, Server}) ->
+    jlib:jid_to_string({User, Server, ""}).
+
+su_to_list({Server, User}) ->
+    jlib:jid_to_string({User, Server, ""}).
+
 
 get_stats(Lang) ->
     OnlineUsers = mnesia:table_info(presence, size),
@@ -1206,17 +1229,20 @@ get_stats(Lang) ->
 
 
 list_online_users(_Lang) ->
-    Users = lists:map(fun({U, R}) -> U end,
-                     ejabberd_sm:dirty_get_sessions_list()),
+    Users = [{S, U} || {U, S, R} <- ejabberd_sm:dirty_get_sessions_list()],
     SUsers = lists:usort(Users),
     lists:flatmap(
-      fun(U) ->
-             [?AC("../user/" ++ U ++ "/", U), ?BR]
+      fun(SU) ->
+             [?AC("../user/" ++ su_to_list(SU) ++ "/", su_to_list(SU)), ?BR]
       end, SUsers).
 
-user_info(User, Query, Lang) ->
-    Res = user_parse_query(User, Query),
-    Resources = ejabberd_sm:get_user_resources(User),
+user_info(SUser, Query, Lang) ->
+    UJID = jlib:string_to_jid(SUser),
+    User = UJID#jid.user,
+    Server = UJID#jid.server,
+    US = {UJID#jid.luser, UJID#jid.lserver},
+    Res = user_parse_query(User, Server, Query),
+    Resources = ejabberd_sm:get_user_resources(User, Server),
     FResources =
        case Resources of
            [] ->
@@ -1227,13 +1253,13 @@ user_info(User, Query, Lang) ->
                                       ?LI([?C(R)])
                               end, lists:sort(Resources)))]
        end,
-    Password = ejabberd_auth:get_password_s(User),
+    Password = ejabberd_auth:get_password_s(User, Server),
     FPassword = [?INPUT("password", "password", Password), ?C(" "),
                 ?INPUTT("submit", "chpassword", "Change Password")],
-    QueueLen = length(mnesia:dirty_read({offline_msg, User})),
+    QueueLen = length(mnesia:dirty_read({offline_msg, US})),
     FQueueLen = [?AC("queue/",
                     integer_to_list(QueueLen))],
-    [?XC("h1", ?T("User ") ++ User)] ++
+    [?XC("h1", ?T("User ") ++ us_to_list(US))] ++
        case Res of
            ok -> [?CT("submitted"), ?P];
            error -> [?CT("bad format"), ?P];
@@ -1247,14 +1273,14 @@ user_info(User, Query, Lang) ->
              [?BR, ?INPUTT("submit", "removeuser", "Remove User")])].
 
 
-user_parse_query(User, Query) ->
+user_parse_query(User, Server, Query) ->
     case lists:keysearch("chpassword", 1, Query) of
        {value, _} ->
            case lists:keysearch("password", 1, Query) of
                {value, {_, undefined}} ->
                    error;
                {value, {_, Password}} ->
-                   ejabberd_auth:set_password(User, Password),
+                   ejabberd_auth:set_password(User, Server, Password),
                    ok;
                _ ->
                    error
@@ -1262,7 +1288,7 @@ user_parse_query(User, Query) ->
        _ ->
            case lists:keysearch("removeuser", 1, Query) of
                {value, _} ->
-                   ejabberd_auth:remove_user(User),
+                   ejabberd_auth:remove_user(User, Server),
                    ok;
                false ->
                    nothing
@@ -1270,12 +1296,16 @@ user_parse_query(User, Query) ->
     end.
 
 
-user_queue(User, Query, Lang) ->
-    Res = user_queue_parse_query(User, Query),
-    Msgs = lists:keysort(3, mnesia:dirty_read({offline_msg, User})),
+user_queue(SUser, Query, Lang) ->
+    UJID = jlib:string_to_jid(SUser),
+    User = UJID#jid.user,
+    Server = UJID#jid.server,
+    US = {UJID#jid.luser, UJID#jid.lserver},
+    Res = user_queue_parse_query(US, Query),
+    Msgs = lists:keysort(3, mnesia:dirty_read({offline_msg, US})),
     FMsgs =
        lists:map(
-         fun({offline_msg, _User, TimeStamp, _Expire, From, To,
+         fun({offline_msg, _US, TimeStamp, _Expire, From, To,
               {xmlelement, Name, Attrs, Els}} = Msg) ->
                  ID = jlib:encode_base64(binary_to_list(term_to_binary(Msg))),
                  {{Year, Month, Day}, {Hour, Minute, Second}} =
@@ -1298,7 +1328,8 @@ user_queue(User, Query, Lang) ->
                       ?XAE("td", [{"class", "valign"}], [?XC("pre", FPacket)])]
                     )
          end, Msgs),
-    [?XC("h1", io_lib:format(?T("~s offline messages queue"), [User]))] ++
+    [?XC("h1", io_lib:format(?T("~s offline messages queue"),
+                            [us_to_list(US)]))] ++
        case Res of
            ok -> [?CT("submitted"), ?P];
            error -> [?CT("bad format"), ?P];
@@ -1319,10 +1350,10 @@ user_queue(User, Query, Lang) ->
               ?INPUTT("submit", "delete", "Delete Selected")
              ])].
 
-user_queue_parse_query(User, Query) ->
+user_queue_parse_query(US, Query) ->
     case lists:keysearch("delete", 1, Query) of
        {value, _} ->
-           Msgs = lists:keysort(3, mnesia:dirty_read({offline_msg, User})),
+           Msgs = lists:keysort(3, mnesia:dirty_read({offline_msg, US})),
            F = fun() ->
                        lists:foreach(
                          fun(Msg) ->
@@ -1344,8 +1375,8 @@ user_queue_parse_query(User, Query) ->
 
 
 
--record(roster, {uj,
-                user,
+-record(roster, {usj,
+                us,
                 jid,
                 name = "",
                 subscription = none,
@@ -1359,11 +1390,14 @@ ask_to_pending(unsubscribe) -> none;
 ask_to_pending(Ask) -> Ask.
 
 
-user_roster(User, Query, Lang, Admin) ->
-    LUser = jlib:nameprep(User),
-    Items1 = mnesia:dirty_index_read(roster, LUser, #roster.user),
-    Res = user_roster_parse_query(User, Items1, Query, Admin),
-    Items = mnesia:dirty_index_read(roster, LUser, #roster.user),
+user_roster(SUser, Query, Lang, Admin) ->
+    UJID = jlib:string_to_jid(SUser),
+    User = UJID#jid.user,
+    Server = UJID#jid.server,
+    US = {UJID#jid.luser, UJID#jid.lserver},
+    Items1 = mnesia:dirty_index_read(roster, US, #roster.us),
+    Res = user_roster_parse_query(User, Server, Items1, Query, Admin),
+    Items = mnesia:dirty_index_read(roster, US, #roster.us),
     SItems = lists:sort(Items),
     FItems =
        case SItems of
@@ -1415,7 +1449,7 @@ user_roster(User, Query, Lang, Admin) ->
                                                       "Remove")])])
                            end, SItems))])]
        end,
-    [?XC("h1", ?T("Roster of ") ++ User)] ++
+    [?XC("h1", ?T("Roster of ") ++ us_to_list(US))] ++
        case Res of
            ok -> [?CT("submitted"), ?P];
            error -> [?CT("bad format"), ?P];
@@ -1428,7 +1462,7 @@ user_roster(User, Query, Lang, Admin) ->
               ?INPUTT("submit", "addjid", "Add JID")
              ])].
 
-user_roster_parse_query(User, Items, Query, Admin) ->
+user_roster_parse_query(User, Server, Items, Query, Admin) ->
     case lists:keysearch("addjid", 1, Query) of
        {value, _} ->
            case lists:keysearch("newjid", 1, Query) of
@@ -1437,7 +1471,7 @@ user_roster_parse_query(User, Items, Query, Admin) ->
                {value, {_, SJID}} ->
                    case jlib:string_to_jid(SJID) of
                        JID when is_record(JID, jid) ->
-                           user_roster_subscribe_jid(User, JID),
+                           user_roster_subscribe_jid(User, Server, JID),
                            ok;
                        error ->
                            error
@@ -1446,69 +1480,25 @@ user_roster_parse_query(User, Items, Query, Admin) ->
                    error
            end;
        false ->
-           case lists:keysearch("adduser", 1, Query) of
-               {value, _} ->
-                   case lists:keysearch("newuser", 1, Query) of
-                       {value, {_, undefined}} ->
-                           error;
-                       {value, {_, U}} ->
-                           if
-                               Admin ->
-                                   user_roster_subscribe_users(User, U);
-                               true ->
-                                   case jlib:make_jid(U, ?MYNAME, "") of
-                                       JID when is_record(JID, jid) ->
-                                           user_roster_subscribe_jid(
-                                             User, JID),
-                                           ok;
-                                       false ->
-                                           error
-                                   end
-                           end;
-                       false ->
-                           error
-                   end;
-               false ->
-                   case catch user_roster_item_parse_query(
-                                User, Items, Query) of
-                       submitted ->
-                           ok;
-                       {'EXIT', _Reason} ->
-                           error;
-                       _ ->
-                           nothing
-                   end
+           case catch user_roster_item_parse_query(
+                        User, Server, Items, Query) of
+               submitted ->
+                   ok;
+               {'EXIT', _Reason} ->
+                   error;
+               _ ->
+                   nothing
            end
     end.
 
-user_roster_subscribe_users(User1, User2) ->
-    case jlib:make_jid(User1, ?MYNAME, "") of
-       JID1 when is_record(JID1, jid) ->
-           case jlib:make_jid(User2, ?MYNAME, "") of
-               JID2 when is_record(JID2, jid) ->
-                   mod_roster:out_subscription(User1, JID2, subscribe),
-                   mod_roster:in_subscription(User2, JID1, subscribe),
-                   mod_roster:out_subscription(User2, JID1, subscribe),
-                   mod_roster:in_subscription(User1, JID2, subscribe),
-                   mod_roster:out_subscription(User1, JID2, subscribed),
-                   mod_roster:in_subscription(User2, JID1, subscribed),
-                   mod_roster:out_subscription(User2, JID1, subscribed),
-                   mod_roster:in_subscription(User1, JID2, subscribed),
-                   ok;
-               false ->
-                   error
-           end;
-       false ->
-           error
-    end.
 
-user_roster_subscribe_jid(User, JID) ->
-    mod_roster:out_subscription(User, JID, subscribe),
-    UJID = jlib:make_jid(User, ?MYNAME, ""),
+user_roster_subscribe_jid(User, Server, JID) ->
+    mod_roster:out_subscription(User, Server, JID, subscribe),
+    UJID = jlib:make_jid(User, Server, ""),
     ejabberd_router:route(
       UJID, JID, {xmlelement, "presence", [{"type", "subscribe"}], []}).
 
-user_roster_item_parse_query(User, Items, Query) ->
+user_roster_item_parse_query(User, Server, Items, Query) ->
     lists:foreach(
       fun(R) ->
              JID = R#roster.jid,
@@ -1516,8 +1506,9 @@ user_roster_item_parse_query(User, Items, Query) ->
                     "validate" ++ term_to_id(JID), 1, Query) of
                  {value, _} ->
                      JID1 = jlib:make_jid(JID),
-                     mod_roster:out_subscription(User, JID1, subscribed),
-                     UJID = jlib:make_jid(User, ?MYNAME, ""),
+                     mod_roster:out_subscription(
+                       User, Server, JID1, subscribed),
+                     UJID = jlib:make_jid(User, Server, ""),
                      ejabberd_router:route(
                        UJID, JID1, {xmlelement, "presence",
                                     [{"type", "subscribed"}], []}),
@@ -1526,7 +1517,7 @@ user_roster_item_parse_query(User, Items, Query) ->
                      case lists:keysearch(
                             "remove" ++ term_to_id(JID), 1, Query) of
                          {value, _} ->
-                             UJID = jlib:make_jid(User, ?MYNAME, ""),
+                             UJID = jlib:make_jid(User, Server, ""),
                              mod_roster:process_iq(
                                UJID, UJID,
                                #iq{type = set,
@@ -2038,3 +2029,200 @@ pretty_print({xmlelement, Name, Attrs, Els}, Prefix) ->
             end
      end].
 
+
+list_shared_roster_groups(Query, Lang) ->
+    Res = list_sr_groups_parse_query(Query),
+    SRGroups = mod_shared_roster:list_groups(?MYNAME),
+    FGroups =
+       ?XAE("table", [],
+            [?XE("tbody",
+                 lists:map(
+                   fun(Group) ->
+                           ?XE("tr",
+                               [?XE("td", [?INPUT("checkbox", "selected",
+                                                  Group)]),
+                                ?XE("td", [?AC(Group ++ "/", Group)])
+                               ]
+                              )
+                   end, lists:sort(SRGroups)) ++
+                 [?XE("tr",
+                      [?X("td"),
+                       ?XE("td", [?INPUT("text", "namenew", "")]),
+                       ?XE("td", [?INPUTT("submit", "addnew", "Add New")])
+                      ]
+                     )]
+                )]),
+    [?XC("h1", ?T("Shared roster groups"))] ++
+       case Res of
+           ok -> [?CT("submitted"), ?P];
+           error -> [?CT("bad format"), ?P];
+           nothing -> []
+       end ++
+       [?XAE("form", [{"method", "post"}],
+             [FGroups,
+              ?BR,
+              ?INPUTT("submit", "delete", "Delete Selected")
+             ])
+       ].
+
+list_sr_groups_parse_query(Query) ->
+    case lists:keysearch("addnew", 1, Query) of
+       {value, _} ->
+           list_sr_groups_parse_addnew(Query);
+       _ ->
+           case lists:keysearch("delete", 1, Query) of
+               {value, _} ->
+                   list_sr_groups_parse_delete(Query);
+               _ ->
+                   nothing
+           end
+    end.
+
+list_sr_groups_parse_addnew(Query) ->
+    case lists:keysearch("namenew", 1, Query) of
+       {value, {_, Group}} when Group /= "" ->
+           mod_shared_roster:create_group(?MYNAME, Group),
+           ok;
+       _ ->
+           error
+    end.
+
+list_sr_groups_parse_delete(Query) ->
+    SRGroups = mod_shared_roster:list_groups(?MYNAME),
+    lists:foreach(
+      fun(Group) ->
+             case lists:member({"selected", Group}, Query) of
+                 true ->
+                     mod_shared_roster:delete_group(?MYNAME, Group);
+                 _ ->
+                     ok
+             end
+      end, SRGroups),
+    ok.
+
+
+shared_roster_group(Group, Query, Lang) ->
+    Res = shared_roster_group_parse_query(?MYNAME, Group, Query),
+    GroupOpts = mod_shared_roster:get_group_opts(?MYNAME, Group),
+    Name = get_opt(GroupOpts, name, ""),
+    Description = get_opt(GroupOpts, description, ""),
+    Members = mod_shared_roster:get_group_users(?MYNAME, Group),
+    Disabled = false,
+    DisplayedGroups = get_opt(GroupOpts, displayed_groups, []),
+    FMembers = [[us_to_list(Member), $\n] || Member <- Members],
+    FDisplayedGroups = [[DG, $\n] || DG <- DisplayedGroups],
+    FGroup =
+       ?XAE("table", [],
+            [?XE("tbody",
+                 [?XE("tr",
+                      [?XCT("td", "Name:"),
+                       ?XE("td", [?INPUT("text", "name", Name)])
+                      ]
+                     ),
+                  ?XE("tr",
+                      [?XCT("td", "Description:"),
+                       ?XE("td", [?XAC("textarea", [{"name", "description"},
+                                                    {"rows", "3"},
+                                                    {"cols", "20"}],
+                                       Description)])
+                      ]
+                     ),
+                  ?XE("tr",
+                      [?XCT("td", "Members:"),
+                       ?XE("td", [?XAC("textarea", [{"name", "members"},
+                                                    {"rows", "3"},
+                                                    {"cols", "20"}],
+                                       FMembers)])
+                      ]
+                     ),
+                  ?XE("tr",
+                      [?XCT("td", "Displayed Groups:"),
+                       ?XE("td", [?XAC("textarea", [{"name", "dispgroups"},
+                                                    {"rows", "3"},
+                                                    {"cols", "20"}],
+                                       FDisplayedGroups)])
+                      ]
+                     )]
+                )]),
+    [?XC("h1", ?T("Shared roster group " ++ Group))] ++
+       case Res of
+           ok -> [?CT("submitted"), ?P];
+           error -> [?CT("bad format"), ?P];
+           nothing -> []
+       end ++
+       [?XAE("form", [{"method", "post"}],
+             [FGroup,
+              ?BR,
+              ?INPUTT("submit", "submit", "Submit")
+             ])
+       ].
+
+shared_roster_group_parse_query(Host, Group, Query) ->
+    case lists:keysearch("submit", 1, Query) of
+       {value, _} ->
+           {value, {_, Name}} = lists:keysearch("name", 1, Query),
+           {value, {_, Description}} = lists:keysearch("description", 1, Query),
+           {value, {_, SMembers}} = lists:keysearch("members", 1, Query),
+           {value, {_, SDispGroups}} = lists:keysearch("dispgroups", 1, Query),
+           NameOpt =
+               if
+                   Name == "" -> [];
+                   true -> [{name, Name}]
+               end,
+           DescriptionOpt =
+               if
+                   Description == "" -> [];
+                   true -> [{description, Description}]
+               end,
+           DispGroups = string:tokens(SDispGroups, "\r\n"),
+           DispGroupsOpt =
+               if
+                   DispGroups == [] -> [];
+                   true -> [{displayed_groups, DispGroups}]
+               end,
+           mod_shared_roster:set_group_opts(
+             ?MYNAME, Group, NameOpt ++ DispGroupsOpt ++ DescriptionOpt),
+
+           OldMembers = mod_shared_roster:get_group_users(?MYNAME, Group),
+           NewMembers =
+               lists:foldl(
+                 fun(_SJID, error) -> error;
+                    (SJID, USs) ->
+                         case jlib:string_to_jid(SJID) of
+                             JID when is_record(JID, jid) ->
+                                 [{JID#jid.luser, JID#jid.lserver} | USs];
+                             error ->
+                                 error
+                         end
+                 end, [], string:tokens(SMembers, "\r\n")),
+           if
+               NewMembers == error -> error;
+               true ->
+                   AddedMembers = NewMembers -- OldMembers,
+                   RemovedMembers = OldMembers -- NewMembers,
+                   lists:foreach(
+                     fun(US) ->
+                             mod_shared_roster:remove_user_from_group(
+                               Host, US, Group)
+                     end, RemovedMembers),
+                   lists:foreach(
+                     fun(US) ->
+                             mod_shared_roster:add_user_to_group(
+                               Host, US, Group)
+                     end, AddedMembers),
+                   ok
+           end;
+       _ ->
+           nothing
+    end.
+
+
+
+get_opt(Opts, Opt, Default) ->
+    case lists:keysearch(Opt, 1, Opts) of
+       {value, {_, Val}} ->
+           Val;
+       false ->
+           Default
+    end.
+