]> granicus.if.org Git - ejabberd/commitdiff
Merge SQL and Mnesia code into one module (EJAB-1560)
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>
Fri, 27 Apr 2012 09:52:05 +0000 (19:52 +1000)
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>
Fri, 27 Apr 2012 09:52:05 +0000 (19:52 +1000)
38 files changed:
src/acl.erl
src/ejabberd.app
src/ejabberd_admin.erl
src/ejabberd_auth_external.erl
src/ejabberd_config.erl
src/ejabberd_piefxis.erl
src/ejd2odbc.erl
src/gen_mod.erl
src/jd2ejd.erl
src/mod_announce.erl
src/mod_announce_odbc.erl [deleted file]
src/mod_blocking.erl
src/mod_blocking_odbc.erl [deleted file]
src/mod_configure.erl
src/mod_irc/Makefile.win32
src/mod_irc/mod_irc.erl
src/mod_irc/mod_irc_odbc.erl [deleted file]
src/mod_last.erl
src/mod_last_odbc.erl [deleted file]
src/mod_muc/Makefile.win32
src/mod_muc/mod_muc.erl
src/mod_muc/mod_muc_odbc.erl [deleted file]
src/mod_muc/mod_muc_room.erl
src/mod_offline.erl
src/mod_offline_odbc.erl [deleted file]
src/mod_privacy.erl
src/mod_privacy_odbc.erl [deleted file]
src/mod_private.erl
src/mod_private_odbc.erl [deleted file]
src/mod_roster.erl
src/mod_roster_odbc.erl [deleted file]
src/mod_shared_roster.erl
src/mod_shared_roster_odbc.erl [deleted file]
src/mod_vcard.erl
src/mod_vcard_odbc.erl [deleted file]
src/mod_vcard_xupdate.erl
src/mod_vcard_xupdate_odbc.erl [deleted file]
src/web/ejabberd_web_admin.erl

index dd7c441de46da8d1b41e3ebe28896a2aefbc4d6b..6ce11f99af8ea82e3e51ee74aa474efc84e6f036 100644 (file)
@@ -241,9 +241,9 @@ is_glob_match(String, Glob) ->
     is_regexp_match(String, ejabberd_regexp:sh_to_awk(Glob)).
 
 loaded_shared_roster_module(Host) ->
-    case {gen_mod:is_loaded(Host, mod_shared_roster_odbc),
-          gen_mod:is_loaded(Host, mod_shared_roster_ldap)} of
-        {true, _} -> mod_shared_roster_odbc;
-        {_, true} -> mod_shared_roster_ldap;
-        _ -> mod_shared_roster
+    case gen_mod:is_loaded(Host, mod_shared_roster_ldap) of
+        true ->
+            mod_shared_roster_ldap;
+        false ->
+            mod_shared_roster
     end.
index 2f08601a8a497ca98caadc32c0c4186efaa69ccf..1af67fbaeb0d9a83e8010380f4aec6dd2445cb2f 100644 (file)
             mod_irc,
             mod_irc_connection,
             mod_last,
-            mod_last_odbc,
             mod_muc,
             mod_muc_log,
             mod_muc_room,
             mod_offline,
-            mod_offline_odbc,
             mod_privacy,
-            mod_privacy_odbc,
             mod_private,
-            mod_private_odbc,
             mod_proxy65,
             mod_proxy65_lib,
             mod_proxy65_service,
             mod_pubsub,
             mod_register,
             mod_roster,
-            mod_roster_odbc,
             mod_service_log,
             mod_shared_roster,
             mod_stats,
             mod_time,
             mod_vcard,
             mod_vcard_ldap,
-            mod_vcard_odbc,
             mod_version,
             node_buddy,
             node_club,
index b7fbbd00cbeda8d89a07a49e1c7cbaa855535fb2..40c8b8dca667e1ea53249d27cd6a6cb416b69063 100644 (file)
@@ -373,13 +373,16 @@ import_dir(Path) ->
 %%%
 
 delete_expired_messages() ->
-    {atomic, ok} = mod_offline:remove_expired_messages(),
-    ok.
+    lists:foreach(
+      fun(Host) ->
+              {atomic, ok} = mod_offline:remove_expired_messages(Host)
+      end, ?MYHOSTS).
 
 delete_old_messages(Days) ->
-    {atomic, _} = mod_offline:remove_old_messages(Days),
-    ok.
-
+    lists:foreach(
+      fun(Host) ->
+              {atomic, _} = mod_offline:remove_old_messages(Days, Host)
+      end, ?MYHOSTS).
 
 %%%
 %%% Mnesia management
index 06ec5548f40ad7eb37eecf61a48d22e50340b19d..ba89534d118323b87399a4f24c8d6ba64ebf3a28 100644 (file)
@@ -293,27 +293,20 @@ get_last_access(User, Server) ->
 get_last_info(User, Server) ->
     case get_mod_last_enabled(Server) of
        mod_last -> mod_last:get_last_info(User, Server);
-       mod_last_odbc -> mod_last_odbc:get_last_info(User, Server);
        no_mod_last -> mod_last_required
     end.
 
-%% @spec (Server) -> mod_last | mod_last_odbc | no_mod_last
+%% @spec (Server) -> mod_last | no_mod_last
 get_mod_last_enabled(Server) ->
-    ML = gen_mod:is_loaded(Server, mod_last),
-    MLO = gen_mod:is_loaded(Server, mod_last_odbc),
-    case {ML, MLO} of
-       {true, _} -> mod_last;
-       {false, true} -> mod_last_odbc;
-       {false, false} -> no_mod_last
+    case gen_mod:is_loaded(Server, mod_last) of
+        true -> mod_last;
+       false -> no_mod_last
     end.
 
 get_mod_last_configured(Server) ->
-    ML = is_configured(Server, mod_last),
-    MLO = is_configured(Server, mod_last_odbc),
-    case {ML, MLO} of
-       {true, _} -> mod_last;
-       {false, true} -> mod_last_odbc;
-       {false, false} -> no_mod_last
+    case is_configured(Server, mod_last) of
+        true -> mod_last;
+       false -> no_mod_last
     end.
 
 is_configured(Host, Module) ->
index 503537e76fe094f5895976396f74ee3dd8875b31..f83d856718f1bfa0fd8830a6e6a5c018aad8f332 100644 (file)
@@ -467,6 +467,8 @@ process_host_term(Term, Host, State) ->
            State;
        {odbc_server, ODBC_server} ->
            add_option({odbc_server, Host}, ODBC_server, State);
+        {modules, Modules} ->
+            add_option({modules, Host}, replace_modules(Modules), State);
        {Opt, Val} ->
            add_option({Opt, Host}, Val, State)
     end.
@@ -610,3 +612,30 @@ is_file_readable(Path) ->
        {error, _Reason} ->
            false
     end.
+
+replace_module(mod_announce_odbc) -> {mod_announce, odbc};
+replace_module(mod_blocking_odbc) -> {mod_blocking, odbc};
+replace_module(mod_irc_odbc) -> {mod_irc, odbc};
+replace_module(mod_last_odbc) -> {mod_last, odbc};
+replace_module(mod_muc_odbc) -> {mod_muc, odbc};
+replace_module(mod_offline_odbc) -> {mod_offline, odbc};
+replace_module(mod_privacy_odbc) -> {mod_privacy, odbc};
+replace_module(mod_private_odbc) -> {mod_private, odbc};
+replace_module(mod_roster_odbc) -> {mod_roster, odbc};
+replace_module(mod_shared_roster_odbc) -> {mod_shared_roster, odbc};
+replace_module(mod_vcard_odbc) -> {mod_vcard, odbc};
+replace_module(mod_vcard_xupdate_odbc) -> {mod_vcard_xupdate, odbc};
+replace_module(Module) -> Module.
+
+replace_modules(Modules) ->
+    lists:map(
+      fun({Module, Opts}) ->
+              case replace_module(Module) of
+                  {NewModule, DBType} ->
+                      NewOpts = [{db_type, DBType} |
+                                 lists:keydelete(db_type, 1, Opts)],
+                      {NewModule, NewOpts};
+                  NewModule ->
+                      {NewModule, Opts}
+              end
+      end, Modules).
index 0ed1f6c0c40853b21ca3e5dde35a823e5bbf88a4..b667301a5f84eb66a0ffb2df7f0e1149608f43fe 100644 (file)
@@ -289,9 +289,10 @@ create_user(User,Password,Domain) ->
 
 populate_user(User,Domain,El=#xmlel{name='query', ns='jabber:iq:roster'}) ->
     io:format("Trying to add/update roster list...",[]),
-    case loaded_module(Domain,[mod_roster_odbc,mod_roster]) of
-       {ok, M} ->
-           case M:set_items(User, Domain, exmpp_xml:xmlel_to_xmlelement(El)) of
+    case loaded_module(Domain, mod_roster) of
+       {ok, _DBType} ->
+           case mod_roster:set_items(User, Domain,
+                                      exmpp_xml:xmlel_to_xmlelement(El)) of
                {atomic, ok} ->
                    io:format(" DONE.~n",[]),
                    ok;
@@ -302,7 +303,7 @@ populate_user(User,Domain,El=#xmlel{name='query', ns='jabber:iq:roster'}) ->
                    {error, not_found}
            end;
        E -> io:format(" ERROR: ~p~n",[E]),
-            ?ERROR_MSG("No modules loaded [mod_roster, mod_roster_odbc] ~s ~n",
+            ?ERROR_MSG("No modules loaded [mod_roster] ~s ~n",
                        [exmpp_xml:document_to_list(El)]),
             {error, not_found}
     end;
@@ -330,10 +331,10 @@ populate_user(User,Domain,El=#xmlel{name='query', ns='jabber:iq:roster'}) ->
 
 populate_user(User,Domain,El=#xmlel{name='vCard', ns='vcard-temp'}) ->
     io:format("Trying to add/update vCards...",[]),
-    case loaded_module(Domain,[mod_vcard,mod_vcard_odbc]) of
-       {ok, M}  ->  FullUser = jid_to_old_jid(exmpp_jid:make(User, Domain)),
+    case loaded_module(Domain, mod_vcard) of
+       {ok, _}  ->  FullUser = jid_to_old_jid(exmpp_jid:make(User, Domain)),
                     IQ = iq_to_old_iq(#iq{type = set, payload = El}),
-                    case M:process_sm_iq(FullUser, FullUser , IQ) of
+                    case mod_vcard:process_sm_iq(FullUser, FullUser , IQ) of
                         {error,_Err} ->
                             io:format(" ERROR.~n",[]),
                             ?ERROR_MSG("Error processing vcard ~s : ~p ~n",
@@ -343,7 +344,7 @@ populate_user(User,Domain,El=#xmlel{name='vCard', ns='vcard-temp'}) ->
                     end;
        _ ->
            io:format(" ERROR.~n",[]),
-           ?ERROR_MSG("No modules loaded [mod_vcard, mod_vcard_odbc] ~s ~n",
+           ?ERROR_MSG("No modules loaded [mod_vcard] ~s ~n",
                       [exmpp_xml:document_to_list(El)]),
            {error, not_found}
     end;
@@ -356,8 +357,8 @@ populate_user(User,Domain,El=#xmlel{name='vCard', ns='vcard-temp'}) ->
 
 populate_user(User,Domain,El=#xmlel{name='offline-messages'}) ->
     io:format("Trying to add/update offline-messages...",[]),
-    case loaded_module(Domain, [mod_offline, mod_offline_odbc]) of
-       {ok, M} ->
+    case loaded_module(Domain, mod_offline) of
+       {ok, _DBType} ->
            ok = exmpp_xml:foreach(
                   fun (_Element, {xmlcdata, _}) ->
                           ok;
@@ -367,11 +368,11 @@ populate_user(User,Domain,El=#xmlel{name='offline-messages'}) ->
                           FullUser = jid_to_old_jid(exmpp_jid:make(User,
                                                                    Domain)),
                           OldChild = exmpp_xml:xmlel_to_xmlelement(Child),
-                          _R = M:store_packet(FullFrom, FullUser, OldChild)
+                          _R = mod_offline:store_packet(FullFrom, FullUser, OldChild)
                   end, El), io:format(" DONE.~n",[]);
        _ ->
            io:format(" ERROR.~n",[]),
-           ?ERROR_MSG("No modules loaded [mod_offline, mod_offline_odbc] ~s ~n",
+           ?ERROR_MSG("No modules loaded [mod_offline] ~s ~n",
                       [exmpp_xml:document_to_list(El)]),
            {error, not_found}
     end;
@@ -384,15 +385,15 @@ populate_user(User,Domain,El=#xmlel{name='offline-messages'}) ->
 
 populate_user(User,Domain,El=#xmlel{name='query', ns='jabber:iq:private'}) ->
     io:format("Trying to add/update private storage...",[]),
-    case loaded_module(Domain,[mod_private_odbc,mod_private]) of
-       {ok, M} ->
+    case loaded_module(Domain, mod_private) of
+       {ok, _DBType} ->
            FullUser = jid_to_old_jid(exmpp_jid:make(User, Domain)),
            IQ = iq_to_old_iq(#iq{type = set,
                                  ns = 'jabber:iq:private',
                                  kind = request,
                                  iq_ns = 'jabberd:client',
                                  payload = El}),
-           case M:process_sm_iq(FullUser, FullUser, IQ ) of
+           case mod_private:process_sm_iq(FullUser, FullUser, IQ ) of
                {error, _Err} ->
                    io:format(" ERROR.~n",[]),
                    ?ERROR_MSG("Error processing private storage ~s : ~p ~n",
@@ -401,7 +402,7 @@ populate_user(User,Domain,El=#xmlel{name='query', ns='jabber:iq:private'}) ->
            end;
        _ ->
            io:format(" ERROR.~n",[]),
-           ?ERROR_MSG("No modules loaded [mod_private, mod_private_odbc] ~s ~n",
+           ?ERROR_MSG("No modules loaded [mod_private] ~s ~n",
                       [exmpp_xml:document_to_list(El)]),
            {error, not_found}
     end;
@@ -415,13 +416,12 @@ populate_user(_User, _Domain, _El) ->
 %%%==================================
 %%%% Utilities
 
-loaded_module(Domain,Options) ->
-    LoadedModules = gen_mod:loaded_modules(Domain),
-    case lists:filter(fun(Module) ->
-                             lists:member(Module, LoadedModules)
-                      end, Options) of
-        [M|_] -> {ok, M};
-        [] -> {error,not_found}
+loaded_module(Domain, Module) ->
+    case gen_mod:is_loaded(Domain, Module) of
+        true ->
+            {ok, gen_mod:db_type(Domain, Module)};
+        false ->
+            {error, not_found}
     end.
 
 jid_to_old_jid(Jid) ->
@@ -574,13 +574,13 @@ build_password_string(Password) when is_list(Password) ->
 
 %% @spec (InfoName::atom(), Username::string(), Host::string()) -> string()
 extract_user_info(roster, Username, Host) ->
-    case loaded_module(Host,[mod_roster_odbc,mod_roster]) of
-       {ok, M} ->
+    case loaded_module(Host, mod_roster) of
+       {ok, _DBType} ->
            From = To = jlib:make_jid(Username, Host, ""),
            SubelGet = {xmlelement, "query", [{"xmlns",?NS_ROSTER}], []},
            %%IQGet = #iq{type=get, xmlns=?NS_ROSTER, payload=SubelGet}, % this is for 3.0.0 version
            IQGet = {iq, "", get, ?NS_ROSTER, "" , SubelGet},
-           Res = M:process_local_iq(From, To, IQGet),
+           Res = mod_roster:process_local_iq(From, To, IQGet),
            %%[El] = Res#iq.payload, % this is for 3.0.0 version
            {iq, _, result, _, _, Els} = Res,
            case Els of
@@ -592,8 +592,8 @@ extract_user_info(roster, Username, Host) ->
     end;
 
 extract_user_info(offline, Username, Host) ->
-    case loaded_module(Host,[mod_offline,mod_offline_odbc]) of
-       {ok, mod_offline} ->
+    case loaded_module(Host, mod_offline) of
+       {ok, mnesia} ->
            Els = mnesia_pop_offline_messages([], Username, Host),
            case Els of
                [] -> "";
@@ -601,30 +601,30 @@ extract_user_info(offline, Username, Host) ->
                    OfEl = {xmlelement, "offline-messages", [], Els},
                    exmpp_xml:document_to_list(OfEl)
            end;
-       {ok, mod_offline_odbc} ->
+       {ok, odbc} ->
            "";
        _E ->
            ""
     end;
 
 extract_user_info(private, Username, Host) ->
-    case loaded_module(Host,[mod_private,mod_private_odbc]) of
-       {ok, mod_private} ->
+    case loaded_module(Host, mod_private) of
+       {ok, mnesia} ->
            get_user_private_mnesia(Username, Host);
-       {ok, mod_private_odbc} ->
+       {ok, odbc} ->
            "";
        _E ->
            ""
     end;
 
 extract_user_info(vcard, Username, Host) ->
-    case loaded_module(Host,[mod_vcard, mod_vcard_odbc, mod_vcard_odbc]) of
-       {ok, M} ->
+    case loaded_module(Host, mod_vcard) of
+       {ok, _DBType} ->
            From = To = jlib:make_jid(Username, Host, ""),
            SubelGet = {xmlelement, "vCard", [{"xmlns",?NS_VCARD}], []},
            %%IQGet = #iq{type=get, xmlns=?NS_VCARD, payload=SubelGet}, % this is for 3.0.0 version
            IQGet = {iq, "", get, ?NS_VCARD, "" , SubelGet},
-           Res = M:process_sm_iq(From, To, IQGet),
+           Res = mod_vcard:process_sm_iq(From, To, IQGet),
            %%[El] = Res#iq.payload, % this is for 3.0.0 version
            {iq, _, result, _, _, Els} = Res,
            case Els of
index a2331137e91511675d9d09213cad8325fe4628a6..7d96aac3417cc67bb6de59fa6c09c3b3aef17ee1 100644 (file)
@@ -383,7 +383,7 @@ export_privacy(Server, Output) ->
                     fun({Name, List}) ->
                             SName = ejabberd_odbc:escape(Name),
                             RItems = lists:map(
-                                       fun mod_privacy_odbc:item_to_raw/1,
+                                       fun mod_privacy:item_to_raw/1,
                                        List),
                             ID = integer_to_list(get_id()),
                             ["delete from privacy_list "
index 941717f6494cd3f24e4bdfcc9a2f35a3dc420ecd..4f5c06e7a4454d5eea673452528979e7819f96bf 100644 (file)
@@ -34,6 +34,8 @@
         get_opt/2,
         get_opt/3,
         get_opt_host/3,
+         db_type/1,
+         db_type/2,
         get_module_opt/4,
         get_module_opt_host/3,
         loaded_modules/1,
@@ -192,6 +194,18 @@ get_opt_host(Host, Opts, Default) ->
     Val = get_opt(host, Opts, Default),
     ejabberd_regexp:greplace(Val, "@HOST@", Host).
 
+db_type(Opts) ->
+    case get_opt(db_type, Opts, mnesia) of
+        odbc -> odbc;
+        _ -> mnesia
+    end.
+
+db_type(Host, Module) ->
+    case get_module_opt(Host, Module, db_type, mnesia) of
+        odbc -> odbc;
+        _ -> mnesia
+    end.
+
 loaded_modules(Host) ->
     ets:select(ejabberd_modules,
               [{#ejabberd_module{_ = '_', module_host = {'$1', Host}},
index 0770e0794f4695612eec28582175a130bae6e3a5..15a467b1e6a4845d6118abca7b2b4285f202d209 100644 (file)
@@ -116,54 +116,28 @@ xdb_data(_User, _Server, {xmlcdata, _CData}) ->
     ok;
 xdb_data(User, Server, {xmlelement, _Name, Attrs, _Els} = El) ->
     From = jlib:make_jid(User, Server, ""),
-    LServer = jlib:nameprep(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 ->
-           case lists:member(mod_roster_odbc,
-                             gen_mod:loaded_modules(LServer)) of
-               true ->
-                   catch mod_roster_odbc:set_items(User, Server, El);
-               false ->
-                   catch mod_roster:set_items(User, Server, El)
-           end,
+            catch mod_roster:set_items(User, Server, El),
            ok;
        ?NS_LAST ->
            TimeStamp = xml:get_attr_s("last", Attrs),
            Status = xml:get_tag_cdata(El),
-           case lists:member(mod_last_odbc,
-                             gen_mod:loaded_modules(LServer)) of
-               true ->
-                   catch mod_last_odbc:store_last_info(
-                           User,
-                           Server,
-                           list_to_integer(TimeStamp),
-                           Status);
-               false ->
-                   catch mod_last:store_last_info(
-                           User,
-                           Server,
-                           list_to_integer(TimeStamp),
-                           Status)
-           end,
+            catch mod_last:store_last_info(
+                    User,
+                    Server,
+                    list_to_integer(TimeStamp),
+                    Status),
            ok;
        ?NS_VCARD ->
-           case lists:member(mod_vcard_odbc,
-                             gen_mod:loaded_modules(LServer)) of
-               true ->
-                   catch mod_vcard_odbc:process_sm_iq(
-                           From,
-                           jlib:make_jid("", Server, ""),
-                           #iq{type = set, xmlns = ?NS_VCARD, sub_el = El});
-               false ->
-                   catch mod_vcard:process_sm_iq(
-                           From,
-                           jlib:make_jid("", Server, ""),
-                           #iq{type = set, xmlns = ?NS_VCARD, sub_el = El})
-           end,
+            catch mod_vcard:process_sm_iq(
+                    From,
+                    jlib:make_jid("", Server, ""),
+                    #iq{type = set, xmlns = ?NS_VCARD, sub_el = El}),
            ok;
        "jabber:x:offline" ->
            process_offline(Server, From, El),
index d398c2b3667b362a996a3970643dd258e5c195ee..d270a6e4ec0a5ba0a7bfa7cb1588019e6c623b67 100644 (file)
 -define(NS_ADMINL(Sub), ["http:","jabber.org","protocol","admin", Sub]).
 tokenize(Node) -> string:tokens(Node, "/#").
 
-start(Host, _Opts) ->
-    mnesia:create_table(motd, [{disc_copies, [node()]},
-                              {attributes, record_info(fields, motd)}]),
-    mnesia:create_table(motd_users, [{disc_copies, [node()]},
-                                    {attributes, record_info(fields, motd_users)}]),
-    update_tables(),
+start(Host, Opts) ->
+    case gen_mod:db_type(Opts) of
+        mnesia ->
+            mnesia:create_table(motd,
+                                [{disc_copies, [node()]},
+                                 {attributes,
+                                  record_info(fields, motd)}]),
+            mnesia:create_table(motd_users,
+                                [{disc_copies, [node()]},
+                                 {attributes,
+                                  record_info(fields, motd_users)}]),
+            update_tables();
+        _ ->
+            ok
+    end,
     ejabberd_hooks:add(local_send_to_resource_hook, Host,
                       ?MODULE, announce, 50),
     ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, disco_identity, 50),
@@ -743,16 +752,33 @@ announce_all_hosts_motd(From, To, Packet) ->
     end.
 
 announce_motd(Host, Packet) ->
-    announce_motd_update(Host, Packet),
-    Sessions = ejabberd_sm:get_vh_session_list(Host),
-    announce_online1(Sessions, Host, Packet),
-    F = fun() ->
-               lists:foreach(
-                 fun({U, S, _R}) ->
-                         mnesia:write(#motd_users{us = {U, S}})
-                 end, Sessions)
-       end,
-    mnesia:transaction(F).
+    LServer = jlib:nameprep(Host),
+    announce_motd_update(LServer, Packet),
+    Sessions = ejabberd_sm:get_vh_session_list(LServer),
+    announce_online1(Sessions, LServer, Packet),
+    case gen_mod:db_type(LServer, ?MODULE) of
+        mnesia ->
+            F = fun() ->
+                        lists:foreach(
+                          fun({U, S, _R}) ->
+                                  mnesia:write(#motd_users{us = {U, S}})
+                          end, Sessions)
+                end,
+            mnesia:transaction(F);
+        odbc ->
+            F = fun() ->
+                        lists:foreach(
+                          fun({U, _S, _R}) ->
+                                  Username = ejabberd_odbc:escape(U),
+                                  odbc_queries:update_t(
+                                    "motd",
+                                    ["username", "xml"],
+                                    [Username, ""],
+                                    ["username='", Username, "'"])
+                          end, Sessions)
+                end,
+            ejabberd_odbc:sql_transaction(LServer, F)
+    end.
 
 announce_motd_update(From, To, Packet) ->
     Host = To#jid.lserver,
@@ -778,10 +804,23 @@ announce_all_hosts_motd_update(From, To, Packet) ->
 
 announce_motd_update(LServer, Packet) ->
     announce_motd_delete(LServer),
-    F = fun() ->
-               mnesia:write(#motd{server = LServer, packet = Packet})
-       end,
-    mnesia:transaction(F).
+    case gen_mod:db_type(LServer, ?MODULE) of
+        mnesia ->
+            F = fun() ->
+                        mnesia:write(#motd{server = LServer, packet = Packet})
+                end,
+            mnesia:transaction(F);
+        odbc ->
+            XML = ejabberd_odbc:escape(xml:element_to_binary(Packet)),
+            F = fun() ->
+                        odbc_queries:update_t(
+                          "motd",
+                          ["username", "xml"],
+                          ["", XML],
+                          ["username=''"])
+                end,
+            ejabberd_odbc:sql_transaction(LServer, F)
+    end.
 
 announce_motd_delete(From, To, Packet) ->
     Host = To#jid.lserver,
@@ -806,21 +845,32 @@ announce_all_hosts_motd_delete(From, To, Packet) ->
     end.
 
 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).
+    case gen_mod:db_type(LServer, ?MODULE) of
+        mnesia ->
+            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);
+        odbc ->
+            F = fun() ->
+                        ejabberd_odbc:sql_query_t(["delete from motd;"])
+                end,
+            ejabberd_odbc:sql_transaction(LServer, F)
+    end.
 
-send_motd(#jid{luser = LUser, lserver = LServer} = JID) ->
+send_motd(JID) ->
+    send_motd(JID, gen_mod:db_type(JID#jid.lserver, ?MODULE)).
+
+send_motd(#jid{luser = LUser, lserver = LServer} = JID, mnesia) ->
     case catch mnesia:dirty_read({motd, LServer}) of
        [#motd{packet = Packet}] ->
            US = {LUser, LServer},
@@ -837,15 +887,69 @@ send_motd(#jid{luser = LUser, lserver = LServer} = JID) ->
            end;
        _ ->
            ok
-    end.
+    end;
+send_motd(#jid{luser = LUser, lserver = LServer} = JID, odbc) when LUser /= "" ->
+    case catch ejabberd_odbc:sql_query(
+                 LServer, ["select xml from motd where username='';"]) of
+        {selected, ["xml"], [{XML}]} ->
+            case xml_stream:parse_element(XML) of
+                {error, _} ->
+                    ok;
+                Packet ->
+                    Username = ejabberd_odbc:escape(LUser),
+                    case catch ejabberd_odbc:sql_query(
+                                 LServer,
+                                 ["select username from motd "
+                                  "where username='", Username, "';"]) of
+                        {selected, ["username"], []} ->
+                            Local = jlib:make_jid("", LServer, ""),
+                            ejabberd_router:route(Local, JID, Packet),
+                            F = fun() ->
+                                        odbc_queries:update_t(
+                                          "motd",
+                                          ["username", "xml"],
+                                          [Username, ""],
+                                          ["username='", Username, "'"])
+                                end,
+                            ejabberd_odbc:sql_transaction(LServer, F);
+                        _ ->
+                            ok
+                    end
+            end;
+        _ ->
+            ok
+    end;
+send_motd(_, odbc) ->
+    ok.
 
 get_stored_motd(LServer) ->
+    case get_stored_motd_packet(LServer, gen_mod:db_type(LServer, ?MODULE)) of
+        {ok, Packet} ->
+            {xml:get_subtag_cdata(Packet, "subject"),
+            xml:get_subtag_cdata(Packet, "body")};
+        error ->
+            {"", ""}
+    end.
+
+get_stored_motd_packet(LServer, mnesia) ->
     case catch mnesia:dirty_read({motd, LServer}) of
        [#motd{packet = Packet}] ->
-           {xml:get_subtag_cdata(Packet, "subject"),
-            xml:get_subtag_cdata(Packet, "body")};
+            {ok, Packet};
        _ ->
-           {"", ""}
+           error
+    end;
+get_stored_motd_packet(LServer, odbc) ->
+    case catch ejabberd_odbc:sql_query(
+                 LServer, ["select xml from motd where username='';"]) of
+        {selected, ["xml"], [{XML}]} ->
+            case xml_stream:parse_element(XML) of
+                {error, _} ->
+                    error;
+                Packet ->
+                    {ok, Packet}
+            end;
+        _ ->
+            error
     end.
 
 %% This function is similar to others, but doesn't perform any ACL verification
diff --git a/src/mod_announce_odbc.erl b/src/mod_announce_odbc.erl
deleted file mode 100644 (file)
index 101f8aa..0000000
+++ /dev/null
@@ -1,885 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File    : mod_announce_odbc.erl
-%%% Author  : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : Manage announce messages
-%%% Created : 11 Aug 2003 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2012   ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
-%%% Implements a small subset of XEP-0133: Service Administration
-%%% Version 1.1 (2005-08-19)
-
--module(mod_announce_odbc).
--author('alexey@process-one.net').
-
--behaviour(gen_mod).
-
--export([start/2,
-        init/0,
-        stop/1,
-        announce/3,
-        send_motd/1,
-        disco_identity/5,
-        disco_features/5,
-        disco_items/5,
-        send_announcement_to_all/3,
-        announce_commands/4,
-        announce_items/4]).
-
--include("ejabberd.hrl").
--include("jlib.hrl").
--include("adhoc.hrl").
-
--define(PROCNAME, ejabberd_announce).
-
--define(NS_ADMINL(Sub), ["http:","jabber.org","protocol","admin", Sub]).
-tokenize(Node) -> string:tokens(Node, "/#").
-
-start(Host, _Opts) ->
-    ejabberd_hooks:add(local_send_to_resource_hook, Host,
-                      ?MODULE, announce, 50),
-    ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, disco_identity, 50),
-    ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 50),
-    ejabberd_hooks:add(disco_local_items, Host, ?MODULE, disco_items, 50),
-    ejabberd_hooks:add(adhoc_local_items, Host, ?MODULE, announce_items, 50),
-    ejabberd_hooks:add(adhoc_local_commands, Host, ?MODULE, announce_commands, 50),
-    ejabberd_hooks:add(user_available_hook, Host,
-                      ?MODULE, send_motd, 50),
-    register(gen_mod:get_module_proc(Host, ?PROCNAME),
-            proc_lib:spawn(?MODULE, init, [])).
-
-init() ->
-    loop().
-
-loop() ->
-    receive
-       {announce_all, From, To, Packet} ->
-           announce_all(From, To, Packet),
-           loop();
-       {announce_all_hosts_all, From, To, Packet} ->
-           announce_all_hosts_all(From, To, Packet),
-           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();
-       {announce_all_hosts_motd, From, To, Packet} ->
-           announce_all_hosts_motd(From, To, Packet),
-           loop();
-       {announce_motd_update, From, To, Packet} ->
-           announce_motd_update(From, To, Packet),
-           loop();
-       {announce_all_hosts_motd_update, From, To, Packet} ->
-           announce_all_hosts_motd_update(From, To, Packet),
-           loop();
-       {announce_motd_delete, From, To, Packet} ->
-           announce_motd_delete(From, To, Packet),
-           loop();
-       {announce_all_hosts_motd_delete, From, To, Packet} ->
-           announce_all_hosts_motd_delete(From, To, Packet),
-           loop();
-       _ ->
-           loop()
-    end.
-
-stop(Host) ->
-    ejabberd_hooks:delete(adhoc_local_commands, Host, ?MODULE, announce_commands, 50),
-    ejabberd_hooks:delete(adhoc_local_items, Host, ?MODULE, announce_items, 50),
-    ejabberd_hooks:delete(disco_local_identity, Host, ?MODULE, disco_identity, 50),
-    ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, disco_features, 50),
-    ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, disco_items, 50),
-    ejabberd_hooks:delete(local_send_to_resource_hook, Host,
-                         ?MODULE, announce, 50),
-    ejabberd_hooks:delete(user_available_hook, Host,
-                         ?MODULE, send_motd, 50),
-    Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
-    exit(whereis(Proc), stop),
-    {wait, Proc}.
-
-%% Announcing via messages to a custom resource
-announce(From, To, Packet) ->
-    case To of
-       #jid{luser = "", lresource = Res} ->
-           {xmlelement, Name, _Attrs, _Els} = Packet,
-           Proc = gen_mod:get_module_proc(To#jid.lserver, ?PROCNAME),
-           case {Res, Name} of
-               {"announce/all", "message"} ->
-                   Proc ! {announce_all, From, To, Packet},
-                   stop;
-               {"announce/all-hosts/all", "message"} ->
-                   Proc ! {announce_all_hosts_all, From, To, Packet},
-                   stop;
-               {"announce/online", "message"} ->
-                   Proc ! {announce_online, From, To, Packet},
-                   stop;
-               {"announce/all-hosts/online", "message"} ->
-                   Proc ! {announce_all_hosts_online, From, To, Packet},
-                   stop;
-               {"announce/motd", "message"} ->
-                   Proc ! {announce_motd, From, To, Packet},
-                   stop;
-               {"announce/all-hosts/motd", "message"} ->
-                   Proc ! {announce_all_hosts_motd, From, To, Packet},
-                   stop;
-               {"announce/motd/update", "message"} ->
-                   Proc ! {announce_motd_update, From, To, Packet},
-                   stop;
-               {"announce/all-hosts/motd/update", "message"} ->
-                   Proc ! {announce_all_hosts_motd_update, From, To, Packet},
-                   stop;
-               {"announce/motd/delete", "message"} ->
-                   Proc ! {announce_motd_delete, From, To, Packet},
-                   stop;
-               {"announce/all-hosts/motd/delete", "message"} ->
-                   Proc ! {announce_all_hosts_motd_delete, From, To, Packet},
-                   stop;
-               _ ->
-                   ok
-           end;
-       _ ->
-           ok
-    end.
-
-%%-------------------------------------------------------------------------
-%% Announcing via ad-hoc commands
--define(INFO_COMMAND(Lang, Node),
-        [{xmlelement, "identity",
-         [{"category", "automation"},
-          {"type", "command-node"},
-          {"name", get_title(Lang, Node)}], []}]).
-
-disco_identity(Acc, _From, _To, Node, Lang) ->
-    LNode = tokenize(Node),
-    case LNode of
-       ?NS_ADMINL("announce") ->
-           ?INFO_COMMAND(Lang, Node);
-       ?NS_ADMINL("announce-allhosts") ->
-           ?INFO_COMMAND(Lang, Node);
-       ?NS_ADMINL("announce-all") ->
-           ?INFO_COMMAND(Lang, Node);
-       ?NS_ADMINL("announce-all-allhosts") ->
-           ?INFO_COMMAND(Lang, Node);
-       ?NS_ADMINL("set-motd") ->
-           ?INFO_COMMAND(Lang, Node);
-       ?NS_ADMINL("set-motd-allhosts") ->
-           ?INFO_COMMAND(Lang, Node);
-       ?NS_ADMINL("edit-motd") ->
-           ?INFO_COMMAND(Lang, Node);
-       ?NS_ADMINL("edit-motd-allhosts") ->
-           ?INFO_COMMAND(Lang, Node);
-       ?NS_ADMINL("delete-motd") ->
-           ?INFO_COMMAND(Lang, Node);
-       ?NS_ADMINL("delete-motd-allhosts") ->
-           ?INFO_COMMAND(Lang, Node);
-       _ ->
-           Acc
-    end.
-
-%%-------------------------------------------------------------------------
-
--define(INFO_RESULT(Allow, Feats),
-       case Allow of
-           deny ->
-               {error, ?ERR_FORBIDDEN};
-           allow ->
-               {result, Feats}
-       end).
-
-disco_features(Acc, From, #jid{lserver = LServer} = _To,
-              "announce", _Lang) ->
-    case gen_mod:is_loaded(LServer, mod_adhoc) of
-       false ->
-           Acc;
-       _ ->
-           Access1 = gen_mod:get_module_opt(LServer, ?MODULE, access, none),
-           Access2 = gen_mod:get_module_opt(global, ?MODULE, access, none),
-           case {acl:match_rule(LServer, Access1, From),
-                 acl:match_rule(global, Access2, From)} of
-               {deny, deny} ->
-                   {error, ?ERR_FORBIDDEN};
-               _ ->
-                   {result, []}
-           end
-    end;
-
-disco_features(Acc, From, #jid{lserver = LServer} = _To,
-              Node, _Lang) ->
-    case gen_mod:is_loaded(LServer, mod_adhoc) of
-       false ->
-           Acc;
-       _ ->
-           Access = gen_mod:get_module_opt(LServer, ?MODULE, access, none),
-           Allow = acl:match_rule(LServer, Access, From),
-           AccessGlobal = gen_mod:get_module_opt(global, ?MODULE, access, none),
-           AllowGlobal = acl:match_rule(global, AccessGlobal, From),
-           case Node of
-               ?NS_ADMIN ++ "#announce" ->
-                   ?INFO_RESULT(Allow, [?NS_COMMANDS]);
-               ?NS_ADMIN ++ "#announce-all" ->
-                   ?INFO_RESULT(Allow, [?NS_COMMANDS]);
-               ?NS_ADMIN ++ "#set-motd" ->
-                   ?INFO_RESULT(Allow, [?NS_COMMANDS]);
-               ?NS_ADMIN ++ "#edit-motd" ->
-                   ?INFO_RESULT(Allow, [?NS_COMMANDS]);
-               ?NS_ADMIN ++ "#delete-motd" ->
-                   ?INFO_RESULT(Allow, [?NS_COMMANDS]);
-               ?NS_ADMIN ++ "#announce-allhosts" ->
-                   ?INFO_RESULT(AllowGlobal, [?NS_COMMANDS]);
-               ?NS_ADMIN ++ "#announce-all-allhosts" ->
-                   ?INFO_RESULT(AllowGlobal, [?NS_COMMANDS]);
-               ?NS_ADMIN ++ "#set-motd-allhosts" ->
-                   ?INFO_RESULT(AllowGlobal, [?NS_COMMANDS]);
-               ?NS_ADMIN ++ "#edit-motd-allhosts" ->
-                   ?INFO_RESULT(AllowGlobal, [?NS_COMMANDS]);
-               ?NS_ADMIN ++ "#delete-motd-allhosts" ->
-                   ?INFO_RESULT(AllowGlobal, [?NS_COMMANDS]);
-               _ ->
-                   Acc
-           end
-    end.
-
-%%-------------------------------------------------------------------------
-
--define(NODE_TO_ITEM(Lang, Server, Node),
-       {xmlelement, "item",
-        [{"jid", Server},
-         {"node", Node},
-         {"name", get_title(Lang, Node)}],
-        []}).
-
--define(ITEMS_RESULT(Allow, Items),
-       case Allow of
-           deny ->
-               {error, ?ERR_FORBIDDEN};
-           allow ->
-               {result, Items}
-       end).
-
-disco_items(Acc, From, #jid{lserver = LServer, server = Server} = _To,
-           "", Lang) ->
-    case gen_mod:is_loaded(LServer, mod_adhoc) of
-       false ->
-           Acc;
-       _ ->
-           Access1 = gen_mod:get_module_opt(LServer, ?MODULE, access, none),
-           Access2 = gen_mod:get_module_opt(global, ?MODULE, access, none),
-           case {acl:match_rule(LServer, Access1, From),
-                 acl:match_rule(global, Access2, From)} of
-               {deny, deny} ->
-                   Acc;
-               _ ->
-                   Items = case Acc of
-                               {result, I} -> I;
-                               _ -> []
-                           end,
-                   Nodes = [?NODE_TO_ITEM(Lang, Server, "announce")],
-                   {result, Items ++ Nodes}
-           end
-    end;
-
-disco_items(Acc, From, #jid{lserver = LServer} = To, "announce", Lang) ->
-    case gen_mod:is_loaded(LServer, mod_adhoc) of
-       false ->
-           Acc;
-       _ ->
-           announce_items(Acc, From, To, Lang)
-    end;
-
-disco_items(Acc, From, #jid{lserver = LServer} = _To, Node, _Lang) ->
-    case gen_mod:is_loaded(LServer, mod_adhoc) of
-       false ->
-           Acc;
-       _ ->
-           Access = gen_mod:get_module_opt(LServer, ?MODULE, access, none),
-           Allow = acl:match_rule(LServer, Access, From),
-           AccessGlobal = gen_mod:get_module_opt(global, ?MODULE, access, none),
-           AllowGlobal = acl:match_rule(global, AccessGlobal, From),
-           case Node of
-               ?NS_ADMIN ++ "#announce" ->
-                   ?ITEMS_RESULT(Allow, []);
-               ?NS_ADMIN ++ "#announce-all" ->
-                   ?ITEMS_RESULT(Allow, []);
-               ?NS_ADMIN ++ "#set-motd" ->
-                   ?ITEMS_RESULT(Allow, []);
-               ?NS_ADMIN ++ "#edit-motd" ->
-                   ?ITEMS_RESULT(Allow, []);
-               ?NS_ADMIN ++ "#delete-motd" ->
-                   ?ITEMS_RESULT(Allow, []);
-               ?NS_ADMIN ++ "#announce-allhosts" ->
-                   ?ITEMS_RESULT(AllowGlobal, []);
-               ?NS_ADMIN ++ "#announce-all-allhosts" ->
-                   ?ITEMS_RESULT(AllowGlobal, []);
-               ?NS_ADMIN ++ "#set-motd-allhosts" ->
-                   ?ITEMS_RESULT(AllowGlobal, []);
-               ?NS_ADMIN ++ "#edit-motd-allhosts" ->
-                   ?ITEMS_RESULT(AllowGlobal, []);
-               ?NS_ADMIN ++ "#delete-motd-allhosts" ->
-                   ?ITEMS_RESULT(AllowGlobal, []);
-               _ ->
-                   Acc
-           end
-    end.
-
-%%-------------------------------------------------------------------------
-
-announce_items(Acc, From, #jid{lserver = LServer, server = Server} = _To, Lang) ->
-    Access1 = gen_mod:get_module_opt(LServer, ?MODULE, access, none),
-    Nodes1 = case acl:match_rule(LServer, Access1, From) of
-                allow ->
-                    [?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN ++ "#announce"),
-                     ?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN ++ "#announce-all"),
-                     ?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN ++ "#set-motd"),
-                     ?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN ++ "#edit-motd"),
-                     ?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN ++ "#delete-motd")];
-                deny ->
-                    []
-            end,
-    Access2 = gen_mod:get_module_opt(global, ?MODULE, access, none),
-    Nodes2 = case acl:match_rule(global, Access2, From) of
-                allow ->
-                    [?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN ++ "#announce-allhosts"),
-                     ?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN ++ "#announce-all-allhosts"),
-                     ?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN ++ "#set-motd-allhosts"),
-                     ?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN ++ "#edit-motd-allhosts"),
-                     ?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN ++ "#delete-motd-allhosts")];
-                deny ->
-                    []
-            end,
-    case {Nodes1, Nodes2} of
-       {[], []} ->
-           Acc;
-       _ ->
-           Items = case Acc of
-                       {result, I} -> I;
-                       _ -> []
-                   end,
-           {result, Items ++ Nodes1 ++ Nodes2}
-    end.
-
-%%-------------------------------------------------------------------------
-
-commands_result(Allow, From, To, Request) ->
-    case Allow of
-       deny ->
-           {error, ?ERR_FORBIDDEN};
-       allow ->
-           announce_commands(From, To, Request)
-    end.
-
-
-announce_commands(Acc, From, #jid{lserver = LServer} = To,
-                 #adhoc_request{ node = Node} = Request) ->
-    LNode = tokenize(Node),
-    F = fun() ->
-               Access = gen_mod:get_module_opt(global, ?MODULE, access, none),
-               Allow = acl:match_rule(global, Access, From),
-               commands_result(Allow, From, To, Request)
-       end,
-    R = case LNode of
-           ?NS_ADMINL("announce-allhosts") -> F();
-           ?NS_ADMINL("announce-all-allhosts") -> F();
-           ?NS_ADMINL("set-motd-allhosts") -> F();
-           ?NS_ADMINL("edit-motd-allhosts") -> F();
-           ?NS_ADMINL("delete-motd-allhosts") -> F();
-           _ ->
-               Access = gen_mod:get_module_opt(LServer, ?MODULE, access, none),
-               Allow = acl:match_rule(LServer, Access, From),
-               case LNode of
-                   ?NS_ADMINL("announce") ->
-                       commands_result(Allow, From, To, Request);
-                   ?NS_ADMINL("announce-all") ->
-                       commands_result(Allow, From, To, Request);
-                   ?NS_ADMINL("set-motd") ->
-                       commands_result(Allow, From, To, Request);
-                   ?NS_ADMINL("edit-motd") ->
-                       commands_result(Allow, From, To, Request);
-                   ?NS_ADMINL("delete-motd") ->
-                       commands_result(Allow, From, To, Request);
-                   _ ->
-                       unknown
-               end
-       end,
-    case R of
-       unknown -> Acc;
-       _ -> {stop, R}
-    end.
-
-%%-------------------------------------------------------------------------
-
-announce_commands(From, To,
-                 #adhoc_request{lang = Lang,
-                                node = Node,
-                                action = Action,
-                                xdata = XData} = Request) ->
-    %% If the "action" attribute is not present, it is
-    %% understood as "execute".  If there was no <actions/>
-    %% element in the first response (which there isn't in our
-    %% case), "execute" and "complete" are equivalent.
-    ActionIsExecute = lists:member(Action,
-                                  ["", "execute", "complete"]),
-    if Action == "cancel" ->
-           %% User cancels request
-           adhoc:produce_response(Request, 
-                                  #adhoc_response{status = canceled});
-       XData == false, ActionIsExecute ->
-           %% User requests form
-           Elements = generate_adhoc_form(Lang, Node, To#jid.lserver),
-           adhoc:produce_response(
-             Request,
-             #adhoc_response{status = executing,
-                             elements = [Elements]});
-       XData /= false, ActionIsExecute ->
-           %% User returns form.
-           case jlib:parse_xdata_submit(XData) of
-               invalid ->
-                   {error, ?ERR_BAD_REQUEST};
-               Fields ->
-                   handle_adhoc_form(From, To, Request, Fields)
-           end;
-       true ->
-           {error, ?ERR_BAD_REQUEST}
-    end.
-
--define(VVALUE(Val),
-       {xmlelement, "value", [], [{xmlcdata, Val}]}).
--define(TVFIELD(Type, Var, Val),
-       {xmlelement, "field", [{"type", Type},
-                              {"var", Var}],
-        vvaluel(Val)}).
--define(HFIELD(), ?TVFIELD("hidden", "FORM_TYPE", ?NS_ADMIN)).
-
-vvaluel(Val) ->
-    case Val of
-        "" -> [];
-        _ -> [?VVALUE(Val)]
-    end.
-
-generate_adhoc_form(Lang, Node, ServerHost) ->
-    LNode = tokenize(Node),
-    {OldSubject, OldBody} = if (LNode == ?NS_ADMINL("edit-motd")) 
-                              or (LNode == ?NS_ADMINL("edit-motd-allhosts")) ->
-                                   get_stored_motd(ServerHost);
-                              true -> 
-                                   {[], []}
-                           end,
-    {xmlelement, "x",
-     [{"xmlns", ?NS_XDATA},
-      {"type", "form"}],
-     [?HFIELD(),
-      {xmlelement, "title", [], [{xmlcdata, get_title(Lang, Node)}]}]
-     ++
-     if (LNode == ?NS_ADMINL("delete-motd"))
-       or (LNode == ?NS_ADMINL("delete-motd-allhosts")) ->
-            [{xmlelement, "field",
-              [{"var", "confirm"},
-               {"type", "boolean"},
-               {"label", translate:translate(Lang, "Really delete message of the day?")}],
-              [{xmlelement, "value",
-                [],
-                [{xmlcdata, "true"}]}]}];
-       true ->
-            [{xmlelement, "field", 
-              [{"var", "subject"},
-               {"type", "text-single"},
-               {"label", translate:translate(Lang, "Subject")}],
-              vvaluel(OldSubject)},
-             {xmlelement, "field",
-              [{"var", "body"},
-               {"type", "text-multi"},
-               {"label", translate:translate(Lang, "Message body")}],
-              vvaluel(OldBody)}]
-     end}.
-
-join_lines([]) ->
-    [];
-join_lines(Lines) ->
-    join_lines(Lines, []).
-join_lines([Line|Lines], Acc) ->
-    join_lines(Lines, ["\n",Line|Acc]);
-join_lines([], Acc) ->
-    %% Remove last newline
-    lists:flatten(lists:reverse(tl(Acc))).
-
-handle_adhoc_form(From, #jid{lserver = LServer} = To,
-                 #adhoc_request{lang = Lang,
-                                node = Node,
-                                sessionid = SessionID},
-                 Fields) ->
-    Confirm = case lists:keysearch("confirm", 1, Fields) of
-                 {value, {"confirm", ["true"]}} ->
-                     true;
-                 {value, {"confirm", ["1"]}} ->
-                     true;
-                 _ ->
-                     false
-             end,
-    Subject = case lists:keysearch("subject", 1, Fields) of
-                 {value, {"subject", SubjectLines}} ->
-                     %% There really shouldn't be more than one
-                     %% subject line, but can we stop them?
-                     join_lines(SubjectLines);
-                 _ ->
-                     []
-             end,
-    Body = case lists:keysearch("body", 1, Fields) of
-              {value, {"body", BodyLines}} ->
-                  join_lines(BodyLines);
-              _ ->
-                  []
-          end,
-    Response = #adhoc_response{lang = Lang,
-                              node = Node,
-                              sessionid = SessionID,
-                              status = completed},
-    Packet = {xmlelement, "message", [{"type", "normal"}], 
-             if Subject /= [] ->
-                     [{xmlelement, "subject", [], 
-                       [{xmlcdata, Subject}]}];
-                true ->
-                     []
-             end ++
-             if Body /= [] ->
-                     [{xmlelement, "body", [],
-                       [{xmlcdata, Body}]}];
-                true ->
-                     []
-             end},
-
-    Proc = gen_mod:get_module_proc(LServer, ?PROCNAME),
-    case {Node, Body} of
-       {?NS_ADMIN ++ "#delete-motd", _} ->
-           if  Confirm ->
-                   Proc ! {announce_motd_delete, From, To, Packet},
-                   adhoc:produce_response(Response);
-               true ->
-                   adhoc:produce_response(Response)
-           end;
-       {?NS_ADMIN ++ "#delete-motd-allhosts", _} ->
-           if  Confirm ->
-                   Proc ! {announce_all_hosts_motd_delete, From, To, Packet},
-                   adhoc:produce_response(Response);
-               true ->
-                   adhoc:produce_response(Response)
-           end;
-       {_, []} ->
-           %% An announce message with no body is definitely an operator error.
-           %% Throw an error and give him/her a chance to send message again.
-           {error, ?ERRT_NOT_ACCEPTABLE(
-                      Lang,
-                      "No body provided for announce message")};
-       %% Now send the packet to ?PROCNAME.
-       %% We don't use direct announce_* functions because it
-       %% leads to large delay in response and <iq/> queries processing
-       {?NS_ADMIN ++ "#announce", _} ->
-           Proc ! {announce_online, From, To, Packet},
-           adhoc:produce_response(Response);
-       {?NS_ADMIN ++ "#announce-allhosts", _} ->           
-           Proc ! {announce_all_hosts_online, From, To, Packet},
-           adhoc:produce_response(Response);
-       {?NS_ADMIN ++ "#announce-all", _} ->
-           Proc ! {announce_all, From, To, Packet},
-           adhoc:produce_response(Response);
-       {?NS_ADMIN ++ "#announce-all-allhosts", _} ->       
-           Proc ! {announce_all_hosts_all, From, To, Packet},
-           adhoc:produce_response(Response);
-       {?NS_ADMIN ++ "#set-motd", _} ->
-           Proc ! {announce_motd, From, To, Packet},
-           adhoc:produce_response(Response);
-       {?NS_ADMIN ++ "#set-motd-allhosts", _} ->           
-           Proc ! {announce_all_hosts_motd, From, To, Packet},
-           adhoc:produce_response(Response);
-       {?NS_ADMIN ++ "#edit-motd", _} ->
-           Proc ! {announce_motd_update, From, To, Packet},
-           adhoc:produce_response(Response);
-       {?NS_ADMIN ++ "#edit-motd-allhosts", _} ->          
-           Proc ! {announce_all_hosts_motd_update, From, To, Packet},
-           adhoc:produce_response(Response);
-       _ ->
-           %% This can't happen, as we haven't registered any other
-           %% command nodes.
-           {error, ?ERR_INTERNAL_SERVER_ERROR}
-    end.
-
-get_title(Lang, "announce") ->
-    translate:translate(Lang, "Announcements");
-get_title(Lang, ?NS_ADMIN ++ "#announce-all") ->
-    translate:translate(Lang, "Send announcement to all users");
-get_title(Lang, ?NS_ADMIN ++ "#announce-all-allhosts") ->
-    translate:translate(Lang, "Send announcement to all users on all hosts");
-get_title(Lang, ?NS_ADMIN ++ "#announce") ->
-    translate:translate(Lang, "Send announcement to all online users");
-get_title(Lang, ?NS_ADMIN ++ "#announce-allhosts") ->
-    translate:translate(Lang, "Send announcement to all online users on all hosts");
-get_title(Lang, ?NS_ADMIN ++ "#set-motd") ->
-    translate:translate(Lang, "Set message of the day and send to online users");
-get_title(Lang, ?NS_ADMIN ++ "#set-motd-allhosts") ->
-    translate:translate(Lang, "Set message of the day on all hosts and send to online users");
-get_title(Lang, ?NS_ADMIN ++ "#edit-motd") ->
-    translate:translate(Lang, "Update message of the day (don't send)");
-get_title(Lang, ?NS_ADMIN ++ "#edit-motd-allhosts") ->
-    translate:translate(Lang, "Update message of the day on all hosts (don't send)");
-get_title(Lang, ?NS_ADMIN ++ "#delete-motd") ->
-    translate:translate(Lang, "Delete message of the day");
-get_title(Lang, ?NS_ADMIN ++ "#delete-motd-allhosts") ->
-    translate:translate(Lang, "Delete message of the day on all hosts").
-
-%%-------------------------------------------------------------------------
-
-announce_all(From, To, Packet) ->
-    Host = To#jid.lserver,
-    Access = gen_mod:get_module_opt(Host, ?MODULE, access, none),
-    case acl:match_rule(Host, Access, From) of
-       deny ->
-           Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
-           ejabberd_router:route(To, From, Err);
-       allow ->
-           Local = jlib:make_jid("", To#jid.server, ""),
-           lists:foreach(
-             fun({User, Server}) ->
-                     Dest = jlib:make_jid(User, Server, ""),
-                     ejabberd_router:route(Local, Dest, Packet)
-             end, ejabberd_auth:get_vh_registered_users(Host))
-    end.
-
-announce_all_hosts_all(From, To, Packet) ->
-    Access = gen_mod:get_module_opt(global, ?MODULE, access, none),
-    case acl:match_rule(global, Access, From) of
-       deny ->
-           Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
-           ejabberd_router:route(To, From, Err);
-       allow ->
-           Local = jlib:make_jid("", To#jid.server, ""),
-           lists:foreach(
-             fun({User, Server}) ->
-                     Dest = jlib:make_jid(User, Server, ""),
-                     ejabberd_router:route(Local, Dest, Packet)
-             end, ejabberd_auth:dirty_get_registered_users())
-    end.
-
-announce_online(From, To, Packet) ->
-    Host = To#jid.lserver,
-    Access = gen_mod:get_module_opt(Host, ?MODULE, access, none),
-    case acl:match_rule(Host, Access, From) of
-       deny ->
-           Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
-           ejabberd_router:route(To, From, Err);
-       allow ->
-           announce_online1(ejabberd_sm:get_vh_session_list(Host),
-                            To#jid.server,
-                            Packet)
-    end.
-
-announce_all_hosts_online(From, To, Packet) ->
-    Access = gen_mod:get_module_opt(global, ?MODULE, access, none),
-    case acl:match_rule(global, Access, From) of
-       deny ->
-           Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
-           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, S, R}) ->
-             Dest = jlib:make_jid(U, S, R),
-             ejabberd_router:route(Local, Dest, Packet)
-      end, Sessions).
-
-announce_motd(From, To, Packet) ->
-    Host = To#jid.lserver,
-    Access = gen_mod:get_module_opt(Host, ?MODULE, access, none),
-    case acl:match_rule(Host, Access, From) of
-       deny ->
-           Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
-           ejabberd_router:route(To, From, Err);
-       allow ->
-           announce_motd(Host, Packet)
-    end.
-
-announce_all_hosts_motd(From, To, Packet) ->
-    Access = gen_mod:get_module_opt(global, ?MODULE, access, none),
-    case acl:match_rule(global, Access, From) of
-       deny ->
-           Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
-           ejabberd_router:route(To, From, Err);
-       allow ->
-           Hosts = ?MYHOSTS,
-           [announce_motd(Host, Packet) || Host <- Hosts]
-    end.
-
-announce_motd(Host, Packet) ->
-    announce_motd_update(Host, Packet),
-    Sessions = ejabberd_sm:get_vh_session_list(Host),
-    announce_online1(Sessions, Host, Packet),
-    F = fun() ->
-                lists:foreach(
-                  fun({U, _S, _R}) ->
-                          Username = ejabberd_odbc:escape(U),
-                          odbc_queries:update_t(
-                            "motd",
-                            ["username", "xml"],
-                            [Username, ""],
-                            ["username='", Username, "'"])
-                  end, Sessions)
-        end,
-    LServer = jlib:nameprep(Host),
-    ejabberd_odbc:sql_transaction(LServer, F).
-
-announce_motd_update(From, To, Packet) ->
-    Host = To#jid.lserver,
-    Access = gen_mod:get_module_opt(Host, ?MODULE, access, none),
-    case acl:match_rule(Host, Access, From) of
-       deny ->
-           Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
-           ejabberd_router:route(To, From, Err);
-       allow ->
-           announce_motd_update(Host, Packet)
-    end.
-
-announce_all_hosts_motd_update(From, To, Packet) ->
-    Access = gen_mod:get_module_opt(global, ?MODULE, access, none),
-    case acl:match_rule(global, Access, From) of
-       deny ->
-           Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
-           ejabberd_router:route(To, From, Err);
-       allow ->
-           Hosts = ?MYHOSTS,
-           [announce_motd_update(Host, Packet) || Host <- Hosts]
-    end.
-
-announce_motd_update(LServer, Packet) ->
-    announce_motd_delete(LServer),
-    XML = ejabberd_odbc:escape(xml:element_to_binary(Packet)),
-    F = fun() ->
-                odbc_queries:update_t(
-                  "motd",
-                  ["username", "xml"],
-                  ["", XML],
-                  ["username=''"])
-        end,
-    ejabberd_odbc:sql_transaction(LServer, F).
-
-announce_motd_delete(From, To, Packet) ->
-    Host = To#jid.lserver,
-    Access = gen_mod:get_module_opt(Host, ?MODULE, access, none),
-    case acl:match_rule(Host, Access, From) of
-       deny ->
-           Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
-           ejabberd_router:route(To, From, Err);
-       allow ->
-           announce_motd_delete(Host)
-    end.
-
-announce_all_hosts_motd_delete(From, To, Packet) ->
-    Access = gen_mod:get_module_opt(global, ?MODULE, access, none),
-    case acl:match_rule(global, Access, From) of
-       deny ->
-           Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
-           ejabberd_router:route(To, From, Err);
-       allow ->
-           Hosts = ?MYHOSTS,
-           [announce_motd_delete(Host) || Host <- Hosts]
-    end.
-
-announce_motd_delete(LServer) ->
-    F = fun() ->
-                ejabberd_odbc:sql_query_t(["delete from motd;"])
-        end,
-    ejabberd_odbc:sql_transaction(LServer, F).
-
-send_motd(#jid{luser = LUser, lserver = LServer} = JID) when LUser /= "" ->
-    case catch ejabberd_odbc:sql_query(
-                 LServer, ["select xml from motd where username='';"]) of
-        {selected, ["xml"], [{XML}]} ->
-            case xml_stream:parse_element(XML) of
-                {error, _} ->
-                    ok;
-                Packet ->
-                    Username = ejabberd_odbc:escape(LUser),
-                    case catch ejabberd_odbc:sql_query(
-                                 LServer,
-                                 ["select username from motd "
-                                  "where username='", Username, "';"]) of
-                        {selected, ["username"], []} ->
-                            Local = jlib:make_jid("", LServer, ""),
-                            ejabberd_router:route(Local, JID, Packet),
-                            F = fun() ->
-                                        odbc_queries:update_t(
-                                          "motd",
-                                          ["username", "xml"],
-                                          [Username, ""],
-                                          ["username='", Username, "'"])
-                                end,
-                            ejabberd_odbc:sql_transaction(LServer, F);
-                        _ ->
-                            ok
-                    end
-            end;
-        _ ->
-            ok
-    end;
-send_motd(_) ->
-    ok.
-
-get_stored_motd(LServer) ->
-    case catch ejabberd_odbc:sql_query(
-                 LServer, ["select xml from motd where username='';"]) of
-        {selected, ["xml"], [{XML}]} ->
-            case xml_stream:parse_element(XML) of
-                {error, _} ->
-                    {"", ""};
-                Packet ->
-                    {xml:get_subtag_cdata(Packet, "subject"),
-                     xml:get_subtag_cdata(Packet, "body")}
-            end;
-        _ ->
-            {"", ""}
-    end.
-
-%% This function is similar to others, but doesn't perform any ACL verification
-send_announcement_to_all(Host, SubjectS, BodyS) ->
-    SubjectEls = if SubjectS /= [] ->
-                     [{xmlelement, "subject", [], [{xmlcdata, SubjectS}]}];
-                true ->
-                     []
-             end,
-    BodyEls = if BodyS /= [] ->
-                     [{xmlelement, "body", [], [{xmlcdata, BodyS}]}];
-                true ->
-                     []
-             end,
-    Packet = {xmlelement, "message", [{"type", "normal"}], SubjectEls ++ BodyEls},
-    Sessions = ejabberd_sm:dirty_get_sessions_list(),
-    Local = jlib:make_jid("", Host, ""),
-    lists:foreach(
-      fun({U, S, R}) ->
-             Dest = jlib:make_jid(U, S, R),
-             ejabberd_router:route(Local, Dest, Packet)
-      end, Sessions).
index e099291b955e2a602260df6928b83d75d693c386..7cb0f5c82dabd7dfeb2ccef69feb6a1f1b030bca 100644 (file)
@@ -92,16 +92,6 @@ process_iq_set(_, From, _To, #iq{xmlns = ?NS_BLOCKING,
 process_iq_set(Acc, _, _,  _) ->
     Acc.
 
-is_list_needdb(Items) ->
-    lists:any(
-      fun(X) ->
-             case X#listitem.type of
-                 subscription -> true;
-                 group -> true;
-                 _ -> false
-             end
-      end, Items).
-
 list_to_blocklist_jids([], JIDs) ->
     JIDs;
 
@@ -148,6 +138,35 @@ parse_blocklist_items([_ | Els], JIDs) ->
     parse_blocklist_items(Els, JIDs).
 
 process_blocklist_block(LUser, LServer, JIDs) ->
+    Filter = fun(List) ->
+                     AlreadyBlocked = list_to_blocklist_jids(List, []),
+                     lists:foldr(
+                       fun(JID, List1) ->
+                               case lists:member(JID, AlreadyBlocked) of
+                                   true ->
+                                       List1;
+                                   false ->
+                                       [#listitem{type = jid,
+                                                  value = JID,
+                                                  action = deny,
+                                                  order = 0,
+                                                  match_all = true}
+                                        | List1]
+                               end
+                       end, List, JIDs)
+             end,
+    case process_blocklist_block(LUser, LServer, Filter,
+                                 gen_mod:db_type(LServer, mod_privacy)) of
+        {atomic, {ok, Default, List}} ->
+            UserList = make_userlist(Default, List),
+           broadcast_list_update(LUser, LServer, Default, UserList),
+           broadcast_blocklist_event(LUser, LServer, {block, JIDs}),
+           {result, [], UserList};
+       _ ->
+           {error, ?ERR_INTERNAL_SERVER_ERROR}
+    end.
+
+process_blocklist_block(LUser, LServer, Filter, mnesia) ->
     F =
        fun() ->
                case mnesia:wread({privacy, {LUser, LServer}}) of
@@ -173,40 +192,72 @@ process_blocklist_block(LUser, LServer, JIDs) ->
                                List = []
                        end
                end,
-
-               AlreadyBlocked = list_to_blocklist_jids(List, []),
-               NewList =
-                   lists:foldr(fun(JID, List1) ->
-                                       case lists:member(JID, AlreadyBlocked) of
-                                           true ->
-                                               List1;
-                                           false ->
-                                               [#listitem{type = jid,
-                                                          value = JID,
-                                                          action = deny,
-                                                          order = 0,
-                                                          match_all = true
-                                                         } | List1]
-                                       end
-                               end, List, JIDs),
+                NewList = Filter(List),
                NewLists = [{NewDefault, NewList} | NewLists1],
                mnesia:write(P#privacy{default = NewDefault,
                                       lists = NewLists}),
                {ok, NewDefault, NewList}
        end,
-    case mnesia:transaction(F) of
-       {atomic, {error, _} = Error} ->
-           Error;
+    mnesia:transaction(F);
+process_blocklist_block(LUser, LServer, Filter, odbc) ->
+    F = fun() ->
+                Default =
+                    case mod_privacy:sql_get_default_privacy_list_t(LUser) of
+                        {selected, ["name"], []} ->
+                            Name = "Blocked contacts",
+                            mod_privacy:sql_add_privacy_list(LUser, Name),
+                            mod_privacy:sql_set_default_privacy_list(
+                              LUser, Name),
+                            Name;
+                        {selected, ["name"], [{Name}]} ->
+                            Name
+                    end,
+                {selected, ["id"], [{ID}]} =
+                    mod_privacy:sql_get_privacy_list_id_t(LUser, Default),
+                case mod_privacy:sql_get_privacy_list_data_by_id_t(ID) of
+                    {selected,
+                     ["t", "value", "action", "ord",
+                      "match_all", "match_iq", "match_message",
+                      "match_presence_in",
+                      "match_presence_out"],
+                     RItems = [_|_]} ->
+                        List = lists:map(
+                                 fun mod_privacy:raw_to_item/1,
+                                 RItems);
+                    _ ->
+                        List = []
+                end,
+                NewList = Filter(List),
+                NewRItems = lists:map(
+                              fun mod_privacy:item_to_raw/1,
+                              NewList),
+                mod_privacy:sql_set_privacy_list(
+                  ID, NewRItems),
+                {ok, Default, NewList}
+        end,
+    ejabberd_odbc:sql_transaction(LServer, F).
+
+process_blocklist_unblock_all(LUser, LServer) ->
+    Filter = fun(List) ->
+                     lists:filter(
+                       fun(#listitem{action = A}) ->
+                               A =/= deny
+                       end, List)
+             end,
+    case process_blocklist_unblock_all(
+           LUser, LServer, Filter, gen_mod:db_type(LServer, mod_privacy)) of
+        {atomic, ok} ->
+           {result, []};
        {atomic, {ok, Default, List}} ->
             UserList = make_userlist(Default, List),
            broadcast_list_update(LUser, LServer, Default, UserList),
-           broadcast_blocklist_event(LUser, LServer, {block, JIDs}),
+           broadcast_blocklist_event(LUser, LServer, unblock_all),
            {result, [], UserList};
        _ ->
            {error, ?ERR_INTERNAL_SERVER_ERROR}
     end.
 
-process_blocklist_unblock_all(LUser, LServer) ->
+process_blocklist_unblock_all(LUser, LServer, Filter, mnesia) ->
     F =
        fun() ->
                case mnesia:read({privacy, {LUser, LServer}}) of
@@ -218,12 +269,7 @@ process_blocklist_unblock_all(LUser, LServer) ->
                        case lists:keysearch(Default, 1, Lists) of
                            {value, {_, List}} ->
                                % Default list, remove all deny items
-                               NewList =
-                                   lists:filter(
-                                     fun(#listitem{action = A}) ->
-                                             A =/= deny
-                                     end, List),
-
+                               NewList = Filter(List),
                                NewLists1 = lists:keydelete(Default, 1, Lists),
                                NewLists = [{Default, NewList} | NewLists1],
                                mnesia:write(P#privacy{lists = NewLists}),
@@ -235,21 +281,67 @@ process_blocklist_unblock_all(LUser, LServer) ->
                        end
                end
        end,
-    case mnesia:transaction(F) of
-       {atomic, {error, _} = Error} ->
-           Error;
-       {atomic, ok} ->
+    mnesia:transaction(F);
+process_blocklist_unblock_all(LUser, LServer, Filter, odbc) ->
+    F = fun() ->
+                case mod_privacy:sql_get_default_privacy_list_t(LUser) of
+                    {selected, ["name"], []} ->
+                        ok;
+                    {selected, ["name"], [{Default}]} ->
+                        {selected, ["id"], [{ID}]} =
+                            mod_privacy:sql_get_privacy_list_id_t(
+                              LUser, Default),
+                        case mod_privacy:sql_get_privacy_list_data_by_id_t(ID) of
+                            {selected,
+                             ["t", "value", "action", "ord",
+                              "match_all", "match_iq", "match_message",
+                              "match_presence_in",
+                              "match_presence_out"],
+                             RItems = [_|_]} ->
+                                List = lists:map(
+                                         fun mod_privacy:raw_to_item/1,
+                                         RItems),
+                                NewList = Filter(List),
+                                NewRItems = lists:map(
+                                              fun mod_privacy:item_to_raw/1,
+                                              NewList),
+                                mod_privacy:sql_set_privacy_list(
+                                  ID, NewRItems),
+                                {ok, Default, NewList};
+                            _ ->
+                                ok
+                        end;
+                    _ ->
+                        ok
+                end
+        end,
+    ejabberd_odbc:sql_transaction(LServer, F).
+
+process_blocklist_unblock(LUser, LServer, JIDs) ->
+    Filter = fun(List) ->
+                     lists:filter(
+                       fun(#listitem{action = deny,
+                                     type = jid,
+                                     value = JID}) ->
+                               not(lists:member(JID, JIDs));
+                          (_) ->
+                               true
+                       end, List)
+             end,
+    case process_blocklist_unblock(LUser, LServer, Filter,
+                                   gen_mod:db_type(LServer, mod_privacy)) of
+        {atomic, ok} ->
            {result, []};
        {atomic, {ok, Default, List}} ->
             UserList = make_userlist(Default, List),
            broadcast_list_update(LUser, LServer, Default, UserList),
-           broadcast_blocklist_event(LUser, LServer, unblock_all),
+           broadcast_blocklist_event(LUser, LServer, {unblock, JIDs}),
            {result, [], UserList};
        _ ->
            {error, ?ERR_INTERNAL_SERVER_ERROR}
     end.
 
-process_blocklist_unblock(LUser, LServer, JIDs) ->
+process_blocklist_unblock(LUser, LServer, Filter, mnesia) ->
     F =
        fun() ->
                case mnesia:read({privacy, {LUser, LServer}}) of
@@ -261,16 +353,7 @@ process_blocklist_unblock(LUser, LServer, JIDs) ->
                        case lists:keysearch(Default, 1, Lists) of
                            {value, {_, List}} ->
                                % Default list, remove matching deny items
-                               NewList =
-                                   lists:filter(
-                                     fun(#listitem{action = deny,
-                                                   type = jid,
-                                                   value = JID}) ->
-                                             not(lists:member(JID, JIDs));
-                                        (_) ->
-                                             true
-                                     end, List),
-
+                               NewList = Filter(List),
                                NewLists1 = lists:keydelete(Default, 1, Lists),
                                NewLists = [{Default, NewList} | NewLists1],
                                mnesia:write(P#privacy{lists = NewLists}),
@@ -282,22 +365,44 @@ process_blocklist_unblock(LUser, LServer, JIDs) ->
                        end
                end
        end,
-    case mnesia:transaction(F) of
-       {atomic, {error, _} = Error} ->
-           Error;
-       {atomic, ok} ->
-           {result, []};
-       {atomic, {ok, Default, List}} ->
-            UserList = make_userlist(Default, List),
-           broadcast_list_update(LUser, LServer, Default, UserList),
-           broadcast_blocklist_event(LUser, LServer, {unblock, JIDs}),
-           {result, [], UserList};
-       _ ->
-           {error, ?ERR_INTERNAL_SERVER_ERROR}
-    end.
+    mnesia:transaction(F);
+process_blocklist_unblock(LUser, LServer, Filter, odbc) ->
+    F = fun() ->
+                case mod_privacy:sql_get_default_privacy_list_t(LUser) of
+                    {selected, ["name"], []} ->
+                        ok;
+                    {selected, ["name"], [{Default}]} ->
+                        {selected, ["id"], [{ID}]} =
+                            mod_privacy:sql_get_privacy_list_id_t(
+                              LUser, Default),
+                        case mod_privacy:sql_get_privacy_list_data_by_id_t(ID) of
+                            {selected,
+                             ["t", "value", "action", "ord",
+                              "match_all", "match_iq", "match_message",
+                              "match_presence_in",
+                              "match_presence_out"],
+                             RItems = [_|_]} ->
+                                List = lists:map(
+                                         fun mod_privacy:raw_to_item/1,
+                                         RItems),
+                                NewList = Filter(List),
+                                NewRItems = lists:map(
+                                              fun mod_privacy:item_to_raw/1,
+                                              NewList),
+                                mod_privacy:sql_set_privacy_list(
+                                  ID, NewRItems),
+                                {ok, Default, NewList};
+                            _ ->
+                                ok
+                        end;
+                    _ ->
+                        ok
+                end
+        end,
+    ejabberd_odbc:sql_transaction(LServer, F).
 
 make_userlist(Name, List) ->
-    NeedDb = is_list_needdb(List),
+    NeedDb = mod_privacy:is_list_needdb(List),
     #userlist{name = Name, list = List, needdb = NeedDb}.
 
 broadcast_list_update(LUser, LServer, Name, UserList) ->
@@ -315,25 +420,52 @@ broadcast_blocklist_event(LUser, LServer, Event) ->
        [{blocking, Event}]}).
 
 process_blocklist_get(LUser, LServer) ->
+    case process_blocklist_get(
+           LUser, LServer, gen_mod:db_type(LServer, mod_privacy)) of
+        error ->
+            {error, ?ERR_INTERNAL_SERVER_ERROR};
+        List ->
+            JIDs = list_to_blocklist_jids(List, []),
+            Items = lists:map(
+                      fun(JID) ->
+                              ?DEBUG("JID: ~p",[JID]),
+                              {xmlelement, "item",
+                               [{"jid", jlib:jid_to_string(JID)}], []}
+                      end, JIDs),
+            {result,
+             [{xmlelement, "blocklist", [{"xmlns", ?NS_BLOCKING}],
+               Items}]}
+    end.
+
+process_blocklist_get(LUser, LServer, mnesia) ->
     case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
        {'EXIT', _Reason} ->
-           {error, ?ERR_INTERNAL_SERVER_ERROR};
+            error;
        [] ->
-           {result, [{xmlelement, "blocklist", [{"xmlns", ?NS_BLOCKING}], []}]};
+            [];
        [#privacy{default = Default, lists = Lists}] ->
            case lists:keysearch(Default, 1, Lists) of
                {value, {_, List}} ->
-                   JIDs = list_to_blocklist_jids(List, []),
-                   Items = lists:map(
-                             fun(JID) ->
-                                     ?DEBUG("JID: ~p",[JID]),
-                                     {xmlelement, "item",
-                                      [{"jid", jlib:jid_to_string(JID)}], []}
-                             end, JIDs),
-                   {result,
-                    [{xmlelement, "blocklist", [{"xmlns", ?NS_BLOCKING}],
-                      Items}]};
+                    List;
                _ ->
-                   {result, [{xmlelement, "blocklist", [{"xmlns", ?NS_BLOCKING}], []}]}
+                    []
            end
+    end;
+process_blocklist_get(LUser, LServer, odbc) ->
+    case catch mod_privacy:sql_get_default_privacy_list(LUser, LServer) of
+       {selected, ["name"], []} ->
+            [];
+       {selected, ["name"], [{Default}]} ->
+           case catch mod_privacy:sql_get_privacy_list_data(
+                         LUser, LServer, Default) of
+               {selected, ["t", "value", "action", "ord", "match_all",
+                           "match_iq", "match_message",
+                           "match_presence_in", "match_presence_out"],
+                RItems} ->
+                    lists:map(fun mod_privacy:raw_to_item/1, RItems);
+                {'EXIT', _} ->
+                    error
+            end;
+        {'EXIT', _} ->
+            error
     end.
diff --git a/src/mod_blocking_odbc.erl b/src/mod_blocking_odbc.erl
deleted file mode 100644 (file)
index 016e794..0000000
+++ /dev/null
@@ -1,365 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File    : mod_blocking_odbc.erl
-%%% Author  : Stephan Maka
-%%% Purpose : XEP-0191: Simple Communications Blocking
-%%% Created : 24 Aug 2008 by Stephan Maka <stephan@spaceboyz.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2012   ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
--module(mod_blocking_odbc).
-
--behaviour(gen_mod).
-
--export([start/2, stop/1,
-        process_iq/3,
-        process_iq_set/4,
-        process_iq_get/5]).
-
--include("ejabberd.hrl").
--include("jlib.hrl").
--include("mod_privacy.hrl").
-
-start(Host, Opts) ->
-    IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
-    ejabberd_hooks:add(privacy_iq_get, Host,
-                      ?MODULE, process_iq_get, 40),
-    ejabberd_hooks:add(privacy_iq_set, Host,
-                      ?MODULE, process_iq_set, 40),
-    mod_disco:register_feature(Host, ?NS_BLOCKING),
-    gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_BLOCKING,
-                                 ?MODULE, process_iq, IQDisc).
-
-stop(Host) ->
-    ejabberd_hooks:delete(privacy_iq_get, Host,
-                         ?MODULE, process_iq_get, 40),
-    ejabberd_hooks:delete(privacy_iq_set, Host,
-                         ?MODULE, process_iq_set, 40),
-    mod_disco:unregister_feature(Host, ?NS_BLOCKING),
-    gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_BLOCKING).
-
-process_iq(_From, _To, IQ) ->
-    SubEl = IQ#iq.sub_el,
-    IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}.
-
-process_iq_get(_, From, _To,
-              #iq{xmlns = ?NS_BLOCKING,
-                  sub_el = {xmlelement, "blocklist", _, _}},
-              _) ->
-    #jid{luser = LUser, lserver = LServer} = From,
-    {stop, process_blocklist_get(LUser, LServer)};
-
-process_iq_get(Acc, _, _, _, _) ->
-    Acc.
-
-process_iq_set(_, From, _To, #iq{xmlns = ?NS_BLOCKING,
-                                sub_el = {xmlelement, SubElName, _, SubEls}}) ->
-    #jid{luser = LUser, lserver = LServer} = From,
-    Res =
-        case {SubElName, xml:remove_cdata(SubEls)} of
-            {"block", []} ->
-                {error, ?ERR_BAD_REQUEST};
-            {"block", Els} ->
-                JIDs = parse_blocklist_items(Els, []),
-                process_blocklist_block(LUser, LServer, JIDs);
-            {"unblock", []} ->
-                process_blocklist_unblock_all(LUser, LServer);
-            {"unblock", Els} ->
-                JIDs = parse_blocklist_items(Els, []),
-                process_blocklist_unblock(LUser, LServer, JIDs);
-            _ ->
-                {error, ?ERR_BAD_REQUEST}
-        end,
-    {stop, Res};
-
-process_iq_set(Acc, _, _,  _) ->
-    Acc.
-
-is_list_needdb(Items) ->
-    lists:any(
-      fun(X) ->
-             case X#listitem.type of
-                 subscription -> true;
-                 group -> true;
-                 _ -> false
-             end
-      end, Items).
-
-list_to_blocklist_jids([], JIDs) ->
-    JIDs;
-
-list_to_blocklist_jids([#listitem{type = jid,
-                                 action = deny,
-                                 value = JID} = Item | Items], JIDs) ->
-    case Item of
-       #listitem{match_all = true} ->
-           Match = true;
-       #listitem{match_iq = true,
-                 match_message = true,
-                 match_presence_in = true,
-                 match_presence_out = true} ->
-           Match = true;
-       _ ->
-           Match = false
-    end,
-    if
-       Match ->
-           list_to_blocklist_jids(Items, [JID | JIDs]);
-       true ->
-           list_to_blocklist_jids(Items, JIDs)
-    end;
-
-% Skip Privacy List items than cannot be mapped to Blocking items
-list_to_blocklist_jids([_ | Items], JIDs) ->
-    list_to_blocklist_jids(Items, JIDs).
-
-parse_blocklist_items([], JIDs) ->
-    JIDs;
-
-parse_blocklist_items([{xmlelement, "item", Attrs, _} | Els], JIDs) ->
-    case xml:get_attr("jid", Attrs) of
-       {value, JID1} ->
-           JID = jlib:jid_tolower(jlib:string_to_jid(JID1)),
-           parse_blocklist_items(Els, [JID | JIDs]);
-       false ->
-           % Tolerate missing jid attribute
-           parse_blocklist_items(Els, JIDs)
-    end;
-
-parse_blocklist_items([_ | Els], JIDs) ->
-    % Tolerate unknown elements
-    parse_blocklist_items(Els, JIDs).
-
-process_blocklist_block(LUser, LServer, JIDs) ->
-    F = fun() ->
-                Default =
-                    case mod_privacy_odbc:sql_get_default_privacy_list_t(LUser) of
-                        {selected, ["name"], []} ->
-                            Name = "Blocked contacts",
-                            mod_privacy_odbc:sql_add_privacy_list(LUser, Name),
-                            mod_privacy_odbc:sql_set_default_privacy_list(
-                              LUser, Name),
-                            Name;
-                        {selected, ["name"], [{Name}]} ->
-                            Name
-                    end,
-                {selected, ["id"], [{ID}]} =
-                    mod_privacy_odbc:sql_get_privacy_list_id_t(LUser, Default),
-                case mod_privacy_odbc:sql_get_privacy_list_data_by_id_t(ID) of
-                    {selected,
-                     ["t", "value", "action", "ord",
-                      "match_all", "match_iq", "match_message",
-                      "match_presence_in",
-                      "match_presence_out"],
-                     RItems = [_|_]} ->
-                        List = lists:map(
-                                 fun mod_privacy_odbc:raw_to_item/1,
-                                 RItems);
-                    _ ->
-                        List = []
-                end,
-                AlreadyBlocked = list_to_blocklist_jids(List, []),
-                NewList =
-                    lists:foldr(
-                      fun(JID, List1) ->
-                              case lists:member(JID, AlreadyBlocked) of
-                                  true ->
-                                      List1;
-                                  false ->
-                                      [#listitem{type = jid,
-                                                 value = JID,
-                                                 action = deny,
-                                                 order = 0,
-                                                 match_all = true
-                                                } | List1]
-                              end
-                      end, List, JIDs),
-                NewRItems = lists:map(
-                              fun mod_privacy_odbc:item_to_raw/1,
-                              NewList),
-                mod_privacy_odbc:sql_set_privacy_list(
-                  ID, NewRItems),
-                {ok, Default, NewList}
-        end,
-    case ejabberd_odbc:sql_transaction(LServer, F) of
-       {atomic, {error, _} = Error} ->
-           Error;
-       {atomic, {ok, Default, List}} ->
-            UserList = make_userlist(Default, List),
-           broadcast_list_update(LUser, LServer, Default, UserList),
-           broadcast_blocklist_event(LUser, LServer, {block, JIDs}),
-           {result, [], UserList};
-       _ ->
-           {error, ?ERR_INTERNAL_SERVER_ERROR}
-    end.
-
-process_blocklist_unblock_all(LUser, LServer) ->
-    F = fun() ->
-                case mod_privacy_odbc:sql_get_default_privacy_list_t(LUser) of
-                    {selected, ["name"], []} ->
-                        ok;
-                    {selected, ["name"], [{Default}]} ->
-                        {selected, ["id"], [{ID}]} =
-                            mod_privacy_odbc:sql_get_privacy_list_id_t(
-                              LUser, Default),
-                        case mod_privacy_odbc:sql_get_privacy_list_data_by_id_t(ID) of
-                            {selected,
-                             ["t", "value", "action", "ord",
-                              "match_all", "match_iq", "match_message",
-                              "match_presence_in",
-                              "match_presence_out"],
-                             RItems = [_|_]} ->
-                                List = lists:map(
-                                         fun mod_privacy_odbc:raw_to_item/1,
-                                         RItems),
-                                NewList =
-                                    lists:filter(
-                                      fun(#listitem{action = A}) ->
-                                              A =/= deny
-                                      end, List),
-                                NewRItems = lists:map(
-                                              fun mod_privacy_odbc:item_to_raw/1,
-                                              NewList),
-                                mod_privacy_odbc:sql_set_privacy_list(
-                                  ID, NewRItems),
-                                {ok, Default, NewList};
-                            _ ->
-                                ok
-                        end;
-                    _ ->
-                        ok
-                end
-        end,
-    case ejabberd_odbc:sql_transaction(LServer, F) of
-       {atomic, {error, _} = Error} ->
-           Error;
-       {atomic, ok} ->
-           {result, []};
-       {atomic, {ok, Default, List}} ->
-            UserList = make_userlist(Default, List),
-           broadcast_list_update(LUser, LServer, Default, UserList),
-           broadcast_blocklist_event(LUser, LServer, unblock_all),
-           {result, [], UserList};
-       _ ->
-           {error, ?ERR_INTERNAL_SERVER_ERROR}
-    end.
-
-process_blocklist_unblock(LUser, LServer, JIDs) ->
-    F = fun() ->
-                case mod_privacy_odbc:sql_get_default_privacy_list_t(LUser) of
-                    {selected, ["name"], []} ->
-                        ok;
-                    {selected, ["name"], [{Default}]} ->
-                        {selected, ["id"], [{ID}]} =
-                            mod_privacy_odbc:sql_get_privacy_list_id_t(
-                              LUser, Default),
-                        case mod_privacy_odbc:sql_get_privacy_list_data_by_id_t(ID) of
-                            {selected,
-                             ["t", "value", "action", "ord",
-                              "match_all", "match_iq", "match_message",
-                              "match_presence_in",
-                              "match_presence_out"],
-                             RItems = [_|_]} ->
-                                List = lists:map(
-                                         fun mod_privacy_odbc:raw_to_item/1,
-                                         RItems),
-                                NewList =
-                                    lists:filter(
-                                      fun(#listitem{action = deny,
-                                                    type = jid,
-                                                    value = JID}) ->
-                                              not(lists:member(JID, JIDs));
-                                         (_) ->
-                                              true
-                                      end, List),
-                                NewRItems = lists:map(
-                                              fun mod_privacy_odbc:item_to_raw/1,
-                                              NewList),
-                                mod_privacy_odbc:sql_set_privacy_list(
-                                  ID, NewRItems),
-                                {ok, Default, NewList};
-                            _ ->
-                                ok
-                        end;
-                    _ ->
-                        ok
-                end
-        end,
-    case ejabberd_odbc:sql_transaction(LServer, F) of
-       {atomic, {error, _} = Error} ->
-           Error;
-       {atomic, ok} ->
-           {result, []};
-       {atomic, {ok, Default, List}} ->
-            UserList = make_userlist(Default, List),
-           broadcast_list_update(LUser, LServer, Default, UserList),
-           broadcast_blocklist_event(LUser, LServer, {unblock, JIDs}),
-           {result, [], UserList};
-       _ ->
-           {error, ?ERR_INTERNAL_SERVER_ERROR}
-    end.
-
-make_userlist(Name, List) ->
-    NeedDb = is_list_needdb(List),
-    #userlist{name = Name, list = List, needdb = NeedDb}.
-
-broadcast_list_update(LUser, LServer, Name, UserList) ->
-    ejabberd_router:route(
-      jlib:make_jid(LUser, LServer, ""),
-      jlib:make_jid(LUser, LServer, ""),
-      {xmlelement, "broadcast", [],
-       [{privacy_list, UserList, Name}]}).
-
-broadcast_blocklist_event(LUser, LServer, Event) ->
-    JID = jlib:make_jid(LUser, LServer, ""),
-    ejabberd_router:route(
-      JID, JID,
-      {xmlelement, "broadcast", [],
-       [{blocking, Event}]}).
-
-process_blocklist_get(LUser, LServer) ->
-    case catch mod_privacy_odbc:sql_get_default_privacy_list(LUser, LServer) of
-       {selected, ["name"], []} ->
-            {result, [{xmlelement, "blocklist",
-                       [{"xmlns", ?NS_BLOCKING}], []}]};
-       {selected, ["name"], [{Default}]} ->
-           case catch mod_privacy_odbc:sql_get_privacy_list_data(
-                         LUser, LServer, Default) of
-               {selected, ["t", "value", "action", "ord", "match_all",
-                           "match_iq", "match_message",
-                           "match_presence_in", "match_presence_out"],
-                RItems} ->
-                    List = lists:map(fun mod_privacy_odbc:raw_to_item/1, RItems),
-                    JIDs = list_to_blocklist_jids(List, []),
-                    Items = lists:map(
-                             fun(JID) ->
-                                     ?DEBUG("JID: ~p",[JID]),
-                                     {xmlelement, "item",
-                                      [{"jid", jlib:jid_to_string(JID)}], []}
-                             end, JIDs),
-                   {result,
-                    [{xmlelement, "blocklist", [{"xmlns", ?NS_BLOCKING}],
-                      Items}]};
-                {'EXIT', _} ->
-                    {error, ?ERR_INTERNAL_SERVER_ERROR}
-            end;
-        {'EXIT', _} ->
-            {error, ?ERR_INTERNAL_SERVER_ERROR}
-    end.
index 29b7276cafce43a8b5551c3d44bac11d4fa037d9..44fee1e9331e66cee020710bde86d9e0759bf350 100644 (file)
@@ -1782,15 +1782,13 @@ stop_node(From, Host, ENode, Action, XData) ->
 
 
 get_last_info(User, Server) ->
-    ML = lists:member(mod_last, gen_mod:loaded_modules(Server)),
-    MLO = lists:member(mod_last_odbc, gen_mod:loaded_modules(Server)),
-    case {ML, MLO} of
-       {true, _} -> mod_last:get_last_info(User, Server);
-       {false, true} -> mod_last_odbc:get_last_info(User, Server);
-       {false, false} -> not_found
+    case gen_mod:is_loaded(Server, mod_last) of
+        true ->
+            mod_last:get_last_info(User, Server);
+        false ->
+            not_found
     end.
 
-
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
 adhoc_sm_commands(_Acc, From,
index 70b71e7d69711653db15afda2b0cc657bc5c571e..fb0671104eea8d3bd2002c150e7b6a8166089eae 100644 (file)
@@ -4,7 +4,7 @@ include ..\Makefile.inc
 EFLAGS = -I .. -pz ..
 
 OUTDIR = ..
-BEAMS = ..\iconv.beam ..\mod_irc.beam ..\mod_irc_odbc.beam ..\mod_irc_connection.beam
+BEAMS = ..\iconv.beam ..\mod_irc.beam ..\mod_irc_connection.beam
 
 SOURCE = iconv_erl.c
 OBJECT = iconv_erl.o
@@ -25,9 +25,6 @@ $(OUTDIR)\iconv.beam : iconv.erl
 $(OUTDIR)\mod_irc.beam : mod_irc.erl
        erlc -W $(EFLAGS) -o $(OUTDIR) mod_irc.erl
 
-$(OUTDIR)\mod_irc_odbc.beam : mod_irc_odbc.erl
-       erlc -W $(EFLAGS) -o $(OUTDIR) mod_irc_odbc.erl
-
 $(OUTDIR)\mod_irc_connection.beam : mod_irc_connection.erl
        erlc -W $(EFLAGS) -o $(OUTDIR) mod_irc_connection.erl
 
index f616a92fda811bf70ec20e2375f4018f3deae45f..12c2919449cc73c5dd9350598e7b89d1a8531185 100644 (file)
@@ -98,11 +98,17 @@ stop(Host) ->
 %%--------------------------------------------------------------------
 init([Host, Opts]) ->
     iconv:start(),
-    mnesia:create_table(irc_custom,
-                       [{disc_copies, [node()]},
-                        {attributes, record_info(fields, irc_custom)}]),
     MyHost = gen_mod:get_opt_host(Host, Opts, "irc.@HOST@"),
-    update_table(MyHost),
+    case gen_mod:db_type(Opts) of
+        mnesia ->
+            mnesia:create_table(irc_custom,
+                                [{disc_copies, [node()]},
+                                 {attributes,
+                                  record_info(fields, irc_custom)}]),
+            update_table(MyHost);
+        _ ->
+            ok
+    end,
     Access = gen_mod:get_opt(access, Opts, all),
     catch ets:new(irc_connection, [named_table,
                                   public,
@@ -218,7 +224,7 @@ do_route1(Host, ServerHost, From, To, Packet) ->
                            Info = ejabberd_hooks:run_fold(
                                     disco_info, ServerHost, [],
                                     [ServerHost, ?MODULE, "", ""]),
-                           case iq_disco(Node, Lang) of
+                           case iq_disco(ServerHost, Node, Lang) of
                                [] ->
                                    Res = IQ#iq{type = result,
                                                sub_el = [{xmlelement, "query",
@@ -263,7 +269,8 @@ do_route1(Host, ServerHost, From, To, Packet) ->
                                                sub_el = [{xmlelement, "query",
                                                           [{"xmlns", XMLNS},
                                                            {"node", Node}],
-                                                          command_items(Host, Lang)}]},
+                                                          command_items(ServerHost,
+                                                                         Host, Lang)}]},
                                    Res = jlib:iq_to_xml(ResIQ);
                                _ ->
                                    Res = jlib:make_error_reply(
@@ -273,7 +280,7 @@ do_route1(Host, ServerHost, From, To, Packet) ->
                                                  From,
                                                  Res);
                        #iq{xmlns = ?NS_REGISTER} = IQ ->
-                           process_register(Host, From, To, IQ);
+                           process_register(ServerHost, Host, From, To, IQ);
                        #iq{type = get, xmlns = ?NS_VCARD = XMLNS,
                            lang = Lang} = IQ ->
                            Res = IQ#iq{type = result,
@@ -287,7 +294,8 @@ do_route1(Host, ServerHost, From, To, Packet) ->
                        #iq{type = set, xmlns = ?NS_COMMANDS,
                            lang = _Lang, sub_el = SubEl} = IQ ->
                            Request = adhoc:parse_request(IQ),
-                           case lists:keysearch(Request#adhoc_request.node, 1, commands()) of
+                           case lists:keysearch(Request#adhoc_request.node,
+                                                 1, commands(ServerHost)) of
                                {value, {_, _, Function}} ->
                                    case catch Function(From, To, Request) of
                                        {'EXIT', Reason} ->
@@ -394,7 +402,7 @@ closed_connection(Host, From, Server) ->
     ets:delete(irc_connection, {From, Server, Host}).
 
 
-iq_disco([], Lang) ->
+iq_disco(_ServerHost, [], Lang) ->
     [{xmlelement, "identity",
       [{"category", "conference"},
        {"type", "irc"},
@@ -404,8 +412,8 @@ iq_disco([], Lang) ->
      {xmlelement, "feature", [{"var", ?NS_REGISTER}], []},
      {xmlelement, "feature", [{"var", ?NS_VCARD}], []},
      {xmlelement, "feature", [{"var", ?NS_COMMANDS}], []}];
-iq_disco(Node, Lang) ->
-    case lists:keysearch(Node, 1, commands()) of
+iq_disco(ServerHost, Node, Lang) ->
+    case lists:keysearch(Node, 1, commands(ServerHost)) of
        {value, {_, Name, _}} ->
            [{xmlelement, "identity",
              [{"category", "automation"},
@@ -428,20 +436,23 @@ iq_get_vcard(Lang) ->
       [{xmlcdata, translate:translate(Lang, "ejabberd IRC module") ++ 
         "\nCopyright (c) 2003-2012 ProcessOne"}]}].
 
-command_items(Host, Lang) ->
+command_items(ServerHost, Host, Lang) ->
     lists:map(fun({Node, Name, _Function})
                 -> {xmlelement, "item",
                     [{"jid", Host},
                      {"node", Node},
                      {"name", translate:translate(Lang, Name)}], []}
-             end, commands()).
+             end, commands(ServerHost)).
 
-commands() ->
+commands(ServerHost) ->
     [{"join", "Join channel", fun adhoc_join/3},
-     {"register", "Configure username, encoding, port and password", fun adhoc_register/3}].
+     {"register", "Configure username, encoding, port and password",
+      fun(From, To, Request) ->
+              adhoc_register(ServerHost, From, To, Request)
+      end}].
 
-process_register(Host, From, To, #iq{} = IQ) ->
-    case catch process_irc_register(Host, From, To, IQ) of
+process_register(ServerHost, Host, From, To, #iq{} = IQ) ->
+    case catch process_irc_register(ServerHost, Host, From, To, IQ) of
        {'EXIT', Reason} ->
            ?ERROR_MSG("~p", [Reason]);
        ResIQ ->
@@ -471,7 +482,7 @@ find_xdata_el1([{xmlelement, Name, Attrs, SubEls} | Els]) ->
 find_xdata_el1([_ | Els]) ->
     find_xdata_el1(Els).
 
-process_irc_register(Host, From, _To,
+process_irc_register(ServerHost, Host, From, _To,
                     #iq{type = Type, xmlns = XMLNS,
                         lang = Lang, sub_el = SubEl} = IQ) ->
     case Type of
@@ -497,7 +508,8 @@ process_irc_register(Host, From, _To,
                                             xml:get_tag_attr_s("node", SubEl),
                                             "/"),
                                    case set_form(
-                                          Host, From, Node, Lang, XData) of
+                                          ServerHost, Host, From,
+                                           Node, Lang, XData) of
                                        {result, Res} ->
                                            IQ#iq{type = result,
                                                  sub_el = [{xmlelement, "query",
@@ -517,7 +529,7 @@ process_irc_register(Host, From, _To,
        get ->
            Node =
                string:tokens(xml:get_tag_attr_s("node", SubEl), "/"),
-           case get_form(Host, From, Node, Lang) of
+           case get_form(ServerHost, Host, From, Node, Lang) of
                {result, Res} ->
                    IQ#iq{type = result,
                          sub_el = [{xmlelement, "query",
@@ -530,23 +542,52 @@ process_irc_register(Host, From, _To,
            end
     end.
 
+get_data(ServerHost, Host, From) ->
+    LServer = jlib:nameprep(ServerHost),
+    get_data(LServer, Host, From, gen_mod:db_type(LServer, ?MODULE)).
 
-
-get_form(Host, From, [], Lang) ->
-    #jid{user = User, server = Server,
-        luser = LUser, lserver = LServer} = From,
+get_data(_LServer, Host, From, mnesia) ->
+    #jid{luser = LUser, lserver = LServer} = From,
     US = {LUser, LServer},
+    case catch mnesia:dirty_read({irc_custom, {US, Host}}) of
+        {'EXIT', _Reason} ->
+            error;
+        [] ->
+            empty;
+        [#irc_custom{data = Data}] ->
+            Data
+    end;
+get_data(LServer, Host, From, odbc) ->
+    SJID = ejabberd_odbc:escape(
+             jlib:jid_to_string(
+               jlib:jid_tolower(
+                 jlib:jid_remove_resource(From)))),
+    SHost = ejabberd_odbc:escape(Host),
+    case catch ejabberd_odbc:sql_query(
+                 LServer,
+                 ["select data from irc_custom where "
+                  "jid='", SJID, "' and host='", SHost, "';"]) of
+        {selected, ["data"], [{SData}]} ->
+            ejabberd_odbc:decode_term(SData);
+        {'EXIT', _} ->
+            error;
+        {selected, _, _} ->
+            empty
+    end.
+
+get_form(ServerHost, Host, From, [], Lang) ->
+    #jid{user = User, server = Server} = From,
     DefaultEncoding = get_default_encoding(Host),
     Customs =
-       case catch mnesia:dirty_read({irc_custom, {US, Host}}) of
-           {'EXIT', _Reason} ->
-               {error, ?ERR_INTERNAL_SERVER_ERROR};
-           [] ->
-               {User, []};
-           [#irc_custom{data = Data}] ->
-               {xml:get_attr_s(username, Data),
-                xml:get_attr_s(connections_params, Data)}
-       end,
+        case get_data(ServerHost, Host, From) of
+            error ->
+                {error, ?ERR_INTERNAL_SERVER_ERROR};
+            empty ->
+                {User, []};
+            Data ->
+                {xml:get_attr_s(username, Data),
+                 xml:get_attr_s(connections_params, Data)}
+        end,
     case Customs of
        {error, _Error} ->
            Customs;
@@ -614,15 +655,41 @@ get_form(Host, From, [], Lang) ->
               ]}]}
     end;
 
-get_form(_Host, _, _, _Lang) ->
+get_form(_ServerHost, _Host, _, _, _Lang) ->
     {error, ?ERR_SERVICE_UNAVAILABLE}.
 
 
+set_data(ServerHost, Host, From, Data) ->
+    LServer = jlib:nameprep(ServerHost),
+    set_data(LServer, Host, From, Data, gen_mod:db_type(LServer, ?MODULE)).
 
-
-set_form(Host, From, [], _Lang, XData) ->
+set_data(_LServer, Host, From, Data, mnesia) ->
     {LUser, LServer, _} = jlib:jid_tolower(From),
     US = {LUser, LServer},
+    F = fun() ->
+                mnesia:write(#irc_custom{us_host = {US, Host}, data = Data})
+        end,
+    mnesia:transaction(F);
+set_data(LServer, Host, From, Data, odbc) ->
+    SJID = ejabberd_odbc:escape(
+             jlib:jid_to_string(
+               jlib:jid_tolower(
+                 jlib:jid_remove_resource(From)))),
+    SHost = ejabberd_odbc:escape(Host),
+    SData = ejabberd_odbc:encode_term(Data),
+    F = fun() ->
+                odbc_queries:update_t(
+                  "irc_custom",
+                  ["jid", "host", "data"],
+                  [SJID, SHost, SData],
+                  ["jid='", SJID,
+                   "' and host='",
+                   SHost, "'"]),
+                ok
+        end,
+    ejabberd_odbc:sql_transaction(LServer, F).
+
+set_form(ServerHost, Host, From, [], _Lang, XData) ->
     case {lists:keysearch("username", 1, XData),
          lists:keysearch("connections_params", 1, XData)} of
        {{value, {_, [Username]}}, {value, {_, Strings}}} ->
@@ -633,17 +700,11 @@ set_form(Host, From, [], _Lang, XData) ->
                {ok, Tokens, _} ->
                    case erl_parse:parse_term(Tokens) of
                        {ok, ConnectionsParams} ->
-                           case mnesia:transaction(
-                                  fun() ->
-                                          mnesia:write(
-                                            #irc_custom{us_host =
-                                                        {US, Host},
-                                                        data =
-                                                        [{username,
-                                                          Username},
-                                                         {connections_params,
-                                                          ConnectionsParams}]})
-                                  end) of
+                            case set_data(ServerHost, Host, From,
+                                          [{username,
+                                            Username},
+                                           {connections_params,
+                                            ConnectionsParams}]) of
                                {atomic, _} ->
                                    {result, []};
                                _ ->
@@ -660,7 +721,7 @@ set_form(Host, From, [], _Lang, XData) ->
     end;
 
 
-set_form(_Host, _, _, _Lang, _XData) ->
+set_form(_ServerHost, _Host, _, _, _Lang, _XData) ->
     {error, ?ERR_SERVICE_UNAVAILABLE}.
 
 
@@ -679,16 +740,14 @@ get_default_encoding(ServerHost) ->
     Result.
 
 get_connection_params(Host, ServerHost, From, IRCServer) ->
-    #jid{user = User, server = _Server,
-        luser = LUser, lserver = LServer} = From,
-    US = {LUser, LServer},
+    #jid{user = User, server = _Server} = From,
     DefaultEncoding = get_default_encoding(ServerHost),
-    case catch mnesia:dirty_read({irc_custom, {US, Host}}) of
-       {'EXIT', _Reason} ->
+    case get_data(ServerHost, Host, From) of
+        error ->
            {User, DefaultEncoding, ?DEFAULT_IRC_PORT, ""};
-       [] ->
+       empty ->
            {User, DefaultEncoding, ?DEFAULT_IRC_PORT, ""};
-       [#irc_custom{data = Data}] ->
+        Data ->
            Username = xml:get_attr_s(username, Data),
            {NewUsername, NewEncoding, NewPort, NewPassword} = 
                case lists:keysearch(IRCServer, 1, xml:get_attr_s(connections_params, Data)) of
@@ -785,28 +844,27 @@ adhoc_join(From, To, #adhoc_request{lang = Lang,
            end
     end.
 
-adhoc_register(_From, _To, #adhoc_request{action = "cancel"} = Request) ->
+adhoc_register(_ServerHost, _From, _To, #adhoc_request{action = "cancel"} = Request) ->
     adhoc:produce_response(Request,
                           #adhoc_response{status = canceled});
-adhoc_register(From, To, #adhoc_request{lang = Lang,
-                                       node = _Node,
-                                       xdata = XData,
-                                       action = Action} = Request) ->
-    #jid{user = User, luser = LUser, lserver = LServer} = From,
+adhoc_register(ServerHost, From, To, #adhoc_request{lang = Lang,
+                                                    node = _Node,
+                                                    xdata = XData,
+                                                    action = Action} = Request) ->
+    #jid{user = User} = From,
     #jid{lserver = Host} = To,
-    US = {LUser, LServer},
     %% Generate form for setting username and encodings.  If the user
     %% hasn't begun to fill out the form, generate an initial form
     %% based on current values.
     if XData == false ->
-           case catch mnesia:dirty_read({irc_custom, {US, Host}}) of
-               {'EXIT', _Reason} ->
+            case get_data(ServerHost, Host, From) of
+                error ->
                    Username = User,
                    ConnectionsParams = [];
-               [] ->
+               empty ->
                    Username = User,
                    ConnectionsParams = [];
-               [#irc_custom{data = Data}] ->
+                Data ->
                    Username = xml:get_attr_s(username, Data),
                    ConnectionsParams = xml:get_attr_s(connections_params, Data)
            end,
@@ -832,17 +890,11 @@ adhoc_register(From, To, #adhoc_request{lang = Lang,
     if Error /= false ->
            Error;
        Action == "complete" ->
-           case mnesia:transaction(
-                  fun () ->
-                          mnesia:write(
-                            #irc_custom{us_host =
-                                        {US, Host},
-                                        data =
-                                        [{username,
-                                          Username},
-                                         {connections_params,
-                                          ConnectionsParams}]})
-                  end) of
+            case set_data(ServerHost, Host, From,
+                          [{username,
+                            Username},
+                           {connections_params,
+                            ConnectionsParams}]) of
                {atomic, _} ->
                    adhoc:produce_response(Request, #adhoc_response{status = completed});
                _ ->
diff --git a/src/mod_irc/mod_irc_odbc.erl b/src/mod_irc/mod_irc_odbc.erl
deleted file mode 100644 (file)
index ab0fafe..0000000
+++ /dev/null
@@ -1,1033 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File    : mod_irc_odbc.erl
-%%% Author  : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : IRC transport
-%%% Created : 15 Feb 2003 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2012   ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
--module(mod_irc_odbc).
--author('alexey@process-one.net').
-
--behaviour(gen_server).
--behaviour(gen_mod).
-
-%% API
--export([start_link/2,
-        start/2,
-        stop/1,
-        closed_connection/3,
-        get_connection_params/3]).
-
-%% gen_server callbacks
--export([init/1, handle_call/3, handle_cast/2, handle_info/2,
-        terminate/2, code_change/3]).
-
--include("ejabberd.hrl").
--include("jlib.hrl").
--include("adhoc.hrl").
-
--define(DEFAULT_IRC_ENCODING, "iso8859-1").
--define(DEFAULT_IRC_PORT, 6667).
--define(POSSIBLE_ENCODINGS, ["koi8-r", "iso8859-1", "iso8859-2", "utf-8", "utf-8+latin-1"]).
-
--record(irc_connection, {jid_server_host, pid}).
-
--record(state, {host, server_host, access}).
-
--define(PROCNAME, ejabberd_mod_irc).
-
-%%====================================================================
-%% API
-%%====================================================================
-%%--------------------------------------------------------------------
-%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
-%% Description: Starts the server
-%%--------------------------------------------------------------------
-start_link(Host, Opts) ->
-    Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
-    gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
-
-start(Host, Opts) ->
-    start_supervisor(Host),
-    Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
-    ChildSpec =
-       {Proc,
-        {?MODULE, start_link, [Host, Opts]},
-        temporary,
-        1000,
-        worker,
-        [?MODULE]},
-    supervisor:start_child(ejabberd_sup, ChildSpec).
-
-stop(Host) ->
-    stop_supervisor(Host),
-    Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
-    gen_server:call(Proc, stop),
-    supervisor:delete_child(ejabberd_sup, Proc).
-
-%%====================================================================
-%% gen_server callbacks
-%%====================================================================
-
-%%--------------------------------------------------------------------
-%% Function: init(Args) -> {ok, State} |
-%%                         {ok, State, Timeout} |
-%%                         ignore               |
-%%                         {stop, Reason}
-%% Description: Initiates the server
-%%--------------------------------------------------------------------
-init([Host, Opts]) ->
-    iconv:start(),
-    MyHost = gen_mod:get_opt_host(Host, Opts, "irc.@HOST@"),
-    Access = gen_mod:get_opt(access, Opts, all),
-    catch ets:new(irc_connection, [named_table,
-                                  public,
-                                  {keypos, #irc_connection.jid_server_host}]),
-    ejabberd_router:register_route(MyHost),
-    {ok, #state{host = MyHost,
-               server_host = Host,
-               access = Access}}.
-
-%%--------------------------------------------------------------------
-%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
-%%                                      {reply, Reply, State, Timeout} |
-%%                                      {noreply, State} |
-%%                                      {noreply, State, Timeout} |
-%%                                      {stop, Reason, Reply, State} |
-%%                                      {stop, Reason, State}
-%% Description: Handling call messages
-%%--------------------------------------------------------------------
-handle_call(stop, _From, State) ->
-    {stop, normal, ok, State}.
-
-%%--------------------------------------------------------------------
-%% Function: handle_cast(Msg, State) -> {noreply, State} |
-%%                                      {noreply, State, Timeout} |
-%%                                      {stop, Reason, State}
-%% Description: Handling cast messages
-%%--------------------------------------------------------------------
-handle_cast(_Msg, State) ->
-    {noreply, State}.
-
-%%--------------------------------------------------------------------
-%% Function: handle_info(Info, State) -> {noreply, State} |
-%%                                       {noreply, State, Timeout} |
-%%                                       {stop, Reason, State}
-%% Description: Handling all non call/cast messages
-%%--------------------------------------------------------------------
-handle_info({route, From, To, Packet},
-           #state{host = Host,
-                  server_host = ServerHost,
-                  access = Access} = State) ->
-    case catch do_route(Host, ServerHost, Access, From, To, Packet) of
-       {'EXIT', Reason} ->
-           ?ERROR_MSG("~p", [Reason]);
-       _ ->
-           ok
-    end,
-    {noreply, State};
-handle_info(_Info, State) ->
-    {noreply, State}.
-
-%%--------------------------------------------------------------------
-%% Function: terminate(Reason, State) -> void()
-%% Description: This function is called by a gen_server when it is about to
-%% terminate. It should be the opposite of Module:init/1 and do any necessary
-%% cleaning up. When it returns, the gen_server terminates with Reason.
-%% The return value is ignored.
-%%--------------------------------------------------------------------
-terminate(_Reason, State) ->
-    ejabberd_router:unregister_route(State#state.host),
-    ok.
-
-%%--------------------------------------------------------------------
-%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
-%% Description: Convert process state when code is changed
-%%--------------------------------------------------------------------
-code_change(_OldVsn, State, _Extra) ->
-    {ok, State}.
-
-%%--------------------------------------------------------------------
-%%% Internal functions
-%%--------------------------------------------------------------------
-start_supervisor(Host) ->
-    Proc = gen_mod:get_module_proc(Host, ejabberd_mod_irc_sup),
-    ChildSpec =
-       {Proc,
-        {ejabberd_tmp_sup, start_link,
-         [Proc, mod_irc_connection]},
-        permanent,
-        infinity,
-        supervisor,
-        [ejabberd_tmp_sup]},
-    supervisor:start_child(ejabberd_sup, ChildSpec).
-
-stop_supervisor(Host) ->
-    Proc = gen_mod:get_module_proc(Host, ejabberd_mod_irc_sup),
-    supervisor:terminate_child(ejabberd_sup, Proc),
-    supervisor:delete_child(ejabberd_sup, Proc).
-
-do_route(Host, ServerHost, Access, From, To, Packet) ->
-    case acl:match_rule(ServerHost, Access, From) of
-       allow ->
-           do_route1(Host, ServerHost, From, To, Packet);
-       _ ->
-           {xmlelement, _Name, Attrs, _Els} = Packet,
-           Lang = xml:get_attr_s("xml:lang", Attrs),
-           ErrText = "Access denied by service policy",
-           Err = jlib:make_error_reply(Packet,
-                                       ?ERRT_FORBIDDEN(Lang, ErrText)),
-           ejabberd_router:route(To, From, Err)
-    end.
-
-do_route1(Host, ServerHost, From, To, Packet) ->
-    #jid{user = ChanServ, resource = Resource} = To,
-    {xmlelement, _Name, _Attrs, _Els} = Packet,
-    case ChanServ of
-       "" ->
-           case Resource of
-               "" ->
-                   case jlib:iq_query_info(Packet) of
-                       #iq{type = get, xmlns = ?NS_DISCO_INFO = XMLNS,
-                           sub_el = SubEl, lang = Lang} = IQ ->
-                           Node = xml:get_tag_attr_s("node", SubEl),
-                           Info = ejabberd_hooks:run_fold(
-                                    disco_info, ServerHost, [],
-                                    [ServerHost, ?MODULE, "", ""]),
-                           case iq_disco(ServerHost, Node, Lang) of
-                               [] ->
-                                   Res = IQ#iq{type = result,
-                                               sub_el = [{xmlelement, "query",
-                                                          [{"xmlns", XMLNS}],
-                                                          []}]},
-                                   ejabberd_router:route(To,
-                                                         From,
-                                                         jlib:iq_to_xml(Res));
-                               DiscoInfo ->
-                                   Res = IQ#iq{type = result,
-                                               sub_el = [{xmlelement, "query",
-                                                          [{"xmlns", XMLNS}],
-                                                          DiscoInfo ++ Info}]},
-                                   ejabberd_router:route(To,
-                                                         From,
-                                                         jlib:iq_to_xml(Res))
-                           end;
-                       #iq{type = get, xmlns = ?NS_DISCO_ITEMS = XMLNS,
-                           sub_el = SubEl, lang = Lang} = IQ ->
-                           Node = xml:get_tag_attr_s("node", SubEl),
-                           case Node of
-                               [] ->
-                                   ResIQ = IQ#iq{type = result,
-                                               sub_el = [{xmlelement, "query",
-                                                          [{"xmlns", XMLNS}],
-                                                          []}]},
-                                   Res = jlib:iq_to_xml(ResIQ);
-                               "join" ->
-                                   ResIQ = IQ#iq{type = result,
-                                               sub_el = [{xmlelement, "query",
-                                                          [{"xmlns", XMLNS}],
-                                                          []}]},
-                                   Res = jlib:iq_to_xml(ResIQ);
-                               "register" ->
-                                   ResIQ = IQ#iq{type = result,
-                                               sub_el = [{xmlelement, "query",
-                                                          [{"xmlns", XMLNS}],
-                                                          []}]},
-                                   Res = jlib:iq_to_xml(ResIQ);
-                               ?NS_COMMANDS ->
-                                   ResIQ = IQ#iq{type = result,
-                                               sub_el = [{xmlelement, "query",
-                                                          [{"xmlns", XMLNS},
-                                                           {"node", Node}],
-                                                          command_items(ServerHost,
-                                                                         Host, Lang)}]},
-                                   Res = jlib:iq_to_xml(ResIQ);
-                               _ ->
-                                   Res = jlib:make_error_reply(
-                                           Packet, ?ERR_ITEM_NOT_FOUND)
-                           end,
-                           ejabberd_router:route(To,
-                                                 From,
-                                                 Res);
-                       #iq{xmlns = ?NS_REGISTER} = IQ ->
-                           process_register(ServerHost, Host, From, To, IQ);
-                       #iq{type = get, xmlns = ?NS_VCARD = XMLNS,
-                           lang = Lang} = IQ ->
-                           Res = IQ#iq{type = result,
-                                       sub_el =
-                                            [{xmlelement, "vCard",
-                                              [{"xmlns", XMLNS}],
-                                              iq_get_vcard(Lang)}]},
-                            ejabberd_router:route(To,
-                                                  From,
-                                                  jlib:iq_to_xml(Res));
-                       #iq{type = set, xmlns = ?NS_COMMANDS,
-                           lang = _Lang, sub_el = SubEl} = IQ ->
-                           Request = adhoc:parse_request(IQ),
-                           case lists:keysearch(Request#adhoc_request.node,
-                                                 1, commands(ServerHost)) of
-                               {value, {_, _, Function}} ->
-                                   case catch Function(From, To, Request) of
-                                       {'EXIT', Reason} ->
-                                           ?ERROR_MSG("~p~nfor ad-hoc handler of ~p",
-                                                      [Reason, {From, To, IQ}]),
-                                           Res = IQ#iq{type = error, sub_el = [SubEl,
-                                                                               ?ERR_INTERNAL_SERVER_ERROR]};
-                                       ignore ->
-                                           Res = ignore;
-                                       {error, Error} ->
-                                           Res = IQ#iq{type = error, sub_el = [SubEl, Error]};
-                                       Command ->
-                                           Res = IQ#iq{type = result, sub_el = [Command]}
-                                   end,
-                                   if Res /= ignore ->
-                                           ejabberd_router:route(To, From, jlib:iq_to_xml(Res));
-                                      true ->
-                                           ok
-                                   end;
-                               _ ->
-                                   Err = jlib:make_error_reply(
-                                           Packet, ?ERR_ITEM_NOT_FOUND),
-                                   ejabberd_router:route(To, From, Err)
-                           end;
-                       #iq{} = _IQ ->
-                           Err = jlib:make_error_reply(
-                                   Packet, ?ERR_FEATURE_NOT_IMPLEMENTED),
-                           ejabberd_router:route(To, From, Err);
-                       _ ->
-                           ok
-                   end;
-               _ ->
-                   Err = jlib:make_error_reply(Packet, ?ERR_BAD_REQUEST),
-                   ejabberd_router:route(To, From, Err)
-           end;
-       _ ->
-           case string:tokens(ChanServ, "%") of
-               [[_ | _] = Channel, [_ | _] = Server] ->
-                   case ets:lookup(irc_connection, {From, Server, Host}) of
-                       [] ->
-                           ?DEBUG("open new connection~n", []),
-                           {Username, Encoding, Port, Password} = get_connection_params(
-                                                    Host, ServerHost, From, Server),
-                           ConnectionUsername =
-                               case Packet of
-                                   %% If the user tries to join a
-                                   %% chatroom, the packet for sure
-                                   %% contains the desired username.
-                                   {xmlelement, "presence", _, _} ->
-                                       Resource;
-                                   %% Otherwise, there is no firm
-                                   %% conclusion from the packet.
-                                   %% Better to use the configured
-                                   %% username (which defaults to the
-                                   %% username part of the JID).
-                                   _ ->
-                                       Username
-                               end,
-                           {ok, Pid} = mod_irc_connection:start(
-                                         From, Host, ServerHost, Server,
-                                         ConnectionUsername, Encoding, Port,
-                                          Password, ?MODULE),
-                           ets:insert(
-                             irc_connection,
-                             #irc_connection{jid_server_host = {From, Server, Host},
-                                             pid = Pid}),
-                           mod_irc_connection:route_chan(
-                             Pid, Channel, Resource, Packet),
-                           ok;
-                       [R] ->
-                           Pid = R#irc_connection.pid,
-                           ?DEBUG("send to process ~p~n",
-                                     [Pid]),
-                           mod_irc_connection:route_chan(
-                             Pid, Channel, Resource, Packet),
-                           ok
-                   end;
-               _ ->
-                   case string:tokens(ChanServ, "!") of
-                       [[_ | _] = Nick, [_ | _] = Server] ->
-                           case ets:lookup(irc_connection, {From, Server, Host}) of
-                               [] ->
-                                   Err = jlib:make_error_reply(
-                                           Packet, ?ERR_SERVICE_UNAVAILABLE),
-                                   ejabberd_router:route(To, From, Err);
-                               [R] ->
-                                   Pid = R#irc_connection.pid,
-                                   ?DEBUG("send to process ~p~n",
-                                             [Pid]),
-                                   mod_irc_connection:route_nick(
-                                     Pid, Nick, Packet),
-                                   ok
-                           end;
-                       _ ->
-                           Err = jlib:make_error_reply(
-                                   Packet, ?ERR_BAD_REQUEST),
-                           ejabberd_router:route(To, From, Err)
-                   end
-           end
-    end.
-
-
-closed_connection(Host, From, Server) ->
-    ets:delete(irc_connection, {From, Server, Host}).
-
-
-iq_disco(_ServerHost, [], Lang) ->
-    [{xmlelement, "identity",
-      [{"category", "conference"},
-       {"type", "irc"},
-       {"name", translate:translate(Lang, "IRC Transport")}], []},
-     {xmlelement, "feature", [{"var", ?NS_DISCO_INFO}], []},
-     {xmlelement, "feature", [{"var", ?NS_MUC}], []},
-     {xmlelement, "feature", [{"var", ?NS_REGISTER}], []},
-     {xmlelement, "feature", [{"var", ?NS_VCARD}], []},
-     {xmlelement, "feature", [{"var", ?NS_COMMANDS}], []}];
-iq_disco(ServerHost, Node, Lang) ->
-    case lists:keysearch(Node, 1, commands(ServerHost)) of
-       {value, {_, Name, _}} ->
-           [{xmlelement, "identity",
-             [{"category", "automation"},
-              {"type", "command-node"},
-              {"name", translate:translate(Lang, Name)}], []},
-            {xmlelement, "feature",
-             [{"var", ?NS_COMMANDS}], []},
-            {xmlelement, "feature",
-             [{"var", ?NS_XDATA}], []}];
-       _ ->
-           []
-    end.
-
-iq_get_vcard(Lang) ->
-    [{xmlelement, "FN", [],
-      [{xmlcdata, "ejabberd/mod_irc"}]},                  
-     {xmlelement, "URL", [],
-      [{xmlcdata, ?EJABBERD_URI}]},
-     {xmlelement, "DESC", [],
-      [{xmlcdata, translate:translate(Lang, "ejabberd IRC module") ++ 
-        "\nCopyright (c) 2003-2012 ProcessOne"}]}].
-
-command_items(ServerHost, Host, Lang) ->
-    lists:map(fun({Node, Name, _Function})
-                -> {xmlelement, "item",
-                    [{"jid", Host},
-                     {"node", Node},
-                     {"name", translate:translate(Lang, Name)}], []}
-             end, commands(ServerHost)).
-
-commands(ServerHost) ->
-    [{"join", "Join channel", fun adhoc_join/3},
-     {"register", "Configure username, encoding, port and password",
-      fun(From, To, Request) ->
-              adhoc_register(ServerHost, From, To, Request)
-      end}].
-
-process_register(ServerHost, Host, From, To, #iq{} = IQ) ->
-    case catch process_irc_register(ServerHost, Host, From, To, IQ) of
-       {'EXIT', Reason} ->
-           ?ERROR_MSG("~p", [Reason]);
-       ResIQ ->
-           if
-               ResIQ /= ignore ->
-                   ejabberd_router:route(To, From,
-                                         jlib:iq_to_xml(ResIQ));
-               true ->
-                   ok
-           end
-    end.
-
-find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
-    find_xdata_el1(SubEls).
-
-find_xdata_el1([]) ->
-    false;
-
-find_xdata_el1([{xmlelement, Name, Attrs, SubEls} | Els]) ->
-    case xml:get_attr_s("xmlns", Attrs) of
-       ?NS_XDATA ->
-           {xmlelement, Name, Attrs, SubEls};
-       _ ->
-           find_xdata_el1(Els)
-    end;
-
-find_xdata_el1([_ | Els]) ->
-    find_xdata_el1(Els).
-
-process_irc_register(ServerHost, Host, From, _To,
-                    #iq{type = Type, xmlns = XMLNS,
-                        lang = Lang, sub_el = SubEl} = IQ) ->
-    case Type of
-       set ->
-           XDataEl = find_xdata_el(SubEl),
-           case XDataEl of
-               false ->
-                   IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ACCEPTABLE]};
-               {xmlelement, _Name, Attrs, _SubEls} ->
-                   case xml:get_attr_s("type", Attrs) of
-                       "cancel" ->
-                           IQ#iq{type = result,
-                               sub_el = [{xmlelement, "query",
-                                          [{"xmlns", XMLNS}], []}]};
-                       "submit" ->
-                           XData = jlib:parse_xdata_submit(XDataEl),
-                           case XData of
-                               invalid ->
-                                   IQ#iq{type = error,
-                                         sub_el = [SubEl, ?ERR_BAD_REQUEST]};
-                               _ ->
-                                   Node = string:tokens(
-                                            xml:get_tag_attr_s("node", SubEl),
-                                            "/"),
-                                   case set_form(
-                                          ServerHost, Host, From,
-                                           Node, Lang, XData) of
-                                       {result, Res} ->
-                                           IQ#iq{type = result,
-                                                 sub_el = [{xmlelement, "query",
-                                                            [{"xmlns", XMLNS}],
-                                                            Res
-                                                           }]};
-                                       {error, Error} ->
-                                           IQ#iq{type = error,
-                                                 sub_el = [SubEl, Error]}
-                                   end
-                           end;
-                       _ ->
-                           IQ#iq{type = error,
-                                 sub_el = [SubEl, ?ERR_BAD_REQUEST]}
-                   end
-           end;
-       get ->
-           Node =
-               string:tokens(xml:get_tag_attr_s("node", SubEl), "/"),
-           case get_form(ServerHost, Host, From, Node, Lang) of
-               {result, Res} ->
-                   IQ#iq{type = result,
-                         sub_el = [{xmlelement, "query",
-                                    [{"xmlns", XMLNS}],
-                                    Res
-                                   }]};
-               {error, Error} ->
-                   IQ#iq{type = error,
-                         sub_el = [SubEl, Error]}
-           end
-    end.
-
-
-
-get_form(ServerHost, Host, From, [], Lang) ->
-    #jid{user = User, server = Server} = From,
-    LServer = jlib:nameprep(ServerHost),
-    SJID = ejabberd_odbc:escape(
-             jlib:jid_to_string(
-               jlib:jid_tolower(
-                 jlib:jid_remove_resource(From)))),
-    SHost = ejabberd_odbc:escape(Host),
-    DefaultEncoding = get_default_encoding(Host),
-    Customs =
-        case catch ejabberd_odbc:sql_query(
-                     LServer,
-                     ["select data from irc_custom where "
-                      "jid='", SJID, "' and host='", SHost, "';"]) of
-            {selected, ["data"], [{SData}]} ->
-                Data = ejabberd_odbc:decode_term(SData),
-                {xml:get_attr_s(username, Data),
-                xml:get_attr_s(connections_params, Data)};
-            {'EXIT', _} ->
-                {error, ?ERR_INTERNAL_SERVER_ERROR};
-            {selected, _, _} ->
-                {User, []}
-        end,
-    case Customs of
-       {error, _Error} ->
-           Customs;
-       {Username, ConnectionsParams} ->
-           {result,
-            [{xmlelement, "instructions", [],
-              [{xmlcdata,
-                translate:translate(
-                  Lang,
-                  "You need an x:data capable client "
-                  "to configure mod_irc settings")}]},
-             {xmlelement, "x", [{"xmlns", ?NS_XDATA}],
-              [{xmlelement, "title", [],
-                [{xmlcdata,
-                  translate:translate(
-                    Lang,
-                    "Registration in mod_irc for ") ++ User ++ "@" ++ Server}]},
-                     {xmlelement, "instructions", [],
-                      [{xmlcdata,
-                        translate:translate(
-                          Lang,
-                          "Enter username, encodings, ports and passwords you wish to use for "
-                          "connecting to IRC servers")}]},
-               {xmlelement, "field", [{"type", "text-single"},
-                                      {"label",
-                                       translate:translate(
-                                         Lang, "IRC Username")},
-                                      {"var", "username"}],
-                [{xmlelement, "value", [], [{xmlcdata, Username}]}]},
-               {xmlelement, "field", [{"type", "fixed"}],
-                [{xmlelement, "value", [],
-                  [{xmlcdata,
-                    lists:flatten(
-                      io_lib:format(
-                        translate:translate(
-                          Lang,
-                          "If you want to specify different ports, "
-                          "passwords, encodings for IRC servers, fill "
-                          "this list with values in format "
-                          "'{\"irc server\", \"encoding\", port, \"password\"}'.  "
-                          "By default this service use \"~s\" encoding, port ~p, "
-                          "empty password."),
-                        [DefaultEncoding, ?DEFAULT_IRC_PORT]))}]}]},
-               {xmlelement, "field", [{"type", "fixed"}],
-                [{xmlelement, "value", [],
-                  [{xmlcdata,
-                    translate:translate(
-                      Lang,
-                      "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, "
-                      "{\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."
-                   )}]}]},
-               {xmlelement, "field", [{"type", "text-multi"},
-                                      {"label",
-                                       translate:translate(Lang, "Connections parameters")},
-                                      {"var", "connections_params"}],
-                        lists:map(
-                          fun(S) ->
-                                  {xmlelement, "value", [], [{xmlcdata, S}]}
-                          end,
-                          string:tokens(
-                            lists:flatten(
-                              io_lib:format("~p.", [ConnectionsParams])),
-                            "\n"))
-               }
-              ]}]}
-    end;
-
-get_form(_ServerHost, _Host, _, _, _Lang) ->
-    {error, ?ERR_SERVICE_UNAVAILABLE}.
-
-
-
-
-set_form(ServerHost, Host, From, [], _Lang, XData) ->
-    LServer = jlib:nameprep(ServerHost),
-    SJID = ejabberd_odbc:escape(
-             jlib:jid_to_string(
-               jlib:jid_tolower(
-                 jlib:jid_remove_resource(From)))),
-    SHost = ejabberd_odbc:escape(Host),
-    case {lists:keysearch("username", 1, XData),
-         lists:keysearch("connections_params", 1, XData)} of
-       {{value, {_, [Username]}}, {value, {_, Strings}}} ->
-           EncString = lists:foldl(fun(S, Res) ->
-                                           Res ++ S ++ "\n"
-                                   end, "", Strings),
-           case erl_scan:string(EncString) of
-               {ok, Tokens, _} ->
-                   case erl_parse:parse_term(Tokens) of
-                       {ok, ConnectionsParams} ->
-                            SData = ejabberd_odbc:encode_term(
-                                      [{username,
-                                        Username},
-                                       {connections_params,
-                                        ConnectionsParams}]),
-                            case ejabberd_odbc:sql_transaction(
-                                   LServer,
-                                   fun() ->
-                                           odbc_queries:update_t(
-                                             "irc_custom",
-                                             ["jid", "host", "data"],
-                                             [SJID, SHost, SData],
-                                             ["jid='", SJID,
-                                              "' and host='",
-                                              SHost, "'"]),
-                                           ok
-                                   end) of
-                               {atomic, _} ->
-                                   {result, []};
-                               _ ->
-                                   {error, ?ERR_NOT_ACCEPTABLE}
-                           end;
-                       _ ->
-                           {error, ?ERR_NOT_ACCEPTABLE}
-                   end;
-               _ ->
-                   {error, ?ERR_NOT_ACCEPTABLE}
-           end;
-       _ ->
-           {error, ?ERR_NOT_ACCEPTABLE}
-    end;
-
-
-set_form(_ServerHost, _Host, _, _, _Lang, _XData) ->
-    {error, ?ERR_SERVICE_UNAVAILABLE}.
-
-
-%% Host = "irc.example.com"
-%% ServerHost = "example.com"
-get_connection_params(Host, From, IRCServer) ->
-    [_ | HostTail] = string:tokens(Host, "."),
-    ServerHost = string:join(HostTail, "."),
-    get_connection_params(Host, ServerHost, From, IRCServer).
-
-get_default_encoding(ServerHost) ->
-    Result = gen_mod:get_module_opt(
-                    ServerHost, ?MODULE, default_encoding,
-                    ?DEFAULT_IRC_ENCODING),
-    ?INFO_MSG("The default_encoding configured for host ~p is: ~p~n", [ServerHost, Result]),
-    Result.
-
-get_connection_params(Host, ServerHost, From, IRCServer) ->
-    #jid{user = User, server = _Server} = From,
-    LServer = jlib:nameprep(ServerHost),
-    SJID = ejabberd_odbc:escape(
-             jlib:jid_to_string(
-               jlib:jid_tolower(
-                 jlib:jid_remove_resource(From)))),
-    SHost = ejabberd_odbc:escape(Host),
-    DefaultEncoding = get_default_encoding(ServerHost),
-    case catch ejabberd_odbc:sql_query(
-                 LServer,
-                 ["select data from irc_custom where "
-                  "jid='", SJID, "' and host='", SHost, "';"]) of
-        {'EXIT', _Reason} ->
-           {User, DefaultEncoding, ?DEFAULT_IRC_PORT, ""};
-       {selected, ["data"], []} ->
-           {User, DefaultEncoding, ?DEFAULT_IRC_PORT, ""};
-        {selected, ["data"], [{SData}]} ->
-            Data = ejabberd_odbc:decode_term(SData),
-           Username = xml:get_attr_s(username, Data),
-           {NewUsername, NewEncoding, NewPort, NewPassword} = 
-               case lists:keysearch(
-                       IRCServer, 1,
-                       xml:get_attr_s(connections_params, Data)) of
-                   {value, {_, Encoding, Port, Password}} ->
-                       {Username, Encoding, Port, Password};
-                   {value, {_, Encoding, Port}} ->
-                       {Username, Encoding, Port, ""};
-                   {value, {_, Encoding}} ->
-                       {Username, Encoding, ?DEFAULT_IRC_PORT, ""};
-                   _ ->
-                       {Username, DefaultEncoding, ?DEFAULT_IRC_PORT, ""}
-               end,
-           {NewUsername, 
-            NewEncoding, 
-            if 
-                 NewPort >= 0 andalso NewPort =< 65535 -> 
-                     NewPort; 
-                 true -> 
-                     ?DEFAULT_IRC_PORT
-            end,
-            NewPassword}
-    end.
-
-adhoc_join(_From, _To, #adhoc_request{action = "cancel"} = Request) ->
-    adhoc:produce_response(Request,
-                          #adhoc_response{status = canceled});
-adhoc_join(From, To, #adhoc_request{lang = Lang,
-                                   node = _Node,
-                                   action = _Action,
-                                   xdata = XData} = Request) ->
-    %% Access control has already been taken care of in do_route.
-    if XData == false ->
-           Form =
-               {xmlelement, "x",
-                [{"xmlns", ?NS_XDATA},
-                 {"type", "form"}],
-                [{xmlelement, "title", [], [{xmlcdata, translate:translate(Lang, "Join IRC channel")}]},
-                 {xmlelement, "field",
-                  [{"var", "channel"},
-                   {"type", "text-single"},
-                   {"label", translate:translate(Lang, "IRC channel (don't put the first #)")}],
-                  [{xmlelement, "required", [], []}]},
-                 {xmlelement, "field",
-                  [{"var", "server"},
-                   {"type", "text-single"},
-                   {"label", translate:translate(Lang, "IRC server")}],
-                  [{xmlelement, "required", [], []}]}]},
-           adhoc:produce_response(Request,
-                                  #adhoc_response{status = executing,
-                                                  elements = [Form]});
-       true ->
-           case jlib:parse_xdata_submit(XData) of
-               invalid ->
-                   {error, ?ERR_BAD_REQUEST};
-               Fields ->
-                   Channel = case lists:keysearch("channel", 1, Fields) of
-                                 {value, {"channel", C}} ->
-                                     C;
-                                 _ ->
-                                     false
-                             end,
-                   Server = case lists:keysearch("server", 1, Fields) of
-                                {value, {"server", S}} ->
-                                    S;
-                                _ ->
-                                    false
-                            end,
-                   if Channel /= false,
-                      Server /= false ->
-                           RoomJID = Channel ++ "%" ++ Server ++ "@" ++ To#jid.server,
-                           Invite = {xmlelement, "message", [],
-                                     [{xmlelement, "x",
-                                       [{"xmlns", ?NS_MUC_USER}],
-                                       [{xmlelement, "invite", 
-                                         [{"from", jlib:jid_to_string(From)}],
-                                         [{xmlelement, "reason", [],
-                                           [{xmlcdata, 
-                                             translate:translate(Lang,
-                                                                 "Join the IRC channel here.")}]}]}]},
-                                      {xmlelement, "x",
-                                       [{"xmlns", ?NS_XCONFERENCE}],
-                                       [{xmlcdata, translate:translate(Lang,
-                                                                 "Join the IRC channel here.")}]},
-                                      {xmlelement, "body", [],
-                                       [{xmlcdata, io_lib:format(
-                                                     translate:translate(Lang,
-                                                                         "Join the IRC channel in this Jabber ID: ~s"),
-                                                     [RoomJID])}]}]},
-                           ejabberd_router:route(jlib:string_to_jid(RoomJID), From, Invite),
-                           adhoc:produce_response(Request, #adhoc_response{status = completed});
-                      true ->
-                           {error, ?ERR_BAD_REQUEST}
-                   end
-           end
-    end.
-
-adhoc_register(_ServerHost, _From, _To, #adhoc_request{action = "cancel"} = Request) ->
-    adhoc:produce_response(Request,
-                          #adhoc_response{status = canceled});
-adhoc_register(ServerHost, From, To, #adhoc_request{lang = Lang,
-                                                    node = _Node,
-                                                    xdata = XData,
-                                                    action = Action} = Request) ->
-    #jid{user = User} = From,
-    #jid{lserver = Host} = To,
-    LServer = jlib:nameprep(ServerHost),
-    SHost = ejabberd_odbc:escape(Host),
-    SJID = ejabberd_odbc:escape(
-             jlib:jid_to_string(
-               jlib:jid_tolower(
-                 jlib:jid_remove_resource(From)))),
-    %% Generate form for setting username and encodings.  If the user
-    %% hasn't begun to fill out the form, generate an initial form
-    %% based on current values.
-    if XData == false ->
-            case catch ejabberd_odbc:sql_query(
-                         LServer,
-                         ["select data from irc_custom where "
-                          "jid='", SJID, "' and host='", SHost, "';"]) of
-               {'EXIT', _Reason} ->
-                   Username = User,
-                   ConnectionsParams = [];
-               {selected, ["data"], []} ->
-                   Username = User,
-                   ConnectionsParams = [];
-               {selected, ["data"], [{Data1}]} ->
-                    Data = ejabberd_odbc:decode_term(Data1),
-                   Username = xml:get_attr_s(username, Data),
-                   ConnectionsParams = xml:get_attr_s(connections_params, Data)
-           end,
-           Error = false;
-       true ->
-           case jlib:parse_xdata_submit(XData) of
-               invalid ->
-                   Error = {error, ?ERR_BAD_REQUEST},
-                   Username = false,
-                   ConnectionsParams = false;
-               Fields ->
-                   Username = case lists:keysearch("username", 1, Fields) of
-                                  {value, {"username", U}} ->
-                                      U;
-                                  _ ->
-                                      User
-                              end,
-                   ConnectionsParams = parse_connections_params(Fields),
-                   Error = false
-           end
-    end,
-    
-    if Error /= false ->
-           Error;
-       Action == "complete" ->
-            SData = ejabberd_odbc:encode_term(
-                      [{username, Username},
-                       {connections_params, ConnectionsParams}]),
-            case catch ejabberd_odbc:sql_transaction(
-                         LServer,
-                         fun() ->
-                                 odbc_queries:update_t(
-                                   "irc_custom",
-                                   ["jid", "host", "data"],
-                                   [SJID, SHost, SData],
-                                   ["jid='", SJID,
-                                    "' and host='", SHost, "'"]),
-                                 ok
-                         end) of
-               {atomic, ok} ->
-                   adhoc:produce_response(Request, #adhoc_response{status = completed});
-               _ ->
-                   {error, ?ERR_INTERNAL_SERVER_ERROR}
-           end;
-       true ->
-           Form = generate_adhoc_register_form(Lang, Username, ConnectionsParams),
-           adhoc:produce_response(Request,
-                                  #adhoc_response{status = executing,
-                                                  elements = [Form],
-                                                  actions = ["next", "complete"]})
-    end.
-
-generate_adhoc_register_form(Lang, Username, ConnectionsParams) ->
-    {xmlelement, "x",
-     [{"xmlns", ?NS_XDATA},
-      {"type", "form"}],
-     [{xmlelement, "title", [], [{xmlcdata, translate:translate(Lang, "IRC settings")}]},
-      {xmlelement, "instructions", [],
-       [{xmlcdata,
-        translate:translate(
-          Lang,
-          "Enter username and encodings you wish to use for "
-          "connecting to IRC servers.  Press 'Next' to get more fields "
-          "to fill in.  Press 'Complete' to save settings.")}]},
-      {xmlelement, "field",
-       [{"var", "username"},
-       {"type", "text-single"},
-       {"label", translate:translate(Lang, "IRC username")}], 
-       [{xmlelement, "required", [], []},
-       {xmlelement, "value", [], [{xmlcdata, Username}]}]}] ++
-    generate_connection_params_fields(Lang, ConnectionsParams, 1, [])}.
-
-generate_connection_params_fields(Lang, [], Number, Acc) ->
-    Field = generate_connection_params_field(Lang, "", "", -1, "", Number),
-    lists:reverse(Field ++ Acc);
-    
-generate_connection_params_fields(Lang, [ConnectionParams | ConnectionsParams], Number, Acc) ->
-    case ConnectionParams of
-       {Server, Encoding, Port, Password} ->
-           Field = generate_connection_params_field(Lang, Server, Encoding, Port, Password, Number),
-           generate_connection_params_fields(Lang, ConnectionsParams, Number + 1, Field ++ Acc);       
-       {Server, Encoding, Port} ->         
-           Field = generate_connection_params_field(Lang, Server, Encoding, Port, [], Number),
-           generate_connection_params_fields(Lang, ConnectionsParams, Number + 1, Field ++ Acc);
-       {Server, Encoding} ->   
-           Field = generate_connection_params_field(Lang, Server, Encoding, [], [], Number),
-           generate_connection_params_fields(Lang, ConnectionsParams, Number + 1, Field ++ Acc);
-       _ -> 
-           []
-    end.
-
-generate_connection_params_field(Lang, Server, Encoding, Port, Password, Number) ->
-    EncodingUsed = case Encoding of
-                      [] ->
-                          get_default_encoding(Server);
-                      _ ->
-                          Encoding
-                  end,
-    PortUsedInt = if
-                   Port >= 0 andalso Port =< 65535 ->
-                       Port;
-                   true ->
-                       ?DEFAULT_IRC_PORT
-              end,     
-    PortUsed = integer_to_list(PortUsedInt),
-    PasswordUsed = case Password of
-                   [] -> 
-                       "";
-                   _ -> 
-                       Password
-                  end,                 
-    NumberString = integer_to_list(Number),
-    %% Fields are in reverse order, as they will be reversed again later.
-    [{xmlelement, "field",
-      [{"var", "password" ++ NumberString},
-       {"type", "text-single"},
-       {"label", io_lib:format(translate:translate(Lang, "Password ~b"), [Number])}],
-      [{xmlelement, "value", [], [{xmlcdata, PasswordUsed}]}]},
-     {xmlelement, "field",
-      [{"var", "port" ++ NumberString},
-       {"type", "text-single"},
-       {"label", io_lib:format(translate:translate(Lang, "Port ~b"), [Number])}],
-      [{xmlelement, "value", [], [{xmlcdata, PortUsed}]}]},    
-     {xmlelement, "field",
-      [{"var", "encoding" ++ NumberString},
-       {"type", "list-single"},
-       {"label", io_lib:format(translate:translate(Lang, "Encoding for server ~b"), [Number])}],
-      [{xmlelement, "value", [], [{xmlcdata, EncodingUsed}]} |
-       lists:map(fun(E) ->
-                        {xmlelement, "option", [{"label", E}],
-                         [{xmlelement, "value", [], [{xmlcdata, E}]}]}
-                end, ?POSSIBLE_ENCODINGS)]},
-     {xmlelement, "field",
-      [{"var", "server" ++ NumberString},
-       {"type", "text-single"},
-       {"label", io_lib:format(translate:translate(Lang, "Server ~b"), [Number])}],
-      [{xmlelement, "value", [], [{xmlcdata, Server}]}]}].
-
-parse_connections_params(Fields) ->
-    %% Find all fields staring with serverN, encodingN, portN and passwordN for any values
-    %% of N, and generate lists of {"N", Value}.
-    Servers = lists:sort(
-               [{lists:nthtail(6, Var), lists:flatten(Value)} || {Var, Value} <- Fields,
-                                                                 lists:prefix("server", Var)]),
-    Encodings = lists:sort(
-                 [{lists:nthtail(8, Var), lists:flatten(Value)} || {Var, Value} <- Fields,
-                                                                   lists:prefix("encoding", Var)]),
-                                                                   
-    Ports = lists:sort(
-             [{lists:nthtail(4, Var), lists:flatten(Value)} || {Var, Value} <- Fields,
-                                                               lists:prefix("port", Var)]),
-                                                                 
-    Passwords = lists:sort(
-                 [{lists:nthtail(8, Var), lists:flatten(Value)} || {Var, Value} <- Fields,
-                                                                   lists:prefix("password", Var)]),
-    
-    %% Now sort the lists, and find the corresponding pairs.
-    parse_connections_params(Servers, Encodings, Ports, Passwords).
-    
-retrieve_connections_params(ConnectionParams, ServerN) ->
-    case ConnectionParams of
-        [{ConnectionParamN, ConnectionParam} | ConnectionParamsTail] ->            
-           if 
-               ServerN == ConnectionParamN ->
-                   {ConnectionParam, ConnectionParamsTail};            
-               ServerN < ConnectionParamN ->
-                   {[], [{ConnectionParamN, ConnectionParam} | ConnectionParamsTail]};
-               ServerN > ConnectionParamN ->    
-                   {[], ConnectionParamsTail}
-           end;
-           _ ->
-               {[], []}
-       end.
-       
-parse_connections_params([], _, _, _) ->
-    [];
-parse_connections_params(_, [], [], []) ->
-    [];        
-
-parse_connections_params([{ServerN, Server} | Servers], Encodings, Ports, Passwords) ->
-    %% Try to match matches of servers, ports, passwords and encodings, no matter what fields
-    %% the client might have left out.
-    {NewEncoding, NewEncodings} = retrieve_connections_params(Encodings, ServerN),
-    {NewPort, NewPorts} = retrieve_connections_params(Ports, ServerN),
-    {NewPassword, NewPasswords} = retrieve_connections_params(Passwords, ServerN),
-    [{Server, NewEncoding, NewPort, NewPassword} | parse_connections_params(Servers, NewEncodings, NewPorts, NewPasswords)].
index 31876d47cbcb0e0c38e7424b8720656665d82f5b..4cc0c5cc35c3ecc6f470bd08f36be22e869965f3 100644 (file)
 
 start(Host, Opts) ->
     IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
-    mnesia:create_table(last_activity,
-                       [{disc_copies, [node()]},
-                        {attributes, record_info(fields, last_activity)}]),
-    update_table(),
+    case gen_mod:db_type(Opts) of
+        mnesia ->
+            mnesia:create_table(last_activity,
+                                [{disc_copies, [node()]},
+                                 {attributes,
+                                  record_info(fields, last_activity)}]),
+            update_table();
+        _ ->
+            ok
+    end,
     gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_LAST,
                                  ?MODULE, process_local_iq, IQDisc),
     gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_LAST,
@@ -145,6 +151,9 @@ process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
 %% @spec (LUser::string(), LServer::string()) ->
 %%      {ok, TimeStamp::integer(), Status::string()} | not_found | {error, Reason}
 get_last(LUser, LServer) ->
+    get_last(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
+
+get_last(LUser, LServer, mnesia) ->
     case catch mnesia:dirty_read(last_activity, {LUser, LServer}) of
        {'EXIT', Reason} ->
            {error, Reason};
@@ -152,6 +161,21 @@ get_last(LUser, LServer) ->
            not_found;
        [#last_activity{timestamp = TimeStamp, status = Status}] ->
            {ok, TimeStamp, Status}
+    end;
+get_last(LUser, LServer, odbc) ->
+    Username = ejabberd_odbc:escape(LUser),
+    case catch odbc_queries:get_last(LServer, Username) of
+       {selected, ["seconds","state"], []} ->
+           not_found;
+       {selected, ["seconds","state"], [{STimeStamp, Status}]} ->
+           case catch list_to_integer(STimeStamp) of
+               TimeStamp when is_integer(TimeStamp) ->
+                   {ok, TimeStamp, Status};
+               Reason ->
+                   {error, {invalid_timestamp, Reason}}
+           end;
+       Reason ->
+           {error, {invalid_result, Reason}}
     end.
 
 get_last_iq(IQ, SubEl, LUser, LServer) ->
@@ -186,13 +210,22 @@ on_presence_update(User, Server, _Resource, Status) ->
 store_last_info(User, Server, TimeStamp, Status) ->
     LUser = jlib:nodeprep(User),
     LServer = jlib:nameprep(Server),
+    DBType = gen_mod:db_type(LServer, ?MODULE),
+    store_last_info(LUser, LServer, TimeStamp, Status, DBType).
+
+store_last_info(LUser, LServer, TimeStamp, Status, mnesia) ->
     US = {LUser, LServer},
     F = fun() ->
                mnesia:write(#last_activity{us = US,
                                            timestamp = TimeStamp,
                                            status = Status})
        end,
-    mnesia:transaction(F).
+    mnesia:transaction(F);
+store_last_info(LUser, LServer, TimeStamp, Status, odbc) ->
+    Username = ejabberd_odbc:escape(LUser),
+    Seconds = ejabberd_odbc:escape(integer_to_list(TimeStamp)),
+    State = ejabberd_odbc:escape(Status),
+    odbc_queries:set_last_t(LServer, Username, Seconds, State).
 
 %% @spec (LUser::string(), LServer::string()) ->
 %%      {ok, TimeStamp::integer(), Status::string()} | not_found
@@ -207,12 +240,18 @@ get_last_info(LUser, LServer) ->
 remove_user(User, Server) ->
     LUser = jlib:nodeprep(User),
     LServer = jlib:nameprep(Server),
+    DBType = gen_mod:db_type(LServer, ?MODULE),
+    remove_user(LUser, LServer, DBType).
+
+remove_user(LUser, LServer, mnesia) ->
     US = {LUser, LServer},
     F = fun() ->
                mnesia:delete({last_activity, US})
        end,
-    mnesia:transaction(F).
-
+    mnesia:transaction(F);
+remove_user(LUser, LServer, odbc) ->
+    Username = ejabberd_odbc:escape(LUser),
+    odbc_queries:del_last(LServer, Username).
 
 update_table() ->
     Fields = record_info(fields, last_activity),
diff --git a/src/mod_last_odbc.erl b/src/mod_last_odbc.erl
deleted file mode 100644 (file)
index 4466909..0000000
+++ /dev/null
@@ -1,204 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File    : mod_last_odbc.erl
-%%% Author  : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : jabber:iq:last support (XEP-0012)
-%%% Created : 24 Oct 2003 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2012   ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
--module(mod_last_odbc).
--author('alexey@process-one.net').
-
--behaviour(gen_mod).
-
--export([start/2,
-        stop/1,
-        process_local_iq/3,
-        process_sm_iq/3,
-        on_presence_update/4,
-        store_last_info/4,
-        get_last_info/2,
-        remove_user/2]).
-
--include("ejabberd.hrl").
--include("jlib.hrl").
--include("mod_privacy.hrl").
-
-start(Host, Opts) ->
-    IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
-    gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_LAST,
-                                 ?MODULE, process_local_iq, IQDisc),
-    gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_LAST,
-                                 ?MODULE, process_sm_iq, IQDisc),
-    ejabberd_hooks:add(remove_user, Host,
-                      ?MODULE, remove_user, 50),
-    ejabberd_hooks:add(unset_presence_hook, Host,
-                      ?MODULE, on_presence_update, 50).
-
-stop(Host) ->
-    ejabberd_hooks:delete(remove_user, Host,
-                         ?MODULE, remove_user, 50),
-    ejabberd_hooks:delete(unset_presence_hook, Host,
-                         ?MODULE, on_presence_update, 50),
-    gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_LAST),
-    gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_LAST).
-
-%%%
-%%% Uptime of ejabberd node
-%%%
-
-process_local_iq(_From, _To, #iq{type = Type, sub_el = SubEl} = IQ) ->
-    case Type of
-       set ->
-           IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
-       get ->
-           Sec = get_node_uptime(),
-           IQ#iq{type = result,
-                 sub_el =  [{xmlelement, "query",
-                             [{"xmlns", ?NS_LAST},
-                              {"seconds", integer_to_list(Sec)}],
-                             []}]}
-    end.
-
-%% @spec () -> integer()
-%% @doc Get the uptime of the ejabberd node, expressed in seconds.
-%% When ejabberd is starting, ejabberd_config:start/0 stores the datetime.
-get_node_uptime() ->
-    case ejabberd_config:get_local_option(node_start) of
-       {_, _, _} = StartNow ->
-           now_to_seconds(now()) - now_to_seconds(StartNow);
-       _undefined ->
-           trunc(element(1, erlang:statistics(wall_clock))/1000)
-    end.
-
-now_to_seconds({MegaSecs, Secs, _MicroSecs}) ->
-    MegaSecs * 1000000 + Secs.
-
-
-%%%
-%%% Serve queries about user last online
-%%%
-process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
-    case Type of
-       set ->
-           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, Server,
-                 {none, []}, [User, Server, From]),
-           if
-               (Subscription == both) or (Subscription == from) ->
-                   UserListRecord = ejabberd_hooks:run_fold(
-                                      privacy_get_user_list, Server,
-                                      #userlist{},
-                                      [User, Server]),
-                   case ejabberd_hooks:run_fold(
-                          privacy_check_packet, Server,
-                                           allow,
-                          [User, Server, UserListRecord,
-                           {To, From,
-                            {xmlelement, "presence", [], []}},
-                           out]) of
-                       allow ->
-                           get_last_iq(IQ, SubEl, User, Server);
-                       deny ->
-                           IQ#iq{type = error,
-                                 sub_el = [SubEl, ?ERR_FORBIDDEN]}
-                   end;
-               true ->
-                   IQ#iq{type = error,
-                         sub_el = [SubEl, ?ERR_FORBIDDEN]}
-           end
-    end.
-
-%% @spec (LUser::string(), LServer::string()) ->
-%%      {ok, TimeStamp::integer(), Status::string()} | not_found | {error, Reason}
-get_last(LUser, LServer) ->
-    Username = ejabberd_odbc:escape(LUser),
-    case catch odbc_queries:get_last(LServer, Username) of
-       {selected, ["seconds","state"], []} ->
-           not_found;
-       {selected, ["seconds","state"], [{STimeStamp, Status}]} ->
-           case catch list_to_integer(STimeStamp) of
-               TimeStamp when is_integer(TimeStamp) ->
-                   {ok, TimeStamp, Status};
-               Reason ->
-                   {error, {invalid_timestamp, Reason}}
-           end;
-       Reason ->
-           {error, {invalid_result, Reason}}
-    end.
-
-get_last_iq(IQ, SubEl, LUser, LServer) ->
-    case ejabberd_sm:get_user_resources(LUser, LServer) of
-       [] ->
-           case get_last(LUser, LServer) of
-               {error, _Reason} ->
-                   IQ#iq{type = error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]};
-               not_found ->
-                   IQ#iq{type = error, sub_el = [SubEl, ?ERR_SERVICE_UNAVAILABLE]};
-               {ok, TimeStamp, Status} ->
-                   TimeStamp2 = now_to_seconds(now()),
-                   Sec = TimeStamp2 - TimeStamp,
-                   IQ#iq{type = result,
-                         sub_el = [{xmlelement, "query",
-                                    [{"xmlns", ?NS_LAST},
-                                     {"seconds", integer_to_list(Sec)}],
-                                    [{xmlcdata, Status}]}]}
-           end;
-       _ ->
-           IQ#iq{type = result,
-                 sub_el = [{xmlelement, "query",
-                            [{"xmlns", ?NS_LAST},
-                             {"seconds", "0"}],
-                            []}]}
-    end.
-
-on_presence_update(User, Server, _Resource, Status) ->
-    TimeStamp = now_to_seconds(now()),
-    store_last_info(User, Server, TimeStamp, Status).
-
-store_last_info(User, Server, TimeStamp, Status) ->
-    LUser = jlib:nodeprep(User),
-    LServer = jlib:nameprep(Server),
-    Username = ejabberd_odbc:escape(LUser),
-    Seconds = ejabberd_odbc:escape(integer_to_list(TimeStamp)),
-    State = ejabberd_odbc:escape(Status),
-    odbc_queries:set_last_t(LServer, Username, Seconds, State).
-
-%% @spec (LUser::string(), LServer::string()) ->
-%%      {ok, TimeStamp::integer(), Status::string()} | not_found
-get_last_info(LUser, LServer) ->
-    case get_last(LUser, LServer) of
-       {error, _Reason} ->
-           not_found;
-       Res ->
-           Res
-    end.
-
-remove_user(User, Server) ->
-    LUser = jlib:nodeprep(User),
-    LServer = jlib:nameprep(Server),
-    Username = ejabberd_odbc:escape(LUser),
-    odbc_queries:del_last(LServer, Username).
index 27b81925ee47b587f87a3a85e9ff94fc07b4cd0c..5107b1069e153688028b496f5552a7bb0cddccc2 100644 (file)
@@ -4,7 +4,7 @@ include ..\Makefile.inc
 EFLAGS = -I .. -pz ..
 
 OUTDIR = ..
-BEAMS = ..\mod_muc.beam ..\mod_muc_odbc.beam ..\mod_muc_log.beam ..\mod_muc_room.beam
+BEAMS = ..\mod_muc.beam ..\mod_muc_log.beam ..\mod_muc_room.beam
 
 ALL : $(BEAMS)
 
@@ -14,9 +14,6 @@ CLEAN :
 $(OUTDIR)\mod_muc.beam : mod_muc.erl
        erlc -W $(EFLAGS) -o $(OUTDIR) mod_muc.erl
 
-$(OUTDIR)\mod_muc_odbc.beam : mod_muc_odbc.erl
-       erlc -W $(EFLAGS) -o $(OUTDIR) mod_muc_odbc.erl
-
 $(OUTDIR)\mod_muc_log.beam : mod_muc_log.erl
        erlc -W $(EFLAGS) -o $(OUTDIR) mod_muc_log.erl
 
index 81573d1a19eb3431ba8dc9f2184d21ffd551d69b..811e3c068672933dffa041d5de7c9b00ea8d0dc0 100644 (file)
@@ -111,26 +111,70 @@ create_room(Host, Name, From, Nick, Opts) ->
     Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
     gen_server:call(Proc, {create, Name, From, Nick, Opts}).
 
-store_room(_ServerHost, Host, Name, Opts) ->
+store_room(ServerHost, Host, Name, Opts) ->
+    LServer = jlib:nameprep(ServerHost),
+    store_room(LServer, Host, Name, Opts, gen_mod:db_type(LServer, ?MODULE)).
+
+store_room(_LServer, Host, Name, Opts, mnesia) ->
     F = fun() ->
                mnesia:write(#muc_room{name_host = {Name, Host},
                                       opts = Opts})
        end,
-    mnesia:transaction(F).
+    mnesia:transaction(F);
+store_room(LServer, Host, Name, Opts, odbc) ->
+    SName = ejabberd_odbc:escape(Name),
+    SHost = ejabberd_odbc:escape(Host),
+    SOpts = ejabberd_odbc:encode_term(Opts),
+    F = fun() ->
+                odbc_queries:update_t(
+                  "muc_room",
+                  ["name", "host", "opts"],
+                  [SName, SHost, SOpts],
+                  ["name='", SName, "' and host='", SHost, "'"])
+       end,
+    ejabberd_odbc:sql_transaction(LServer, F).
 
-restore_room(_ServerHost, Host, Name) ->
+restore_room(ServerHost, Host, Name) ->
+    LServer = jlib:nameprep(ServerHost),
+    restore_room(LServer, Host, Name, gen_mod:db_type(LServer, ?MODULE)).
+
+restore_room(_LServer, Host, Name, mnesia) ->
     case catch mnesia:dirty_read(muc_room, {Name, Host}) of
        [#muc_room{opts = Opts}] ->
            Opts;
        _ ->
            error
+    end;
+restore_room(LServer, Host, Name, odbc) ->
+    SName = ejabberd_odbc:escape(Name),
+    SHost = ejabberd_odbc:escape(Host),
+    case catch ejabberd_odbc:sql_query(
+                 LServer, ["select opts from muc_room where name='",
+                           SName, "' and host='", SHost, "';"]) of
+        {selected, ["opts"], [{Opts}]} ->
+            ejabberd_odbc:decode_term(Opts);
+        _ ->
+            error
     end.
 
-forget_room(_ServerHost, Host, Name) ->
+forget_room(ServerHost, Host, Name) ->
+    LServer = jlib:nameprep(ServerHost),
+    forget_room(LServer, Host, Name, gen_mod:db_type(LServer, ?MODULE)).
+
+forget_room(_LServer, Host, Name, mnesia) ->
     F = fun() ->
                mnesia:delete({muc_room, {Name, Host}})
        end,
-    mnesia:transaction(F).
+    mnesia:transaction(F);
+forget_room(LServer, Host, Name, odbc) ->
+    SName = ejabberd_odbc:escape(Name),
+    SHost = ejabberd_odbc:escape(Host),
+    F = fun() ->
+               ejabberd_odbc:sql_query_t(
+                  ["delete from muc_room where name='",
+                   SName, "' and host='", SHost, "';"])
+       end,
+    ejabberd_odbc:sql_transaction(LServer, F).
 
 process_iq_disco_items(Host, From, To, #iq{lang = Lang} = IQ) ->
     Rsm = jlib:rsm_decode(IQ),
@@ -144,7 +188,11 @@ process_iq_disco_items(Host, From, To, #iq{lang = Lang} = IQ) ->
 
 can_use_nick(_ServerHost, _Host, _JID, "") ->
     false;
-can_use_nick(_ServerHost, Host, JID, Nick) ->
+can_use_nick(ServerHost, Host, JID, Nick) ->
+    LServer = jlib:nameprep(ServerHost),
+    can_use_nick(LServer, Host, JID, Nick, gen_mod:db_type(LServer, ?MODULE)).
+
+can_use_nick(_LServer, Host, JID, Nick, mnesia) ->
     {LUser, LServer, _} = jlib:jid_tolower(JID),
     LUS = {LUser, LServer},
     case catch mnesia:dirty_select(
@@ -160,6 +208,21 @@ can_use_nick(_ServerHost, Host, JID, Nick) ->
            true;
        [#muc_registered{us_host = {U, _Host}}] ->
            U == LUS
+    end;
+can_use_nick(LServer, Host, JID, Nick, odbc) ->
+    SJID = jlib:jid_to_string(
+             jlib:jid_tolower(
+               jlib:jid_remove_resource(JID))),
+    SNick = ejabberd_odbc:escape(Nick),
+    SHost = ejabberd_odbc:escape(Host),
+    case catch ejabberd_odbc:sql_query(
+                 LServer, ["select jid from muc_registered ",
+                           "where nick='", SNick, "' and host='",
+                           SHost, "';"]) of
+        {selected, ["jid"], [{SJID1}]} ->
+            SJID == SJID1;
+        _ ->
+            true
     end.
 
 %%====================================================================
@@ -174,21 +237,28 @@ can_use_nick(_ServerHost, Host, JID, Nick) ->
 %% Description: Initiates the server
 %%--------------------------------------------------------------------
 init([Host, Opts]) ->
-    mnesia:create_table(muc_room,
-                       [{disc_copies, [node()]},
-                        {attributes, record_info(fields, muc_room)}]),
-    mnesia:create_table(muc_registered,
-                       [{disc_copies, [node()]},
-                        {attributes, record_info(fields, muc_registered)}]),
+    MyHost = gen_mod:get_opt_host(Host, Opts, "conference.@HOST@"),
+    case gen_mod:db_type(Opts) of
+        mnesia ->
+            update_tables(MyHost),
+            mnesia:create_table(muc_room,
+                                [{disc_copies, [node()]},
+                                 {attributes,
+                                  record_info(fields, muc_room)}]),
+            mnesia:create_table(muc_registered,
+                                [{disc_copies, [node()]},
+                                 {attributes,
+                                  record_info(fields, muc_registered)}]),
+            mnesia:add_table_index(muc_registered, nick);
+        _ ->
+            ok
+    end,
     mnesia:create_table(muc_online_room,
                        [{ram_copies, [node()]},
                         {attributes, record_info(fields, muc_online_room)}]),
     mnesia:add_table_copy(muc_online_room, node(), ram_copies),
     catch ets:new(muc_online_users, [bag, named_table, public, {keypos, 2}]),
-    MyHost = gen_mod:get_opt_host(Host, Opts, "conference.@HOST@"),
-    update_tables(MyHost),
     clean_table_from_bad_node(node(), MyHost),
-    mnesia:add_table_index(muc_registered, nick),
     mnesia:subscribe(system),
     Access = gen_mod:get_opt(access, Opts, all),
     AccessCreate = gen_mod:get_opt(access_create, Opts, all),
@@ -238,7 +308,7 @@ handle_call({create, Room, From, Nick, Opts},
                  Host, ServerHost, Access,
                  Room, HistorySize,
                  RoomShaper, From,
-                 Nick, NewOpts, ?MODULE),
+                 Nick, NewOpts),
     register_room(Host, Room, Pid),
     {reply, ok, State}.
 
@@ -379,7 +449,7 @@ do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
                                                [{xmlelement, "query",
                                                  [{"xmlns", XMLNS}],
                                                  iq_get_register_info(
-                                                   Host, From, Lang)}]},
+                                                   ServerHost, Host, From, Lang)}]},
                                    ejabberd_router:route(To,
                                                          From,
                                                          jlib:iq_to_xml(Res));
@@ -387,7 +457,8 @@ do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
                                    xmlns = ?NS_REGISTER = XMLNS,
                                    lang = Lang,
                                    sub_el = SubEl} = IQ ->
-                                   case process_iq_register_set(Host, From, SubEl, Lang) of
+                                   case process_iq_register_set(
+                                           ServerHost, Host, From, SubEl, Lang) of
                                        {result, IQRes} ->
                                            Res = IQ#iq{type = result,
                                                        sub_el =
@@ -519,52 +590,72 @@ check_user_can_create_room(ServerHost, AccessCreate, From, RoomID) ->
            false
     end.
 
+get_rooms(ServerHost, Host) ->
+    LServer = jlib:nameprep(ServerHost),
+    get_rooms(LServer, Host, gen_mod:db_type(LServer, ?MODULE)).
 
-load_permanent_rooms(Host, ServerHost, Access, HistorySize, RoomShaper) ->
+get_rooms(_LServer, Host, mnesia) ->
     case catch mnesia:dirty_select(
                 muc_room, [{#muc_room{name_host = {'_', Host}, _ = '_'},
                             [],
                             ['$_']}]) of
        {'EXIT', Reason} ->
            ?ERROR_MSG("~p", [Reason]),
-           ok;
+           [];
        Rs ->
-           lists:foreach(
-             fun(R) ->
-                     {Room, Host} = R#muc_room.name_host,
-                     case mnesia:dirty_read(muc_online_room, {Room, Host}) of
-                         [] ->
-                             {ok, Pid} = mod_muc_room:start(
-                                           Host,
-                                           ServerHost,
-                                           Access,
-                                           Room,
-                                           HistorySize,
-                                           RoomShaper,
-                                           R#muc_room.opts,
-                                            ?MODULE),
-                             register_room(Host, Room, Pid);
-                         _ ->
-                             ok
-                     end
-             end, Rs)
+            Rs
+    end;
+get_rooms(LServer, Host, odbc) ->
+    SHost = ejabberd_odbc:escape(Host),
+    case catch ejabberd_odbc:sql_query(
+                 LServer, ["select name, opts from muc_room ",
+                           "where host='", SHost, "';"]) of
+        {'EXIT', Reason} ->
+           ?ERROR_MSG("~p", [Reason]),
+            [];
+        {selected, ["name", "opts"], RoomOpts} ->
+            lists:map(
+              fun({Room, Opts}) ->
+                      #muc_room{name_host = {Room, Host},
+                                opts = ejabberd_odbc:decode_term(Opts)}
+              end, RoomOpts)
     end.
 
+load_permanent_rooms(Host, ServerHost, Access, HistorySize, RoomShaper) ->
+    lists:foreach(
+      fun(R) ->
+              {Room, Host} = R#muc_room.name_host,
+              case mnesia:dirty_read(muc_online_room, {Room, Host}) of
+                  [] ->
+                      {ok, Pid} = mod_muc_room:start(
+                                    Host,
+                                    ServerHost,
+                                    Access,
+                                    Room,
+                                    HistorySize,
+                                    RoomShaper,
+                                    R#muc_room.opts),
+                      register_room(Host, Room, Pid);
+                  _ ->
+                      ok
+              end
+      end, get_rooms(ServerHost, Host)).
+
 start_new_room(Host, ServerHost, Access, Room,
               HistorySize, RoomShaper, From,
               Nick, DefRoomOpts) ->
-    case mnesia:dirty_read(muc_room, {Room, Host}) of
-       [] ->
+    case restore_room(ServerHost, Room, Host) of
+        error ->
            ?DEBUG("MUC: open new room '~s'~n", [Room]),
            mod_muc_room:start(Host, ServerHost, Access,
                               Room, HistorySize,
                               RoomShaper, From,
-                              Nick, DefRoomOpts, ?MODULE);
-       [#muc_room{opts = Opts}|_] ->
+                              Nick, DefRoomOpts);
+        Opts ->
            ?DEBUG("MUC: restore room '~s'~n", [Room]),
            mod_muc_room:start(Host, ServerHost, Access,
                               Room, HistorySize,
-                              RoomShaper, Opts, ?MODULE)
+                              RoomShaper, Opts)
     end.
 
 register_room(Host, Room, Pid) ->
@@ -693,18 +784,44 @@ flush() ->
 iq_get_unique(From) ->
        {xmlcdata, sha:sha(term_to_binary([From, now(), randoms:get_string()]))}.
 
-iq_get_register_info(Host, From, Lang) ->
+get_nick(ServerHost, Host, From) ->
+    LServer = jlib:nameprep(ServerHost),
+    get_nick(LServer, Host, From, gen_mod:db_type(LServer, ?MODULE)).
+
+get_nick(_LServer, Host, From, mnesia) ->
     {LUser, LServer, _} = jlib:jid_tolower(From),
     LUS = {LUser, LServer},
+    case catch mnesia:dirty_read(muc_registered, {LUS, Host}) of
+        {'EXIT', _Reason} ->
+            error;
+        [] ->
+            error;
+        [#muc_registered{nick = Nick}] ->
+            Nick
+    end;
+get_nick(LServer, Host, From, odbc) ->
+    SJID = ejabberd_odbc:escape(
+             jlib:jid_to_string(
+               jlib:jid_tolower(
+                 jlib:jid_remove_resource(From)))),
+    SHost = ejabberd_odbc:escape(Host),
+    case catch ejabberd_odbc:sql_query(
+                 LServer, ["select nick from muc_registered where "
+                           "jid='", SJID, "' and host='", SHost, "';"]) of
+        {selected, ["nick"], [{Nick}]} ->
+            Nick;
+        _ ->
+            error
+    end.
+
+iq_get_register_info(ServerHost, Host, From, Lang) ->
     {Nick, Registered} =
-       case catch mnesia:dirty_read(muc_registered, {LUS, Host}) of
-           {'EXIT', _Reason} ->
-               {"", []};
-           [] ->
-               {"", []};
-           [#muc_registered{nick = N}] ->
-               {N, [{xmlelement, "registered", [], []}]}
-       end,
+        case get_nick(ServerHost, Host, From) of
+            error ->
+                {"", []};
+            N ->
+                {N, [{xmlelement, "registered", [], []}]}
+        end,
     Registered ++
        [{xmlelement, "instructions", [],
          [{xmlcdata,
@@ -722,7 +839,11 @@ iq_get_register_info(Host, From, Lang) ->
                Lang, "Enter nickname you want to register")}]},
           ?XFIELD("text-single", "Nickname", "nick", Nick)]}].
 
-iq_set_register_info(Host, From, Nick, Lang) ->
+set_nick(ServerHost, Host, From, Nick) ->
+    LServer = jlib:nameprep(ServerHost),
+    set_nick(LServer, Host, From, Nick, gen_mod:db_type(LServer, ?MODULE)).
+
+set_nick(_LServer, Host, From, Nick, mnesia) ->
     {LUser, LServer, _} = jlib:jid_tolower(From),
     LUS = {LUser, LServer},
     F = fun() ->
@@ -755,7 +876,48 @@ iq_set_register_info(Host, From, Nick, Lang) ->
                        end
                end
        end,
-    case mnesia:transaction(F) of
+    mnesia:transaction(F);
+set_nick(LServer, Host, From, Nick, odbc) ->
+    JID = jlib:jid_to_string(
+            jlib:jid_tolower(
+              jlib:jid_remove_resource(From))),
+    SJID = ejabberd_odbc:escape(JID),
+    SNick = ejabberd_odbc:escape(Nick),
+    SHost = ejabberd_odbc:escape(Host),
+    F = fun() ->
+                case Nick of
+                    "" ->
+                        ejabberd_odbc:sql_query_t(
+                          ["delete from muc_registered where ",
+                           "jid='", SJID, "' and host='", Host, "';"]),
+                        ok;
+                    _ ->
+                        Allow =
+                            case ejabberd_odbc:sql_query_t(
+                                   ["select jid from muc_registered ",
+                                    "where nick='", SNick, "' and host='",
+                                    SHost, "';"]) of
+                                {selected, ["jid"], [{J}]} ->
+                                    J == JID;
+                                _ ->
+                                    true
+                            end,
+                        if Allow ->
+                                odbc_queries:update_t(
+                                  "muc_registered",
+                                  ["jid", "host", "nick"],
+                                  [SJID, SHost, SNick],
+                                  ["jid='", SJID, "' and host='", SHost, "'"]),
+                                ok;
+                           true ->
+                                false
+                        end
+                end
+        end,
+    ejabberd_odbc:sql_transaction(LServer, F).
+
+iq_set_register_info(ServerHost, Host, From, Nick, Lang) ->
+    case set_nick(ServerHost, Host, From, Nick) of
        {atomic, ok} ->
            {result, []};
        {atomic, false} ->
@@ -765,7 +927,7 @@ iq_set_register_info(Host, From, Nick, Lang) ->
            {error, ?ERR_INTERNAL_SERVER_ERROR}
     end.
 
-process_iq_register_set(Host, From, SubEl, Lang) ->
+process_iq_register_set(ServerHost, Host, From, SubEl, Lang) ->
     {xmlelement, _Name, _Attrs, Els} = SubEl,
     case xml:get_subtag(SubEl, "remove") of
        false ->
@@ -783,7 +945,8 @@ process_iq_register_set(Host, From, SubEl, Lang) ->
                                _ ->
                                    case lists:keysearch("nick", 1, XData) of
                                        {value, {_, [Nick]}} when Nick /= "" ->
-                                           iq_set_register_info(Host, From, Nick, Lang);
+                                           iq_set_register_info(ServerHost, Host,
+                                                                 From, Nick, Lang);
                                        _ ->
                                            ErrText = "You must fill in field \"Nickname\" in the form",
                                            {error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)}
@@ -796,7 +959,7 @@ process_iq_register_set(Host, From, SubEl, Lang) ->
                    {error, ?ERR_BAD_REQUEST}
            end;
        _ ->
-           iq_set_register_info(Host, From, "", Lang)
+           iq_set_register_info(ServerHost, Host, From, "", Lang)
     end.
 
 iq_get_vcard(Lang) ->
diff --git a/src/mod_muc/mod_muc_odbc.erl b/src/mod_muc/mod_muc_odbc.erl
deleted file mode 100644 (file)
index 47dc4c9..0000000
+++ /dev/null
@@ -1,875 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File    : mod_muc_odbc.erl
-%%% Author  : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : MUC support (XEP-0045)
-%%% Created : 19 Mar 2003 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2012   ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
--module(mod_muc_odbc).
--author('alexey@process-one.net').
-
--behaviour(gen_server).
--behaviour(gen_mod).
-
-%% API
--export([start_link/2,
-        start/2,
-        stop/1,
-        room_destroyed/4,
-        store_room/4,
-        restore_room/3,
-        forget_room/3,
-        create_room/5,
-        process_iq_disco_items/4,
-        broadcast_service_message/2,
-        can_use_nick/4]).
-
-%% gen_server callbacks
--export([init/1, handle_call/3, handle_cast/2, handle_info/2,
-        terminate/2, code_change/3]).
-
--include("ejabberd.hrl").
--include("jlib.hrl").
-
-
--record(muc_online_room, {name_host, pid}).
-
--record(state, {host,
-               server_host,
-               access,
-               history_size,
-               default_room_opts,
-               room_shaper}).
-
--define(PROCNAME, ejabberd_mod_muc).
-
-%%====================================================================
-%% API
-%%====================================================================
-%%--------------------------------------------------------------------
-%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
-%% Description: Starts the server
-%%--------------------------------------------------------------------
-start_link(Host, Opts) ->
-    Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
-    gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
-
-start(Host, Opts) ->
-    start_supervisor(Host),
-    Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
-    ChildSpec =
-       {Proc,
-        {?MODULE, start_link, [Host, Opts]},
-        temporary,
-        1000,
-        worker,
-        [?MODULE]},
-    supervisor:start_child(ejabberd_sup, ChildSpec).
-
-stop(Host) ->
-    stop_supervisor(Host),
-    Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
-    gen_server:call(Proc, stop),
-    supervisor:delete_child(ejabberd_sup, Proc).
-
-%% This function is called by a room in three situations:
-%% A) The owner of the room destroyed it
-%% B) The only participant of a temporary room leaves it
-%% C) mod_muc_odbc:stop was called, and each room is being terminated
-%%    In this case, the mod_muc_odbc process died before the room processes
-%%    So the message sending must be catched
-room_destroyed(Host, Room, Pid, ServerHost) ->
-    catch gen_mod:get_module_proc(ServerHost, ?PROCNAME) !
-       {room_destroyed, {Room, Host}, Pid},
-    ok.
-
-%% @doc Create a room.
-%% If Opts = default, the default room options are used.
-%% Else use the passed options as defined in mod_muc_room.
-create_room(Host, Name, From, Nick, Opts) ->
-    Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
-    gen_server:call(Proc, {create, Name, From, Nick, Opts}).
-
-store_room(ServerHost, Host, Name, Opts) ->
-    SName = ejabberd_odbc:escape(Name),
-    SHost = ejabberd_odbc:escape(Host),
-    LServer = jlib:nameprep(ServerHost),
-    SOpts = ejabberd_odbc:encode_term(Opts),
-    F = fun() ->
-                odbc_queries:update_t(
-                  "muc_room",
-                  ["name", "host", "opts"],
-                  [SName, SHost, SOpts],
-                  ["name='", SName, "' and host='", SHost, "'"])
-       end,
-    ejabberd_odbc:sql_transaction(LServer, F).
-
-restore_room(ServerHost, Host, Name) ->
-    SName = ejabberd_odbc:escape(Name),
-    SHost = ejabberd_odbc:escape(Host),
-    LServer = jlib:nameprep(ServerHost),
-    case catch ejabberd_odbc:sql_query(
-                 LServer, ["select opts from muc_room where name='",
-                           SName, "' and host='", SHost, "';"]) of
-        {selected, ["opts"], [{Opts}]} ->
-            ejabberd_odbc:decode_term(Opts);
-        _ ->
-            error
-    end.
-
-forget_room(ServerHost, Host, Name) ->
-    SName = ejabberd_odbc:escape(Name),
-    SHost = ejabberd_odbc:escape(Host),
-    LServer = jlib:nameprep(ServerHost),
-    F = fun() ->
-               ejabberd_odbc:sql_query_t(
-                  ["delete from muc_room where name='",
-                   SName, "' and host='", SHost, "';"])
-       end,
-    ejabberd_odbc:sql_transaction(LServer, F).
-
-process_iq_disco_items(Host, From, To, #iq{lang = Lang} = IQ) ->
-    Rsm = jlib:rsm_decode(IQ),
-    Res = IQ#iq{type = result,
-               sub_el = [{xmlelement, "query",
-                          [{"xmlns", ?NS_DISCO_ITEMS}],
-                          iq_disco_items(Host, From, Lang, Rsm)}]},
-    ejabberd_router:route(To,
-                         From,
-                         jlib:iq_to_xml(Res)).
-
-can_use_nick(_ServerHost, _Host, _JID, "") ->
-    false;
-can_use_nick(ServerHost, Host, JID, Nick) ->
-    SJID = jlib:jid_to_string(
-             jlib:jid_tolower(
-               jlib:jid_remove_resource(JID))),
-    SNick = ejabberd_odbc:escape(Nick),
-    SHost = ejabberd_odbc:escape(Host),
-    LServer = jlib:nameprep(ServerHost),
-    case catch ejabberd_odbc:sql_query(
-                 LServer, ["select jid from muc_registered ",
-                           "where nick='", SNick, "' and host='",
-                           SHost, "';"]) of
-        {selected, ["jid"], [{SJID1}]} ->
-            SJID == SJID1;
-        _ ->
-            true
-    end.
-
-%%====================================================================
-%% gen_server callbacks
-%%====================================================================
-
-%%--------------------------------------------------------------------
-%% Function: init(Args) -> {ok, State} |
-%%                         {ok, State, Timeout} |
-%%                         ignore               |
-%%                         {stop, Reason}
-%% Description: Initiates the server
-%%--------------------------------------------------------------------
-init([Host, Opts]) ->
-    mnesia:create_table(muc_online_room,
-                       [{ram_copies, [node()]},
-                        {attributes, record_info(fields, muc_online_room)}]),
-    mnesia:add_table_copy(muc_online_room, node(), ram_copies),
-    catch ets:new(muc_online_users, [bag, named_table, public, {keypos, 2}]),
-    MyHost = gen_mod:get_opt_host(Host, Opts, "conference.@HOST@"),
-    clean_table_from_bad_node(node(), MyHost),
-    mnesia:subscribe(system),
-    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),
-    AccessPersistent = gen_mod:get_opt(access_persistent, Opts, all),
-    HistorySize = gen_mod:get_opt(history_size, Opts, 20),
-    DefRoomOpts = gen_mod:get_opt(default_room_options, Opts, []),
-    RoomShaper = gen_mod:get_opt(room_shaper, Opts, none),
-    ejabberd_router:register_route(MyHost),
-    load_permanent_rooms(MyHost, Host,
-                        {Access, AccessCreate, AccessAdmin, AccessPersistent},
-                        HistorySize,
-                        RoomShaper),
-    {ok, #state{host = MyHost,
-               server_host = Host,
-               access = {Access, AccessCreate, AccessAdmin, AccessPersistent},
-               default_room_opts = DefRoomOpts,
-               history_size = HistorySize,
-               room_shaper = RoomShaper}}.
-
-%%--------------------------------------------------------------------
-%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
-%%                                      {reply, Reply, State, Timeout} |
-%%                                      {noreply, State} |
-%%                                      {noreply, State, Timeout} |
-%%                                      {stop, Reason, Reply, State} |
-%%                                      {stop, Reason, State}
-%% Description: Handling call messages
-%%--------------------------------------------------------------------
-handle_call(stop, _From, State) ->
-    {stop, normal, ok, State};
-
-handle_call({create, Room, From, Nick, Opts},
-           _From,
-           #state{host = Host,
-                  server_host = ServerHost,
-                  access = Access,
-                  default_room_opts = DefOpts,
-                  history_size = HistorySize,
-                  room_shaper = RoomShaper} = State) ->
-    ?DEBUG("MUC: create new room '~s'~n", [Room]),
-    NewOpts = case Opts of
-                 default -> DefOpts;
-                 _ -> Opts
-             end,
-    {ok, Pid} = mod_muc_room:start(
-                 Host, ServerHost, Access,
-                 Room, HistorySize,
-                 RoomShaper, From,
-                 Nick, NewOpts, ?MODULE),
-    register_room(Host, Room, Pid),
-    {reply, ok, State}.
-
-%%--------------------------------------------------------------------
-%% Function: handle_cast(Msg, State) -> {noreply, State} |
-%%                                      {noreply, State, Timeout} |
-%%                                      {stop, Reason, State}
-%% Description: Handling cast messages
-%%--------------------------------------------------------------------
-handle_cast(_Msg, State) ->
-    {noreply, State}.
-
-%%--------------------------------------------------------------------
-%% Function: handle_info(Info, State) -> {noreply, State} |
-%%                                       {noreply, State, Timeout} |
-%%                                       {stop, Reason, State}
-%% Description: Handling all non call/cast messages
-%%--------------------------------------------------------------------
-handle_info({route, From, To, Packet},
-           #state{host = Host,
-                  server_host = ServerHost,
-                  access = Access,
-                  default_room_opts = DefRoomOpts,
-                  history_size = HistorySize,
-                  room_shaper = RoomShaper} = State) ->
-    case catch do_route(Host, ServerHost, Access, HistorySize, RoomShaper,
-                       From, To, Packet, DefRoomOpts) of
-       {'EXIT', Reason} ->
-           ?ERROR_MSG("~p", [Reason]);
-       _ ->
-           ok
-    end,
-    {noreply, State};
-handle_info({room_destroyed, RoomHost, Pid}, State) ->
-    F = fun() ->
-               mnesia:delete_object(#muc_online_room{name_host = RoomHost,
-                                                     pid = Pid})
-       end,
-    mnesia:transaction(F),
-    {noreply, State};
-handle_info({mnesia_system_event, {mnesia_down, Node}}, State) ->
-    clean_table_from_bad_node(Node),
-    {noreply, State};
-handle_info(_Info, State) ->
-    {noreply, State}.
-
-%%--------------------------------------------------------------------
-%% Function: terminate(Reason, State) -> void()
-%% Description: This function is called by a gen_server when it is about to
-%% terminate. It should be the opposite of Module:init/1 and do any necessary
-%% cleaning up. When it returns, the gen_server terminates with Reason.
-%% The return value is ignored.
-%%--------------------------------------------------------------------
-terminate(_Reason, State) ->
-    ejabberd_router:unregister_route(State#state.host),
-    ok.
-
-%%--------------------------------------------------------------------
-%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
-%% Description: Convert process state when code is changed
-%%--------------------------------------------------------------------
-code_change(_OldVsn, State, _Extra) ->
-    {ok, State}.
-
-%%--------------------------------------------------------------------
-%%% Internal functions
-%%--------------------------------------------------------------------
-start_supervisor(Host) ->
-    Proc = gen_mod:get_module_proc(Host, ejabberd_mod_muc_sup),
-    ChildSpec =
-       {Proc,
-        {ejabberd_tmp_sup, start_link,
-         [Proc, mod_muc_room]},
-        permanent,
-        infinity,
-        supervisor,
-        [ejabberd_tmp_sup]},
-    supervisor:start_child(ejabberd_sup, ChildSpec).
-
-stop_supervisor(Host) ->
-    Proc = gen_mod:get_module_proc(Host, ejabberd_mod_muc_sup),
-    supervisor:terminate_child(ejabberd_sup, Proc),
-    supervisor:delete_child(ejabberd_sup, Proc).
-
-do_route(Host, ServerHost, Access, HistorySize, RoomShaper,
-        From, To, Packet, DefRoomOpts) ->
-    {AccessRoute, _AccessCreate, _AccessAdmin, _AccessPersistent} = Access,
-    case acl:match_rule(ServerHost, AccessRoute, From) of
-       allow ->
-           do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
-                     From, To, Packet, DefRoomOpts);
-       _ ->
-           {xmlelement, _Name, Attrs, _Els} = Packet,
-           Lang = xml:get_attr_s("xml:lang", Attrs),
-           ErrText = "Access denied by service policy",
-           Err = jlib:make_error_reply(Packet,
-                                       ?ERRT_FORBIDDEN(Lang, ErrText)),
-           ejabberd_router:route_error(To, From, Err, Packet)
-    end.
-
-
-do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
-         From, To, Packet, DefRoomOpts) ->
-    {_AccessRoute, AccessCreate, AccessAdmin, _AccessPersistent} = Access,
-    {Room, _, Nick} = jlib:jid_tolower(To),
-    {xmlelement, Name, Attrs, _Els} = Packet,
-    case Room of
-       "" ->
-           case Nick of
-               "" ->
-                   case Name of
-                       "iq" ->
-                           case jlib:iq_query_info(Packet) of
-                               #iq{type = get, xmlns = ?NS_DISCO_INFO = XMLNS,
-                                   sub_el = _SubEl, lang = Lang} = IQ ->
-                                   Info = ejabberd_hooks:run_fold(
-                                            disco_info, ServerHost, [],
-                                            [ServerHost, ?MODULE, "", ""]),
-                                   Res = IQ#iq{type = result,
-                                               sub_el = [{xmlelement, "query",
-                                                          [{"xmlns", XMLNS}],
-                                                          iq_disco_info(Lang)
-                                                          ++Info}]},
-                                   ejabberd_router:route(To,
-                                                         From,
-                                                         jlib:iq_to_xml(Res));
-                               #iq{type = get,
-                                   xmlns = ?NS_DISCO_ITEMS} = IQ ->
-                                   spawn(?MODULE,
-                                         process_iq_disco_items,
-                                         [Host, From, To, IQ]);
-                               #iq{type = get,
-                                   xmlns = ?NS_REGISTER = XMLNS,
-                                   lang = Lang,
-                                   sub_el = _SubEl} = IQ ->
-                                   Res = IQ#iq{type = result,
-                                               sub_el =
-                                               [{xmlelement, "query",
-                                                 [{"xmlns", XMLNS}],
-                                                 iq_get_register_info(
-                                                   ServerHost, Host, From, Lang)}]},
-                                   ejabberd_router:route(To,
-                                                         From,
-                                                         jlib:iq_to_xml(Res));
-                               #iq{type = set,
-                                   xmlns = ?NS_REGISTER = XMLNS,
-                                   lang = Lang,
-                                   sub_el = SubEl} = IQ ->
-                                   case process_iq_register_set(
-                                           ServerHost, Host, From, SubEl, Lang) of
-                                       {result, IQRes} ->
-                                           Res = IQ#iq{type = result,
-                                                       sub_el =
-                                                       [{xmlelement, "query",
-                                                         [{"xmlns", XMLNS}],
-                                                         IQRes}]},
-                                           ejabberd_router:route(
-                                             To, From, jlib:iq_to_xml(Res));
-                                       {error, Error} ->
-                                           Err = jlib:make_error_reply(
-                                                   Packet, Error),
-                                           ejabberd_router:route(
-                                             To, From, Err)
-                                   end;
-                               #iq{type = get,
-                                   xmlns = ?NS_VCARD = XMLNS,
-                                   lang = Lang,
-                                   sub_el = _SubEl} = IQ ->
-                                   Res = IQ#iq{type = result,
-                                               sub_el =
-                                               [{xmlelement, "vCard",
-                                                 [{"xmlns", XMLNS}],
-                                                 iq_get_vcard(Lang)}]},
-                                   ejabberd_router:route(To,
-                                                         From,
-                                                         jlib:iq_to_xml(Res));
-                               #iq{type = get,
-                                  xmlns = ?NS_MUC_UNIQUE
-                                  } = IQ ->
-                                  Res = IQ#iq{type = result,
-                                               sub_el =
-                                               [{xmlelement, "unique",
-                                                  [{"xmlns", ?NS_MUC_UNIQUE}],
-                                                  [iq_get_unique(From)]}]},
-                                  ejabberd_router:route(To,
-                                                        From,
-                                                        jlib:iq_to_xml(Res));
-                               #iq{} ->
-                                   Err = jlib:make_error_reply(
-                                           Packet,
-                                           ?ERR_FEATURE_NOT_IMPLEMENTED),
-                                   ejabberd_router:route(To, From, Err);
-                               _ ->
-                                   ok
-                           end;
-                       "message" ->
-                           case xml:get_attr_s("type", Attrs) of
-                               "error" ->
-                                   ok;
-                               _ ->
-                                   case acl:match_rule(ServerHost, AccessAdmin, From) of
-                                       allow ->
-                                           Msg = xml:get_path_s(
-                                                   Packet,
-                                                   [{elem, "body"}, cdata]),
-                                           broadcast_service_message(Host, Msg);
-                                       _ ->
-                                           Lang = xml:get_attr_s("xml:lang", Attrs),
-                                           ErrText = "Only service administrators "
-                                                     "are allowed to send service messages",
-                                           Err = jlib:make_error_reply(
-                                                   Packet,
-                                                   ?ERRT_FORBIDDEN(Lang, ErrText)),
-                                           ejabberd_router:route(
-                                             To, From, Err)
-                                   end
-                           end;
-                       "presence" ->
-                           ok
-                   end;
-               _ ->
-                   case xml:get_attr_s("type", Attrs) of
-                       "error" ->
-                           ok;
-                       "result" ->
-                           ok;
-                       _ ->
-                           Err = jlib:make_error_reply(
-                                   Packet, ?ERR_ITEM_NOT_FOUND),
-                           ejabberd_router:route(To, From, Err)
-                   end
-           end;
-       _ ->
-           case mnesia:dirty_read(muc_online_room, {Room, Host}) of
-               [] ->
-                   Type = xml:get_attr_s("type", Attrs),
-                   case {Name, Type} of
-                       {"presence", ""} ->
-                           case check_user_can_create_room(ServerHost,
-                                                           AccessCreate, From,
-                                                           Room) of
-                               true ->
-                                   {ok, Pid} = start_new_room(
-                                                 Host, ServerHost, Access,
-                                                 Room, HistorySize,
-                                                 RoomShaper, From,
-                                                 Nick, DefRoomOpts),
-                                   register_room(Host, Room, Pid),
-                                   mod_muc_room:route(Pid, From, Nick, Packet),
-                                   ok;
-                               false ->
-                                   Lang = xml:get_attr_s("xml:lang", Attrs),
-                                   ErrText = "Room creation is denied by service policy",
-                                   Err = jlib:make_error_reply(
-                                           Packet, ?ERRT_FORBIDDEN(Lang, ErrText)),
-                                   ejabberd_router:route(To, From, Err)
-                           end;
-                       _ ->
-                           Lang = xml:get_attr_s("xml:lang", Attrs),
-                           ErrText = "Conference room does not exist",
-                           Err = jlib:make_error_reply(
-                                   Packet, ?ERRT_ITEM_NOT_FOUND(Lang, ErrText)),
-                           ejabberd_router:route(To, From, Err)
-                   end;
-               [R] ->
-                   Pid = R#muc_online_room.pid,
-                   ?DEBUG("MUC: send to process ~p~n", [Pid]),
-                   mod_muc_room:route(Pid, From, Nick, Packet),
-                   ok
-           end
-    end.
-
-check_user_can_create_room(ServerHost, AccessCreate, From, RoomID) ->
-    case acl:match_rule(ServerHost, AccessCreate, From) of
-       allow ->
-           (length(RoomID) =< gen_mod:get_module_opt(ServerHost, ?MODULE,
-                                                     max_room_id, infinite));
-       _ ->
-           false
-    end.
-
-
-load_permanent_rooms(Host, ServerHost, Access, HistorySize, RoomShaper) ->
-    SHost = ejabberd_odbc:escape(Host),
-    LServer = jlib:nameprep(ServerHost),
-    case catch ejabberd_odbc:sql_query(
-                 LServer, ["select name, opts from muc_room ",
-                           "where host='", SHost, "';"]) of
-        {'EXIT', Reason} ->
-           ?ERROR_MSG("~p", [Reason]),
-           ok;
-        {selected, ["name", "opts"], RoomOpts} ->
-            lists:foreach(
-              fun({Room, Opts}) ->
-                      case mnesia:dirty_read(muc_online_room, {Room, Host}) of
-                         [] ->
-                             {ok, Pid} = mod_muc_room:start(
-                                           Host,
-                                           ServerHost,
-                                           Access,
-                                           Room,
-                                           HistorySize,
-                                           RoomShaper,
-                                           ejabberd_odbc:decode_term(Opts),
-                                            ?MODULE),
-                             register_room(Host, Room, Pid);
-                         _ ->
-                             ok
-                     end
-              end, RoomOpts)
-    end.
-
-start_new_room(Host, ServerHost, Access, Room,
-              HistorySize, RoomShaper, From,
-              Nick, DefRoomOpts) ->
-    SHost = ejabberd_odbc:escape(Host),
-    LServer = jlib:nameprep(ServerHost),
-    SRoom = ejabberd_odbc:escape(Room),
-    case ejabberd_odbc:sql_query(
-           LServer, ["select opts from muc_room where name='", SRoom,
-                     "' and host='", SHost, "';"]) of
-       {selected, ["opts"], []} ->
-           ?DEBUG("MUC: open new room '~s'~n", [Room]),
-           mod_muc_room:start(Host, ServerHost, Access,
-                              Room, HistorySize,
-                              RoomShaper, From,
-                              Nick, DefRoomOpts, ?MODULE);
-        {selected, ["opts"], [{Opts}|_]} ->
-           ?DEBUG("MUC: restore room '~s'~n", [Room]),
-           mod_muc_room:start(Host, ServerHost, Access,
-                              Room, HistorySize,
-                              RoomShaper, ejabberd_odbc:decode_term(Opts),
-                               ?MODULE)
-    end.
-
-register_room(Host, Room, Pid) ->
-    F = fun() ->
-               mnesia:write(#muc_online_room{name_host = {Room, Host},
-                                             pid = Pid})
-       end,
-    mnesia:transaction(F).
-
-
-iq_disco_info(Lang) ->
-    [{xmlelement, "identity",
-      [{"category", "conference"},
-       {"type", "text"},
-       {"name", translate:translate(Lang, "Chatrooms")}], []},
-     {xmlelement, "feature", [{"var", ?NS_DISCO_INFO}], []},
-     {xmlelement, "feature", [{"var", ?NS_DISCO_ITEMS}], []},
-     {xmlelement, "feature", [{"var", ?NS_MUC}], []},
-     {xmlelement, "feature", [{"var", ?NS_MUC_UNIQUE}], []},
-     {xmlelement, "feature", [{"var", ?NS_REGISTER}], []},
-     {xmlelement, "feature", [{"var", ?NS_RSM}], []},
-     {xmlelement, "feature", [{"var", ?NS_VCARD}], []}].
-
-
-iq_disco_items(Host, From, Lang, none) ->
-    lists:zf(fun(#muc_online_room{name_host = {Name, _Host}, pid = Pid}) ->
-                    case catch gen_fsm:sync_send_all_state_event(
-                                 Pid, {get_disco_item, From, Lang}, 100) of
-                        {item, Desc} ->
-                            flush(),
-                            {true,
-                             {xmlelement, "item",
-                              [{"jid", jlib:jid_to_string({Name, Host, ""})},
-                               {"name", Desc}], []}};
-                        _ ->
-                            false
-                    end
-            end, get_vh_rooms(Host));
-
-iq_disco_items(Host, From, Lang, Rsm) ->
-    {Rooms, RsmO} = get_vh_rooms(Host, Rsm),
-    RsmOut = jlib:rsm_encode(RsmO),
-    lists:zf(fun(#muc_online_room{name_host = {Name, _Host}, pid = Pid}) ->
-                    case catch gen_fsm:sync_send_all_state_event(
-                                 Pid, {get_disco_item, From, Lang}, 100) of
-                        {item, Desc} ->
-                            flush(),
-                            {true,
-                             {xmlelement, "item",
-                              [{"jid", jlib:jid_to_string({Name, Host, ""})},
-                               {"name", Desc}], []}};
-                        _ ->
-                            false
-                    end
-            end, Rooms) ++ RsmOut.
-
-get_vh_rooms(Host, #rsm_in{max=M, direction=Direction, id=I, index=Index})->
-    AllRooms = lists:sort(get_vh_rooms(Host)),
-    Count = erlang:length(AllRooms),
-    Guard = case Direction of
-               _ when Index =/= undefined -> [{'==', {element, 2, '$1'}, Host}];
-               aft -> [{'==', {element, 2, '$1'}, Host}, {'>=',{element, 1, '$1'} ,I}];
-               before when I =/= []-> [{'==', {element, 2, '$1'}, Host}, {'=<',{element, 1, '$1'} ,I}];
-               _ -> [{'==', {element, 2, '$1'}, Host}]
-           end,
-    L = lists:sort(
-         mnesia:dirty_select(muc_online_room,
-                             [{#muc_online_room{name_host = '$1', _ = '_'},
-                               Guard,
-                               ['$_']}])),
-    L2 = if
-            Index == undefined andalso Direction == before ->
-                lists:reverse(lists:sublist(lists:reverse(L), 1, M));
-            Index == undefined ->
-                lists:sublist(L, 1, M);
-            Index > Count  orelse Index < 0 ->
-                [];
-            true ->
-                lists:sublist(L, Index+1, M)
-        end,
-    if
-       L2 == [] ->
-           {L2, #rsm_out{count=Count}};
-       true ->
-           H = hd(L2),
-           NewIndex = get_room_pos(H, AllRooms),
-           T=lists:last(L2),
-           {F, _}=H#muc_online_room.name_host,
-           {Last, _}=T#muc_online_room.name_host,
-           {L2, #rsm_out{first=F, last=Last, count=Count, index=NewIndex}}
-    end.
-
-%% @doc Return the position of desired room in the list of rooms.
-%% The room must exist in the list. The count starts in 0.
-%% @spec (Desired::muc_online_room(), Rooms::[muc_online_room()]) -> integer()
-get_room_pos(Desired, Rooms) ->
-    get_room_pos(Desired, Rooms, 0).
-get_room_pos(Desired, [HeadRoom | _], HeadPosition)
-  when (Desired#muc_online_room.name_host ==
-       HeadRoom#muc_online_room.name_host) ->
-    HeadPosition;
-get_room_pos(Desired, [_ | Rooms], HeadPosition) ->
-    get_room_pos(Desired, Rooms, HeadPosition + 1).
-
-flush() ->
-    receive
-       _ ->
-           flush()
-    after 0 ->
-           ok
-    end.
-
--define(XFIELD(Type, Label, Var, Val),
-       {xmlelement, "field", [{"type", Type},
-                              {"label", translate:translate(Lang, Label)},
-                              {"var", Var}],
-        [{xmlelement, "value", [], [{xmlcdata, Val}]}]}).
-
-%% @doc Get a pseudo unique Room Name. The Room Name is generated as a hash of 
-%%      the requester JID, the local time and a random salt.
-%%
-%%      "pseudo" because we don't verify that there is not a room
-%%       with the returned Name already created, nor mark the generated Name 
-%%       as "already used".  But in practice, it is unique enough. See
-%%       http://xmpp.org/extensions/xep-0045.html#createroom-unique
-iq_get_unique(From) ->
-       {xmlcdata, sha:sha(term_to_binary([From, now(), randoms:get_string()]))}.
-
-iq_get_register_info(ServerHost, Host, From, Lang) ->
-    SJID = ejabberd_odbc:escape(
-             jlib:jid_to_string(
-               jlib:jid_tolower(
-                 jlib:jid_remove_resource(From)))),
-    SHost = ejabberd_odbc:escape(Host),
-    LServer = jlib:nameprep(ServerHost),
-    {Nick, Registered} =
-        case catch ejabberd_odbc:sql_query(
-                     LServer, ["select nick from muc_registered where "
-                               "jid='", SJID, "' and host='", SHost, "';"]) of
-            {selected, ["nick"], [{N}]} ->
-                {N, [{xmlelement, "registered", [], []}]};
-            _ ->
-                {"", []}
-        end,
-    Registered ++
-       [{xmlelement, "instructions", [],
-         [{xmlcdata,
-           translate:translate(
-             Lang, "You need a client that supports x:data to register the nickname")}]},
-        {xmlelement, "x",
-         [{"xmlns", ?NS_XDATA}],
-         [{xmlelement, "title", [],
-           [{xmlcdata,
-             translate:translate(
-               Lang, "Nickname Registration at ") ++ Host}]},
-          {xmlelement, "instructions", [],
-           [{xmlcdata,
-             translate:translate(
-               Lang, "Enter nickname you want to register")}]},
-          ?XFIELD("text-single", "Nickname", "nick", Nick)]}].
-
-iq_set_register_info(ServerHost, Host, From, Nick, Lang) ->
-    JID = jlib:jid_to_string(
-            jlib:jid_tolower(
-              jlib:jid_remove_resource(From))),
-    SJID = ejabberd_odbc:escape(JID),
-    SNick = ejabberd_odbc:escape(Nick),
-    SHost = ejabberd_odbc:escape(Host),
-    LServer = jlib:nameprep(ServerHost),
-    F = fun() ->
-                case Nick of
-                    "" ->
-                        ejabberd_odbc:sql_query_t(
-                          ["delete from muc_registered where ",
-                           "jid='", SJID, "' and host='", Host, "';"]),
-                        ok;
-                    _ ->
-                        Allow =
-                            case ejabberd_odbc:sql_query_t(
-                                   ["select jid from muc_registered ",
-                                    "where nick='", SNick, "' and host='",
-                                    SHost, "';"]) of
-                                {selected, ["jid"], [{J}]} ->
-                                    J == JID;
-                                _ ->
-                                    true
-                            end,
-                        if Allow ->
-                                odbc_queries:update_t(
-                                  "muc_registered",
-                                  ["jid", "host", "nick"],
-                                  [SJID, SHost, SNick],
-                                  ["jid='", SJID, "' and host='", SHost, "'"]),
-                                ok;
-                           true ->
-                                false
-                        end
-                end
-        end,
-    case catch ejabberd_odbc:sql_transaction(LServer, F) of
-        {atomic, ok} ->
-            {result, []};
-        {atomic, false} ->
-            ErrText = "That nickname is registered by another person",
-           {error, ?ERRT_CONFLICT(Lang, ErrText)};
-       _ ->
-           {error, ?ERR_INTERNAL_SERVER_ERROR}
-    end.
-
-process_iq_register_set(ServerHost, Host, From, SubEl, Lang) ->
-    {xmlelement, _Name, _Attrs, Els} = SubEl,
-    case xml:get_subtag(SubEl, "remove") of
-       false ->
-           case xml:remove_cdata(Els) of
-               [{xmlelement, "x", _Attrs1, _Els1} = XEl] ->
-                   case {xml:get_tag_attr_s("xmlns", XEl),
-                         xml:get_tag_attr_s("type", XEl)} of
-                       {?NS_XDATA, "cancel"} ->
-                           {result, []};
-                       {?NS_XDATA, "submit"} ->
-                           XData = jlib:parse_xdata_submit(XEl),
-                           case XData of
-                               invalid ->
-                                   {error, ?ERR_BAD_REQUEST};
-                               _ ->
-                                   case lists:keysearch("nick", 1, XData) of
-                                       {value, {_, [Nick]}} when Nick /= "" ->
-                                           iq_set_register_info(ServerHost, Host,
-                                                                 From, Nick, Lang);
-                                       _ ->
-                                           ErrText = "You must fill in field \"Nickname\" in the form",
-                                           {error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)}
-                                   end
-                           end;
-                       _ ->
-                           {error, ?ERR_BAD_REQUEST}
-                   end;
-               _ ->
-                   {error, ?ERR_BAD_REQUEST}
-           end;
-       _ ->
-           iq_set_register_info(ServerHost, Host, From, "", Lang)
-    end.
-
-iq_get_vcard(Lang) ->
-    [{xmlelement, "FN", [],
-      [{xmlcdata, "ejabberd/mod_muc"}]},
-     {xmlelement, "URL", [],
-      [{xmlcdata, ?EJABBERD_URI}]},
-     {xmlelement, "DESC", [],
-      [{xmlcdata, translate:translate(Lang, "ejabberd MUC module") ++
-         "\nCopyright (c) 2003-2012 ProcessOne"}]}].
-
-
-broadcast_service_message(Host, Msg) ->
-    lists:foreach(
-      fun(#muc_online_room{pid = Pid}) ->
-             gen_fsm:send_all_state_event(
-               Pid, {service_message, Msg})
-      end, get_vh_rooms(Host)).
-
-get_vh_rooms(Host) ->
-    mnesia:dirty_select(muc_online_room,
-                       [{#muc_online_room{name_host = '$1', _ = '_'},
-                         [{'==', {element, 2, '$1'}, Host}],
-                         ['$_']}]).
-
-
-clean_table_from_bad_node(Node) ->
-    F = fun() ->
-               Es = mnesia:select(
-                      muc_online_room,
-                      [{#muc_online_room{pid = '$1', _ = '_'},
-                        [{'==', {node, '$1'}, Node}],
-                        ['$_']}]),
-               lists:foreach(fun(E) ->
-                                     mnesia:delete_object(E)
-                             end, Es)
-        end,
-    mnesia:async_dirty(F).
-
-clean_table_from_bad_node(Node, Host) ->
-    F = fun() ->
-               Es = mnesia:select(
-                      muc_online_room,
-                      [{#muc_online_room{pid = '$1',
-                                         name_host = {'_', Host},
-                                         _ = '_'},
-                        [{'==', {node, '$1'}, Node}],
-                        ['$_']}]),
-               lists:foreach(fun(E) ->
-                                     mnesia:delete_object(E)
-                             end, Es)
-        end,
-    mnesia:async_dirty(F).
index a5845175c15e18ee5c50f9239f5099cda7341fea..670460be79d14745e43b778f2452d420855fded8 100644 (file)
 
 
 %% External exports
--export([start_link/10,
-        start_link/8,
-        start/10,
-        start/8,
+-export([start_link/9,
+        start_link/7,
+        start/9,
+        start/7,
         route/4]).
 
 %% gen_fsm callbacks
 -ifdef(NO_TRANSIENT_SUPERVISORS).
 -define(SUPERVISOR_START, 
        gen_fsm:start(?MODULE, [Host, ServerHost, Access, Room, HistorySize,
-                               RoomShaper, Creator, Nick, DefRoomOpts, Mod],
+                               RoomShaper, Creator, Nick, DefRoomOpts],
                      ?FSMOPTS)).
 -else.
 -define(SUPERVISOR_START, 
        Supervisor = gen_mod:get_module_proc(ServerHost, ejabberd_mod_muc_sup),
        supervisor:start_child(
          Supervisor, [Host, ServerHost, Access, Room, HistorySize, RoomShaper,
-                      Creator, Nick, DefRoomOpts, Mod])).
+                      Creator, Nick, DefRoomOpts])).
 -endif.
 
 %%%----------------------------------------------------------------------
 %%% API
 %%%----------------------------------------------------------------------
 start(Host, ServerHost, Access, Room, HistorySize, RoomShaper,
-      Creator, Nick, DefRoomOpts, Mod) ->
+      Creator, Nick, DefRoomOpts) ->
     ?SUPERVISOR_START.
 
-start(Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts, Mod) ->
+start(Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts) ->
     Supervisor = gen_mod:get_module_proc(ServerHost, ejabberd_mod_muc_sup),
     supervisor:start_child(
       Supervisor, [Host, ServerHost, Access, Room, HistorySize, RoomShaper,
-                  Opts, Mod]).
+                  Opts]).
 
 start_link(Host, ServerHost, Access, Room, HistorySize, RoomShaper,
-          Creator, Nick, DefRoomOpts, Mod) ->
+          Creator, Nick, DefRoomOpts) ->
     gen_fsm:start_link(?MODULE, [Host, ServerHost, Access, Room, HistorySize,
-                                RoomShaper, Creator, Nick, DefRoomOpts, Mod],
+                                RoomShaper, Creator, Nick, DefRoomOpts],
                       ?FSMOPTS).
 
-start_link(Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts, Mod) ->
+start_link(Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts) ->
     gen_fsm:start_link(?MODULE, [Host, ServerHost, Access, Room, HistorySize,
-                                RoomShaper, Opts, Mod],
+                                RoomShaper, Opts],
                       ?FSMOPTS).
 
 %%%----------------------------------------------------------------------
@@ -110,14 +110,12 @@ start_link(Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts, Mod) -
 %%          ignore                              |
 %%          {stop, StopReason}
 %%----------------------------------------------------------------------
-init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Creator, _Nick,
-      DefRoomOpts, Mod]) ->
+init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Creator, _Nick, DefRoomOpts]) ->
     process_flag(trap_exit, true),
     Shaper = shaper:new(RoomShaper),
     State = set_affiliation(Creator, owner,
                            #state{host = Host,
                                   server_host = ServerHost,
-                                   mod = Mod,
                                   access = Access,
                                   room = Room,
                                   history = lqueue_new(HistorySize),
@@ -130,12 +128,11 @@ init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Creator, _Nick,
     add_to_log(room_existence, created, State1),
     add_to_log(room_existence, started, State1),
     {ok, normal_state, State1};
-init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts, Mod]) ->
+init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts]) ->
     process_flag(trap_exit, true),
     Shaper = shaper:new(RoomShaper),
     State = set_opts(Opts, #state{host = Host,
                                  server_host = ServerHost,
-                                  mod = Mod,
                                  access = Access,
                                  room = Room,
                                  history = lqueue_new(HistorySize),
@@ -164,8 +161,7 @@ normal_state({route, From, "",
                    MinMessageInterval =
                        trunc(gen_mod:get_module_opt(
                                StateData#state.server_host,
-                               StateData#state.mod,
-                                min_message_interval, 0) * 1000000),
+                               mod_muc, min_message_interval, 0) * 1000000),
                    Size = element_size(Packet),
                    {MessageShaper, MessageShaperInterval} =
                        shaper:update(Activity#activity.message_shaper, Size),
@@ -286,7 +282,7 @@ normal_state({route, From, "",
                                                             StateData),
                                                     case (NSD#state.config)#config.persistent of
                                                        true ->
-                                                            (NSD#state.mod):store_room(
+                                                            mod_muc:store_room(
                                                               NSD#state.server_host,
                                                               NSD#state.host,
                                                               NSD#state.room,
@@ -485,7 +481,7 @@ normal_state({route, From, Nick,
     MinPresenceInterval =
        trunc(gen_mod:get_module_opt(
                StateData#state.server_host,
-               StateData#state.mod, min_presence_interval, 0) * 1000000),
+               mod_muc, min_presence_interval, 0) * 1000000),
     if
        (Now >= Activity#activity.presence_time + MinPresenceInterval) and
        (Activity#activity.presence == undefined) ->
@@ -855,9 +851,8 @@ terminate(Reason, _StateName, StateData) ->
               tab_remove_online_user(LJID, StateData)
        end, [], StateData#state.users),
     add_to_log(room_existence, stopped, StateData),
-    (StateData#state.mod):room_destroyed(
-      StateData#state.host, StateData#state.room, self(),
-      StateData#state.server_host),
+    mod_muc:room_destroyed(StateData#state.host, StateData#state.room, self(),
+                          StateData#state.server_host),
     ok.
 
 %%%----------------------------------------------------------------------
@@ -892,7 +887,7 @@ process_groupchat_message(From, {xmlelement, "message", Attrs, _Els} = Packet,
                                              FromNick},
                                        case (NSD#state.config)#config.persistent of
                                            true ->
-                                               (NSD#state.mod):store_room(
+                                               mod_muc:store_room(
                                                   NSD#state.server_host,
                                                  NSD#state.host,
                                                  NSD#state.room,
@@ -1031,9 +1026,9 @@ process_presence(From, Nick, {xmlelement, "presence", Attrs, _Els} = Packet,
                        case is_nick_change(From, Nick, StateData) of
                            true ->
                                case {nick_collision(From, Nick, StateData),
-                                     (StateData#state.mod):can_use_nick(
-                                       StateData#state.server_host,
-                                        StateData#state.host, From, Nick),
+                                     mod_muc:can_use_nick(
+                                        StateData#state.server_host,
+                                       StateData#state.host, From, Nick),
                                       {(StateData#state.config)#config.allow_visitor_nickchange,
                                        is_visitor(From, StateData)}} of
                                     {_, _, {false, true}} ->
@@ -1428,11 +1423,11 @@ get_max_users(StateData) ->
 
 get_service_max_users(StateData) ->
     gen_mod:get_module_opt(StateData#state.server_host,
-                          StateData#state.mod, max_users, ?MAX_USERS_DEFAULT).
+                          mod_muc, max_users, ?MAX_USERS_DEFAULT).
 
 get_max_users_admin_threshold(StateData) ->
     gen_mod:get_module_opt(StateData#state.server_host,
-                          StateData#state.mod, max_users_admin_threshold, 5).
+                          mod_muc, max_users_admin_threshold, 5).
 
 get_user_activity(JID, StateData) ->
     case treap:lookup(jlib:jid_tolower(JID),
@@ -1442,11 +1437,11 @@ get_user_activity(JID, StateData) ->
            MessageShaper =
                shaper:new(gen_mod:get_module_opt(
                             StateData#state.server_host,
-                            StateData#state.mod, user_message_shaper, none)),
+                            mod_muc, user_message_shaper, none)),
            PresenceShaper =
                shaper:new(gen_mod:get_module_opt(
                             StateData#state.server_host,
-                            StateData#state.mod, user_presence_shaper, none)),
+                            mod_muc, user_presence_shaper, none)),
            #activity{message_shaper = MessageShaper,
                      presence_shaper = PresenceShaper}
     end.
@@ -1455,11 +1450,11 @@ store_user_activity(JID, UserActivity, StateData) ->
     MinMessageInterval =
        gen_mod:get_module_opt(
          StateData#state.server_host,
-         StateData#state.mod, min_message_interval, 0),
+         mod_muc, min_message_interval, 0),
     MinPresenceInterval =
        gen_mod:get_module_opt(
          StateData#state.server_host,
-         StateData#state.mod, min_presence_interval, 0),
+         mod_muc, min_presence_interval, 0),
     Key = jlib:jid_tolower(JID),
     Now = now_to_usec(now()),
     Activity1 = clean_treap(StateData#state.activity, {1, -Now}),
@@ -1740,7 +1735,7 @@ add_new_user(From, Nick, {xmlelement, _, Attrs, Els} = Packet, StateData) ->
     NConferences = tab_count_user(From),
     MaxConferences = gen_mod:get_module_opt(
                       StateData#state.server_host,
-                      StateData#state.mod, max_user_conferences, 10),
+                      mod_muc, max_user_conferences, 10),
     Collision = nick_collision(From, Nick, StateData),
     case {(ServiceAffiliation == owner orelse
           ((Affiliation == admin orelse Affiliation == owner) andalso
@@ -1748,8 +1743,9 @@ add_new_user(From, Nick, {xmlelement, _, Attrs, Els} = Packet, StateData) ->
           NUsers < MaxUsers) andalso
          NConferences < MaxConferences,
          Collision,
-         (StateData#state.mod):can_use_nick(StateData#state.server_host,
-                                             StateData#state.host, From, Nick),
+         mod_muc:can_use_nick(
+            StateData#state.server_host,
+            StateData#state.host, From, Nick),
          get_default_role(Affiliation, StateData)} of
        {false, _, _, _} ->
            % max user reached and user is not admin or owner
@@ -2589,9 +2585,9 @@ process_admin_items_set(UJID, Items, Lang, StateData) ->
                  end, StateData, lists:flatten(Res)),
            case (NSD#state.config)#config.persistent of
                true ->
-                   (NSD#state.mod):store_room(NSD#state.server_host,
-                                               NSD#state.host, NSD#state.room,
-                                               make_opts(NSD));
+                   mod_muc:store_room(NSD#state.server_host,
+                                       NSD#state.host, NSD#state.room,
+                                      make_opts(NSD));
                _ ->
                    ok
            end,
@@ -3091,8 +3087,8 @@ is_allowed_room_name_desc_limits(XEl, StateData) ->
                             jlib:parse_xdata_submit(XEl)) of
            {value, {_, [N]}} ->
                length(N) =< gen_mod:get_module_opt(StateData#state.server_host,
-                                                   StateData#state.mod,
-                                                    max_room_name, infinite);
+                                                   mod_muc, max_room_name,
+                                                   infinite);
            _ ->
                true
        end,
@@ -3101,8 +3097,8 @@ is_allowed_room_name_desc_limits(XEl, StateData) ->
                             jlib:parse_xdata_submit(XEl)) of
            {value, {_, [D]}} ->
                length(D) =< gen_mod:get_module_opt(StateData#state.server_host,
-                                                   StateData#state.mod,
-                                                    max_room_desc, infinite);
+                                                   mod_muc, max_room_desc,
+                                                   infinite);
            _ ->
                true
        end,
@@ -3173,9 +3169,7 @@ is_password_settings_correct(XEl, StateData) ->
           || JID <- JIDList]}).
 
 get_default_room_maxusers(RoomState) ->
-    DefRoomOpts = gen_mod:get_module_opt(
-                    RoomState#state.server_host,
-                    RoomState#state.mod, default_room_options, []),
+    DefRoomOpts = gen_mod:get_module_opt(RoomState#state.server_host, mod_muc, default_room_options, []),
     RoomState2 = set_opts(DefRoomOpts, RoomState),
     (RoomState2#state.config)#config.max_users.
 
@@ -3487,14 +3481,14 @@ set_xoption([_ | _Opts], _Config) ->
 
 change_config(Config, StateData) ->
     NSD = StateData#state{config = Config},
-    Mod = StateData#state.mod,
     case {(StateData#state.config)#config.persistent,
          Config#config.persistent} of
        {_, true} ->
-           Mod:store_room(NSD#state.server_host, NSD#state.host,
-                           NSD#state.room, make_opts(NSD));
+           mod_muc:store_room(NSD#state.server_host, NSD#state.host,
+                               NSD#state.room, make_opts(NSD));
        {true, false} ->
-           Mod:forget_room(NSD#state.server_host, NSD#state.host, NSD#state.room);
+           mod_muc:forget_room(NSD#state.server_host, NSD#state.host,
+                                NSD#state.room);
        {false, false} ->
            ok
     end,
@@ -3625,7 +3619,7 @@ destroy_room(DEl, StateData) ->
       end, ?DICT:to_list(StateData#state.users)),
     case (StateData#state.config)#config.persistent of
        true ->
-           (StateData#state.mod):forget_room(
+           mod_muc:forget_room(
               StateData#state.server_host,
               StateData#state.host, StateData#state.room);
        false ->
index 20874c833cb3bff3788b52440dbcbb0dbcf6cd5e..75b1966b224d65486f79aa19a31b6a45a312f851 100644 (file)
@@ -1,7 +1,7 @@
 %%%----------------------------------------------------------------------
 %%% File    : mod_offline.erl
 %%% Author  : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : Store and manage offline messages in Mnesia database.
+%%% Purpose : Store and manage offline messages.
 %%% Created :  5 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
 %%%
 %%%
 
 -behaviour(gen_mod).
 
+-export([count_offline_messages/2]).
+
 -export([start/2,
-        loop/1,
+        loop/2,
         stop/1,
         store_packet/3,
         resend_offline_messages/2,
         pop_offline_messages/3,
         get_sm_features/5,
-        remove_expired_messages/0,
-        remove_old_messages/1,
+        remove_expired_messages/1,
+        remove_old_messages/2,
         remove_user/2,
         get_queue_length/2,
         webadmin_page/3,
 -define(MAX_USER_MESSAGES, infinity).
 
 start(Host, Opts) ->
-    mnesia:create_table(offline_msg,
-                       [{disc_only_copies, [node()]},
-                        {type, bag},
-                        {attributes, record_info(fields, offline_msg)}]),
-    update_table(),
+    case gen_mod:db_type(Opts) of
+        mnesia ->
+            mnesia:create_table(offline_msg,
+                                [{disc_only_copies, [node()]},
+                                 {type, bag},
+                                 {attributes,
+                                  record_info(fields, offline_msg)}]),
+            update_table();
+        _ ->
+            ok
+    end,
     ejabberd_hooks:add(offline_message_hook, Host,
                       ?MODULE, store_packet, 50),
     ejabberd_hooks:add(resend_offline_messages_hook, Host,
@@ -83,44 +91,88 @@ start(Host, Opts) ->
                        ?MODULE, webadmin_user_parse_query, 50),
     AccessMaxOfflineMsgs = gen_mod:get_opt(access_max_user_messages, Opts, max_user_offline_messages),
     register(gen_mod:get_module_proc(Host, ?PROCNAME),
-            spawn(?MODULE, loop, [AccessMaxOfflineMsgs])).
+            spawn(?MODULE, loop, [Host, AccessMaxOfflineMsgs])).
 
-loop(AccessMaxOfflineMsgs) ->
+loop(Host, AccessMaxOfflineMsgs) ->
     receive
-       #offline_msg{us=US} = Msg ->
-           Msgs = receive_all(US, [Msg]),
+        #offline_msg{us = User} = Msg ->
+            DBType = gen_mod:db_type(Host, ?MODULE),
+           Msgs = receive_all(User, [Msg], DBType),
            Len = length(Msgs),
-           {User, Host} = US,
            MaxOfflineMsgs = get_max_user_messages(AccessMaxOfflineMsgs,
                                                   User, Host),
-           F = fun() ->
-                       %% Only count messages if needed:
-                       Count = if MaxOfflineMsgs =/= infinity ->
-                                       Len + p1_mnesia:count_records(
-                                               offline_msg, 
-                                               #offline_msg{us=US, _='_'});
-                                  true -> 
-                                       0
-                               end,
-                       if
-                           Count > MaxOfflineMsgs ->
-                               discard_warn_sender(Msgs);
-                           true ->
-                               if
-                                   Len >= ?OFFLINE_TABLE_LOCK_THRESHOLD ->
-                                       mnesia:write_lock_table(offline_msg);
-                                   true ->
-                                       ok
-                               end,
-                               lists:foreach(fun(M) ->
-                                                     mnesia:write(M)
-                                             end, Msgs)
-                       end
-               end,
-           mnesia:transaction(F),
-           loop(AccessMaxOfflineMsgs);
-       _ ->
-           loop(AccessMaxOfflineMsgs)
+            store_offline_msg(Host, User, Msgs, Len, MaxOfflineMsgs, DBType),
+            loop(Host, AccessMaxOfflineMsgs);
+        _ ->
+           loop(Host, AccessMaxOfflineMsgs)
+    end.
+
+store_offline_msg(_Host, US, Msgs, Len, MaxOfflineMsgs, mnesia) ->
+    F = fun() ->
+                %% Only count messages if needed:
+                Count = if MaxOfflineMsgs =/= infinity ->
+                                Len + p1_mnesia:count_records(
+                                        offline_msg, 
+                                        #offline_msg{us=US, _='_'});
+                           true -> 
+                                0
+                        end,
+                if
+                    Count > MaxOfflineMsgs ->
+                        discard_warn_sender(Msgs);
+                    true ->
+                        if
+                            Len >= ?OFFLINE_TABLE_LOCK_THRESHOLD ->
+                                mnesia:write_lock_table(offline_msg);
+                            true ->
+                                ok
+                        end,
+                        lists:foreach(fun(M) ->
+                                              mnesia:write(M)
+                                      end, Msgs)
+                end
+        end,
+    mnesia:transaction(F);
+store_offline_msg(Host, User, Msgs, Len, MaxOfflineMsgs, odbc) ->
+    Count = if MaxOfflineMsgs =/= infinity ->
+                    Len + count_offline_messages(User, Host);
+               true -> 0
+            end,
+    if 
+        Count > MaxOfflineMsgs ->
+            discard_warn_sender(Msgs);
+        true ->
+            Query = lists:map(
+                      fun(M) ->
+                              Username =
+                                  ejabberd_odbc:escape(
+                                           (M#offline_msg.to)#jid.luser),
+                              From = M#offline_msg.from,
+                              To = M#offline_msg.to,
+                              {xmlelement, Name, Attrs, Els} =
+                                  M#offline_msg.packet,
+                              Attrs2 = jlib:replace_from_to_attrs(
+                                         jlib:jid_to_string(From),
+                                         jlib:jid_to_string(To),
+                                         Attrs),
+                              Packet = {xmlelement, Name, Attrs2,
+                                        Els ++
+                                            [jlib:timestamp_to_xml(
+                                               calendar:now_to_universal_time(
+                                                 M#offline_msg.timestamp),
+                                               utc,
+                                               jlib:make_jid("", Host, ""),
+                                               "Offline Storage"),
+                                             %% TODO: Delete the next three lines once XEP-0091 is Obsolete
+                                             jlib:timestamp_to_xml( 
+                                               calendar:now_to_universal_time(
+                                                 M#offline_msg.timestamp))]},
+                              XML =
+                                  ejabberd_odbc:escape(
+                                    xml:element_to_binary(Packet)),
+                              odbc_queries:add_spool_sql(Username, XML)
+                      end, Msgs),
+            odbc_queries:add_spool(Host, Query)
     end.
 
 %% Function copied from ejabberd_sm.erl:
@@ -132,15 +184,27 @@ get_max_user_messages(AccessRule, LUser, Host) ->
        _ -> ?MAX_USER_MESSAGES
     end.
 
-receive_all(US, Msgs) ->
+receive_all(US, Msgs, DBType) ->
     receive
        #offline_msg{us=US} = Msg ->
-           receive_all(US, [Msg | Msgs])
+           receive_all(US, [Msg | Msgs], DBType)
     after 0 ->
-           Msgs
+            %% FIXME: the diff between mnesia and odbc version:
+            %%
+            %%      after 0 ->
+            %% -           Msgs
+            %% +           lists:reverse(Msgs)
+            %%      end.
+            %%
+            %% Is it a bug in mnesia version?
+            case DBType of
+                mnesia ->
+                    Msgs;
+                odbc ->
+                    lists:reverse(Msgs)
+            end
     end.
 
-
 stop(Host) ->
     ejabberd_hooks:delete(offline_message_hook, Host,
                          ?MODULE, store_packet, 50),
@@ -325,6 +389,10 @@ resend_offline_messages(User, Server) ->
 pop_offline_messages(Ls, User, Server) ->
     LUser = jlib:nodeprep(User),
     LServer = jlib:nameprep(Server),
+    pop_offline_messages(Ls, LUser, LServer,
+                         gen_mod:db_type(LServer, ?MODULE)).
+
+pop_offline_messages(Ls, LUser, LServer, mnesia) ->
     US = {LUser, LServer},
     F = fun() ->
                Rs = mnesia:wread({offline_msg, US}),
@@ -346,7 +414,7 @@ pop_offline_messages(Ls, User, Server) ->
                                 calendar:now_to_universal_time(
                                   R#offline_msg.timestamp),
                                 utc,
-                                jlib:make_jid("", Server, ""),
+                                jlib:make_jid("", LServer, ""),
                                 "Offline Storage"),
                               %% TODO: Delete the next three lines once XEP-0091 is Obsolete
                               jlib:timestamp_to_xml(
@@ -365,10 +433,39 @@ pop_offline_messages(Ls, User, Server) ->
                      lists:keysort(#offline_msg.timestamp, Rs)));
        _ ->
            Ls
+    end;
+pop_offline_messages(Ls, LUser, LServer, odbc) ->
+    EUser = ejabberd_odbc:escape(LUser),
+    case odbc_queries:get_and_del_spool_msg_t(LServer, EUser) of
+       {atomic, {selected, ["username","xml"], Rs}} ->
+           Ls ++ lists:flatmap(
+                   fun({_, XML}) ->
+                           case xml_stream:parse_element(XML) of
+                               {error, _Reason} ->
+                                   [];
+                               El ->
+                                   To = jlib:string_to_jid(
+                                          xml:get_tag_attr_s("to", El)),
+                                   From = jlib:string_to_jid(
+                                            xml:get_tag_attr_s("from", El)),
+                                   if
+                                       (To /= error) and
+                                       (From /= error) ->
+                                           [{route, From, To, El}];
+                                       true ->
+                                           []
+                                   end
+                           end
+                   end, Rs);
+       _ ->
+           Ls
     end.
 
+remove_expired_messages(Server) ->
+    LServer = jlib:nameprep(Server),
+    remove_expired_messages(LServer, gen_mod:db_type(LServer, ?MODULE)).
 
-remove_expired_messages() ->
+remove_expired_messages(_LServer, mnesia) ->
     TimeStamp = now(),
     F = fun() ->
                mnesia:write_lock_table(offline_msg),
@@ -387,9 +484,16 @@ remove_expired_messages() ->
                          end
                  end, ok, offline_msg)
        end,
-    mnesia:transaction(F).
+    mnesia:transaction(F);
+remove_expired_messages(_LServer, odbc) ->
+    %% TODO
+    {atomic, ok}.
 
-remove_old_messages(Days) ->
+remove_old_messages(Days, Server) ->
+    LServer = jlib:nameprep(Server),
+    remove_old_messages(Days, LServer, gen_mod:db_type(LServer, ?MODULE)).
+
+remove_old_messages(Days, _LServer, mnesia) ->
     {MegaSecs, Secs, _MicroSecs} = now(),
     S = MegaSecs * 1000000 + Secs - 60 * 60 * 24 * Days,
     MegaSecs1 = S div 1000000,
@@ -404,16 +508,25 @@ remove_old_messages(Days) ->
                     (_Rec, _Acc) -> ok
                  end, ok, offline_msg)
        end,
-    mnesia:transaction(F).
+    mnesia:transaction(F);
+remove_old_messages(_Days, _LServer, odbc) ->
+    %% TODO
+    {atomic, ok}.
 
 remove_user(User, Server) ->
     LUser = jlib:nodeprep(User),
     LServer = jlib:nameprep(Server),
+    remove_user(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
+
+remove_user(LUser, LServer, mnesia) ->
     US = {LUser, LServer},
     F = fun() ->
                mnesia:delete({offline_msg, US})
        end,
-    mnesia:transaction(F).
+    mnesia:transaction(F);
+remove_user(LUser, LServer, odbc) ->
+    Username = ejabberd_odbc:escape(LUser),
+    odbc_queries:del_spool_msg(LServer, Username).
 
 update_table() ->
     Fields = record_info(fields, offline_msg),
@@ -527,36 +640,76 @@ webadmin_page(_, Host,
 
 webadmin_page(Acc, _, _) -> Acc.
 
+read_all_msgs(LUser, LServer, mnesia) ->
+    US = {LUser, LServer},
+    lists:keysort(#offline_msg.timestamp,
+                  mnesia:dirty_read({offline_msg, US}));
+read_all_msgs(LUser, LServer, odbc) ->
+    Username = ejabberd_odbc:escape(LUser),
+    case catch ejabberd_odbc:sql_query(
+                 LServer,
+                 ["select xml from spool"
+                  "  where username='", Username, "'"
+                  "  order by seq;"]) of
+        {selected, ["username", "xml"], Rs} ->
+            lists:flatmap(
+              fun({XML}) ->
+                      case xml_stream:parse_element(XML) of
+                          {error, _Reason} ->
+                              [];
+                          El ->
+                              [El]
+                      end
+              end, Rs);
+        _ ->
+            []
+    end.
+
+format_user_queue(Msgs, mnesia) ->
+    lists:map(
+      fun(#offline_msg{timestamp = TimeStamp, from = From, to = To,
+                       packet = {xmlelement, Name, Attrs, Els}} = Msg) ->
+              ID = jlib:encode_base64(binary_to_list(term_to_binary(Msg))),
+              {{Year, Month, Day}, {Hour, Minute, Second}} =
+                  calendar:now_to_local_time(TimeStamp),
+              Time = lists:flatten(
+                       io_lib:format(
+                         "~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w",
+                         [Year, Month, Day, Hour, Minute, Second])),
+              SFrom = jlib:jid_to_string(From),
+              STo = jlib:jid_to_string(To),
+              Attrs2 = jlib:replace_from_to_attrs(SFrom, STo, Attrs),
+              Packet = {xmlelement, Name, Attrs2, Els},
+              FPacket = ejabberd_web_admin:pretty_print_xml(Packet),
+              ?XE("tr",
+                  [?XAE("td", [{"class", "valign"}], [?INPUT("checkbox", "selected", ID)]),
+                   ?XAC("td", [{"class", "valign"}], Time),
+                   ?XAC("td", [{"class", "valign"}], SFrom),
+                   ?XAC("td", [{"class", "valign"}], STo),
+                   ?XAE("td", [{"class", "valign"}], [?XC("pre", FPacket)])]
+                 )
+      end, Msgs);
+format_user_queue(Msgs, odbc) ->
+    lists:map(
+      fun({xmlelement, _Name, _Attrs, _Els} = Msg) ->
+              ID = jlib:encode_base64(binary_to_list(term_to_binary(Msg))),
+              Packet = Msg,
+              FPacket = ejabberd_web_admin:pretty_print_xml(Packet),
+              ?XE("tr",
+                  [?XAE("td", [{"class", "valign"}], [?INPUT("checkbox", "selected", ID)]),
+                   ?XAE("td", [{"class", "valign"}], [?XC("pre", FPacket)])]
+                 )
+      end, Msgs).
+
 user_queue(User, Server, Query, Lang) ->
-    US = {jlib:nodeprep(User), jlib:nameprep(Server)},
-    Res = user_queue_parse_query(US, Query),
-    MsgsAll = lists:keysort(#offline_msg.timestamp,
-                           mnesia:dirty_read({offline_msg, US})),
-    Msgs = get_messages_subset(User, Server, MsgsAll),
-    FMsgs =
-       lists:map(
-         fun(#offline_msg{timestamp = TimeStamp, from = From, to = To,
-                          packet = {xmlelement, Name, Attrs, Els}} = Msg) ->
-                 ID = jlib:encode_base64(binary_to_list(term_to_binary(Msg))),
-                 {{Year, Month, Day}, {Hour, Minute, Second}} =
-                     calendar:now_to_local_time(TimeStamp),
-                 Time = lists:flatten(
-                          io_lib:format(
-                            "~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w",
-                            [Year, Month, Day, Hour, Minute, Second])),
-                 SFrom = jlib:jid_to_string(From),
-                 STo = jlib:jid_to_string(To),
-                 Attrs2 = jlib:replace_from_to_attrs(SFrom, STo, Attrs),
-                 Packet = {xmlelement, Name, Attrs2, Els},
-                 FPacket = ejabberd_web_admin:pretty_print_xml(Packet),
-                 ?XE("tr",
-                     [?XAE("td", [{"class", "valign"}], [?INPUT("checkbox", "selected", ID)]),
-                      ?XAC("td", [{"class", "valign"}], Time),
-                      ?XAC("td", [{"class", "valign"}], SFrom),
-                      ?XAC("td", [{"class", "valign"}], STo),
-                      ?XAE("td", [{"class", "valign"}], [?XC("pre", FPacket)])]
-                    )
-         end, Msgs),
+    LUser = jlib:nodeprep(User),
+    LServer = jlib:nameprep(Server),
+    US = {LUser, LServer},
+    DBType = gen_mod:db_type(LServer, ?MODULE),
+    Res = user_queue_parse_query(LUser, LServer, Query, DBType),
+    MsgsAll = read_all_msgs(LUser, LServer, DBType),
+    Msgs = get_messages_subset(User, Server, MsgsAll, DBType),
+    FMsgs = format_user_queue(Msgs, DBType),
     [?XC("h1", io_lib:format(?T("~s's Offline Messages Queue"),
                             [us_to_list(US)]))] ++
        case Res of
@@ -587,7 +740,8 @@ user_queue(User, Server, Query, Lang) ->
               ?INPUTT("submit", "delete", "Delete Selected")
              ])].
 
-user_queue_parse_query(US, Query) ->
+user_queue_parse_query(LUser, LServer, Query, mnesia) ->
+    US = {LUser, LServer},
     case lists:keysearch("delete", 1, Query) of
        {value, _} ->
            Msgs = lists:keysort(#offline_msg.timestamp,
@@ -609,15 +763,74 @@ user_queue_parse_query(US, Query) ->
            ok;
        false ->
            nothing
+    end;
+user_queue_parse_query(LUser, LServer, Query, odbc) ->
+    Username = ejabberd_odbc:escape(LUser),
+    case lists:keysearch("delete", 1, Query) of
+       {value, _} ->
+           Msgs = case catch ejabberd_odbc:sql_query(
+                               LServer,
+                               ["select xml, seq from spool"
+                                "  where username='", Username, "'"
+                                "  order by seq;"]) of
+                      {selected, ["xml", "seq"], Rs} ->
+                          lists:flatmap(
+                            fun({XML, Seq}) ->
+                                    case xml_stream:parse_element(XML) of
+                                        {error, _Reason} ->
+                                            [];
+                                        El ->
+                                            [{El, Seq}]
+                                    end
+                            end, Rs);
+                      _ ->
+                          []
+                  end,
+           F = fun() ->
+                       lists:foreach(
+                         fun({Msg, Seq}) ->
+                                 ID = jlib:encode_base64(
+                                        binary_to_list(term_to_binary(Msg))),
+                                 case lists:member({"selected", ID}, Query) of
+                                     true ->
+                                         SSeq = ejabberd_odbc:escape(Seq),
+                                         catch ejabberd_odbc:sql_query(
+                                                 LServer,
+                                                 ["delete from spool"
+                                                  "  where username='", Username, "'"
+                                                  "  and seq='", SSeq, "';"]);
+                                     false ->
+                                         ok
+                                 end
+                         end, Msgs)
+               end,
+           mnesia:transaction(F),
+           ok;
+       false ->
+           nothing
     end.
 
 us_to_list({User, Server}) ->
     jlib:jid_to_string({User, Server, ""}).
 
-get_queue_length(User, Server) ->
-    length(mnesia:dirty_read({offline_msg, {User, Server}})).
+get_queue_length(LUser, LServer) ->
+    get_queue_length(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
+
+get_queue_length(LUser, LServer, mnesia) ->
+    length(mnesia:dirty_read({offline_msg, {LUser, LServer}}));
+get_queue_length(LUser, LServer, odbc) ->
+    Username = ejabberd_odbc:escape(LUser),
+    case catch ejabberd_odbc:sql_query(
+                 LServer,
+                 ["select count(*) from spool"
+                  "  where username='", Username, "';"]) of
+        {selected, [_], [{SCount}]} ->
+            list_to_integer(SCount);
+        _ ->
+            0
+    end.
 
-get_messages_subset(User, Host, MsgsAll) ->
+get_messages_subset(User, Host, MsgsAll, DBType) ->
     Access = gen_mod:get_module_opt(Host, ?MODULE, access_max_user_messages,
                                    max_user_offline_messages),
     MaxOfflineMsgs = case get_max_user_messages(Access, User, Host) of
@@ -625,17 +838,23 @@ get_messages_subset(User, Host, MsgsAll) ->
                         _ -> 100
                     end,
     Length = length(MsgsAll),
-    get_messages_subset2(MaxOfflineMsgs, Length, MsgsAll).
+    get_messages_subset2(MaxOfflineMsgs, Length, MsgsAll, DBType).
 
-get_messages_subset2(Max, Length, MsgsAll) when Length =< Max*2 ->
+get_messages_subset2(Max, Length, MsgsAll, _DBType) when Length =< Max*2 ->
     MsgsAll;
-get_messages_subset2(Max, Length, MsgsAll) ->
+get_messages_subset2(Max, Length, MsgsAll, mnesia) ->
     FirstN = Max,
     {MsgsFirstN, Msgs2} = lists:split(FirstN, MsgsAll),
     MsgsLastN = lists:nthtail(Length - FirstN - FirstN, Msgs2),
     NoJID = jlib:make_jid("...", "...", ""),
     IntermediateMsg = #offline_msg{timestamp = now(), from = NoJID, to = NoJID,
                                   packet = {xmlelement, "...", [], []}},
+    MsgsFirstN ++ [IntermediateMsg] ++ MsgsLastN;
+get_messages_subset2(Max, Length, MsgsAll, odbc) ->
+    FirstN = Max,
+    {MsgsFirstN, Msgs2} = lists:split(FirstN, MsgsAll),
+    MsgsLastN = lists:nthtail(Length - FirstN - FirstN, Msgs2),
+    IntermediateMsg = {xmlelement, "...", [], []},
     MsgsFirstN ++ [IntermediateMsg] ++ MsgsLastN.
 
 webadmin_user(Acc, User, Server, Lang) ->
@@ -644,16 +863,29 @@ webadmin_user(Acc, User, Server, Lang) ->
                     integer_to_list(QueueLen))],
     Acc ++ [?XCT("h3", "Offline Messages:")] ++ FQueueLen ++ [?C(" "), ?INPUTT("submit", "removealloffline", "Remove All Offline Messages")].
 
-webadmin_user_parse_query(_, "removealloffline", User, Server, _Query) ->
-    US = {User, Server},
+delete_all_msgs(User, Server) ->
+    LUser = jlib:nodeprep(User),
+    LServer = jlib:nameprep(Server),
+    delete_all_msgs(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
+
+delete_all_msgs(LUser, LServer, mnesia) ->
+    US = {LUser, LServer},
     F = fun() ->
-            mnesia:write_lock_table(offline_msg),
-            lists:foreach(
-              fun(Msg) ->
-                      mnesia:delete_object(Msg)
-              end, mnesia:dirty_read({offline_msg, US}))
+                mnesia:write_lock_table(offline_msg),
+                lists:foreach(
+                  fun(Msg) ->
+                          mnesia:delete_object(Msg)
+                  end, mnesia:dirty_read({offline_msg, US}))
         end,
-    case mnesia:transaction(F) of
+    mnesia:transaction(F);
+delete_all_msgs(LUser, LServer, odbc) ->
+    Username = ejabberd_odbc:escape(LUser),
+    odbc_queries:del_spool_msg(LServer, Username),
+    %% TODO: process the output
+    {atomic, ok}.
+
+webadmin_user_parse_query(_, "removealloffline", User, Server, _Query) ->
+    case delete_all_msgs(User, Server) of
          {aborted, Reason} ->
             ?ERROR_MSG("Failed to remove offline messages: ~p", [Reason]),
             {stop, error};
@@ -663,3 +895,14 @@ webadmin_user_parse_query(_, "removealloffline", User, Server, _Query) ->
     end;
 webadmin_user_parse_query(Acc, _Action, _User, _Server, _Query) ->
     Acc.
+
+%% Returns as integer the number of offline messages for a given user
+count_offline_messages(LUser, LServer) ->
+    Username = ejabberd_odbc:escape(LUser),
+    case catch odbc_queries:count_records_where(
+                LServer, "spool", "where username='" ++ Username ++ "'") of
+        {selected, [_], [{Res}]} ->
+            list_to_integer(Res);
+        _ ->
+            0
+    end.
diff --git a/src/mod_offline_odbc.erl b/src/mod_offline_odbc.erl
deleted file mode 100644 (file)
index 0d30d57..0000000
+++ /dev/null
@@ -1,548 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File    : mod_offline_odbc.erl
-%%% Author  : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : Store and manage offline messages in relational database.
-%%% Created :  5 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2012   ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
--module(mod_offline_odbc).
--author('alexey@process-one.net').
-
--behaviour(gen_mod).
-
--export([count_offline_messages/2]).
-
--export([start/2,
-        loop/2,
-        stop/1,
-        store_packet/3,
-        pop_offline_messages/3,
-        get_sm_features/5,
-        remove_user/2,
-        get_queue_length/2,
-        webadmin_page/3,
-        webadmin_user/4,
-        webadmin_user_parse_query/5]).
-
--include("ejabberd.hrl").
--include("jlib.hrl").
--include("web/ejabberd_http.hrl").
--include("web/ejabberd_web_admin.hrl").
-
--record(offline_msg, {user, timestamp, expire, from, to, packet}).
-
--define(PROCNAME, ejabberd_offline).
--define(OFFLINE_TABLE_LOCK_THRESHOLD, 1000).
-
-%% default value for the maximum number of user messages
--define(MAX_USER_MESSAGES, infinity).
-
-start(Host, Opts) ->
-    ejabberd_hooks:add(offline_message_hook, Host,
-                      ?MODULE, store_packet, 50),
-    ejabberd_hooks:add(resend_offline_messages_hook, Host,
-                      ?MODULE, pop_offline_messages, 50),
-    ejabberd_hooks:add(remove_user, Host,
-                      ?MODULE, remove_user, 50),
-    ejabberd_hooks:add(anonymous_purge_hook, Host,
-                      ?MODULE, remove_user, 50),
-    ejabberd_hooks:add(disco_sm_features, Host,
-                      ?MODULE, get_sm_features, 50),
-    ejabberd_hooks:add(disco_local_features, Host,
-                      ?MODULE, get_sm_features, 50),
-    ejabberd_hooks:add(webadmin_page_host, Host,
-                      ?MODULE, webadmin_page, 50),
-    ejabberd_hooks:add(webadmin_user, Host,
-                      ?MODULE, webadmin_user, 50),
-    ejabberd_hooks:add(webadmin_user_parse_query, Host,
-                       ?MODULE, webadmin_user_parse_query, 50),
-    AccessMaxOfflineMsgs = gen_mod:get_opt(access_max_user_messages, Opts, max_user_offline_messages),
-    register(gen_mod:get_module_proc(Host, ?PROCNAME),
-            spawn(?MODULE, loop, [Host, AccessMaxOfflineMsgs])).
-
-loop(Host, AccessMaxOfflineMsgs) ->
-    receive
-       #offline_msg{user = User} = Msg ->
-           Msgs = receive_all(User, [Msg]),
-           Len = length(Msgs),
-           MaxOfflineMsgs = get_max_user_messages(AccessMaxOfflineMsgs,
-                                                  User, Host),
-
-           %% Only count existing messages if needed:
-           Count = if MaxOfflineMsgs =/= infinity ->
-                           Len + count_offline_messages(User, Host);
-                      true -> 0
-                   end,
-           if 
-               Count > MaxOfflineMsgs ->
-                   discard_warn_sender(Msgs);
-               true ->
-                   Query = lists:map(
-                             fun(M) ->
-                                     Username =
-                                         ejabberd_odbc:escape(
-                                           (M#offline_msg.to)#jid.luser),
-                                     From = M#offline_msg.from,
-                                     To = M#offline_msg.to,
-                                     {xmlelement, Name, Attrs, Els} =
-                                         M#offline_msg.packet,
-                                     Attrs2 = jlib:replace_from_to_attrs(
-                                                jlib:jid_to_string(From),
-                                                jlib:jid_to_string(To),
-                                                Attrs),
-                                     Packet = {xmlelement, Name, Attrs2,
-                                               Els ++
-                                               [jlib:timestamp_to_xml(
-                                                  calendar:now_to_universal_time(
-                                            M#offline_msg.timestamp),
-                                          utc,
-                                          jlib:make_jid("", Host, ""),
-                                          "Offline Storage"),
-                                        %% TODO: Delete the next three lines once XEP-0091 is Obsolete
-                                        jlib:timestamp_to_xml( 
-                                          calendar:now_to_universal_time(
-                                                    M#offline_msg.timestamp))]},
-                                     XML =
-                                         ejabberd_odbc:escape(
-                                           xml:element_to_binary(Packet)),
-                                     odbc_queries:add_spool_sql(Username, XML)
-                             end, Msgs),
-                   case catch odbc_queries:add_spool(Host, Query) of
-                       {'EXIT', Reason} ->
-                           ?ERROR_MSG("~p~n", [Reason]);
-                       {error, Reason} ->
-                           ?ERROR_MSG("~p~n", [Reason]);
-                       _ ->
-                           ok
-                   end
-           end,
-           loop(Host, AccessMaxOfflineMsgs);
-       _ ->
-           loop(Host, AccessMaxOfflineMsgs)
-    end.
-
-%% Function copied from ejabberd_sm.erl:
-get_max_user_messages(AccessRule, LUser, Host) ->
-    case acl:match_rule(
-          Host, AccessRule, jlib:make_jid(LUser, Host, "")) of
-       Max when is_integer(Max) -> Max;
-       infinity -> infinity;
-       _ -> ?MAX_USER_MESSAGES
-    end.
-
-receive_all(Username, Msgs) ->
-    receive
-       #offline_msg{user=Username} = Msg ->
-           receive_all(Username, [Msg | Msgs])
-    after 0 ->
-           lists:reverse(Msgs)
-    end.
-
-
-stop(Host) ->
-    ejabberd_hooks:delete(offline_message_hook, Host,
-                         ?MODULE, store_packet, 50),
-    ejabberd_hooks:delete(resend_offline_messages_hook, Host,
-                         ?MODULE, pop_offline_messages, 50),
-    ejabberd_hooks:delete(remove_user, Host,
-                         ?MODULE, remove_user, 50),
-    ejabberd_hooks:delete(anonymous_purge_hook, Host,
-                         ?MODULE, remove_user, 50),
-    ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 50),
-    ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, get_sm_features, 50),
-    ejabberd_hooks:delete(webadmin_page_host, Host,
-                         ?MODULE, webadmin_page, 50),
-    ejabberd_hooks:delete(webadmin_user, Host,
-                         ?MODULE, webadmin_user, 50),
-    ejabberd_hooks:delete(webadmin_user_parse_query, Host,
-                          ?MODULE, webadmin_user_parse_query, 50),
-    Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
-    exit(whereis(Proc), stop),
-    ok.
-
-get_sm_features(Acc, _From, _To, "", _Lang) ->
-    Feats = case Acc of
-               {result, I} -> I;
-               _ -> []
-           end,
-    {result, Feats ++ [?NS_FEATURE_MSGOFFLINE]};
-
-get_sm_features(_Acc, _From, _To, ?NS_FEATURE_MSGOFFLINE, _Lang) ->
-    %% override all lesser features...
-    {result, []};
-
-get_sm_features(Acc, _From, _To, _Node, _Lang) ->
-    Acc.
-
-
-store_packet(From, To, Packet) ->
-    Type = xml:get_tag_attr_s("type", Packet),
-    if
-       (Type /= "error") and (Type /= "groupchat") and
-       (Type /= "headline") ->
-           case check_event_chatstates(From, To, Packet) of
-               true ->
-                   #jid{luser = LUser} = To,
-                   TimeStamp = now(),
-                   {xmlelement, _Name, _Attrs, Els} = Packet,
-                   Expire = find_x_expire(TimeStamp, Els),
-                   gen_mod:get_module_proc(To#jid.lserver, ?PROCNAME) !
-                       #offline_msg{user = LUser,
-                                    timestamp = TimeStamp,
-                                    expire = Expire,
-                                    from = From,
-                                    to = To,
-                                    packet = Packet},
-                   stop;
-               _ ->
-                   ok
-           end;
-       true ->
-           ok
-    end.
-
-%% Check if the packet has any content about XEP-0022 or XEP-0085
-check_event_chatstates(From, To, Packet) ->
-    {xmlelement, Name, Attrs, Els} = Packet,
-    case find_x_event_chatstates(Els, {false, false, false}) of
-       %% There wasn't any x:event or chatstates subelements
-       {false, false, _} ->
-           true;
-       %% There a chatstates subelement and other stuff, but no x:event
-       {false, CEl, true} when CEl /= false ->
-           true;
-       %% There was only a subelement: a chatstates
-       {false, CEl, false} when CEl /= false ->
-           %% Don't allow offline storage
-           false;
-       %% There was an x:event element, and maybe also other stuff
-       {El, _, _} when El /= false ->
-           case xml:get_subtag(El, "id") of
-               false ->
-                   case xml:get_subtag(El, "offline") of
-                       false ->
-                           true;
-                       _ ->
-                           ID = case xml:get_tag_attr_s("id", Packet) of
-                                    "" ->
-                                        {xmlelement, "id", [], []};
-                                    S ->
-                                        {xmlelement, "id", [],
-                                         [{xmlcdata, S}]}
-                                end,
-                           ejabberd_router:route(
-                             To, From, {xmlelement, Name, Attrs,
-                                        [{xmlelement, "x",
-                                          [{"xmlns", ?NS_EVENT}],
-                                          [ID,
-                                           {xmlelement, "offline", [], []}]}]
-                                       }),
-                           true
-                   end;
-               _ ->
-                   false
-           end
-    end.
-
-%% Check if the packet has subelements about XEP-0022, XEP-0085 or other
-find_x_event_chatstates([], Res) ->
-    Res;
-find_x_event_chatstates([{xmlcdata, _} | Els], Res) ->
-    find_x_event_chatstates(Els, Res);
-find_x_event_chatstates([El | Els], {A, B, C}) ->
-    case xml:get_tag_attr_s("xmlns", El) of
-       ?NS_EVENT ->
-           find_x_event_chatstates(Els, {El, B, C});
-       ?NS_CHATSTATES ->
-           find_x_event_chatstates(Els, {A, El, C});
-       _ ->
-           find_x_event_chatstates(Els, {A, B, true})
-    end.
-
-find_x_expire(_, []) ->
-    never;
-find_x_expire(TimeStamp, [{xmlcdata, _} | Els]) ->
-    find_x_expire(TimeStamp, Els);
-find_x_expire(TimeStamp, [El | Els]) ->
-    case xml:get_tag_attr_s("xmlns", El) of
-       ?NS_EXPIRE ->
-           Val = xml:get_tag_attr_s("seconds", El),
-           case catch list_to_integer(Val) of
-               {'EXIT', _} ->
-                   never;
-               Int when Int > 0 ->
-                   {MegaSecs, Secs, MicroSecs} = TimeStamp,
-                   S = MegaSecs * 1000000 + Secs + Int,
-                   MegaSecs1 = S div 1000000,
-                   Secs1 = S rem 1000000,
-                   {MegaSecs1, Secs1, MicroSecs};
-               _ ->
-                   never
-           end;
-       _ ->
-           find_x_expire(TimeStamp, Els)
-    end.
-
-
-pop_offline_messages(Ls, User, Server) ->
-    LUser = jlib:nodeprep(User),
-    LServer = jlib:nameprep(Server),
-    EUser = ejabberd_odbc:escape(LUser),
-    case odbc_queries:get_and_del_spool_msg_t(LServer, EUser) of
-       {atomic, {selected, ["username","xml"], Rs}} ->
-           Ls ++ lists:flatmap(
-                   fun({_, XML}) ->
-                           case xml_stream:parse_element(XML) of
-                               {error, _Reason} ->
-                                   [];
-                               El ->
-                                   To = jlib:string_to_jid(
-                                          xml:get_tag_attr_s("to", El)),
-                                   From = jlib:string_to_jid(
-                                            xml:get_tag_attr_s("from", El)),
-                                   if
-                                       (To /= error) and
-                                       (From /= error) ->
-                                           [{route, From, To, El}];
-                                       true ->
-                                           []
-                                   end
-                           end
-                   end, Rs);
-       _ ->
-           Ls
-    end.
-
-
-remove_user(User, Server) ->
-    LUser = jlib:nodeprep(User),
-    LServer = jlib:nameprep(Server),
-    Username = ejabberd_odbc:escape(LUser),
-    odbc_queries:del_spool_msg(LServer, Username).
-
-
-%% Helper functions:
-
-%% TODO: Warning - This function is a duplicate from mod_offline.erl
-%% It is duplicate to stay consistent (many functions are duplicated
-%% in this module). It will be refactored later on.
-%% Warn senders that their messages have been discarded:
-discard_warn_sender(Msgs) ->
-    lists:foreach(
-      fun(#offline_msg{from=From, to=To, packet=Packet}) ->
-             ErrText = "Your contact offline message queue is full. The message has been discarded.",
-             Lang = xml:get_tag_attr_s("xml:lang", Packet),
-             Err = jlib:make_error_reply(
-                     Packet, ?ERRT_RESOURCE_CONSTRAINT(Lang, ErrText)),
-             ejabberd_router:route(
-               To,
-               From, Err)
-      end, Msgs).
-
-
-webadmin_page(_, Host,
-             #request{us = _US,
-                      path = ["user", U, "queue"],
-                      q = Query,
-                      lang = Lang} = _Request) ->
-    Res = user_queue(U, Host, Query, Lang),
-    {stop, Res};
-
-webadmin_page(Acc, _, _) -> Acc.
-
-user_queue(User, Server, Query, Lang) ->
-    LUser = jlib:nodeprep(User),
-    LServer = jlib:nameprep(Server),
-    Username = ejabberd_odbc:escape(LUser),
-    US = {LUser, LServer},
-    Res = user_queue_parse_query(Username, LServer, Query),
-    MsgsAll = case catch ejabberd_odbc:sql_query(
-                       LServer,
-                       ["select username, xml from spool"
-                        "  where username='", Username, "'"
-                        "  order by seq;"]) of
-              {selected, ["username", "xml"], Rs} ->
-                  lists:flatmap(
-                    fun({_, XML}) ->
-                            case xml_stream:parse_element(XML) of
-                                {error, _Reason} ->
-                                    [];
-                                El ->
-                                    [El]
-                            end
-                    end, Rs);
-              _ ->
-                  []
-          end,
-    Msgs = get_messages_subset(User, Server, MsgsAll),
-    FMsgs =
-       lists:map(
-         fun({xmlelement, _Name, _Attrs, _Els} = Msg) ->
-                 ID = jlib:encode_base64(binary_to_list(term_to_binary(Msg))),
-                 Packet = Msg,
-                 FPacket = ejabberd_web_admin:pretty_print_xml(Packet),
-                 ?XE("tr",
-                     [?XAE("td", [{"class", "valign"}], [?INPUT("checkbox", "selected", ID)]),
-                      ?XAE("td", [{"class", "valign"}], [?XC("pre", FPacket)])]
-                    )
-         end, Msgs),
-    [?XC("h1", io_lib:format(?T("~s's Offline Messages Queue"),
-                            [us_to_list(US)]))] ++
-       case Res of
-           ok -> [?XREST("Submitted")];
-           nothing -> []
-       end ++
-       [?XAE("form", [{"action", ""}, {"method", "post"}],
-             [?XE("table",
-                  [?XE("thead",
-                       [?XE("tr",
-                            [?X("td"),
-                             ?XCT("td", "Packet")
-                            ])]),
-                   ?XE("tbody",
-                       if
-                           FMsgs == [] ->
-                               [?XE("tr",
-                                    [?XAC("td", [{"colspan", "4"}], " ")]
-                                   )];
-                           true ->
-                               FMsgs
-                       end
-                      )]),
-              ?BR,
-              ?INPUTT("submit", "delete", "Delete Selected")
-             ])].
-
-user_queue_parse_query(Username, LServer, Query) ->
-    case lists:keysearch("delete", 1, Query) of
-       {value, _} ->
-           Msgs = case catch ejabberd_odbc:sql_query(
-                               LServer,
-                               ["select xml, seq from spool"
-                                "  where username='", Username, "'"
-                                "  order by seq;"]) of
-                      {selected, ["xml", "seq"], Rs} ->
-                          lists:flatmap(
-                            fun({XML, Seq}) ->
-                                    case xml_stream:parse_element(XML) of
-                                        {error, _Reason} ->
-                                            [];
-                                        El ->
-                                            [{El, Seq}]
-                                    end
-                            end, Rs);
-                      _ ->
-                          []
-                  end,
-           F = fun() ->
-                       lists:foreach(
-                         fun({Msg, Seq}) ->
-                                 ID = jlib:encode_base64(
-                                        binary_to_list(term_to_binary(Msg))),
-                                 case lists:member({"selected", ID}, Query) of
-                                     true ->
-                                         SSeq = ejabberd_odbc:escape(Seq),
-                                         catch ejabberd_odbc:sql_query(
-                                                 LServer,
-                                                 ["delete from spool"
-                                                  "  where username='", Username, "'"
-                                                  "  and seq='", SSeq, "';"]);
-                                     false ->
-                                         ok
-                                 end
-                         end, Msgs)
-               end,
-           mnesia:transaction(F),
-           ok;
-       false ->
-           nothing
-    end.
-
-us_to_list({User, Server}) ->
-    jlib:jid_to_string({User, Server, ""}).
-
-get_queue_length(Username, LServer) ->
-    case catch ejabberd_odbc:sql_query(
-                           LServer,
-                           ["select count(*) from spool"
-                            "  where username='", Username, "';"]) of
-                  {selected, [_], [{SCount}]} ->
-                      SCount;
-                  _ ->
-                      0
-              end.
-
-get_messages_subset(User, Host, MsgsAll) ->
-    Access = gen_mod:get_module_opt(Host, ?MODULE, access_max_user_messages,
-                                   max_user_offline_messages),
-    MaxOfflineMsgs = case get_max_user_messages(Access, User, Host) of
-                        Number when is_integer(Number) -> Number;
-                        _ -> 100
-                    end,
-    Length = length(MsgsAll),
-    get_messages_subset2(MaxOfflineMsgs, Length, MsgsAll).
-
-get_messages_subset2(Max, Length, MsgsAll) when Length =< Max*2 ->
-    MsgsAll;
-get_messages_subset2(Max, Length, MsgsAll) ->
-    FirstN = Max,
-    {MsgsFirstN, Msgs2} = lists:split(FirstN, MsgsAll),
-    MsgsLastN = lists:nthtail(Length - FirstN - FirstN, Msgs2),
-    IntermediateMsg = {xmlelement, "...", [], []},
-    MsgsFirstN ++ [IntermediateMsg] ++ MsgsLastN.
-
-webadmin_user(Acc, User, Server, Lang) ->
-    LUser = jlib:nodeprep(User),
-    LServer = jlib:nameprep(Server),
-    Username = ejabberd_odbc:escape(LUser),
-    QueueLen = get_queue_length(Username, LServer),
-    FQueueLen = [?AC("queue/", QueueLen)],
-    Acc ++ [?XCT("h3", "Offline Messages:")] ++ FQueueLen ++ [?C(" "), ?INPUTT("submit", "removealloffline", "Remove All Offline Messages")].
-
-webadmin_user_parse_query(_, "removealloffline", User, Server, _Query) ->
-    case catch odbc_queries:del_spool_msg(Server, User) of
-         {'EXIT', Reason} ->
-            ?ERROR_MSG("Failed to remove offline messages: ~p", [Reason]),
-            {stop, error};
-         {error, Reason} ->
-            ?ERROR_MSG("Failed to remove offline messages: ~p", [Reason]),
-            {stop, error};
-         _ ->
-            ?INFO_MSG("Removed all offline messages for ~s@~s", [User, Server]),
-            {stop, ok}
-    end;
-webadmin_user_parse_query(Acc, _Action, _User, _Server, _Query) ->
-    Acc.
-
-%% ------------------------------------------------
-%% mod_offline: number of messages quota management
-
-%% Returns as integer the number of offline messages for a given user
-count_offline_messages(LUser, LServer) ->
-    Username = ejabberd_odbc:escape(LUser),
-    case catch odbc_queries:count_records_where(
-                LServer, "spool", "where username='" ++ Username ++ "'") of
-        {selected, [_], [{Res}]} ->
-            list_to_integer(Res);
-        _ ->
-            0
-    end.
index 665b8866a6d827cd0c90d6aac5e5baeff8faee2a..42809ef8b2dce202a24bd3f5853152c0e8569749 100644 (file)
         get_user_list/3,
         check_packet/6,
         remove_user/2,
+         item_to_raw/1,
+         raw_to_item/1,
+         is_list_needdb/1,
         updated_list/3]).
 
+%% For mod_blocking
+-export([sql_add_privacy_list/2,
+         sql_get_default_privacy_list/2,
+         sql_get_default_privacy_list_t/1,
+         sql_get_privacy_list_data/3,
+         sql_get_privacy_list_data_by_id_t/1,
+         sql_get_privacy_list_id_t/2,
+         sql_set_default_privacy_list/2,
+         sql_set_privacy_list/2]).
+
 -include("ejabberd.hrl").
 -include("jlib.hrl").
 -include("mod_privacy.hrl").
 
 start(Host, Opts) ->
     IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
-    mnesia:create_table(privacy, [{disc_copies, [node()]},
-                                 {attributes, record_info(fields, privacy)}]),
-    update_table(),
+    case gen_mod:db_type(Opts) of
+        mnesia ->
+            mnesia:create_table(privacy,
+                                [{disc_copies, [node()]},
+                                 {attributes, record_info(fields, privacy)}]),
+            update_table();
+        _ ->
+            ok
+    end,
     ejabberd_hooks:add(privacy_iq_get, Host,
                       ?MODULE, process_iq_get, 50),
     ejabberd_hooks:add(privacy_iq_set, Host,
@@ -102,68 +121,116 @@ process_iq_get(_, From, _To, #iq{sub_el = SubEl},
            {error, ?ERR_BAD_REQUEST}
     end.
 
-
 process_lists_get(LUser, LServer, Active) ->
+    case process_lists_get(LUser, LServer, Active,
+                           gen_mod:db_type(LServer, ?MODULE)) of
+        error ->
+            {error, ?ERR_INTERNAL_SERVER_ERROR};
+        {_Default, []} ->
+            {result, [{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}], []}]};
+        {Default, LItems} ->
+            DItems =
+                case Default of
+                    none ->
+                        LItems;
+                    _ ->
+                        [{xmlelement, "default",
+                          [{"name", Default}], []} | LItems]
+                end,
+            ADItems =
+                case Active of
+                    none ->
+                        DItems;
+                    _ ->
+                        [{xmlelement, "active",
+                          [{"name", Active}], []} | DItems]
+                end,
+            {result,
+             [{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}],
+               ADItems}]}
+    end.
+
+process_lists_get(LUser, LServer, _Active, mnesia) ->
     case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
        {'EXIT', _Reason} ->
-           {error, ?ERR_INTERNAL_SERVER_ERROR};
+            error;
        [] ->
-           {result, [{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}], []}]};
+            {none, []};
        [#privacy{default = Default, lists = Lists}] ->
-           case Lists of
-               [] ->
-                   {result, [{xmlelement, "query",
-                              [{"xmlns", ?NS_PRIVACY}], []}]};
-               _ ->
-                   LItems = lists:map(
-                              fun({N, _}) ->
-                                      {xmlelement, "list",
-                                       [{"name", N}], []}
-                              end, Lists),
-                   DItems =
-                       case Default of
-                           none ->
-                               LItems;
-                           _ ->
-                               [{xmlelement, "default",
-                                 [{"name", Default}], []} | LItems]
-                       end,
-                   ADItems =
-                       case Active of
-                           none ->
-                               DItems;
-                           _ ->
-                               [{xmlelement, "active",
-                                 [{"name", Active}], []} | DItems]
-                       end,
-                   {result,
-                    [{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}],
-                      ADItems}]}
-           end
+            LItems = lists:map(
+                       fun({N, _}) ->
+                               {xmlelement, "list", [{"name", N}], []}
+                       end, Lists),
+            {Default, LItems}
+    end;
+process_lists_get(LUser, LServer, _Active, odbc) ->
+    Default = case catch sql_get_default_privacy_list(LUser, LServer) of
+                 {selected, ["name"], []} ->
+                     none;
+                 {selected, ["name"], [{DefName}]} ->
+                     DefName;
+                 _ ->
+                     none
+             end,
+    case catch sql_get_privacy_list_names(LUser, LServer) of
+       {selected, ["name"], Names} ->
+            LItems = lists:map(
+                       fun({N}) ->
+                               {xmlelement, "list", [{"name", N}], []}
+                       end, Names),
+            {Default, LItems};
+        _ ->
+            error
     end.
 
 process_list_get(LUser, LServer, {value, Name}) ->
+    case process_list_get(LUser, LServer, Name,
+                          gen_mod:db_type(LServer, ?MODULE)) of
+        error ->
+            {error, ?ERR_INTERNAL_SERVER_ERROR};
+        not_found ->
+            {error, ?ERR_ITEM_NOT_FOUND};
+        Items ->
+            LItems = lists:map(fun item_to_xml/1, Items),
+            {result,
+             [{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}],
+               [{xmlelement, "list",
+                 [{"name", Name}], LItems}]}]}
+    end;
+process_list_get(_LUser, _LServer, false) ->
+    {error, ?ERR_BAD_REQUEST}.
+
+process_list_get(LUser, LServer, Name, mnesia) ->
     case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
        {'EXIT', _Reason} ->
-           {error, ?ERR_INTERNAL_SERVER_ERROR};
+            error;
        [] ->
-           {error, ?ERR_ITEM_NOT_FOUND};
-           %{result, [{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}], []}]};
+            not_found;
        [#privacy{lists = Lists}] ->
            case lists:keysearch(Name, 1, Lists) of
                {value, {_, List}} ->
-                   LItems = lists:map(fun item_to_xml/1, List),
-                   {result,
-                    [{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}],
-                      [{xmlelement, "list",
-                        [{"name", Name}], LItems}]}]};
+                    List;
                _ ->
-                   {error, ?ERR_ITEM_NOT_FOUND}
+                    not_found
            end
     end;
-
-process_list_get(_LUser, _LServer, false) ->
-    {error, ?ERR_BAD_REQUEST}.
+process_list_get(LUser, LServer, Name, odbc) ->
+    case catch sql_get_privacy_list_id(LUser, LServer, Name) of
+       {selected, ["id"], []} ->
+            not_found;
+       {selected, ["id"], [{ID}]} ->
+           case catch sql_get_privacy_list_data_by_id(ID, LServer) of
+               {selected, ["t", "value", "action", "ord", "match_all",
+                           "match_iq", "match_message",
+                           "match_presence_in", "match_presence_out"],
+                RItems} ->
+                   lists:map(fun raw_to_item/1, RItems);
+               _ ->
+                    error
+           end;
+       _ ->
+            error
+    end.
 
 item_to_xml(Item) ->
     Attrs1 = [{"action", action_to_list(Item#listitem.action)},
@@ -269,98 +336,194 @@ process_iq_set(_, From, _To, #iq{sub_el = SubEl}) ->
            {error, ?ERR_BAD_REQUEST}
     end.
 
+process_default_set(LUser, LServer, Value) ->
+    case process_default_set(LUser, LServer, Value,
+                             gen_mod:db_type(LServer, ?MODULE)) of
+        {atomic, error} ->
+            {error, ?ERR_INTERNAL_SERVER_ERROR};
+        {atomic, not_found} ->
+            {error, ?ERR_ITEM_NOT_FOUND};
+       {atomic, ok} ->
+            {result, []};
+       _ ->
+           {error, ?ERR_INTERNAL_SERVER_ERROR}
+    end.
 
-process_default_set(LUser, LServer, {value, Name}) ->
+process_default_set(LUser, LServer, {value, Name}, mnesia) ->
     F = fun() ->
                case mnesia:read({privacy, {LUser, LServer}}) of
                    [] ->
-                       {error, ?ERR_ITEM_NOT_FOUND};
+                        not_found;
                    [#privacy{lists = Lists} = P] ->
                        case lists:keymember(Name, 1, Lists) of
                            true ->
                                mnesia:write(P#privacy{default = Name,
                                                       lists = Lists}),
-                               {result, []};
+                                ok;
                            false ->
-                               {error, ?ERR_ITEM_NOT_FOUND}
+                                not_found
                        end
                end
        end,
-    case mnesia:transaction(F) of
-       {atomic, {error, _} = Error} ->
-           Error;
-       {atomic, {result, _} = Res} ->
-           Res;
-       _ ->
-           {error, ?ERR_INTERNAL_SERVER_ERROR}
-    end;
-
-process_default_set(LUser, LServer, false) ->
+    mnesia:transaction(F);
+process_default_set(LUser, LServer, {value, Name}, odbc) ->
+    F = fun() ->
+               case sql_get_privacy_list_names_t(LUser) of
+                   {selected, ["name"], []} ->
+                        not_found;
+                   {selected, ["name"], Names} ->
+                       case lists:member({Name}, Names) of
+                           true ->
+                               sql_set_default_privacy_list(LUser, Name),
+                                ok;
+                           false ->
+                                not_found
+                       end
+               end
+       end,
+    odbc_queries:sql_transaction(LServer, F);
+process_default_set(LUser, LServer, false, mnesia) ->
     F = fun() ->
                case mnesia:read({privacy, {LUser, LServer}}) of
                    [] ->
-                       {result, []};
+                        ok;
                    [R] ->
-                       mnesia:write(R#privacy{default = none}),
-                       {result, []}
+                       mnesia:write(R#privacy{default = none})
                end
        end,
-    case mnesia:transaction(F) of
-       {atomic, {error, _} = Error} ->
-           Error;
-       {atomic, {result, _} = Res} ->
-           Res;
+    mnesia:transaction(F);
+process_default_set(LUser, LServer, false, odbc) ->
+    case catch sql_unset_default_privacy_list(LUser, LServer) of
+       {'EXIT', _Reason} ->
+            {atomic, error};
+       {error, _Reason} ->
+            {atomic, error};
        _ ->
-           {error, ?ERR_INTERNAL_SERVER_ERROR}
+           {atomic, ok}
     end.
 
-
 process_active_set(LUser, LServer, {value, Name}) ->
+    case process_active_set(LUser, LServer, Name,
+                            gen_mod:db_type(LServer, ?MODULE)) of
+        error ->
+            {error, ?ERR_ITEM_NOT_FOUND};
+        Items ->
+            NeedDb = is_list_needdb(Items),
+            {result, [], #userlist{name = Name, list = Items, needdb = NeedDb}}
+    end;
+process_active_set(_LUser, _LServer, false) ->
+    {result, [], #userlist{}}.
+
+process_active_set(LUser, LServer, Name, mnesia) ->
     case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
        [] ->
-           {error, ?ERR_ITEM_NOT_FOUND};
+            error;
        [#privacy{lists = Lists}] ->
            case lists:keysearch(Name, 1, Lists) of
                {value, {_, List}} ->
-                   NeedDb = is_list_needdb(List),
-                   {result, [], #userlist{name = Name, list = List, needdb = NeedDb}};
+                    List;
                false ->
-                   {error, ?ERR_ITEM_NOT_FOUND}
+                    error
            end
     end;
+process_active_set(LUser, LServer, Name, odbc) ->
+    case catch sql_get_privacy_list_id(LUser, LServer, Name) of
+       {selected, ["id"], []} ->
+            error;
+       {selected, ["id"], [{ID}]} ->
+           case catch sql_get_privacy_list_data_by_id(ID, LServer) of
+               {selected, ["t", "value", "action", "ord", "match_all",
+                           "match_iq", "match_message",
+                           "match_presence_in", "match_presence_out"],
+                RItems} ->
+                   lists:map(fun raw_to_item/1, RItems);
+               _ ->
+                    error
+           end;
+       _ ->
+            error
+    end.
 
-process_active_set(_LUser, _LServer, false) ->
-    {result, [], #userlist{}}.
-
+remove_privacy_list(LUser, LServer, Name, mnesia) ->
+    F =        fun() ->
+                case mnesia:read({privacy, {LUser, LServer}}) of
+                    [] ->
+                        ok;
+                    [#privacy{default = Default, lists = Lists} = P] ->
+                        %% TODO: check active
+                        if
+                            Name == Default ->
+                                conflict;
+                            true ->
+                                NewLists =
+                                    lists:keydelete(Name, 1, Lists),
+                                mnesia:write(
+                                  P#privacy{lists = NewLists})
+                        end
+                end
+        end,
+    mnesia:transaction(F);
+remove_privacy_list(LUser, LServer, Name, odbc) ->
+    F =        fun() ->
+                case sql_get_default_privacy_list_t(LUser) of
+                    {selected, ["name"], []} ->
+                        sql_remove_privacy_list(LUser, Name),
+                        ok;
+                    {selected, ["name"], [{Default}]} ->
+                        %% TODO: check active
+                        if
+                            Name == Default ->
+                                conflict;
+                            true ->
+                                sql_remove_privacy_list(LUser, Name),
+                                ok
+                        end
+                end
+        end,
+    odbc_queries:sql_transaction(LServer, F).
+
+set_privacy_list(LUser, LServer, Name, List, mnesia) ->
+    F =        fun() ->
+                case mnesia:wread({privacy, {LUser, LServer}}) of
+                    [] ->
+                        NewLists = [{Name, List}],
+                        mnesia:write(#privacy{us = {LUser, LServer},
+                                              lists = NewLists});
+                    [#privacy{lists = Lists} = P] ->
+                        NewLists1 = lists:keydelete(Name, 1, Lists),
+                        NewLists = [{Name, List} | NewLists1],
+                        mnesia:write(P#privacy{lists = NewLists})
+               end
+        end,
+    mnesia:transaction(F);
+set_privacy_list(LUser, LServer, Name, List, odbc) ->
+    RItems = lists:map(fun item_to_raw/1, List),
+    F =        fun() ->
+                ID =
+                    case sql_get_privacy_list_id_t(LUser, Name) of
+                        {selected, ["id"], []} ->
+                            sql_add_privacy_list(LUser, Name),
+                            {selected, ["id"], [{I}]} =
+                                sql_get_privacy_list_id_t(LUser, Name),
+                            I;
+                        {selected, ["id"], [{I}]} ->
+                            I
+                    end,
+                sql_set_privacy_list(ID, RItems),
+                ok
+        end,
+    odbc_queries:sql_transaction(LServer, F).
 
 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, LServer}}) of
-                           [] ->
-                               {result, []};
-                           [#privacy{default = Default, lists = Lists} = P] ->
-                               % TODO: check active
-                               if
-                                   Name == Default ->
-                                       {error, ?ERR_CONFLICT};
-                                   true ->
-                                       NewLists =
-                                           lists:keydelete(Name, 1, Lists),
-                                       mnesia:write(
-                                         P#privacy{lists = NewLists}),
-                                       {result, []}
-                               end
-                       end
-               end,
-           case mnesia:transaction(F) of
-               {atomic, {error, _} = Error} ->
-                   Error;
-               {atomic, {result, _} = Res} ->
+           case remove_privacy_list(LUser, LServer, Name,
+                                     gen_mod:db_type(LServer, ?MODULE)) of
+               {atomic, conflict} ->
+                    {error, ?ERR_CONFLICT};
+               {atomic, ok} ->
                    ejabberd_router:route(
                      jlib:make_jid(LUser, LServer, ""),
                      jlib:make_jid(LUser, LServer, ""),
@@ -368,30 +531,14 @@ process_list_set(LUser, LServer, {value, Name}, Els) ->
                       [{privacy_list,
                         #userlist{name = Name, list = []},
                         Name}]}),
-                   Res;
+                    {result, []};
                _ ->
                    {error, ?ERR_INTERNAL_SERVER_ERROR}
            end;
        List ->
-           F =
-               fun() ->
-                       case mnesia:wread({privacy, {LUser, LServer}}) of
-                           [] ->
-                               NewLists = [{Name, List}],
-                               mnesia:write(#privacy{us = {LUser, LServer},
-                                                     lists = NewLists}),
-                               {result, []};
-                           [#privacy{lists = Lists} = P] ->
-                               NewLists1 = lists:keydelete(Name, 1, Lists),
-                               NewLists = [{Name, List} | NewLists1],
-                               mnesia:write(P#privacy{lists = NewLists}),
-                               {result, []}
-                       end
-               end,
-           case mnesia:transaction(F) of
-               {atomic, {error, _} = Error} ->
-                   Error;
-               {atomic, {result, _} = Res} ->
+           case set_privacy_list(LUser, LServer, Name, List,
+                                  gen_mod:db_type(LServer, ?MODULE)) of
+               {atomic, ok} ->
                    NeedDb = is_list_needdb(List),
                    ejabberd_router:route(
                      jlib:make_jid(LUser, LServer, ""),
@@ -400,7 +547,7 @@ process_list_set(LUser, LServer, {value, Name}, Els) ->
                       [{privacy_list,
                         #userlist{name = Name, list = List, needdb = NeedDb},
                         Name}]}),
-                   Res;
+                   {result, []};
                _ ->
                    {error, ?ERR_INTERNAL_SERVER_ERROR}
            end
@@ -532,30 +679,51 @@ is_list_needdb(Items) ->
              end
       end, Items).
 
-get_user_list(_, User, Server) ->
+get_user_list(Acc, User, Server) ->
     LUser = jlib:nodeprep(User),
     LServer = jlib:nameprep(Server),
+    {Default, Items} = get_user_list(Acc, LUser, LServer,
+                                     gen_mod:db_type(LServer, ?MODULE)),
+    NeedDb = is_list_needdb(Items),
+    #userlist{name = Default, list = Items, needdb = NeedDb}.
+
+get_user_list(_, LUser, LServer, mnesia) ->
     case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
        [] ->
-           #userlist{};
+            {none, []};
        [#privacy{default = Default, lists = Lists}] ->
            case Default of
                none ->
-                   #userlist{};
+                    {none, []};
                _ ->
                    case lists:keysearch(Default, 1, Lists) of
                        {value, {_, List}} ->
-                           NeedDb = is_list_needdb(List),
-                           #userlist{name = Default, list = List, needdb = NeedDb};
+                            {Default, List};
                        _ ->
-                           #userlist{}
+                            {none, []}
                    end
            end;
        _ ->
-           #userlist{}
+            {none, []}
+    end;
+get_user_list(_, LUser, LServer, odbc) ->
+    case catch sql_get_default_privacy_list(LUser, LServer) of
+       {selected, ["name"], []} ->
+            {none, []};
+       {selected, ["name"], [{Default}]} ->
+           case catch sql_get_privacy_list_data(LUser, LServer, Default) of
+               {selected, ["t", "value", "action", "ord", "match_all",
+                           "match_iq", "match_message",
+                           "match_presence_in", "match_presence_out"],
+                RItems} ->
+                   {Default, lists:map(fun raw_to_item/1, RItems)};
+               _ ->
+                    {none, []}
+           end;
+       _ ->
+            {none, []}
     end.
 
-
 %% From is the sender, To is the destination.
 %% If Dir = out, User@Server is the sender account (From).
 %% If Dir = in, User@Server is the destination account (To).
@@ -699,12 +867,16 @@ is_type_match(Type, Value, JID, Subscription, Groups) ->
 remove_user(User, Server) ->
     LUser = jlib:nodeprep(User),
     LServer = jlib:nameprep(Server),
+    remove_user(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
+
+remove_user(LUser, LServer, mnesia) ->
     F = fun() ->
                mnesia:delete({privacy,
                               {LUser, LServer}})
         end,
-    mnesia:transaction(F).
-
+    mnesia:transaction(F);
+remove_user(LUser, LServer, odbc) ->
+    sql_del_privacy_lists(LUser, LServer).
 
 updated_list(_,
             #userlist{name = OldName} = Old,
@@ -716,6 +888,160 @@ updated_list(_,
            Old
     end.
 
+raw_to_item({SType, SValue, SAction, SOrder, SMatchAll, SMatchIQ,
+            SMatchMessage, SMatchPresenceIn, SMatchPresenceOut}) ->
+    {Type, Value} =
+       case SType of
+           "n" ->
+               {none, none};
+           "j" ->
+               case jlib:string_to_jid(SValue) of
+                   #jid{} = JID ->
+                       {jid, jlib:jid_tolower(JID)}
+               end;
+           "g" ->
+               {group, SValue};
+           "s" ->
+               case SValue of
+                   "none" ->
+                       {subscription, none};
+                   "both" ->
+                       {subscription, both};
+                   "from" ->
+                       {subscription, from};
+                   "to" ->
+                       {subscription, to}
+               end
+       end,
+    Action =
+       case SAction of
+           "a" -> allow;
+           "d" -> deny
+       end,
+    Order = list_to_integer(SOrder),
+    MatchAll = ejabberd_odbc:to_bool(SMatchAll),
+    MatchIQ = ejabberd_odbc:to_bool(SMatchIQ),
+    MatchMessage = ejabberd_odbc:to_bool(SMatchMessage),
+    MatchPresenceIn = ejabberd_odbc:to_bool(SMatchPresenceIn),
+    MatchPresenceOut =  ejabberd_odbc:to_bool(SMatchPresenceOut),
+    #listitem{type = Type,
+             value = Value,
+             action = Action,
+             order = Order,
+             match_all = MatchAll,
+             match_iq = MatchIQ,
+             match_message = MatchMessage,
+             match_presence_in = MatchPresenceIn,
+             match_presence_out = MatchPresenceOut
+            }.
+
+item_to_raw(#listitem{type = Type,
+                     value = Value,
+                     action = Action,
+                     order = Order,
+                     match_all = MatchAll,
+                     match_iq = MatchIQ,
+                     match_message = MatchMessage,
+                     match_presence_in = MatchPresenceIn,
+                     match_presence_out = MatchPresenceOut
+                    }) ->
+    {SType, SValue} =
+       case Type of
+           none ->
+               {"n", ""};
+           jid ->
+               {"j", ejabberd_odbc:escape(jlib:jid_to_string(Value))};
+           group ->
+               {"g", ejabberd_odbc:escape(Value)};
+           subscription ->
+               case Value of
+                   none ->
+                       {"s", "none"};
+                   both ->
+                       {"s", "both"};
+                   from ->
+                       {"s", "from"};
+                   to ->
+                       {"s", "to"}
+               end
+       end,
+    SAction =
+       case Action of
+           allow -> "a";
+           deny -> "d"
+       end,
+    SOrder = integer_to_list(Order),
+    SMatchAll = if MatchAll -> "1"; true -> "0" end,
+    SMatchIQ = if MatchIQ -> "1"; true -> "0" end,
+    SMatchMessage = if MatchMessage -> "1"; true -> "0" end,
+    SMatchPresenceIn = if MatchPresenceIn -> "1"; true -> "0" end,
+    SMatchPresenceOut = if MatchPresenceOut -> "1"; true -> "0" end,
+    [SType, SValue, SAction, SOrder, SMatchAll, SMatchIQ,
+     SMatchMessage, SMatchPresenceIn, SMatchPresenceOut].
+
+sql_get_default_privacy_list(LUser, LServer) ->
+    Username = ejabberd_odbc:escape(LUser),
+    odbc_queries:get_default_privacy_list(LServer, Username).
+
+sql_get_default_privacy_list_t(LUser) ->
+    Username = ejabberd_odbc:escape(LUser),
+    odbc_queries:get_default_privacy_list_t(Username).
+
+sql_get_privacy_list_names(LUser, LServer) ->
+    Username = ejabberd_odbc:escape(LUser),
+    odbc_queries:get_privacy_list_names(LServer, Username).
+
+sql_get_privacy_list_names_t(LUser) ->
+    Username = ejabberd_odbc:escape(LUser),
+    odbc_queries:get_privacy_list_names_t(Username).
+
+sql_get_privacy_list_id(LUser, LServer, Name) ->
+    Username = ejabberd_odbc:escape(LUser),
+    SName = ejabberd_odbc:escape(Name),
+    odbc_queries:get_privacy_list_id(LServer, Username, SName).
+
+sql_get_privacy_list_id_t(LUser, Name) ->
+    Username = ejabberd_odbc:escape(LUser),
+    SName = ejabberd_odbc:escape(Name),
+    odbc_queries:get_privacy_list_id_t(Username, SName).
+
+sql_get_privacy_list_data(LUser, LServer, Name) ->
+    Username = ejabberd_odbc:escape(LUser),
+    SName = ejabberd_odbc:escape(Name),
+    odbc_queries:get_privacy_list_data(LServer, Username, SName).
+
+sql_get_privacy_list_data_by_id(ID, LServer) ->
+    odbc_queries:get_privacy_list_data_by_id(LServer, ID).
+
+sql_get_privacy_list_data_by_id_t(ID) ->
+    odbc_queries:get_privacy_list_data_by_id_t(ID).
+
+sql_set_default_privacy_list(LUser, Name) ->
+    Username = ejabberd_odbc:escape(LUser),
+    SName = ejabberd_odbc:escape(Name),
+    odbc_queries:set_default_privacy_list(Username, SName).
+
+sql_unset_default_privacy_list(LUser, LServer) ->
+    Username = ejabberd_odbc:escape(LUser),
+    odbc_queries:unset_default_privacy_list(LServer, Username).
+
+sql_remove_privacy_list(LUser, Name) ->
+    Username = ejabberd_odbc:escape(LUser),
+    SName = ejabberd_odbc:escape(Name),
+    odbc_queries:remove_privacy_list(Username, SName).
+
+sql_add_privacy_list(LUser, Name) ->
+    Username = ejabberd_odbc:escape(LUser),
+    SName = ejabberd_odbc:escape(Name),
+    odbc_queries:add_privacy_list(Username, SName).
+
+sql_set_privacy_list(ID, RItems) ->
+    odbc_queries:set_privacy_list(ID, RItems).
+
+sql_del_privacy_lists(LUser, LServer) ->
+    Username = ejabberd_odbc:escape(LUser),
+    Server = ejabberd_odbc:escape(LServer),
+    odbc_queries:del_privacy_lists(LServer, Server, Username).
 
 update_table() ->
     Fields = record_info(fields, privacy),
diff --git a/src/mod_privacy_odbc.erl b/src/mod_privacy_odbc.erl
deleted file mode 100644 (file)
index 87303b5..0000000
+++ /dev/null
@@ -1,878 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File    : mod_privacy_odbc.erl
-%%% Author  : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : jabber:iq:privacy support
-%%% Created :  5 Oct 2006 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2012   ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
--module(mod_privacy_odbc).
--author('alexey@process-one.net').
-
--behaviour(gen_mod).
-
--export([start/2, stop/1,
-        process_iq/3,
-        process_iq_set/4,
-        process_iq_get/5,
-        get_user_list/3,
-        check_packet/6,
-        remove_user/2,
-         item_to_raw/1,
-         raw_to_item/1,
-        updated_list/3]).
-
-%% For mod_blocking_odbc
--export([sql_add_privacy_list/2,
-         sql_get_default_privacy_list/2,
-         sql_get_default_privacy_list_t/1,
-         sql_get_privacy_list_data/3,
-         sql_get_privacy_list_data_by_id_t/1,
-         sql_get_privacy_list_id_t/2,
-         sql_set_default_privacy_list/2,
-         sql_set_privacy_list/2]).
-
--include("ejabberd.hrl").
--include("jlib.hrl").
--include("mod_privacy.hrl").
-
-start(Host, Opts) ->
-    IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
-    ejabberd_hooks:add(privacy_iq_get, Host,
-                      ?MODULE, process_iq_get, 50),
-    ejabberd_hooks:add(privacy_iq_set, Host,
-                      ?MODULE, process_iq_set, 50),
-    ejabberd_hooks:add(privacy_get_user_list, Host,
-                      ?MODULE, get_user_list, 50),
-    ejabberd_hooks:add(privacy_check_packet, Host,
-                      ?MODULE, check_packet, 50),
-    ejabberd_hooks:add(privacy_updated_list, Host,
-                      ?MODULE, updated_list, 50),
-    ejabberd_hooks:add(remove_user, Host,
-                      ?MODULE, remove_user, 50),
-    gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PRIVACY,
-                                 ?MODULE, process_iq, IQDisc).
-
-stop(Host) ->
-    ejabberd_hooks:delete(privacy_iq_get, Host,
-                         ?MODULE, process_iq_get, 50),
-    ejabberd_hooks:delete(privacy_iq_set, Host,
-                         ?MODULE, process_iq_set, 50),
-    ejabberd_hooks:delete(privacy_get_user_list, Host,
-                         ?MODULE, get_user_list, 50),
-    ejabberd_hooks:delete(privacy_check_packet, Host,
-                         ?MODULE, check_packet, 50),
-    ejabberd_hooks:delete(privacy_updated_list, Host,
-                         ?MODULE, updated_list, 50),
-    ejabberd_hooks:delete(remove_user, Host,
-                         ?MODULE, remove_user, 50),
-    gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PRIVACY).
-
-process_iq(_From, _To, IQ) ->
-    SubEl = IQ#iq.sub_el,
-    IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}.
-
-
-process_iq_get(_, From, _To, #iq{sub_el = SubEl},
-              #userlist{name = Active}) ->
-    #jid{luser = LUser, lserver = LServer} = From,
-    {xmlelement, _, _, Els} = SubEl,
-    case xml:remove_cdata(Els) of
-       [] ->
-           process_lists_get(LUser, LServer, Active);
-       [{xmlelement, Name, Attrs, _SubEls}] ->
-           case Name of
-               "list" ->
-                   ListName = xml:get_attr("name", Attrs),
-                   process_list_get(LUser, LServer, ListName);
-               _ ->
-                   {error, ?ERR_BAD_REQUEST}
-           end;
-       _ ->
-           {error, ?ERR_BAD_REQUEST}
-    end.
-
-
-process_lists_get(LUser, LServer, Active) ->
-    Default = case catch sql_get_default_privacy_list(LUser, LServer) of
-                 {selected, ["name"], []} ->
-                     none;
-                 {selected, ["name"], [{DefName}]} ->
-                     DefName;
-                 _ ->
-                     none
-             end,
-    case catch sql_get_privacy_list_names(LUser, LServer) of
-       {selected, ["name"], []} ->
-           {result, [{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}], []}]};
-       {selected, ["name"], Names} ->
-           LItems = lists:map(
-                      fun({N}) ->
-                              {xmlelement, "list",
-                               [{"name", N}], []}
-                      end, Names),
-           DItems =
-               case Default of
-                   none ->
-                       LItems;
-                   _ ->
-                       [{xmlelement, "default",
-                         [{"name", Default}], []} | LItems]
-               end,
-           ADItems =
-               case Active of
-                   none ->
-                       DItems;
-                   _ ->
-                       [{xmlelement, "active",
-                         [{"name", Active}], []} | DItems]
-               end,
-           {result,
-            [{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}],
-              ADItems}]};
-       _ ->
-           {error, ?ERR_INTERNAL_SERVER_ERROR}
-    end.
-
-process_list_get(LUser, LServer, {value, Name}) ->
-    case catch sql_get_privacy_list_id(LUser, LServer, Name) of
-       {selected, ["id"], []} ->
-           {error, ?ERR_ITEM_NOT_FOUND};
-       {selected, ["id"], [{ID}]} ->
-           case catch sql_get_privacy_list_data_by_id(ID, LServer) of
-               {selected, ["t", "value", "action", "ord", "match_all",
-                           "match_iq", "match_message",
-                           "match_presence_in", "match_presence_out"],
-                RItems} ->
-                   Items = lists:map(fun raw_to_item/1, RItems),
-                   LItems = lists:map(fun item_to_xml/1, Items),
-                   {result,
-                    [{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}],
-                      [{xmlelement, "list",
-                        [{"name", Name}], LItems}]}]};
-               _ ->
-                   {error, ?ERR_INTERNAL_SERVER_ERROR}
-           end;
-       _ ->
-           {error, ?ERR_INTERNAL_SERVER_ERROR}
-    end;
-
-process_list_get(_LUser, _LServer, false) ->
-    {error, ?ERR_BAD_REQUEST}.
-
-item_to_xml(Item) ->
-    Attrs1 = [{"action", action_to_list(Item#listitem.action)},
-             {"order", order_to_list(Item#listitem.order)}],
-    Attrs2 = case Item#listitem.type of
-                none ->
-                    Attrs1;
-                Type ->
-                    [{"type", type_to_list(Item#listitem.type)},
-                     {"value", value_to_list(Type, Item#listitem.value)} |
-                     Attrs1]
-            end,
-    SubEls = case Item#listitem.match_all of
-                true ->
-                    [];
-                false ->
-                    SE1 = case Item#listitem.match_iq of
-                              true ->
-                                  [{xmlelement, "iq", [], []}];
-                              false ->
-                                  []
-                          end,
-                    SE2 = case Item#listitem.match_message of
-                              true ->
-                                  [{xmlelement, "message", [], []} | SE1];
-                              false ->
-                                  SE1
-                          end,
-                    SE3 = case Item#listitem.match_presence_in of
-                              true ->
-                                  [{xmlelement, "presence-in", [], []} | SE2];
-                              false ->
-                                  SE2
-                          end,
-                    SE4 = case Item#listitem.match_presence_out of
-                              true ->
-                                  [{xmlelement, "presence-out", [], []} | SE3];
-                              false ->
-                                  SE3
-                          end,
-                    SE4
-            end,
-    {xmlelement, "item", Attrs2, SubEls}.
-
-
-action_to_list(Action) ->
-    case Action of
-       allow -> "allow";
-       deny -> "deny"
-    end.
-
-order_to_list(Order) ->
-    integer_to_list(Order).
-
-type_to_list(Type) ->
-    case Type of
-       jid -> "jid";
-       group -> "group";
-       subscription -> "subscription"
-    end.
-
-value_to_list(Type, Val) ->
-    case Type of
-       jid -> jlib:jid_to_string(Val);
-       group -> Val;
-       subscription ->
-           case Val of
-               both -> "both";
-               to -> "to";
-               from -> "from";
-               none -> "none"
-           end
-    end.
-
-
-
-list_to_action(S) ->
-    case S of
-       "allow" -> allow;
-       "deny" -> deny
-    end.
-
-
-
-process_iq_set(_, From, _To, #iq{sub_el = SubEl}) ->
-    #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, LServer, ListName,
-                                    xml:remove_cdata(SubEls));
-               "active" ->
-                   process_active_set(LUser, LServer, ListName);
-               "default" ->
-                   process_default_set(LUser, LServer, ListName);
-               _ ->
-                   {error, ?ERR_BAD_REQUEST}
-           end;
-       _ ->
-           {error, ?ERR_BAD_REQUEST}
-    end.
-
-
-process_default_set(LUser, LServer, {value, Name}) ->
-    F = fun() ->
-               case sql_get_privacy_list_names_t(LUser) of
-                   {selected, ["name"], []} ->
-                       {error, ?ERR_ITEM_NOT_FOUND};
-                   {selected, ["name"], Names} ->
-                       case lists:member({Name}, Names) of
-                           true ->
-                               sql_set_default_privacy_list(LUser, Name),
-                               {result, []};
-                           false ->
-                               {error, ?ERR_ITEM_NOT_FOUND}
-                       end
-               end
-       end,
-    case odbc_queries:sql_transaction(LServer, F) of
-       {atomic, {error, _} = Error} ->
-           Error;
-       {atomic, {result, _} = Res} ->
-           Res;
-       _ ->
-           {error, ?ERR_INTERNAL_SERVER_ERROR}
-    end;
-
-process_default_set(LUser, LServer, false) ->
-    case catch sql_unset_default_privacy_list(LUser, LServer) of
-       {'EXIT', _Reason} ->
-           {error, ?ERR_INTERNAL_SERVER_ERROR};
-       {error, _Reason} ->
-           {error, ?ERR_INTERNAL_SERVER_ERROR};
-       _ ->
-           {result, []}
-    end.
-
-
-process_active_set(LUser, LServer, {value, Name}) ->
-    case catch sql_get_privacy_list_id(LUser, LServer, Name) of
-       {selected, ["id"], []} ->
-           {error, ?ERR_ITEM_NOT_FOUND};
-       {selected, ["id"], [{ID}]} ->
-           case catch sql_get_privacy_list_data_by_id(ID, LServer) of
-               {selected, ["t", "value", "action", "ord", "match_all",
-                           "match_iq", "match_message",
-                           "match_presence_in", "match_presence_out"],
-                RItems} ->
-                   Items = lists:map(fun raw_to_item/1, RItems),
-                   NeedDb = is_list_needdb(Items),
-                   {result, [], #userlist{name = Name, list = Items, needdb = NeedDb}};
-               _ ->
-                   {error, ?ERR_INTERNAL_SERVER_ERROR}
-           end;
-       _ ->
-           {error, ?ERR_INTERNAL_SERVER_ERROR}
-    end;
-
-process_active_set(_LUser, _LServer, false) ->
-    {result, [], #userlist{}}.
-
-
-
-
-
-
-process_list_set(LUser, LServer, {value, Name}, Els) ->
-    case parse_items(Els) of
-       false ->
-           {error, ?ERR_BAD_REQUEST};
-       remove ->
-           F =
-               fun() ->
-                       case sql_get_default_privacy_list_t(LUser) of
-                           {selected, ["name"], []} ->
-                               sql_remove_privacy_list(LUser, Name),
-                               {result, []};
-                           {selected, ["name"], [{Default}]} ->
-                               %% TODO: check active
-                               if
-                                   Name == Default ->
-                                       {error, ?ERR_CONFLICT};
-                                   true ->
-                                       sql_remove_privacy_list(LUser, Name),
-                                       {result, []}
-                               end
-                       end
-               end,
-           case odbc_queries:sql_transaction(LServer, F) of
-               {atomic, {error, _} = Error} ->
-                   Error;
-               {atomic, {result, _} = Res} ->
-                   ejabberd_router:route(
-                     jlib:make_jid(LUser, LServer, ""),
-                     jlib:make_jid(LUser, LServer, ""),
-                     {xmlelement, "broadcast", [],
-                      [{privacy_list,
-                        #userlist{name = Name, list = []},
-                        Name}]}),
-                   Res;
-               _ ->
-                   {error, ?ERR_INTERNAL_SERVER_ERROR}
-           end;
-       List ->
-           RItems = lists:map(fun item_to_raw/1, List),
-           F =
-               fun() ->
-                       ID =
-                           case sql_get_privacy_list_id_t(LUser, Name) of
-                               {selected, ["id"], []} ->
-                                   sql_add_privacy_list(LUser, Name),
-                                   {selected, ["id"], [{I}]} =
-                                       sql_get_privacy_list_id_t(LUser, Name),
-                                   I;
-                               {selected, ["id"], [{I}]} ->
-                                   I
-                           end,
-                       sql_set_privacy_list(ID, RItems),
-                       {result, []}
-               end,
-           case odbc_queries:sql_transaction(LServer, F) of
-               {atomic, {error, _} = Error} ->
-                   Error;
-               {atomic, {result, _} = Res} ->
-                   NeedDb = is_list_needdb(List),
-                   ejabberd_router:route(
-                     jlib:make_jid(LUser, LServer, ""),
-                     jlib:make_jid(LUser, LServer, ""),
-                     {xmlelement, "broadcast", [],
-                      [{privacy_list,
-                        #userlist{name = Name, list = List, needdb = NeedDb},
-                        Name}]}),
-                   Res;
-               _ ->
-                   {error, ?ERR_INTERNAL_SERVER_ERROR}
-           end
-    end;
-
-process_list_set(_LUser, _LServer, false, _Els) ->
-    {error, ?ERR_BAD_REQUEST}.
-
-
-parse_items([]) ->
-    remove;
-parse_items(Els) ->
-    parse_items(Els, []).
-
-parse_items([], Res) ->
-    %% Sort the items by their 'order' attribute
-    lists:keysort(#listitem.order, Res);
-parse_items([{xmlelement, "item", Attrs, SubEls} | Els], Res) ->
-    Type   = xml:get_attr("type",   Attrs),
-    Value  = xml:get_attr("value",  Attrs),
-    SAction = xml:get_attr("action", Attrs),
-    SOrder = xml:get_attr("order",  Attrs),
-    Action = case catch list_to_action(element(2, SAction)) of
-                {'EXIT', _} -> false;
-                Val -> Val
-            end,
-    Order = case catch list_to_integer(element(2, SOrder)) of
-               {'EXIT', _} ->
-                   false;
-               IntVal ->
-                   if
-                       IntVal >= 0 ->
-                           IntVal;
-                       true ->
-                           false
-                   end
-           end,
-    if
-       (Action /= false) and (Order /= false) ->
-           I1 = #listitem{action = Action, order = Order},
-           I2 = case {Type, Value} of
-                    {{value, T}, {value, V}} ->
-                        case T of
-                            "jid" ->
-                                case jlib:string_to_jid(V) of
-                                    error ->
-                                        false;
-                                    JID ->
-                                        I1#listitem{
-                                          type = jid,
-                                          value = jlib:jid_tolower(JID)}
-                                end;
-                            "group" ->
-                                I1#listitem{type = group,
-                                            value = V};
-                            "subscription" ->
-                                case V of
-                                    "none" ->
-                                        I1#listitem{type = subscription,
-                                                    value = none};
-                                    "both" ->
-                                        I1#listitem{type = subscription,
-                                                    value = both};
-                                    "from" ->
-                                        I1#listitem{type = subscription,
-                                                    value = from};
-                                    "to" ->
-                                        I1#listitem{type = subscription,
-                                                    value = to};
-                                    _ ->
-                                        false
-                                end
-                        end;
-                    {{value, _}, false} ->
-                        false;
-                    _ ->
-                        I1
-                end,
-           case I2 of
-               false ->
-                   false;
-               _ ->
-                   case parse_matches(I2, xml:remove_cdata(SubEls)) of
-                       false ->
-                           false;
-                       I3 ->
-                           parse_items(Els, [I3 | Res])
-                   end
-           end;
-       true ->
-           false
-    end;
-
-parse_items(_, _Res) ->
-    false.
-
-
-parse_matches(Item, []) ->
-    Item#listitem{match_all = true};
-parse_matches(Item, Els) ->
-    parse_matches1(Item, Els).
-
-parse_matches1(Item, []) ->
-    Item;
-parse_matches1(Item, [{xmlelement, "message", _, _} | Els]) ->
-    parse_matches1(Item#listitem{match_message = true}, Els);
-parse_matches1(Item, [{xmlelement, "iq", _, _} | Els]) ->
-    parse_matches1(Item#listitem{match_iq = true}, Els);
-parse_matches1(Item, [{xmlelement, "presence-in", _, _} | Els]) ->
-    parse_matches1(Item#listitem{match_presence_in = true}, Els);
-parse_matches1(Item, [{xmlelement, "presence-out", _, _} | Els]) ->
-    parse_matches1(Item#listitem{match_presence_out = true}, Els);
-parse_matches1(_Item, [{xmlelement, _, _, _} | _Els]) ->
-    false.
-
-
-
-
-
-is_list_needdb(Items) ->
-    lists:any(
-      fun(X) ->
-             case X#listitem.type of
-                 subscription -> true;
-                 group -> true;
-                 _ -> false
-             end
-      end, Items).
-
-get_user_list(_, User, Server) ->
-    LUser = jlib:nodeprep(User),
-    LServer = jlib:nameprep(Server),
-
-    case catch sql_get_default_privacy_list(LUser, LServer) of
-       {selected, ["name"], []} ->
-           #userlist{};
-       {selected, ["name"], [{Default}]} ->
-           case catch sql_get_privacy_list_data(LUser, LServer, Default) of
-               {selected, ["t", "value", "action", "ord", "match_all",
-                           "match_iq", "match_message",
-                           "match_presence_in", "match_presence_out"],
-                RItems} ->
-                   Items = lists:map(fun raw_to_item/1, RItems),
-                   NeedDb = is_list_needdb(Items),
-                   #userlist{name = Default, list = Items, needdb = NeedDb};
-               _ ->
-                   #userlist{}
-           end;
-       _ ->
-           #userlist{}
-    end.
-
-
-%% From is the sender, To is the destination.
-%% If Dir = out, User@Server is the sender account (From).
-%% If Dir = in, User@Server is the destination account (To).
-check_packet(_, _User, _Server,
-            _UserList,
-            {#jid{luser = "", lserver = Server} = _From,
-              #jid{lserver = Server} = _To,
-              _},
-            in) ->
-    allow;
-check_packet(_, _User, _Server,
-            _UserList,
-            {#jid{lserver = Server} = _From,
-              #jid{luser = "", lserver = Server} = _To,
-              _},
-            out) ->
-    allow;
-check_packet(_, _User, _Server,
-            _UserList,
-            {#jid{luser = User, lserver = Server} = _From,
-              #jid{luser = User, lserver = Server} = _To,
-              _},
-            _Dir) ->
-    allow;
-check_packet(_, User, Server,
-            #userlist{list = List, needdb = NeedDb},
-            {From, To, {xmlelement, PName, Attrs, _}},
-            Dir) ->
-    case List of
-       [] ->
-           allow;
-       _ ->
-           PType = case PName of
-                       "message" -> message;
-                       "iq" -> iq;
-                       "presence" ->
-                           case xml:get_attr_s("type", Attrs) of
-                               %% notification
-                               "" -> presence;
-                               "unavailable" -> presence;
-                               %% subscribe, subscribed, unsubscribe,
-                               %% unsubscribed, error, probe, or other
-                               _ -> other
-                           end
-                   end,
-           PType2 = case {PType, Dir} of
-                        {message, in} -> message;
-                        {iq, in} -> iq;
-                        {presence, in} -> presence_in;
-                        {presence, out} -> presence_out;
-                        {_, _} -> other
-                    end,
-           LJID = case Dir of
-                      in -> jlib:jid_tolower(From);
-                      out -> jlib:jid_tolower(To)
-                  end,
-           {Subscription, Groups} =
-               case NeedDb of
-                   true -> ejabberd_hooks:run_fold(roster_get_jid_info,
-                                                   jlib:nameprep(Server),
-                                                   {none, []},
-                                                   [User, Server, LJID]);
-                   false -> {[], []}
-               end,
-           check_packet_aux(List, PType2, LJID, Subscription, Groups)
-    end.
-
-check_packet_aux([], _PType, _JID, _Subscription, _Groups) ->
-    allow;
-check_packet_aux([Item | List], PType, JID, Subscription, Groups) ->
-    #listitem{type = Type, value = Value, action = Action} = Item,
-    case is_ptype_match(Item, PType) of
-       true ->
-           case Type of
-               none ->
-                   Action;
-               _ ->
-                   case is_type_match(Type, Value,
-                                      JID, Subscription, Groups) of
-                       true ->
-                           Action;
-                       false ->
-                           check_packet_aux(List, PType,
-                                            JID, Subscription, Groups)
-                   end
-           end;
-       false ->
-           check_packet_aux(List, PType, JID, Subscription, Groups)
-    end.
-
-
-is_ptype_match(Item, PType) ->
-    case Item#listitem.match_all of
-       true ->
-           true;
-       false ->
-           case PType of
-               message ->
-                   Item#listitem.match_message;
-               iq ->
-                   Item#listitem.match_iq;
-               presence_in ->
-                   Item#listitem.match_presence_in;
-               presence_out ->
-                   Item#listitem.match_presence_out;
-               other ->
-                   false
-           end
-    end.
-
-
-is_type_match(Type, Value, JID, Subscription, Groups) ->
-    case Type of
-       jid ->
-           case Value of
-               {"", Server, ""} ->
-                   case JID of
-                       {_, Server, _} ->
-                           true;
-                       _ ->
-                           false
-                   end;
-               {User, Server, ""} ->
-                   case JID of
-                       {User, Server, _} ->
-                           true;
-                       _ ->
-                           false
-                   end;
-               _ ->
-                   Value == JID
-           end;
-       subscription ->
-           Value == Subscription;
-       group ->
-           lists:member(Value, Groups)
-    end.
-
-
-remove_user(User, Server) ->
-    LUser = jlib:nodeprep(User),
-    LServer = jlib:nameprep(Server),
-    sql_del_privacy_lists(LUser, LServer).
-
-
-updated_list(_,
-            #userlist{name = OldName} = Old,
-            #userlist{name = NewName} = New) ->
-    if
-       OldName == NewName ->
-           New;
-       true ->
-           Old
-    end.
-
-
-raw_to_item({SType, SValue, SAction, SOrder, SMatchAll, SMatchIQ,
-            SMatchMessage, SMatchPresenceIn, SMatchPresenceOut}) ->
-    {Type, Value} =
-       case SType of
-           "n" ->
-               {none, none};
-           "j" ->
-               case jlib:string_to_jid(SValue) of
-                   #jid{} = JID ->
-                       {jid, jlib:jid_tolower(JID)}
-               end;
-           "g" ->
-               {group, SValue};
-           "s" ->
-               case SValue of
-                   "none" ->
-                       {subscription, none};
-                   "both" ->
-                       {subscription, both};
-                   "from" ->
-                       {subscription, from};
-                   "to" ->
-                       {subscription, to}
-               end
-       end,
-    Action =
-       case SAction of
-           "a" -> allow;
-           "d" -> deny
-       end,
-    Order = list_to_integer(SOrder),
-    MatchAll = ejabberd_odbc:to_bool(SMatchAll),
-    MatchIQ = ejabberd_odbc:to_bool(SMatchIQ),
-    MatchMessage = ejabberd_odbc:to_bool(SMatchMessage),
-    MatchPresenceIn = ejabberd_odbc:to_bool(SMatchPresenceIn),
-    MatchPresenceOut =  ejabberd_odbc:to_bool(SMatchPresenceOut),
-    #listitem{type = Type,
-             value = Value,
-             action = Action,
-             order = Order,
-             match_all = MatchAll,
-             match_iq = MatchIQ,
-             match_message = MatchMessage,
-             match_presence_in = MatchPresenceIn,
-             match_presence_out = MatchPresenceOut
-            }.
-
-item_to_raw(#listitem{type = Type,
-                     value = Value,
-                     action = Action,
-                     order = Order,
-                     match_all = MatchAll,
-                     match_iq = MatchIQ,
-                     match_message = MatchMessage,
-                     match_presence_in = MatchPresenceIn,
-                     match_presence_out = MatchPresenceOut
-                    }) ->
-    {SType, SValue} =
-       case Type of
-           none ->
-               {"n", ""};
-           jid ->
-               {"j", ejabberd_odbc:escape(jlib:jid_to_string(Value))};
-           group ->
-               {"g", ejabberd_odbc:escape(Value)};
-           subscription ->
-               case Value of
-                   none ->
-                       {"s", "none"};
-                   both ->
-                       {"s", "both"};
-                   from ->
-                       {"s", "from"};
-                   to ->
-                       {"s", "to"}
-               end
-       end,
-    SAction =
-       case Action of
-           allow -> "a";
-           deny -> "d"
-       end,
-    SOrder = integer_to_list(Order),
-    SMatchAll = if MatchAll -> "1"; true -> "0" end,
-    SMatchIQ = if MatchIQ -> "1"; true -> "0" end,
-    SMatchMessage = if MatchMessage -> "1"; true -> "0" end,
-    SMatchPresenceIn = if MatchPresenceIn -> "1"; true -> "0" end,
-    SMatchPresenceOut = if MatchPresenceOut -> "1"; true -> "0" end,
-    [SType, SValue, SAction, SOrder, SMatchAll, SMatchIQ,
-     SMatchMessage, SMatchPresenceIn, SMatchPresenceOut].
-
-sql_get_default_privacy_list(LUser, LServer) ->
-    Username = ejabberd_odbc:escape(LUser),
-    odbc_queries:get_default_privacy_list(LServer, Username).
-
-sql_get_default_privacy_list_t(LUser) ->
-    Username = ejabberd_odbc:escape(LUser),
-    odbc_queries:get_default_privacy_list_t(Username).
-
-sql_get_privacy_list_names(LUser, LServer) ->
-    Username = ejabberd_odbc:escape(LUser),
-    odbc_queries:get_privacy_list_names(LServer, Username).
-
-sql_get_privacy_list_names_t(LUser) ->
-    Username = ejabberd_odbc:escape(LUser),
-    odbc_queries:get_privacy_list_names_t(Username).
-
-sql_get_privacy_list_id(LUser, LServer, Name) ->
-    Username = ejabberd_odbc:escape(LUser),
-    SName = ejabberd_odbc:escape(Name),
-    odbc_queries:get_privacy_list_id(LServer, Username, SName).
-
-sql_get_privacy_list_id_t(LUser, Name) ->
-    Username = ejabberd_odbc:escape(LUser),
-    SName = ejabberd_odbc:escape(Name),
-    odbc_queries:get_privacy_list_id_t(Username, SName).
-
-sql_get_privacy_list_data(LUser, LServer, Name) ->
-    Username = ejabberd_odbc:escape(LUser),
-    SName = ejabberd_odbc:escape(Name),
-    odbc_queries:get_privacy_list_data(LServer, Username, SName).
-
-sql_get_privacy_list_data_by_id(ID, LServer) ->
-    odbc_queries:get_privacy_list_data_by_id(LServer, ID).
-
-sql_get_privacy_list_data_by_id_t(ID) ->
-    odbc_queries:get_privacy_list_data_by_id_t(ID).
-
-sql_set_default_privacy_list(LUser, Name) ->
-    Username = ejabberd_odbc:escape(LUser),
-    SName = ejabberd_odbc:escape(Name),
-    odbc_queries:set_default_privacy_list(Username, SName).
-
-sql_unset_default_privacy_list(LUser, LServer) ->
-    Username = ejabberd_odbc:escape(LUser),
-    odbc_queries:unset_default_privacy_list(LServer, Username).
-
-sql_remove_privacy_list(LUser, Name) ->
-    Username = ejabberd_odbc:escape(LUser),
-    SName = ejabberd_odbc:escape(Name),
-    odbc_queries:remove_privacy_list(Username, SName).
-
-sql_add_privacy_list(LUser, Name) ->
-    Username = ejabberd_odbc:escape(LUser),
-    SName = ejabberd_odbc:escape(Name),
-    odbc_queries:add_privacy_list(Username, SName).
-
-sql_set_privacy_list(ID, RItems) ->
-    odbc_queries:set_privacy_list(ID, RItems).
-
-sql_del_privacy_lists(LUser, LServer) ->
-    Username = ejabberd_odbc:escape(LUser),
-    Server = ejabberd_odbc:escape(LServer),
-    odbc_queries:del_privacy_lists(LServer, Server, Username).
index ba5c00a8e2a03bd0553d89b43e1fd2e6c4c48b8a..5ca6204f32e147bd7e09f0dacf6b9d382f0ec9c6 100644 (file)
 
 start(Host, 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(),
+    case gen_mod:db_type(Opts) of
+        mnesia ->
+            mnesia:create_table(private_storage,
+                                [{disc_only_copies, [node()]},
+                                 {attributes,
+                                  record_info(fields, private_storage)}]),
+            update_table();
+        _ ->
+            ok
+    end,
     ejabberd_hooks:add(remove_user, Host,
                       ?MODULE, remove_user, 50),
     gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PRIVATE,
@@ -73,12 +79,21 @@ process_sm_iq(#jid{luser = LUser, lserver = LServer},
                       sub_el = [IQ#iq.sub_el, ?ERR_NOT_ACCEPTABLE]
                     };
                 Data ->
-                    mnesia:transaction(fun() ->
-                        lists:foreach(fun
-                            (Datum) ->
-                                set_data(LUser, LServer, Datum)
-                        end, Data)
-                    end),
+                    DBType = gen_mod:db_type(LServer, ?MODULE),
+                    F = fun() ->
+                                lists:foreach(
+                                  fun
+                                      (Datum) ->
+                                          set_data(LUser, LServer,
+                                                   Datum, DBType)
+                                  end, Data)
+                        end,
+                    case DBType of
+                        odbc ->
+                            ejabberd_odbc:sql_transaction(LServer, F);
+                        mnesia ->
+                            mnesia:transaction(F)
+                    end,
                     IQ#iq{type = result, sub_el = []}
             end;
         _ ->
@@ -132,30 +147,53 @@ filter_xmlels([_ | Xmlels], Data) ->
     filter_xmlels(Xmlels, Data).
 
 
-set_data(LUser, LServer, {XmlNS, Xmlel}) ->
+set_data(LUser, LServer, {XmlNS, Xmlel}, mnesia) ->
     mnesia:write(#private_storage{
-        usns = {LUser, LServer, XmlNS},
-        xml  = Xmlel
-    }).
-
+                    usns = {LUser, LServer, XmlNS},
+                    xml  = Xmlel});
+set_data(LUser, LServer, {XMLNS, El}, odbc) ->
+    Username = ejabberd_odbc:escape(LUser),
+    LXMLNS = ejabberd_odbc:escape(XMLNS),
+    SData = ejabberd_odbc:escape(
+              xml:element_to_binary(El)),
+    odbc_queries:set_private_data(LServer, Username, LXMLNS, SData).
 
 get_data(LUser, LServer, Data) ->
-    get_data(LUser, LServer, Data, []).
+    get_data(LUser, LServer, gen_mod:db_type(LServer, ?MODULE), Data, []).
 
-get_data(_LUser, _LServer, [], Storage_Xmlels) ->
+get_data(_LUser, _LServer, _DBType, [], Storage_Xmlels) ->
     lists:reverse(Storage_Xmlels);
-get_data(LUser, LServer, [{XmlNS, Xmlel} | Data], Storage_Xmlels) ->
+get_data(LUser, LServer, mnesia, [{XmlNS, Xmlel} | Data], Storage_Xmlels) ->
     case mnesia:dirty_read(private_storage, {LUser, LServer, XmlNS}) of
         [#private_storage{xml = Storage_Xmlel}] ->
-            get_data(LUser, LServer, Data, [Storage_Xmlel | Storage_Xmlels]);
+            get_data(LUser, LServer, mnesia, Data,
+                     [Storage_Xmlel | Storage_Xmlels]);
         _ ->
-            get_data(LUser, LServer, Data, [Xmlel | Storage_Xmlels])
+            get_data(LUser, LServer, mnesia, Data,
+                     [Xmlel | Storage_Xmlels])
+    end;
+get_data(LUser, LServer, odbc, [{XMLNS, El} | Els], Res) ->
+    Username = ejabberd_odbc:escape(LUser),
+    LXMLNS = ejabberd_odbc:escape(XMLNS),
+    case catch odbc_queries:get_private_data(LServer, Username, LXMLNS) of
+        {selected, ["data"], [{SData}]} ->
+            case xml_stream:parse_element(SData) of
+                Data when element(1, Data) == xmlelement ->
+                    get_data(LUser, LServer, odbc, Els, [Data | Res])
+            end;
+        %% MREMOND: I wonder when the query could return a vcard ?
+        {selected, ["vcard"], []} ->
+            get_data(LUser, LServer, odbc, Els, [El | Res]);
+        _ -> 
+            get_data(LUser, LServer, odbc, Els, [El | Res])
     end.
 
-
 remove_user(User, Server) ->
     LUser = jlib:nodeprep(User),
     LServer = jlib:nameprep(Server),
+    remove_user(LUser, LServer, gen_mod:db_type(Server, ?MODULE)).
+
+remove_user(LUser, LServer, mnesia) ->
     F = fun() ->
                Namespaces = mnesia:select(
                            private_storage,
@@ -169,8 +207,10 @@ remove_user(User, Server) ->
                                         {LUser, LServer, Namespace}})
                     end, Namespaces)
         end,
-    mnesia:transaction(F).
-
+    mnesia:transaction(F);
+remove_user(LUser, LServer, odbc) ->
+    Username = ejabberd_odbc:escape(LUser),
+    odbc_queries:del_user_private_storage(LServer, Username).
 
 update_table() ->
     Fields = record_info(fields, private_storage),
diff --git a/src/mod_private_odbc.erl b/src/mod_private_odbc.erl
deleted file mode 100644 (file)
index 45fee4c..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File    : mod_private_odbc.erl
-%%% Author  : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : Private storage support
-%%% Created :  5 Oct 2006 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2012   ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
--module(mod_private_odbc).
--author('alexey@process-one.net').
-
--behaviour(gen_mod).
-
--export([start/2,
-        stop/1,
-        process_sm_iq/3,
-        remove_user/2]).
-
--include("ejabberd.hrl").
--include("jlib.hrl").
-
-start(Host, Opts) ->
-    IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
-    ejabberd_hooks:add(remove_user, Host,
-                      ?MODULE, remove_user, 50),
-    gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PRIVATE,
-                                 ?MODULE, process_sm_iq, IQDisc).
-
-stop(Host) ->
-    ejabberd_hooks:delete(remove_user, Host,
-                         ?MODULE, remove_user, 50),
-    gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PRIVATE).
-
-
-process_sm_iq(From, _To, #iq{type = Type, sub_el = SubEl} = IQ) ->
-    #jid{luser = LUser, lserver = LServer} = From,
-    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, LServer, El)
-                                 end, Els)
-                       end,
-                   odbc_queries:sql_transaction(LServer, F),
-                   IQ#iq{type = result,
-                         sub_el = [{xmlelement, Name, Attrs, []}]};
-               get ->
-                   case catch get_data(LUser, LServer, Els) of
-                       {'EXIT', _Reason} ->
-                           IQ#iq{type = error,
-                                 sub_el = [SubEl,
-                                           ?ERR_INTERNAL_SERVER_ERROR]};
-                       Res ->
-                           IQ#iq{type = result,
-                                 sub_el = [{xmlelement, Name, Attrs, Res}]}
-                   end
-           end;
-       false ->
-           IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
-    end.
-
-set_data(LUser, LServer, El) ->
-    case El of
-       {xmlelement, _Name, Attrs, _Els} ->
-           XMLNS = xml:get_attr_s("xmlns", Attrs),
-           case XMLNS of
-               "" ->
-                   ignore;
-               _ ->
-                   Username = ejabberd_odbc:escape(LUser),
-                   LXMLNS = ejabberd_odbc:escape(XMLNS),
-                   SData = ejabberd_odbc:escape(
-                             xml:element_to_binary(El)),
-                       odbc_queries:set_private_data(LServer, Username, LXMLNS, SData)
-           end;
-       _ ->
-           ignore
-    end.
-
-get_data(LUser, LServer, Els) ->
-    get_data(LUser, LServer, Els, []).
-
-get_data(_LUser, _LServer, [], Res) ->
-    lists:reverse(Res);
-get_data(LUser, LServer, [El | Els], Res) ->
-    case El of
-       {xmlelement, _Name, Attrs, _} ->
-           XMLNS = xml:get_attr_s("xmlns", Attrs),
-           Username = ejabberd_odbc:escape(LUser),
-           LXMLNS = ejabberd_odbc:escape(XMLNS),
-           case catch odbc_queries:get_private_data(LServer, Username, LXMLNS) of
-               {selected, ["data"], [{SData}]} ->
-                   case xml_stream:parse_element(SData) of
-                       Data when element(1, Data) == xmlelement ->
-                           get_data(LUser, LServer, Els,
-                                    [Data | Res])
-                   end;
-               %% MREMOND: I wonder when the query could return a vcard ?
-               {selected, ["vcard"], []} ->
-                   get_data(LUser, LServer, Els,
-                            [El | Res]);
-           _ -> 
-               get_data(LUser, LServer, Els,[El | Res])
-           end;
-       _ ->
-           get_data(LUser, LServer, Els, Res)
-    end.
-
-
-remove_user(User, Server) ->
-    LUser = jlib:nodeprep(User),
-    LServer = jlib:nameprep(Server),
-    Username = ejabberd_odbc:escape(LUser),
-    odbc_queries:del_user_private_storage(LServer, Username).
index 3383148fcf1d3f956ea9f871b1a414257a96849f..5e6b4fd521be7960b2380c5829205cdf2101e6d1 100644 (file)
 
 start(Host, Opts) ->
     IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
-    mnesia:create_table(roster,[{disc_copies, [node()]},
-                               {attributes, record_info(fields, roster)}]),
-    mnesia:create_table(roster_version, [{disc_copies, [node()]},
-                               {attributes, record_info(fields, roster_version)}]),
-
-    update_table(),
-    mnesia:add_table_index(roster, us),
-    mnesia:add_table_index(roster_version, us),
+    case gen_mod:db_type(Opts) of
+       mnesia ->
+            mnesia:create_table(roster,
+                                [{disc_copies, [node()]},
+                                 {attributes, record_info(fields, roster)}]),
+            mnesia:create_table(roster_version,
+                                [{disc_copies, [node()]},
+                                 {attributes,
+                                  record_info(fields, roster_version)}]),
+            update_table(),
+            mnesia:add_table_index(roster, us),
+            mnesia:add_table_index(roster_version, us);
+       _ ->
+            ok
+    end,
     ejabberd_hooks:add(roster_get, Host,
                       ?MODULE, get_user_roster, 50),
     ejabberd_hooks:add(roster_in_subscription, Host,
@@ -166,17 +173,69 @@ get_versioning_feature(Acc, Host) ->
        false -> []
     end.
 
-roster_version(LServer ,LUser) ->
-       US = {LUser, LServer},
-       case roster_version_on_db(LServer) of
-               true ->
-                       case mnesia:dirty_read(roster_version, US) of
-                               [#roster_version{version = V}] -> V;
-                               [] -> not_found
-                       end;
-               false ->
-                       roster_hash(ejabberd_hooks:run_fold(roster_get, LServer, [], [US]))
-       end.
+roster_version(LServer, LUser) ->
+    US = {LUser, LServer},
+    case roster_version_on_db(LServer) of
+        true ->
+            case read_roster_version(LUser, LServer) of
+                error ->
+                    not_found;
+                V ->
+                    V
+            end;
+        false ->
+            roster_hash(ejabberd_hooks:run_fold(roster_get, LServer, [], [US]))
+    end.
+
+read_roster_version(LUser, LServer) ->
+    read_roster_version(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
+
+read_roster_version(LUser, LServer, mnesia) ->
+    US = {LUser, LServer},
+    case mnesia:dirty_read(roster_version, US) of
+        [#roster_version{version = V}] -> V;
+        [] -> error
+    end;
+read_roster_version(LServer, LUser, odbc) ->
+    Username = ejabberd_odbc:escape(LUser),
+    case odbc_queries:get_roster_version(LServer, Username) of
+        {selected, ["version"], [{Version}]} ->
+            Version;
+        {selected, ["version"], []} ->
+            error
+    end.
+
+write_roster_version(LUser, LServer) ->
+    write_roster_version(LUser, LServer, false).
+
+write_roster_version_t(LUser, LServer) ->
+    write_roster_version(LUser, LServer, true).
+
+write_roster_version(LUser, LServer, InTransaction) ->
+    Ver = sha:sha(term_to_binary(now())),
+    write_roster_version(LUser, LServer, InTransaction, Ver,
+                         gen_mod:db_type(LServer, ?MODULE)),
+    Ver.
+
+write_roster_version(LUser, LServer, InTransaction, Ver, mnesia) ->
+    US = {LUser, LServer},
+    if InTransaction ->
+            mnesia:write(#roster_version{us = US, version = Ver});
+       true ->
+            mnesia:dirty_write(#roster_version{us = US, version = Ver})
+    end;
+write_roster_version(LUser, LServer, InTransaction, Ver, odbc) ->
+    Username = ejabberd_odbc:escape(LUser),
+    EVer = ejabberd_odbc:escape(Ver),
+    if InTransaction ->
+            odbc_queries:set_roster_version(Username, EVer);
+       true ->
+            odbc_queries:sql_transaction(
+              LServer,
+              fun() ->
+                      odbc_queries:set_roster_version(Username, EVer)
+              end)
+    end.
 
 %% Load roster from DB only if neccesary. 
 %% It is neccesary if
@@ -189,60 +248,119 @@ process_iq_get(From, To, #iq{sub_el = SubEl} = IQ) ->
     LServer = From#jid.lserver,
     US = {LUser, LServer},
     try
-           {ItemsToSend, VersionToSend} = 
-               case {xml:get_tag_attr("ver", SubEl), 
-                     roster_versioning_enabled(LServer),
-                     roster_version_on_db(LServer)} of
+        {ItemsToSend, VersionToSend} = 
+            case {xml:get_tag_attr("ver", SubEl), 
+                  roster_versioning_enabled(LServer),
+                  roster_version_on_db(LServer)} of
                {{value, RequestedVersion}, true, true} ->
-                       %% Retrieve version from DB. Only load entire roster
-                       %% when neccesary.
-                       case mnesia:dirty_read(roster_version, US) of
-                               [#roster_version{version = RequestedVersion}] ->
-                                       {false, false};
-                               [#roster_version{version = NewVersion}] ->
-                                       {lists:map(fun item_to_xml/1, 
-                                               ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [], [US])), NewVersion};
-                               [] ->
-                                       RosterVersion = sha:sha(term_to_binary(now())),
-                                       mnesia:dirty_write(#roster_version{us = US, version = RosterVersion}),
-                                       {lists:map(fun item_to_xml/1,
-                                               ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [], [US])), RosterVersion}
-                       end;
-
+                    %% Retrieve version from DB. Only load entire roster
+                    %% when neccesary.
+                    case read_roster_version(LUser, LServer) of
+                        error ->
+                            RosterVersion = write_roster_version(LUser, LServer),
+                            {lists:map(
+                               fun item_to_xml/1,
+                               ejabberd_hooks:run_fold(
+                                 roster_get, To#jid.lserver, [], [US])),
+                             RosterVersion};
+                        RequestedVersion ->
+                            {false, false};
+                        NewVersion ->
+                            {lists:map(
+                               fun item_to_xml/1, 
+                               ejabberd_hooks:run_fold(
+                                 roster_get, To#jid.lserver, [], [US])),
+                             NewVersion}
+                    end;
                {{value, RequestedVersion}, true, false} ->
-                       RosterItems = ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [] , [US]),
-                       case roster_hash(RosterItems) of
-                               RequestedVersion ->
-                                       {false, false};
-                               New ->
-                                       {lists:map(fun item_to_xml/1, RosterItems), New}
-                       end;
-                       
+                    RosterItems = ejabberd_hooks:run_fold(
+                                    roster_get, To#jid.lserver, [] , [US]),
+                    case roster_hash(RosterItems) of
+                        RequestedVersion ->
+                            {false, false};
+                        New ->
+                            {lists:map(fun item_to_xml/1, RosterItems), New}
+                    end;
                _ ->
-                       {lists:map(fun item_to_xml/1, 
-                                       ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [], [US])), false}
-               end,
-               IQ#iq{type = result, sub_el = case {ItemsToSend, VersionToSend} of
-                                                {false, false} ->  [];
-                                                {Items, false} -> [{xmlelement, "query", [{"xmlns", ?NS_ROSTER}], Items}];
-                                                {Items, Version} -> [{xmlelement, "query", [{"xmlns", ?NS_ROSTER}, {"ver", Version}], Items}]
-                                               end}
-    catch 
+                    {lists:map(
+                       fun item_to_xml/1, 
+                       ejabberd_hooks:run_fold(
+                         roster_get, To#jid.lserver, [], [US])),
+                     false}
+            end,
+        IQ#iq{type = result,
+              sub_el = case {ItemsToSend, VersionToSend} of
+                           {false, false} ->
+                               [];
+                           {Items, false} ->
+                               [{xmlelement, "query",
+                                 [{"xmlns", ?NS_ROSTER}], Items}];
+                           {Items, Version} ->
+                               [{xmlelement, "query",
+                                 [{"xmlns", ?NS_ROSTER}, {"ver", Version}],
+                                 Items}]
+                       end}
+    catch
        _:_ ->  
-               IQ#iq{type =error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]}
+            IQ#iq{type =error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]}
     end.
 
+get_user_roster(Acc, {LUser, LServer}) ->
+    Items = get_roster(LUser, LServer),
+    lists:filter(fun(#roster{subscription = none, ask = in}) ->
+                         false;
+                    (_) ->
+                         true
+                 end, Items) ++ Acc.
+
+get_roster(LUser, LServer) ->
+    get_roster(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
 
-get_user_roster(Acc, US) ->
+get_roster(LUser, LServer, mnesia) ->
+    US = {LUser, LServer},
     case catch mnesia:dirty_index_read(roster, US, #roster.us) of
        Items when is_list(Items) ->
-           lists:filter(fun(#roster{subscription = none, ask = in}) ->
-                                false;
-                           (_) ->
-                                true
-                        end, Items) ++ Acc;
+            Items;
+        _ ->
+            []
+    end;
+get_roster(LUser, LServer, odbc) ->
+    Username = ejabberd_odbc:escape(LUser),
+    case catch odbc_queries:get_roster(LServer, Username) of
+       {selected, ["username", "jid", "nick", "subscription", "ask",
+                   "askmessage", "server", "subscribe", "type"],
+        Items} when is_list(Items) ->
+           JIDGroups = case catch odbc_queries:get_roster_jid_groups(LServer, Username) of
+                           {selected, ["jid","grp"], JGrps}
+                           when is_list(JGrps) ->
+                               JGrps;
+                           _ ->
+                               []
+                       end,
+            GroupsDict =
+                lists:foldl(
+                  fun({J, G}, Acc) ->
+                          dict:append(J, G, Acc)
+                  end, dict:new(), JIDGroups),
+           RItems = lists:flatmap(
+                      fun(I) ->
+                              case raw_to_record(LServer, I) of
+                                  %% Bad JID in database:
+                                  error ->
+                                      [];
+                                  R ->
+                                      SJID = jlib:jid_to_string(R#roster.jid),
+                                      Groups =
+                                           case dict:find(SJID, GroupsDict) of
+                                               {ok, Gs} -> Gs;
+                                               error -> []
+                                           end,
+                                      [R#roster{groups = Groups}]
+                              end
+                      end, Items),
+           RItems;
        _ ->
-           Acc
+           []
     end.
 
 
@@ -280,6 +398,50 @@ item_to_xml(Item) ->
     SubEls = SubEls1 ++ Item#roster.xs,
     {xmlelement, "item", Attrs4, SubEls}.
 
+get_roster_by_jid_t(LUser, LServer, LJID) ->
+    DBType = gen_mod:db_type(LServer, ?MODULE),
+    get_roster_by_jid_t(LUser, LServer, LJID, DBType).
+
+get_roster_by_jid_t(LUser, LServer, LJID, mnesia) ->
+    case mnesia:read({roster, {LUser, LServer, LJID}}) of
+        [] ->
+            #roster{usj = {LUser, LServer, LJID},
+                    us = {LUser, LServer},
+                    jid = LJID};
+        [I] ->
+            I#roster{jid = LJID,
+                     name = "",
+                     groups = [],
+                     xs = []}
+    end;
+get_roster_by_jid_t(LUser, LServer, LJID, odbc) ->
+    Username = ejabberd_odbc:escape(LUser),
+    SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
+    {selected,
+     ["username", "jid", "nick", "subscription",
+      "ask", "askmessage", "server", "subscribe", "type"],
+     Res} = odbc_queries:get_roster_by_jid(LServer, Username, SJID),
+    case Res of
+        [] ->
+            #roster{usj = {LUser, LServer, LJID},
+                    us = {LUser, LServer},
+                    jid = LJID};
+        [I] ->
+            R = raw_to_record(LServer, I),
+            case R of
+                %% Bad JID in database:
+                error ->
+                    #roster{usj = {LUser, LServer, LJID},
+                            us = {LUser, LServer},
+                            jid = LJID};
+                _ ->
+                    R#roster{
+                      usj = {LUser, LServer, LJID},
+                      us = {LUser, LServer},
+                      jid = LJID,
+                      name = ""}
+            end
+    end.
 
 process_iq_set(From, To, #iq{sub_el = SubEl} = IQ) ->
     {xmlelement, _Name, _Attrs, Els} = SubEl,
@@ -293,40 +455,28 @@ process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) ->
        error ->
            ok;
        _ ->
-           JID = {JID1#jid.user, JID1#jid.server, JID1#jid.resource},
            LJID = jlib:jid_tolower(JID1),
            F = fun() ->
-                       Res = mnesia:read({roster, {LUser, LServer, LJID}}),
-                       Item = case Res of
-                                  [] ->
-                                      #roster{usj = {LUser, LServer, LJID},
-                                              us = {LUser, LServer},
-                                              jid = JID};
-                                  [I] ->
-                                      I#roster{jid = JID,
-                                               name = "",
-                                               groups = [],
-                                               xs = []}
-                              end,
+                        Item = get_roster_by_jid_t(LUser, LServer, LJID),
                        Item1 = process_item_attrs(Item, Attrs),
                        Item2 = process_item_els(Item1, Els),
                        case Item2#roster.subscription of
                            remove ->
-                               mnesia:delete({roster, {LUser, LServer, LJID}});
+                                del_roster_t(LUser, LServer, LJID);
                            _ ->
-                               mnesia:write(Item2)
+                                update_roster_t(LUser, LServer, LJID, Item2)
                        end,
                        %% If the item exist in shared roster, take the
                        %% subscription information from there:
                        Item3 = ejabberd_hooks:run_fold(roster_process_item,
                                                        LServer, Item2, [LServer]),
                        case roster_version_on_db(LServer) of
-                               true -> mnesia:write(#roster_version{us = {LUser, LServer}, version = sha:sha(term_to_binary(now()))});
-                               false -> ok
+                            true -> write_roster_version_t(LUser, LServer);
+                            false -> ok
                        end,
                        {Item, Item3}
                end,
-           case mnesia:transaction(F) of
+           case transaction(LServer, F) of
                {atomic, {OldItem, Item}} ->
                    push_item(User, LServer, To, Item),
                    case Item#roster.subscription of
@@ -351,7 +501,7 @@ process_item_attrs(Item, [{Attr, Val} | Attrs]) ->
                error ->
                    process_item_attrs(Item, Attrs);
                JID1 ->
-                   JID = {JID1#jid.user, JID1#jid.server, JID1#jid.resource},
+                   JID = {JID1#jid.luser, JID1#jid.lserver, JID1#jid.lresource},
                    process_item_attrs(Item#roster{jid = JID}, Attrs)
            end;
        "name" ->
@@ -435,30 +585,54 @@ push_item_version(Server, User, From, Item, RosterVersion)  ->
                          push_item(User, Server, Resource, From, Item, RosterVersion)
                end, ejabberd_sm:get_user_resources(User, Server)).
 
-get_subscription_lists(_, User, Server) ->
+get_subscription_lists(Acc, User, Server) ->
     LUser = jlib:nodeprep(User),
     LServer = jlib:nameprep(Server),
+    DBType = gen_mod:db_type(LServer, ?MODULE),
+    Items = get_subscription_lists(Acc, LUser, LServer, DBType),
+    fill_subscription_lists(LServer, Items, [], []).
+
+get_subscription_lists(_, LUser, LServer, mnesia) ->
     US = {LUser, LServer},
     case mnesia:dirty_index_read(roster, US, #roster.us) of
        Items when is_list(Items) ->
-           fill_subscription_lists(Items, [], []);
+            Items;
        _ ->
            {[], []}
+    end;
+get_subscription_lists(_, LUser, LServer, odbc) ->
+    Username = ejabberd_odbc:escape(LUser),
+    case catch odbc_queries:get_roster(LServer, Username) of
+       {selected, ["username", "jid", "nick", "subscription", "ask",
+                   "askmessage", "server", "subscribe", "type"],
+        Items} when is_list(Items) ->
+            Items;
+        _ ->
+            []
     end.
 
-fill_subscription_lists([I | Is], F, T) ->
+fill_subscription_lists(LServer, [#roster{} = I | Is], F, T) ->
     J = element(3, I#roster.usj),
     case I#roster.subscription of
        both ->
-           fill_subscription_lists(Is, [J | F], [J | T]);
+           fill_subscription_lists(LServer, Is, [J | F], [J | T]);
        from ->
-           fill_subscription_lists(Is, [J | F], T);
+           fill_subscription_lists(LServer, Is, [J | F], T);
        to ->
-           fill_subscription_lists(Is, F, [J | T]);
+           fill_subscription_lists(LServer, Is, F, [J | T]);
        _ ->
-           fill_subscription_lists(Is, F, T)
+           fill_subscription_lists(LServer, Is, F, T)
     end;
-fill_subscription_lists([], F, T) ->
+fill_subscription_lists(LServer, [RawI | Is], F, T) ->
+    I = raw_to_record(LServer, RawI),
+    case I of
+       %% Bad JID in database:
+       error ->
+           fill_subscription_lists(LServer, Is, F, T);
+       _ ->
+            fill_subscription_lists(LServer, [I | Is], F, T)
+    end;
+fill_subscription_lists(_LServer, [], F, T) ->
     {F, T}.
 
 ask_to_pending(subscribe) -> out;
@@ -466,6 +640,25 @@ ask_to_pending(unsubscribe) -> none;
 ask_to_pending(Ask) -> Ask.
 
 
+roster_subscribe_t(LUser, LServer, LJID, Item) ->
+    DBType = gen_mod:db_type(LServer, ?MODULE),
+    roster_subscribe_t(LUser, LServer, LJID, Item, DBType).
+
+roster_subscribe_t(_LUser, _LServer, _LJID, Item, mnesia) ->
+    mnesia:write(Item);
+roster_subscribe_t(LUser, LServer, LJID, Item, odbc) ->
+    ItemVals = record_to_string(Item),
+    Username = ejabberd_odbc:escape(LUser),
+    SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
+    odbc_queries:roster_subscribe(LServer, Username, SJID, ItemVals).
+
+transaction(LServer, F) ->
+    case gen_mod:db_type(LServer, ?MODULE) of
+        mnesia ->
+            mnesia:transaction(F);
+        odbc ->
+            ejabberd_odbc:sql_transaction(LServer, F)
+    end.
 
 in_subscription(_, User, Server, JID, Type, Reason) ->
     process_subscription(in, User, Server, JID, Type, Reason).
@@ -473,23 +666,54 @@ in_subscription(_, User, Server, JID, Type, Reason) ->
 out_subscription(User, Server, JID, Type) ->
     process_subscription(out, User, Server, JID, Type, []).
 
+get_roster_by_jid_with_groups_t(LUser, LServer, LJID) ->
+    DBType = gen_mod:db_type(LServer, ?MODULE),
+    get_roster_by_jid_with_groups_t(LUser, LServer, LJID, DBType).
+
+get_roster_by_jid_with_groups_t(LUser, LServer, LJID, mnesia) ->
+    case mnesia:read({roster, {LUser, LServer, LJID}}) of
+        [] ->
+            #roster{usj = {LUser, LServer, LJID},
+                    us = {LUser, LServer},
+                    jid = LJID};
+        [I] ->
+            I
+    end;
+get_roster_by_jid_with_groups_t(LUser, LServer, LJID, odbc) ->
+    Username = ejabberd_odbc:escape(LUser),
+    SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
+    case odbc_queries:get_roster_by_jid(LServer, Username, SJID) of
+        {selected,
+         ["username", "jid", "nick", "subscription", "ask",
+          "askmessage", "server", "subscribe", "type"],
+         [I]} ->
+            %% raw_to_record can return error, but
+            %% jlib_to_string would fail before this point
+            R = raw_to_record(LServer, I),
+            Groups =
+                case odbc_queries:get_roster_groups(LServer, Username, SJID) of
+                    {selected, ["grp"], JGrps} when is_list(JGrps) ->
+                        [JGrp || {JGrp} <- JGrps];
+                    _ ->
+                        []
+                end,
+            R#roster{groups = Groups};
+        {selected,
+         ["username", "jid", "nick", "subscription", "ask",
+          "askmessage", "server", "subscribe", "type"],
+         []} ->
+            #roster{usj = {LUser, LServer, LJID},
+                    us = {LUser, LServer},
+                    jid = LJID}
+    end.
+
 process_subscription(Direction, User, Server, JID1, Type, Reason) ->
     LUser = jlib:nodeprep(User),
     LServer = jlib:nameprep(Server),
-    US = {LUser, LServer},
     LJID = jlib:jid_tolower(JID1),
     F = fun() ->
-               Item = case mnesia:read({roster, {LUser, LServer, LJID}}) of
-                          [] ->
-                              JID = {JID1#jid.user,
-                                     JID1#jid.server,
-                                     JID1#jid.resource},
-                              #roster{usj = {LUser, LServer, LJID},
-                                      us = US,
-                                      jid = JID};
-                          [I] ->
-                              I
-                      end,
+               Item = get_roster_by_jid_with_groups_t(
+                         LUser, LServer, LJID),
                NewState = case Direction of
                               out ->
                                   out_state_change(Item#roster.subscription,
@@ -518,21 +742,21 @@ process_subscription(Direction, User, Server, JID1, Type, Reason) ->
                        {none, AutoReply};
                    {none, none} when Item#roster.subscription == none,
                                      Item#roster.ask == in ->
-                       mnesia:delete({roster, {LUser, LServer, LJID}}),
+                        del_roster_t(LUser, LServer, LJID),
                        {none, AutoReply};
                    {Subscription, Pending} ->
                        NewItem = Item#roster{subscription = Subscription,
                                              ask = Pending,
                                              askmessage = list_to_binary(AskMessage)},
-                       mnesia:write(NewItem),
+                        roster_subscribe_t(LUser, LServer, LJID, NewItem),
                        case roster_version_on_db(LServer) of
-                               true -> mnesia:write(#roster_version{us = {LUser, LServer}, version = sha:sha(term_to_binary(now()))});
-                               false -> ok
+                            true -> write_roster_version_t(LUser, LServer);
+                            false -> ok
                        end,
                        {{push, NewItem}, AutoReply}
                end
        end,
-    case mnesia:transaction(F) of
+    case transaction(LServer, F) of
        {atomic, {Push, AutoReply}} ->
            case AutoReply of
                none ->
@@ -663,6 +887,9 @@ in_auto_reply(_,    _,    _)  ->           none.
 remove_user(User, Server) ->
     LUser = jlib:nodeprep(User),
     LServer = jlib:nameprep(Server),
+    remove_user(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
+
+remove_user(LUser, LServer, mnesia) ->
     US = {LUser, LServer},
     send_unsubscription_to_rosteritems(LUser, LServer),
     F = fun() ->
@@ -671,7 +898,12 @@ remove_user(User, Server) ->
                              end,
                              mnesia:index_read(roster, US, #roster.us))
         end,
-    mnesia:transaction(F).
+    mnesia:transaction(F);
+remove_user(LUser, LServer, odbc) ->
+    Username = ejabberd_odbc:escape(LUser),
+    send_unsubscription_to_rosteritems(LUser, LServer),
+    odbc_queries:del_user_roster_t(LServer, Username),
+    ok.
 
 %% For each contact with Subscription:
 %% Both or From, send a "unsubscribed" presence stanza;
@@ -725,11 +957,36 @@ set_items(User, Server, SubEl) ->
     LUser = jlib:nodeprep(User),
     LServer = jlib:nameprep(Server),
     F = fun() ->
-               lists:foreach(fun(El) ->
-                                     process_item_set_t(LUser, LServer, El)
-                             end, Els)
-       end,
-    mnesia:transaction(F).
+                lists:foreach(
+                  fun(El) ->
+                          process_item_set_t(LUser, LServer, El)
+                  end, Els)
+        end,
+    transaction(LServer, F).
+
+update_roster_t(LUser, LServer, LJID, Item) ->
+    DBType = gen_mod:db_type(LServer, ?MODULE),
+    update_roster_t(LUser, LServer, LJID, Item, DBType).
+
+update_roster_t(_LUser, _LServer,_LJID, Item, mnesia) ->
+    mnesia:write(Item);
+update_roster_t(LUser, _LServer, LJID, Item, odbc) ->
+    Username = ejabberd_odbc:escape(LUser),
+    SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
+    ItemVals = record_to_string(Item),
+    ItemGroups = groups_to_string(Item),
+    odbc_queries:update_roster_sql(Username, SJID, ItemVals, ItemGroups).
+
+del_roster_t(LUser, LServer, LJID) ->
+    DBType = gen_mod:db_type(LServer, ?MODULE),
+    del_roster_t(LUser, LServer, LJID, DBType).
+
+del_roster_t(LUser, LServer, LJID, mnesia) ->
+    mnesia:delete({roster, {LUser, LServer, LJID}});
+del_roster_t(LUser, _LServer, LJID, odbc) ->
+    Username = ejabberd_odbc:escape(LUser),
+    SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
+    odbc_queries:del_roster_sql(Username, SJID).
 
 process_item_set_t(LUser, LServer, {xmlelement, _Name, Attrs, Els}) ->
     JID1 = jlib:string_to_jid(xml:get_attr_s("jid", Attrs)),
@@ -744,12 +1001,12 @@ process_item_set_t(LUser, LServer, {xmlelement, _Name, Attrs, Els}) ->
                           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, LServer, LJID}});
-               _ ->
-                   mnesia:write(Item2)
-           end
+            case Item2#roster.subscription of
+                remove ->
+                    del_roster_t(LUser, LServer, LJID);
+                _ ->
+                    update_roster_t(LUser, LServer, LJID, Item2)
+            end
     end;
 process_item_set_t(_LUser, _LServer, _) ->
     ok.
@@ -761,7 +1018,7 @@ process_item_attrs_ws(Item, [{Attr, Val} | Attrs]) ->
                error ->
                    process_item_attrs_ws(Item, Attrs);
                JID1 ->
-                   JID = {JID1#jid.user, JID1#jid.server, JID1#jid.resource},
+                   JID = {JID1#jid.luser, JID1#jid.lserver, JID1#jid.lresource},
                    process_item_attrs_ws(Item#roster{jid = JID}, Attrs)
            end;
        "name" ->
@@ -795,6 +1052,11 @@ process_item_attrs_ws(Item, []) ->
     Item.
 
 get_in_pending_subscriptions(Ls, User, Server) ->
+    LServer = jlib:nameprep(Server),
+    get_in_pending_subscriptions(Ls, User, Server,
+                                 gen_mod:db_type(LServer, ?MODULE)).
+
+get_in_pending_subscriptions(Ls, User, Server, mnesia) ->
     JID = jlib:make_jid(User, Server, ""),
     US = {JID#jid.luser, JID#jid.lserver},
     case mnesia:dirty_index_read(roster, US, #roster.us) of
@@ -825,30 +1087,99 @@ get_in_pending_subscriptions(Ls, User, Server) ->
                      Result));
        _ ->
            Ls
+    end;
+get_in_pending_subscriptions(Ls, User, Server, odbc) ->
+    JID = jlib:make_jid(User, Server, ""),
+    LUser = JID#jid.luser,
+    LServer = JID#jid.lserver,
+    Username = ejabberd_odbc:escape(LUser),
+    case catch odbc_queries:get_roster(LServer, Username) of
+       {selected, ["username", "jid", "nick", "subscription", "ask",
+                   "askmessage", "server", "subscribe", "type"],
+        Items} when is_list(Items) ->
+           Ls ++ lists:map(
+                   fun(R) ->
+                           Message = R#roster.askmessage,
+                           {xmlelement, "presence",
+                            [{"from", jlib:jid_to_string(R#roster.jid)},
+                             {"to", jlib:jid_to_string(JID)},
+                             {"type", "subscribe"}],
+                            [{xmlelement, "status", [],
+                              [{xmlcdata, Message}]}]}
+                   end,
+                   lists:flatmap(
+                     fun(I) ->
+                             case raw_to_record(LServer, I) of
+                                 %% Bad JID in database:
+                                 error ->
+                                     [];
+                                 R ->
+                                     case R#roster.ask of
+                                         in   -> [R];
+                                         both -> [R];
+                                         _ -> []
+                                     end
+                             end
+                     end,
+                     Items));
+       _ ->
+           Ls
     end.
 
-
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
-get_jid_info(_, User, Server, JID) ->
+read_subscription_and_groups(User, Server, LJID) ->
     LUser = jlib:nodeprep(User),
-    LJID = jlib:jid_tolower(JID),
     LServer = jlib:nameprep(Server),
+    read_subscription_and_groups(LUser, LServer, LJID,
+                                 gen_mod:db_type(LServer, ?MODULE)).
+
+read_subscription_and_groups(LUser, LServer, LJID, mnesia) ->
     case catch mnesia:dirty_read(roster, {LUser, LServer, LJID}) of
        [#roster{subscription = Subscription, groups = Groups}] ->
            {Subscription, Groups};
-       _ ->
+        _ ->
+            error
+    end;
+read_subscription_and_groups(LUser, LServer, LJID, odbc) ->
+    Username = ejabberd_odbc:escape(LUser),
+    SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
+    case catch odbc_queries:get_subscription(LServer, Username, SJID) of
+       {selected, ["subscription"], [{SSubscription}]} ->
+           Subscription = case SSubscription of
+                              "B" -> both;
+                              "T" -> to;
+                              "F" -> from;
+                              _ -> none
+                          end,
+           Groups = case catch odbc_queries:get_rostergroup_by_jid(
+                                  LServer, Username, SJID) of
+                        {selected, ["grp"], JGrps} when is_list(JGrps) ->
+                            [JGrp || {JGrp} <- JGrps];
+                        _ ->
+                            []
+                    end,
+           {Subscription, Groups};
+        _ ->
+            error
+    end.
+
+get_jid_info(_, User, Server, JID) ->
+    LJID = jlib:jid_tolower(JID),
+    case read_subscription_and_groups(User, Server, LJID) of
+        {Subscription, Groups} ->
+            {Subscription, Groups};
+        error ->
            LRJID = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
            if
                LRJID == LJID ->
                    {none, []};
                true ->
-                   case catch mnesia:dirty_read(
-                                roster, {LUser, LServer, LRJID}) of
-                       [#roster{subscription = Subscription,
-                                groups = Groups}] ->
+                    case read_subscription_and_groups(
+                           User, Server, LRJID) of
+                        {Subscription, Groups} ->
                            {Subscription, Groups};
-                       _ ->
+                       error ->
                            {none, []}
                    end
            end
@@ -856,6 +1187,75 @@ get_jid_info(_, User, Server, JID) ->
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
+raw_to_record(LServer, {User, SJID, Nick, SSubscription, SAsk, SAskMessage,
+                       _SServer, _SSubscribe, _SType}) ->
+    case jlib:string_to_jid(SJID) of
+       error ->
+           error;
+       JID ->
+           LJID = jlib:jid_tolower(JID),
+           Subscription = case SSubscription of
+                              "B" -> both;
+                              "T" -> to;
+                              "F" -> from;
+                              _ -> none
+                          end,
+           Ask = case SAsk of
+                     "S" -> subscribe;
+                     "U" -> unsubscribe;
+                     "B" -> both;
+                     "O" -> out;
+                     "I" -> in;
+                     _ -> none
+                 end,
+           #roster{usj = {User, LServer, LJID},
+                   us = {User, LServer},
+                   jid = LJID,
+                   name = Nick,
+                   subscription = Subscription,
+                   ask = Ask,
+                   askmessage = SAskMessage}
+    end.
+
+record_to_string(#roster{us = {User, _Server},
+                        jid = JID,
+                        name = Name,
+                        subscription = Subscription,
+                        ask = Ask,
+                        askmessage = AskMessage}) ->
+    Username = ejabberd_odbc:escape(User),
+    SJID = ejabberd_odbc:escape(jlib:jid_to_string(jlib:jid_tolower(JID))),
+    Nick = ejabberd_odbc:escape(Name),
+    SSubscription = case Subscription of
+                       both -> "B";
+                       to   -> "T";
+                       from -> "F";
+                       none -> "N"
+                   end,
+    SAsk = case Ask of
+              subscribe   -> "S";
+              unsubscribe -> "U";
+              both        -> "B";
+              out         -> "O";
+              in          -> "I";
+              none        -> "N"
+          end,
+    SAskMessage = ejabberd_odbc:escape(AskMessage),
+    [Username, SJID, Nick, SSubscription, SAsk, SAskMessage, "N", "", "item"].
+
+groups_to_string(#roster{us = {User, _Server},
+                        jid = JID,
+                        groups = Groups}) ->
+    Username = ejabberd_odbc:escape(User),
+    SJID = ejabberd_odbc:escape(jlib:jid_to_string(jlib:jid_tolower(JID))),
+
+    %% Empty groups do not need to be converted to string to be inserted in
+    %% the database
+    lists:foldl(
+      fun([], Acc) -> Acc;
+        (Group, Acc) ->
+             G = ejabberd_odbc:escape(Group),
+             [[Username, SJID, G]|Acc] end, [], Groups).
 
 update_table() ->
     Fields = record_info(fields, roster),
@@ -927,10 +1327,12 @@ webadmin_page(_, Host,
 webadmin_page(Acc, _, _) -> Acc.
 
 user_roster(User, Server, Query, Lang) ->
-    US = {jlib:nodeprep(User), jlib:nameprep(Server)},
-    Items1 = mnesia:dirty_index_read(roster, US, #roster.us),
+    LUser = jlib:nodeprep(User),
+    LServer = jlib:nameprep(Server),
+    US = {LUser, LServer},
+    Items1 = get_roster(LUser, LServer),
     Res = user_roster_parse_query(User, Server, Items1, Query),
-    Items = mnesia:dirty_index_read(roster, US, #roster.us),
+    Items = get_roster(LUser, LServer),
     SItems = lists:sort(Items),
     FItems =
        case SItems of
diff --git a/src/mod_roster_odbc.erl b/src/mod_roster_odbc.erl
deleted file mode 100644 (file)
index b4162ca..0000000
+++ /dev/null
@@ -1,1211 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File    : mod_roster_odbc.erl
-%%% Author  : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : Roster management
-%%% Created : 15 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2012   ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
-%%% @doc Roster management (Mnesia storage).
-%%%
-%%% Includes support for XEP-0237: Roster Versioning.
-%%% The roster versioning follows an all-or-nothing strategy:
-%%%  - If the version supplied by the client is the latest, return an empty response.
-%%%  - If not, return the entire new roster (with updated version string).
-%%% Roster version is a hash digest of the entire roster.
-%%% No additional data is stored in DB.
-
--module(mod_roster_odbc).
--author('alexey@process-one.net').
-
--behaviour(gen_mod).
-
--export([start/2, stop/1,
-        process_iq/3,
-        process_local_iq/3,
-        get_user_roster/2,
-        get_subscription_lists/3,
-        get_in_pending_subscriptions/3,
-        in_subscription/6,
-        out_subscription/4,
-        set_items/3,
-        remove_user/2,
-        get_jid_info/4,
-        webadmin_page/3,
-        webadmin_user/4,
-        get_versioning_feature/2,
-        roster_versioning_enabled/1]).
-
--include("ejabberd.hrl").
--include("jlib.hrl").
--include("mod_roster.hrl").
--include("web/ejabberd_http.hrl").
--include("web/ejabberd_web_admin.hrl").
-
-
-start(Host, Opts) ->
-    IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
-    ejabberd_hooks:add(roster_get, Host,
-                      ?MODULE, get_user_roster, 50),
-    ejabberd_hooks:add(roster_in_subscription, Host,
-                      ?MODULE, in_subscription, 50),
-    ejabberd_hooks:add(roster_out_subscription, Host,
-                      ?MODULE, out_subscription, 50),
-    ejabberd_hooks:add(roster_get_subscription_lists, Host,
-                      ?MODULE, get_subscription_lists, 50),
-    ejabberd_hooks:add(roster_get_jid_info, Host,
-                      ?MODULE, get_jid_info, 50),
-    ejabberd_hooks:add(remove_user, Host,
-                      ?MODULE, remove_user, 50),
-    ejabberd_hooks:add(anonymous_purge_hook, Host,
-                      ?MODULE, remove_user, 50),
-    ejabberd_hooks:add(resend_subscription_requests_hook, Host,
-                      ?MODULE, get_in_pending_subscriptions, 50),
-    ejabberd_hooks:add(roster_get_versioning_feature, Host,
-                      ?MODULE, get_versioning_feature, 50),
-    ejabberd_hooks:add(webadmin_page_host, Host,
-                      ?MODULE, webadmin_page, 50),
-    ejabberd_hooks:add(webadmin_user, Host,
-                      ?MODULE, webadmin_user, 50),
-    gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_ROSTER,
-                                 ?MODULE, process_iq, IQDisc).
-
-stop(Host) ->
-    ejabberd_hooks:delete(roster_get, Host,
-                         ?MODULE, get_user_roster, 50),
-    ejabberd_hooks:delete(roster_in_subscription, Host,
-                         ?MODULE, in_subscription, 50),
-    ejabberd_hooks:delete(roster_out_subscription, Host,
-                         ?MODULE, out_subscription, 50),
-    ejabberd_hooks:delete(roster_get_subscription_lists, Host,
-                         ?MODULE, get_subscription_lists, 50),
-    ejabberd_hooks:delete(roster_get_jid_info, Host,
-                         ?MODULE, get_jid_info, 50),
-    ejabberd_hooks:delete(remove_user, Host,
-                         ?MODULE, remove_user, 50),
-    ejabberd_hooks:delete(anonymous_purge_hook, Host,
-                         ?MODULE, remove_user, 50),
-    ejabberd_hooks:delete(resend_subscription_requests_hook, Host,
-                      ?MODULE, get_in_pending_subscriptions, 50),
-    ejabberd_hooks:delete(roster_get_versioning_feature, Host,
-                         ?MODULE, get_versioning_feature, 50),
-    ejabberd_hooks:delete(webadmin_page_host, Host,
-                         ?MODULE, webadmin_page, 50),
-    ejabberd_hooks:delete(webadmin_user, Host,
-                         ?MODULE, webadmin_user, 50),
-    gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_ROSTER).
-
-
-process_iq(From, To, IQ) ->
-    #iq{sub_el = SubEl} = IQ,
-    #jid{lserver = LServer} = From,
-    case lists:member(LServer, ?MYHOSTS) of
-       true ->
-           process_local_iq(From, To, IQ);
-       _ ->
-           IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]}
-    end.
-
-process_local_iq(From, To, #iq{type = Type} = IQ) ->
-    case Type of
-       set ->
-           process_iq_set(From, To, IQ);
-       get ->
-           process_iq_get(From, To, IQ)
-    end.
-
-
-roster_hash(Items) ->
-       sha:sha(term_to_binary(
-               lists:sort(
-                       [R#roster{groups = lists:sort(Grs)} || 
-                               R = #roster{groups = Grs} <- Items]))).
-               
-roster_versioning_enabled(Host) ->
-    gen_mod:get_module_opt(Host, ?MODULE, versioning, false).
-
-roster_version_on_db(Host) ->
-    gen_mod:get_module_opt(Host, ?MODULE, store_current_id, false).
-
-%% Returns a list that may contain an xmlelement with the XEP-237 feature if it's enabled.
-get_versioning_feature(Acc, Host) ->
-    case roster_versioning_enabled(Host) of
-       true ->
-           Feature = {xmlelement,
-                      "ver",
-                      [{"xmlns", ?NS_ROSTER_VER}],
-                      [{xmlelement, "optional", [], []}]},
-           [Feature | Acc];
-       false -> []
-    end.
-
-roster_version(LServer ,LUser) ->
-       US = {LUser, LServer},
-       case roster_version_on_db(LServer) of
-               true ->
-                       case odbc_queries:get_roster_version(ejabberd_odbc:escape(LServer), ejabberd_odbc:escape(LUser)) of
-                               {selected, ["version"], [{Version}]} -> Version;
-                               {selected, ["version"], []} -> 
-                               %% If for some reason we don't had it on DB. Create a version Id and store it.
-                               %% (we did the same on process_iq_get, that is called when client get roster,
-                               %%  not sure why it can still not be on DB at this point)
-                               RosterVersion = sha:sha(term_to_binary(now())),
-                               {atomic, {updated,1}} = odbc_queries:sql_transaction(LServer, fun() ->
-                                   odbc_queries:set_roster_version(ejabberd_odbc:escape(LUser), RosterVersion)
-                               end),
-                               RosterVersion
-                       end;
-               false ->
-                       roster_hash(ejabberd_hooks:run_fold(roster_get, LServer, [], [US]))
-       end.
-
-%% Load roster from DB only if neccesary. 
-%% It is neccesary if
-%%     - roster versioning is disabled in server OR
-%%     - roster versioning is not used by the client OR
-%%     - roster versioning is used by server and client, BUT the server isn't storing versions on db OR
-%%     - the roster version from client don't match current version.
-process_iq_get(From, To, #iq{sub_el = SubEl} = IQ) ->
-    LUser = From#jid.luser,
-    LServer = From#jid.lserver,
-    US = {LUser, LServer},
-
-    try
-           {ItemsToSend, VersionToSend} = 
-               case {xml:get_tag_attr("ver", SubEl), 
-                     roster_versioning_enabled(LServer),
-                     roster_version_on_db(LServer)} of
-               {{value, RequestedVersion}, true, true} ->
-                       %% Retrieve version from DB. Only load entire roster
-                       %% when neccesary.
-                       case odbc_queries:get_roster_version(ejabberd_odbc:escape(LServer), ejabberd_odbc:escape(LUser)) of
-                               {selected, ["version"], [{RequestedVersion}]} ->
-                                       {false, false};
-                               {selected, ["version"], [{NewVersion}]} ->
-                                       {lists:map(fun item_to_xml/1, 
-                                               ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [], [US])), NewVersion};
-                               {selected, ["version"], []} ->
-                                       RosterVersion = sha:sha(term_to_binary(now())),
-                                       {atomic, {updated,1}} = odbc_queries:sql_transaction(LServer, fun() ->
-                                                                       odbc_queries:set_roster_version(ejabberd_odbc:escape(LUser), RosterVersion)
-                                                                 end),
-
-                                       {lists:map(fun item_to_xml/1,
-                                               ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [], [US])), RosterVersion}
-                       end;
-
-               {{value, RequestedVersion}, true, false} ->
-                       RosterItems = ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [] , [US]),
-                       case roster_hash(RosterItems) of
-                               RequestedVersion ->
-                                       {false, false};
-                               New ->
-                                       {lists:map(fun item_to_xml/1, RosterItems), New}
-                       end;
-                       
-               _ ->
-                       {lists:map(fun item_to_xml/1, 
-                                       ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [], [US])), false}
-               end,
-               IQ#iq{type = result, sub_el = case {ItemsToSend, VersionToSend} of
-                                                {false, false} ->  [];
-                                                {Items, false} -> [{xmlelement, "query", [{"xmlns", ?NS_ROSTER}], Items}];
-                                                {Items, Version} -> [{xmlelement, "query", [{"xmlns", ?NS_ROSTER}, {"ver", Version}], Items}]
-                                               end}
-    catch 
-       _:_ ->  
-               IQ#iq{type =error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]}
-    end.
-
-
-get_user_roster(Acc, {LUser, LServer}) ->
-    Items = get_roster(LUser, LServer),
-    lists:filter(fun(#roster{subscription = none, ask = in}) ->
-                        false;
-                   (_) ->
-                        true
-                end, Items) ++ Acc.
-
-get_roster(LUser, LServer) ->
-    Username = ejabberd_odbc:escape(LUser),
-    case catch odbc_queries:get_roster(LServer, Username) of
-       {selected, ["username", "jid", "nick", "subscription", "ask",
-                   "askmessage", "server", "subscribe", "type"],
-        Items} when is_list(Items) ->
-           JIDGroups = case catch odbc_queries:get_roster_jid_groups(LServer, Username) of
-                           {selected, ["jid","grp"], JGrps}
-                           when is_list(JGrps) ->
-                               JGrps;
-                           _ ->
-                               []
-                       end,
-            GroupsDict =
-                lists:foldl(
-                  fun({J, G}, Acc) ->
-                          dict:append(J, G, Acc)
-                  end, dict:new(), JIDGroups),
-           RItems = lists:flatmap(
-                      fun(I) ->
-                              case raw_to_record(LServer, I) of
-                                  %% Bad JID in database:
-                                  error ->
-                                      [];
-                                  R ->
-                                      SJID = jlib:jid_to_string(R#roster.jid),
-                                      Groups =
-                                           case dict:find(SJID, GroupsDict) of
-                                               {ok, Gs} -> Gs;
-                                               error -> []
-                                           end,
-                                      [R#roster{groups = Groups}]
-                              end
-                      end, Items),
-           RItems;
-       _ ->
-           []
-    end.
-
-
-item_to_xml(Item) ->
-    Attrs1 = [{"jid", jlib:jid_to_string(Item#roster.jid)}],
-    Attrs2 = case Item#roster.name of
-                "" ->
-                    Attrs1;
-                Name ->
-                    [{"name", Name} | Attrs1]
-            end,
-    Attrs3 = case Item#roster.subscription of
-                none ->
-                    [{"subscription", "none"} | Attrs2];
-                from ->
-                    [{"subscription", "from"} | Attrs2];
-                to ->
-                    [{"subscription", "to"} | Attrs2];
-                both ->
-                    [{"subscription", "both"} | Attrs2];
-                remove ->
-                    [{"subscription", "remove"} | Attrs2]
-            end,
-    Attrs = case ask_to_pending(Item#roster.ask) of
-               out ->
-                   [{"ask", "subscribe"} | Attrs3];
-               both ->
-                   [{"ask", "subscribe"} | Attrs3];
-               _ ->
-                   Attrs3
-           end,
-    SubEls = lists:map(fun(G) ->
-                              {xmlelement, "group", [], [{xmlcdata, G}]}
-                      end, Item#roster.groups),
-    {xmlelement, "item", Attrs, SubEls}.
-
-
-process_iq_set(From, To, #iq{sub_el = SubEl} = IQ) ->
-    {xmlelement, _Name, _Attrs, Els} = SubEl,
-    lists:foreach(fun(El) -> process_item_set(From, To, El) end, Els),
-    IQ#iq{type = result, sub_el = []}.
-
-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, lserver = LServer} = From,
-    case JID1 of
-       error ->
-           ok;
-       _ ->
-           LJID = jlib:jid_tolower(JID1),
-           Username = ejabberd_odbc:escape(LUser),
-           SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
-           F = fun() ->
-                       {selected,
-                        ["username", "jid", "nick", "subscription",
-                         "ask", "askmessage", "server", "subscribe", "type"],
-                        Res} = odbc_queries:get_roster_by_jid(LServer, Username, SJID),
-                       Item = case Res of
-                                  [] ->
-                                      #roster{usj = {LUser, LServer, LJID},
-                                              us = {LUser, LServer},
-                                              jid = LJID};
-                                  [I] ->
-                                      R = raw_to_record(LServer, I),
-                                      case R of
-                                          %% Bad JID in database:
-                                          error ->
-                                              #roster{usj = {LUser, LServer, LJID},
-                                                      us = {LUser, LServer},
-                                                      jid = LJID};
-                                          _ ->
-                                              R#roster{
-                                                usj = {LUser, LServer, LJID},
-                                                us = {LUser, LServer},
-                                                jid = LJID,
-                                                name = ""}
-                                      end
-                              end,
-                       Item1 = process_item_attrs(Item, Attrs),
-                       Item2 = process_item_els(Item1, Els),
-                       case Item2#roster.subscription of
-                           remove ->
-                   odbc_queries:del_roster(LServer, Username, SJID);
-                           _ ->
-                               ItemVals = record_to_string(Item2),
-                               ItemGroups = groups_to_string(Item2),
-                               odbc_queries:update_roster(LServer, Username, SJID, ItemVals, ItemGroups)
-                       end,
-                       %% If the item exist in shared roster, take the
-                       %% subscription information from there:
-                       Item3 = ejabberd_hooks:run_fold(roster_process_item,
-                                                       LServer, Item2, [LServer]),
-                       case roster_version_on_db(LServer) of
-                               true -> odbc_queries:set_roster_version(ejabberd_odbc:escape(LUser), sha:sha(term_to_binary(now())));
-                               false -> ok
-                       end,
-                       {Item, Item3}
-               end,
-           case odbc_queries:sql_transaction(LServer, F) of
-               {atomic, {OldItem, Item}} ->
-                   push_item(User, LServer, To, Item),
-                   case Item#roster.subscription of
-                       remove ->
-                           send_unsubscribing_presence(From, OldItem),
-                           ok;
-                       _ ->
-                           ok
-                   end;
-               E ->
-                   ?DEBUG("ROSTER: roster item set error: ~p~n", [E]),
-                   ok
-           end
-    end;
-process_item_set(_From, _To, _) ->
-    ok.
-
-process_item_attrs(Item, [{Attr, Val} | Attrs]) ->
-    case Attr of
-       "jid" ->
-           case jlib:string_to_jid(Val) of
-               error ->
-                   process_item_attrs(Item, Attrs);
-               JID1 ->
-                   JID = {JID1#jid.luser, JID1#jid.lserver, JID1#jid.lresource},
-                   process_item_attrs(Item#roster{jid = JID}, Attrs)
-           end;
-       "name" ->
-           process_item_attrs(Item#roster{name = Val}, Attrs);
-       "subscription" ->
-           case Val of
-               "remove" ->
-                   process_item_attrs(Item#roster{subscription = remove},
-                                      Attrs);
-               _ ->
-                   process_item_attrs(Item, Attrs)
-           end;
-       "ask" ->
-           process_item_attrs(Item, Attrs);
-       _ ->
-           process_item_attrs(Item, Attrs)
-    end;
-process_item_attrs(Item, []) ->
-    Item.
-
-
-process_item_els(Item, [{xmlelement, Name, _Attrs, SEls} | Els]) ->
-    case Name of
-       "group" ->
-           Groups = [xml:get_cdata(SEls) | Item#roster.groups],
-           process_item_els(Item#roster{groups = Groups}, Els);
-       _ ->
-           process_item_els(Item, Els)
-    end;
-process_item_els(Item, [{xmlcdata, _} | Els]) ->
-    process_item_els(Item, Els);
-process_item_els(Item, []) ->
-    Item.
-
-
-push_item(User, Server, From, Item) ->
-    ejabberd_sm:route(jlib:make_jid("", "", ""),
-                     jlib:make_jid(User, Server, ""),
-                     {xmlelement, "broadcast", [],
-                      [{item,
-                        Item#roster.jid,
-                        Item#roster.subscription}]}),
-    case roster_versioning_enabled(Server) of
-       true ->
-               push_item_version(Server, User, From, Item, roster_version(Server, User));
-       false ->
-           lists:foreach(fun(Resource) ->
-                         push_item(User, Server, Resource, From, Item)
-                 end, ejabberd_sm:get_user_resources(User, Server))
-    end.
-
-% TODO: don't push to those who not load roster
-push_item(User, Server, Resource, From, Item) ->
-    ResIQ = #iq{type = set, xmlns = ?NS_ROSTER,
-               id = "push" ++ randoms:get_string(),
-               sub_el = [{xmlelement, "query",
-                          [{"xmlns", ?NS_ROSTER}],
-                          [item_to_xml(Item)]}]},
-    ejabberd_router:route(
-      From,
-      jlib:make_jid(User, Server, Resource),
-      jlib:iq_to_xml(ResIQ)).
-
-%% @doc Roster push, calculate and include the version attribute.
-%% TODO: don't push to those who didn't load roster
-push_item_version(Server, User, From, Item, RosterVersion)  ->
-    lists:foreach(fun(Resource) ->
-                         push_item_version(User, Server, Resource, From, Item, RosterVersion)
-               end, ejabberd_sm:get_user_resources(User, Server)).
-
-push_item_version(User, Server, Resource, From, Item, RosterVersion) ->
-    IQPush = #iq{type = 'set', xmlns = ?NS_ROSTER,
-                id = "push" ++ randoms:get_string(),
-                sub_el = [{xmlelement, "query",
-                           [{"xmlns", ?NS_ROSTER},
-                            {"ver", RosterVersion}],
-                           [item_to_xml(Item)]}]},
-    ejabberd_router:route(
-      From,
-      jlib:make_jid(User, Server, Resource),
-      jlib:iq_to_xml(IQPush)).
-
-get_subscription_lists(_, User, Server) ->
-    LUser = jlib:nodeprep(User),
-    LServer = jlib:nameprep(Server),
-    Username = ejabberd_odbc:escape(LUser),
-    case catch odbc_queries:get_roster(LServer, Username) of
-       {selected, ["username", "jid", "nick", "subscription", "ask",
-                   "askmessage", "server", "subscribe", "type"],
-        Items} when is_list(Items) ->
-           fill_subscription_lists(LServer, Items, [], []);
-       _ ->
-           {[], []}
-    end.
-
-fill_subscription_lists(LServer, [RawI | Is], F, T) ->
-    I = raw_to_record(LServer, RawI),
-    case I of
-       %% Bad JID in database:
-       error ->
-           fill_subscription_lists(LServer, Is, F, T);
-       _ ->
-           J = I#roster.jid,
-           case I#roster.subscription of
-               both ->
-                   fill_subscription_lists(LServer, Is, [J | F], [J | T]);
-               from ->
-                   fill_subscription_lists(LServer, Is, [J | F], T);
-               to ->
-                   fill_subscription_lists(LServer, Is, F, [J | T]);
-               _ ->
-                   fill_subscription_lists(LServer, Is, F, T)
-           end
-    end;
-fill_subscription_lists(_LServer, [], F, T) ->
-    {F, T}.
-
-ask_to_pending(subscribe) -> out;
-ask_to_pending(unsubscribe) -> none;
-ask_to_pending(Ask) -> Ask.
-
-
-
-in_subscription(_, User, Server, JID, Type, Reason) ->
-    process_subscription(in, User, Server, JID, Type, Reason).
-
-out_subscription(User, Server, JID, Type) ->
-    process_subscription(out, User, Server, JID, Type, []).
-
-process_subscription(Direction, User, Server, JID1, Type, Reason) ->
-    LUser = jlib:nodeprep(User),
-    LServer = jlib:nameprep(Server),
-    LJID = jlib:jid_tolower(JID1),
-    Username = ejabberd_odbc:escape(LUser),
-    SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
-    F = fun() ->
-               Item =
-                   case odbc_queries:get_roster_by_jid(LServer, Username, SJID) of
-                       {selected,
-                        ["username", "jid", "nick", "subscription", "ask",
-                         "askmessage", "server", "subscribe", "type"],
-                        [I]} ->
-                           %% raw_to_record can return error, but
-                           %% jlib_to_string would fail before this point
-                           R = raw_to_record(LServer, I),
-                           Groups =
-                               case odbc_queries:get_roster_groups(LServer, Username, SJID) of
-                                   {selected, ["grp"], JGrps} when is_list(JGrps) ->
-                                       [JGrp || {JGrp} <- JGrps];
-                                   _ ->
-                                       []
-                               end,
-                           R#roster{groups = Groups};
-                       {selected,
-                        ["username", "jid", "nick", "subscription", "ask",
-                         "askmessage", "server", "subscribe", "type"],
-                        []} ->
-                           #roster{usj = {LUser, LServer, LJID},
-                                   us = {LUser, LServer},
-                                   jid = LJID}
-                   end,
-               NewState = case Direction of
-                              out ->
-                                  out_state_change(Item#roster.subscription,
-                                                   Item#roster.ask,
-                                                   Type);
-                              in ->
-                                  in_state_change(Item#roster.subscription,
-                                                  Item#roster.ask,
-                                                  Type)
-                          end,
-               AutoReply = case Direction of
-                               out ->
-                                   none;
-                               in ->
-                                   in_auto_reply(Item#roster.subscription,
-                                                 Item#roster.ask,
-                                                 Type)
-                           end,
-               AskMessage = case NewState of
-                                {_, both} -> Reason;
-                                {_, in}   -> Reason;
-                                _         -> ""
-                            end,
-               case NewState of
-                   none ->
-                       {none, AutoReply};
-                   {none, none} when Item#roster.subscription == none,
-                                     Item#roster.ask == in ->
-                       odbc_queries:del_roster(LServer, Username, SJID),
-                       {none, AutoReply};
-                   {Subscription, Pending} ->
-                       NewItem = Item#roster{subscription = Subscription,
-                                             ask = Pending,
-                                             askmessage = AskMessage},
-                       ItemVals = record_to_string(NewItem),
-                       odbc_queries:roster_subscribe(LServer, Username, SJID, ItemVals),
-                       case roster_version_on_db(LServer) of
-                               true -> odbc_queries:set_roster_version(ejabberd_odbc:escape(LUser), sha:sha(term_to_binary(now())));
-                               false -> ok
-                       end,
-                       {{push, NewItem}, AutoReply}
-               end
-       end,
-    case odbc_queries:sql_transaction(LServer, F) of
-       {atomic, {Push, AutoReply}} ->
-           case AutoReply of
-               none ->
-                   ok;
-               _ ->
-                   T = case AutoReply of
-                           subscribed -> "subscribed";
-                           unsubscribed -> "unsubscribed"
-                       end,
-                   ejabberd_router:route(
-                     jlib:make_jid(User, Server, ""), JID1,
-                     {xmlelement, "presence", [{"type", T}], []})
-           end,
-           case Push of
-               {push, Item} ->
-                   if
-                       Item#roster.subscription == none,
-                       Item#roster.ask == in ->
-                           ok;
-                       true ->
-                           push_item(User, Server,
-                                     jlib:make_jid(User, Server, ""), Item)
-                   end,
-                   true;
-               none ->
-                   false
-           end;
-       _ ->
-           false
-    end.
-
-
-%% in_state_change(Subscription, Pending, Type) -> NewState
-%% NewState = none | {NewSubscription, NewPending}
--ifdef(ROSTER_GATEWAY_WORKAROUND).
--define(NNSD, {to, none}).
--define(NISD, {to, in}).
--else.
--define(NNSD, none).
--define(NISD, none).
--endif.
-
-in_state_change(none, none, subscribe)    -> {none, in};
-in_state_change(none, none, subscribed)   -> ?NNSD;
-in_state_change(none, none, unsubscribe)  -> none;
-in_state_change(none, none, unsubscribed) -> none;
-in_state_change(none, out,  subscribe)    -> {none, both};
-in_state_change(none, out,  subscribed)   -> {to, none};
-in_state_change(none, out,  unsubscribe)  -> none;
-in_state_change(none, out,  unsubscribed) -> {none, none};
-in_state_change(none, in,   subscribe)    -> none;
-in_state_change(none, in,   subscribed)   -> ?NISD;
-in_state_change(none, in,   unsubscribe)  -> {none, none};
-in_state_change(none, in,   unsubscribed) -> none;
-in_state_change(none, both, subscribe)    -> none;
-in_state_change(none, both, subscribed)   -> {to, in};
-in_state_change(none, both, unsubscribe)  -> {none, out};
-in_state_change(none, both, unsubscribed) -> {none, in};
-in_state_change(to,   none, subscribe)    -> {to, in};
-in_state_change(to,   none, subscribed)   -> none;
-in_state_change(to,   none, unsubscribe)  -> none;
-in_state_change(to,   none, unsubscribed) -> {none, none};
-in_state_change(to,   in,   subscribe)    -> none;
-in_state_change(to,   in,   subscribed)   -> none;
-in_state_change(to,   in,   unsubscribe)  -> {to, none};
-in_state_change(to,   in,   unsubscribed) -> {none, in};
-in_state_change(from, none, subscribe)    -> none;
-in_state_change(from, none, subscribed)   -> {both, none};
-in_state_change(from, none, unsubscribe)  -> {none, none};
-in_state_change(from, none, unsubscribed) -> none;
-in_state_change(from, out,  subscribe)    -> none;
-in_state_change(from, out,  subscribed)   -> {both, none};
-in_state_change(from, out,  unsubscribe)  -> {none, out};
-in_state_change(from, out,  unsubscribed) -> {from, none};
-in_state_change(both, none, subscribe)    -> none;
-in_state_change(both, none, subscribed)   -> none;
-in_state_change(both, none, unsubscribe)  -> {to, none};
-in_state_change(both, none, unsubscribed) -> {from, none}.
-
-out_state_change(none, none, subscribe)    -> {none, out};
-out_state_change(none, none, subscribed)   -> none;
-out_state_change(none, none, unsubscribe)  -> none;
-out_state_change(none, none, unsubscribed) -> none;
-out_state_change(none, out,  subscribe)    -> {none, out}; %% We need to resend query (RFC3921, section 9.2)
-out_state_change(none, out,  subscribed)   -> none;
-out_state_change(none, out,  unsubscribe)  -> {none, none};
-out_state_change(none, out,  unsubscribed) -> none;
-out_state_change(none, in,   subscribe)    -> {none, both};
-out_state_change(none, in,   subscribed)   -> {from, none};
-out_state_change(none, in,   unsubscribe)  -> none;
-out_state_change(none, in,   unsubscribed) -> {none, none};
-out_state_change(none, both, subscribe)    -> none;
-out_state_change(none, both, subscribed)   -> {from, out};
-out_state_change(none, both, unsubscribe)  -> {none, in};
-out_state_change(none, both, unsubscribed) -> {none, out};
-out_state_change(to,   none, subscribe)    -> none;
-out_state_change(to,   none, subscribed)   -> {both, none};
-out_state_change(to,   none, unsubscribe)  -> {none, none};
-out_state_change(to,   none, unsubscribed) -> none;
-out_state_change(to,   in,   subscribe)    -> none;
-out_state_change(to,   in,   subscribed)   -> {both, none};
-out_state_change(to,   in,   unsubscribe)  -> {none, in};
-out_state_change(to,   in,   unsubscribed) -> {to, none};
-out_state_change(from, none, subscribe)    -> {from, out};
-out_state_change(from, none, subscribed)   -> none;
-out_state_change(from, none, unsubscribe)  -> none;
-out_state_change(from, none, unsubscribed) -> {none, none};
-out_state_change(from, out,  subscribe)    -> none;
-out_state_change(from, out,  subscribed)   -> none;
-out_state_change(from, out,  unsubscribe)  -> {from, none};
-out_state_change(from, out,  unsubscribed) -> {none, out};
-out_state_change(both, none, subscribe)    -> none;
-out_state_change(both, none, subscribed)   -> none;
-out_state_change(both, none, unsubscribe)  -> {from, none};
-out_state_change(both, none, unsubscribed) -> {to, none}.
-
-in_auto_reply(from, none, subscribe)    -> subscribed;
-in_auto_reply(from, out,  subscribe)    -> subscribed;
-in_auto_reply(both, none, subscribe)    -> subscribed;
-in_auto_reply(none, in,   unsubscribe)  -> unsubscribed;
-in_auto_reply(none, both, unsubscribe)  -> unsubscribed;
-in_auto_reply(to,   in,   unsubscribe)  -> unsubscribed;
-in_auto_reply(from, none, unsubscribe)  -> unsubscribed;
-in_auto_reply(from, out,  unsubscribe)  -> unsubscribed;
-in_auto_reply(both, none, unsubscribe)  -> unsubscribed;
-in_auto_reply(_,    _,    _)  ->           none.
-
-
-remove_user(User, Server) ->
-    LUser = jlib:nodeprep(User),
-    LServer = jlib:nameprep(Server),
-    Username = ejabberd_odbc:escape(LUser),
-    send_unsubscription_to_rosteritems(LUser, LServer),
-    odbc_queries:del_user_roster_t(LServer, Username),
-    ok.
-
-%% For each contact with Subscription:
-%% Both or From, send a "unsubscribed" presence stanza;
-%% Both or To, send a "unsubscribe" presence stanza.
-send_unsubscription_to_rosteritems(LUser, LServer) ->
-    RosterItems = get_user_roster([], {LUser, LServer}),
-    From = jlib:make_jid({LUser, LServer, ""}),
-    lists:foreach(fun(RosterItem) ->
-                         send_unsubscribing_presence(From, RosterItem)
-                 end,
-                 RosterItems).
-
-%% @spec (From::jid(), Item::roster()) -> ok
-send_unsubscribing_presence(From, Item) ->
-    IsTo = case Item#roster.subscription of
-              both -> true;
-              to -> true;
-              _ -> false
-          end,
-    IsFrom = case Item#roster.subscription of
-                both -> true;
-                from -> true;
-                _ -> false
-            end,
-    if IsTo ->
-           send_presence_type(
-             jlib:jid_remove_resource(From),
-             jlib:make_jid(Item#roster.jid), "unsubscribe");
-       true -> ok
-    end,
-    if IsFrom ->
-           send_presence_type(
-             jlib:jid_remove_resource(From),
-             jlib:make_jid(Item#roster.jid), "unsubscribed");
-       true -> ok
-    end,
-    ok.
-
-send_presence_type(From, To, Type) ->
-    ejabberd_router:route(
-      From, To,
-      {xmlelement, "presence",
-       [{"type", Type}],
-       []}).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-set_items(User, Server, SubEl) ->
-    {xmlelement, _Name, _Attrs, Els} = SubEl,
-    LUser = jlib:nodeprep(User),
-    LServer = jlib:nameprep(Server),
-    catch odbc_queries:sql_transaction(
-           LServer,
-           lists:flatmap(fun(El) ->
-                                 process_item_set_t(LUser, LServer, El)
-                         end, 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 ->
-           [];
-       _ ->
-           LJID = {JID1#jid.luser, JID1#jid.lserver, JID1#jid.lresource},
-           Username = ejabberd_odbc:escape(LUser),
-           SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
-           Item = #roster{usj = {LUser, LServer, LJID},
-                          us = {LUser, LServer},
-                          jid = LJID},
-           Item1 = process_item_attrs_ws(Item, Attrs),
-           Item2 = process_item_els(Item1, Els),
-           case Item2#roster.subscription of
-               remove ->
-                   odbc_queries:del_roster_sql(Username, SJID);
-               _ ->
-                   ItemVals = record_to_string(Item1),
-                   ItemGroups = groups_to_string(Item2),
-                   odbc_queries:update_roster_sql(Username, SJID, ItemVals, ItemGroups)
-           end
-    end;
-process_item_set_t(_LUser, _LServer, _) ->
-    [].
-
-process_item_attrs_ws(Item, [{Attr, Val} | Attrs]) ->
-    case Attr of
-       "jid" ->
-           case jlib:string_to_jid(Val) of
-               error ->
-                   process_item_attrs_ws(Item, Attrs);
-               JID1 ->
-                   JID = {JID1#jid.luser, JID1#jid.lserver, JID1#jid.lresource},
-                   process_item_attrs_ws(Item#roster{jid = JID}, Attrs)
-           end;
-       "name" ->
-           process_item_attrs_ws(Item#roster{name = Val}, Attrs);
-       "subscription" ->
-           case Val of
-               "remove" ->
-                   process_item_attrs_ws(Item#roster{subscription = remove},
-                                         Attrs);
-               "none" ->
-                   process_item_attrs_ws(Item#roster{subscription = none},
-                                         Attrs);
-               "both" ->
-                   process_item_attrs_ws(Item#roster{subscription = both},
-                                         Attrs);
-               "from" ->
-                   process_item_attrs_ws(Item#roster{subscription = from},
-                                         Attrs);
-               "to" ->
-                   process_item_attrs_ws(Item#roster{subscription = to},
-                                         Attrs);
-               _ ->
-                   process_item_attrs_ws(Item, Attrs)
-           end;
-       "ask" ->
-           process_item_attrs_ws(Item, Attrs);
-       _ ->
-           process_item_attrs_ws(Item, Attrs)
-    end;
-process_item_attrs_ws(Item, []) ->
-    Item.
-
-get_in_pending_subscriptions(Ls, User, Server) ->
-    JID = jlib:make_jid(User, Server, ""),
-    LUser = JID#jid.luser,
-    LServer = JID#jid.lserver,
-    Username = ejabberd_odbc:escape(LUser),
-    case catch odbc_queries:get_roster(LServer, Username) of
-       {selected, ["username", "jid", "nick", "subscription", "ask",
-                   "askmessage", "server", "subscribe", "type"],
-        Items} when is_list(Items) ->
-           Ls ++ lists:map(
-                   fun(R) ->
-                           Message = R#roster.askmessage,
-                           {xmlelement, "presence",
-                            [{"from", jlib:jid_to_string(R#roster.jid)},
-                             {"to", jlib:jid_to_string(JID)},
-                             {"type", "subscribe"}],
-                            [{xmlelement, "status", [],
-                              [{xmlcdata, Message}]}]}
-                   end,
-                   lists:flatmap(
-                     fun(I) ->
-                             case raw_to_record(LServer, I) of
-                                 %% Bad JID in database:
-                                 error ->
-                                     [];
-                                 R ->
-                                     case R#roster.ask of
-                                         in   -> [R];
-                                         both -> [R];
-                                         _ -> []
-                                     end
-                             end
-                     end,
-                     Items));
-       _ ->
-           Ls
-    end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-get_jid_info(_, User, Server, JID) ->
-    LUser = jlib:nodeprep(User),
-    LServer = jlib:nameprep(Server),
-    LJID = jlib:jid_tolower(JID),
-    Username = ejabberd_odbc:escape(LUser),
-    SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
-    case catch odbc_queries:get_subscription(LServer, Username, SJID) of
-       {selected, ["subscription"], [{SSubscription}]} ->
-           Subscription = case SSubscription of
-                              "B" -> both;
-                              "T" -> to;
-                              "F" -> from;
-                              _ -> none
-                          end,
-           Groups = case catch odbc_queries:get_rostergroup_by_jid(LServer, Username, SJID) of
-                        {selected, ["grp"], JGrps} when is_list(JGrps) ->
-                            [JGrp || {JGrp} <- JGrps];
-                        _ ->
-                            []
-                    end,
-           {Subscription, Groups};
-       _ ->
-           LRJID = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
-           if
-               LRJID == LJID ->
-                   {none, []};
-               true ->
-                   SRJID = ejabberd_odbc:escape(jlib:jid_to_string(LRJID)),
-                   case catch odbc_queries:get_subscription(LServer, Username, SRJID) of
-                       {selected, ["subscription"], [{SSubscription}]} ->
-                           Subscription = case SSubscription of
-                                              "B" -> both;
-                                              "T" -> to;
-                                              "F" -> from;
-                                              _ -> none
-                                          end,
-                           Groups = case catch odbc_queries:get_rostergroup_by_jid(LServer, Username, SRJID) of
-                                        {selected, ["grp"], JGrps} when is_list(JGrps) ->
-                                            [JGrp || {JGrp} <- JGrps];
-                                        _ ->
-                                            []
-                                    end,
-                           {Subscription, Groups};
-                       _ ->
-                           {none, []}
-                   end
-           end
-    end.
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-raw_to_record(LServer, {User, SJID, Nick, SSubscription, SAsk, SAskMessage,
-                       _SServer, _SSubscribe, _SType}) ->
-    case jlib:string_to_jid(SJID) of
-       error ->
-           error;
-       JID ->
-           LJID = jlib:jid_tolower(JID),
-           Subscription = case SSubscription of
-                              "B" -> both;
-                              "T" -> to;
-                              "F" -> from;
-                              _ -> none
-                          end,
-           Ask = case SAsk of
-                     "S" -> subscribe;
-                     "U" -> unsubscribe;
-                     "B" -> both;
-                     "O" -> out;
-                     "I" -> in;
-                     _ -> none
-                 end,
-           #roster{usj = {User, LServer, LJID},
-                   us = {User, LServer},
-                   jid = LJID,
-                   name = Nick,
-                   subscription = Subscription,
-                   ask = Ask,
-                   askmessage = SAskMessage}
-    end.
-
-record_to_string(#roster{us = {User, _Server},
-                        jid = JID,
-                        name = Name,
-                        subscription = Subscription,
-                        ask = Ask,
-                        askmessage = AskMessage}) ->
-    Username = ejabberd_odbc:escape(User),
-    SJID = ejabberd_odbc:escape(jlib:jid_to_string(jlib:jid_tolower(JID))),
-    Nick = ejabberd_odbc:escape(Name),
-    SSubscription = case Subscription of
-                       both -> "B";
-                       to   -> "T";
-                       from -> "F";
-                       none -> "N"
-                   end,
-    SAsk = case Ask of
-              subscribe   -> "S";
-              unsubscribe -> "U";
-              both        -> "B";
-              out         -> "O";
-              in          -> "I";
-              none        -> "N"
-          end,
-    SAskMessage = ejabberd_odbc:escape(AskMessage),
-    [Username, SJID, Nick, SSubscription, SAsk, SAskMessage, "N", "", "item"].
-
-groups_to_string(#roster{us = {User, _Server},
-                        jid = JID,
-                        groups = Groups}) ->
-    Username = ejabberd_odbc:escape(User),
-    SJID = ejabberd_odbc:escape(jlib:jid_to_string(jlib:jid_tolower(JID))),
-
-    %% Empty groups do not need to be converted to string to be inserted in
-    %% the database
-    lists:foldl(
-      fun([], Acc) -> Acc;
-        (Group, Acc) ->
-             G = ejabberd_odbc:escape(Group),
-             [[Username, SJID, G]|Acc] end, [], Groups).
-
-webadmin_page(_, Host,
-             #request{us = _US,
-                      path = ["user", U, "roster"],
-                      q = Query,
-                      lang = Lang} = _Request) ->
-    Res = user_roster(U, Host, Query, Lang),
-    {stop, Res};
-
-webadmin_page(Acc, _, _) -> Acc.
-
-user_roster(User, Server, Query, Lang) ->
-    LUser = jlib:nodeprep(User),
-    LServer = jlib:nameprep(Server),
-    US = {LUser, LServer},
-    Items1 = get_roster(LUser, LServer),
-    Res = user_roster_parse_query(User, Server, Items1, Query),
-    Items = get_roster(LUser, LServer),
-    SItems = lists:sort(Items),
-    FItems =
-       case SItems of
-           [] ->
-               [?CT("None")];
-           _ ->
-               [?XE("table",
-                    [?XE("thead",
-                         [?XE("tr",
-                              [?XCT("td", "Jabber ID"),
-                               ?XCT("td", "Nickname"),
-                               ?XCT("td", "Subscription"),
-                               ?XCT("td", "Pending"),
-                               ?XCT("td", "Groups")
-                              ])]),
-                     ?XE("tbody",
-                         lists:map(
-                           fun(R) ->
-                                   Groups =
-                                       lists:flatmap(
-                                         fun(Group) ->
-                                                 [?C(Group), ?BR]
-                                         end, R#roster.groups),
-                                   Pending = ask_to_pending(R#roster.ask),
-                                   TDJID = build_contact_jid_td(R#roster.jid),
-                                   ?XE("tr",
-                                       [TDJID,
-                                        ?XAC("td", [{"class", "valign"}],
-                                             R#roster.name),
-                                        ?XAC("td", [{"class", "valign"}],
-                                             atom_to_list(R#roster.subscription)),
-                                        ?XAC("td", [{"class", "valign"}],
-                                             atom_to_list(Pending)),
-                                        ?XAE("td", [{"class", "valign"}], Groups),
-                                        if
-                                            Pending == in ->
-                                                ?XAE("td", [{"class", "valign"}],
-                                                     [?INPUTT("submit",
-                                                              "validate" ++
-                                                              ejabberd_web_admin:term_to_id(R#roster.jid),
-                                                              "Validate")]);
-                                            true ->
-                                                ?X("td")
-                                        end,
-                                        ?XAE("td", [{"class", "valign"}],
-                                             [?INPUTT("submit",
-                                                      "remove" ++
-                                                      ejabberd_web_admin:term_to_id(R#roster.jid),
-                                                      "Remove")])])
-                           end, SItems))])]
-       end,
-    [?XC("h1", ?T("Roster of ") ++ us_to_list(US))] ++
-       case Res of
-           ok -> [?XREST("Submitted")];
-           error -> [?XREST("Bad format")];
-           nothing -> []
-       end ++
-       [?XAE("form", [{"action", ""}, {"method", "post"}],
-             FItems ++
-             [?P,
-              ?INPUT("text", "newjid", ""), ?C(" "),
-              ?INPUTT("submit", "addjid", "Add Jabber ID")
-             ])].
-
-build_contact_jid_td(RosterJID) ->
-    %% Convert {U, S, R} into {jid, U, S, R, U, S, R}:
-    ContactJID = jlib:make_jid(RosterJID),
-    JIDURI = case {ContactJID#jid.luser, ContactJID#jid.lserver} of
-                {"", _} -> "";
-                {CUser, CServer} ->
-                    case lists:member(CServer, ?MYHOSTS) of
-                        false -> "";
-                        true -> "/admin/server/" ++ CServer ++ "/user/" ++ CUser ++ "/"
-                    end
-            end,
-    case JIDURI of
-       [] ->
-           ?XAC("td", [{"class", "valign"}], jlib:jid_to_string(RosterJID));
-       URI when is_list(URI) ->
-           ?XAE("td", [{"class", "valign"}], [?AC(JIDURI, jlib:jid_to_string(RosterJID))])
-    end.
-
-user_roster_parse_query(User, Server, Items, Query) ->
-    case lists:keysearch("addjid", 1, Query) of
-       {value, _} ->
-           case lists:keysearch("newjid", 1, Query) of
-               {value, {_, undefined}} ->
-                   error;
-               {value, {_, SJID}} ->
-                   case jlib:string_to_jid(SJID) of
-                       JID when is_record(JID, jid) ->
-                           user_roster_subscribe_jid(User, Server, JID),
-                           ok;
-                       error ->
-                           error
-                   end;
-               false ->
-                   error
-           end;
-       false ->
-           case catch user_roster_item_parse_query(
-                        User, Server, Items, Query) of
-               submitted ->
-                   ok;
-               {'EXIT', _Reason} ->
-                   error;
-               _ ->
-                   nothing
-           end
-    end.
-
-
-user_roster_subscribe_jid(User, Server, JID) ->
-    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, Server, Items, Query) ->
-    lists:foreach(
-      fun(R) ->
-             JID = R#roster.jid,
-             case lists:keysearch(
-                    "validate" ++ ejabberd_web_admin:term_to_id(JID), 1, Query) of
-                 {value, _} ->
-                     JID1 = jlib:make_jid(JID),
-                     out_subscription(
-                       User, Server, JID1, subscribed),
-                     UJID = jlib:make_jid(User, Server, ""),
-                     ejabberd_router:route(
-                       UJID, JID1, {xmlelement, "presence",
-                                    [{"type", "subscribed"}], []}),
-                     throw(submitted);
-                 false ->
-                     case lists:keysearch(
-                            "remove" ++ ejabberd_web_admin:term_to_id(JID), 1, Query) of
-                         {value, _} ->
-                             UJID = jlib:make_jid(User, Server, ""),
-                             process_iq(
-                               UJID, UJID,
-                               #iq{type = set,
-                                   sub_el = {xmlelement, "query",
-                                             [{"xmlns", ?NS_ROSTER}],
-                                             [{xmlelement, "item",
-                                               [{"jid", jlib:jid_to_string(JID)},
-                                                {"subscription", "remove"}],
-                                               []}]}}),
-                             throw(submitted);
-                         false ->
-                             ok
-                     end
-
-             end
-      end, Items),
-    nothing.
-
-us_to_list({User, Server}) ->
-    jlib:jid_to_string({User, Server, ""}).
-
-webadmin_user(Acc, _User, _Server, Lang) ->
-    Acc ++ [?XE("h3", [?ACT("roster/", "Roster")])].
index a8dd977eb71907b11738575e66c860a5acc83bcc..d5fa1e3b219522a85eafb8976d06fedb246832ea 100644 (file)
 -record(sr_group, {group_host, opts}).
 -record(sr_user, {us, group_host}).
 
-start(Host, _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),
+start(Host, Opts) ->
+    case gen_mod:db_type(Opts) of
+        mnesia ->
+            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);
+        _ ->
+            ok
+    end,
     ejabberd_hooks:add(webadmin_menu_host, Host,
                       ?MODULE, webadmin_menu, 70),
     ejabberd_hooks:add(webadmin_page_host, Host,
@@ -181,7 +186,7 @@ get_user_roster(Items, US) ->
 get_vcard_module(Server) ->
     Modules = gen_mod:loaded_modules(Server),
     [M || M <- Modules,
-         (M == mod_vcard) or (M == mod_vcard_odbc) or (M == mod_vcard_ldap)].
+         (M == mod_vcard) or (M == mod_vcard_ldap)].
 
 get_rosteritem_name([], _, _) ->
     "";
@@ -237,15 +242,14 @@ process_item(RosterItem, Host) ->
                [] ->
                     %% Remove pending subscription by setting it
                     %% unsubscribed.
-                    Mod = get_roster_mod(ServerFrom),
 
                     %% Remove pending out subscription
-                    Mod:out_subscription(UserTo, ServerTo,
+                    mod_roster:out_subscription(UserTo, ServerTo,
                                          jlib:make_jid(UserFrom, ServerFrom, ""),
                                          unsubscribe),
 
                     %% Remove pending in subscription
-                    Mod:in_subscription(aaaa, UserFrom, ServerFrom,
+                    mod_roster:in_subscription(aaaa, UserFrom, ServerFrom,
                                         jlib:make_jid(UserTo, ServerTo, ""),
                                         unsubscribe, ""),
 
@@ -274,8 +278,6 @@ build_roster_record(User1, Server1, User2, Server2, Name2, Groups) ->
 
 set_new_rosteritems(UserFrom, ServerFrom,
                    UserTo, ServerTo, ResourceTo, NameTo, GroupsFrom) ->
-    Mod = get_roster_mod(ServerFrom),
-
     RIFrom = build_roster_record(UserFrom, ServerFrom,
                                 UserTo, ServerTo, NameTo, GroupsFrom),
     set_item(UserFrom, ServerFrom, ResourceTo, RIFrom),
@@ -287,20 +289,20 @@ set_new_rosteritems(UserFrom, ServerFrom,
     set_item(UserTo, ServerTo, "", RITo),
 
     %% From requests
-    Mod:out_subscription(UserFrom, ServerFrom, JIDTo, subscribe),
-    Mod:in_subscription(aaa, UserTo, ServerTo, JIDFrom, subscribe, ""),
+    mod_roster:out_subscription(UserFrom, ServerFrom, JIDTo, subscribe),
+    mod_roster:in_subscription(aaa, UserTo, ServerTo, JIDFrom, subscribe, ""),
 
     %% To accepts
-    Mod:out_subscription(UserTo, ServerTo, JIDFrom, subscribed),
-    Mod:in_subscription(aaa, UserFrom, ServerFrom, JIDTo, subscribed, ""),
+    mod_roster:out_subscription(UserTo, ServerTo, JIDFrom, subscribed),
+    mod_roster:in_subscription(aaa, UserFrom, ServerFrom, JIDTo, subscribed, ""),
 
     %% To requests
-    Mod:out_subscription(UserTo, ServerTo, JIDFrom, subscribe),
-    Mod:in_subscription(aaa, UserFrom, ServerFrom, JIDTo, subscribe, ""),
+    mod_roster:out_subscription(UserTo, ServerTo, JIDFrom, subscribe),
+    mod_roster:in_subscription(aaa, UserFrom, ServerFrom, JIDTo, subscribe, ""),
 
     %% From accepts
-    Mod:out_subscription(UserFrom, ServerFrom, JIDTo, subscribed),
-    Mod:in_subscription(aaa, UserTo, ServerTo, JIDFrom, subscribed, ""),
+    mod_roster:out_subscription(UserFrom, ServerFrom, JIDTo, subscribed),
+    mod_roster:in_subscription(aaa, UserTo, ServerTo, JIDFrom, subscribed, ""),
 
     RIFrom.
 
@@ -361,15 +363,13 @@ in_subscription(Acc, User, Server, JID, Type, _Reason) ->
     process_subscription(in, User, Server, JID, Type, Acc).
 
 out_subscription(UserFrom, ServerFrom, JIDTo, unsubscribed) ->
-    Mod = get_roster_mod(ServerFrom),
-
     %% Remove pending out subscription
     #jid{luser = UserTo, lserver = ServerTo} = JIDTo,
     JIDFrom = jlib:make_jid(UserFrom, UserTo, ""),
-    Mod:out_subscription(UserTo, ServerTo, JIDFrom, unsubscribe),
+    mod_roster:out_subscription(UserTo, ServerTo, JIDFrom, unsubscribe),
 
     %% Remove pending in subscription
-    Mod:in_subscription(aaaa, UserFrom, ServerFrom, JIDTo, unsubscribe, ""),
+    mod_roster:in_subscription(aaaa, UserFrom, ServerFrom, JIDTo, unsubscribe, ""),
 
     process_subscription(out, UserFrom, ServerFrom, JIDTo, unsubscribed, false);
 out_subscription(User, Server, JID, Type) ->
@@ -401,32 +401,70 @@ process_subscription(Direction, User, Server, JID, _Type, Acc) ->
     end.
 
 list_groups(Host) ->
+    list_groups(Host, gen_mod:db_type(Host, ?MODULE)).
+
+list_groups(Host, mnesia) ->
     mnesia:dirty_select(
       sr_group,
       [{#sr_group{group_host = {'$1', '$2'},
                  _ = '_'},
        [{'==', '$2', Host}],
-       ['$1']}]).
+       ['$1']}]);
+list_groups(Host, odbc) ->
+    case ejabberd_odbc:sql_query(
+           Host, ["select name from sr_group;"]) of
+        {selected, ["name"], Rs} ->
+            [G || {G} <- Rs];
+        _ ->
+            []
+    end.
 
 groups_with_opts(Host) ->
+    groups_with_opts(Host, gen_mod:db_type(Host, ?MODULE)).
+
+groups_with_opts(Host, mnesia) ->
     Gs = mnesia:dirty_select(
           sr_group,
           [{#sr_group{group_host={'$1', Host}, opts='$2', _='_'},
             [],
             [['$1','$2']] }]),
-    lists:map(fun([G,O]) -> {G, O} end, Gs).
+    lists:map(fun([G,O]) -> {G, O} end, Gs);
+groups_with_opts(Host, odbc) ->
+    case ejabberd_odbc:sql_query(
+           Host, ["select name, opts from sr_group;"]) of
+        {selected, ["name", "opts"], Rs} ->
+            [{G, ejabberd_odbc:decode_term(Opts)} || {G, Opts} <- Rs];
+        _ ->
+            []
+    end.
 
 create_group(Host, Group) ->
     create_group(Host, Group, []).
 
 create_group(Host, Group, Opts) ->
+    create_group(Host, Group, Opts, gen_mod:db_type(Host, ?MODULE)).
+
+create_group(Host, Group, Opts, mnesia) ->
     R = #sr_group{group_host = {Group, Host}, opts = Opts},
     F = fun() ->
                mnesia:write(R)
        end,
-    mnesia:transaction(F).
+    mnesia:transaction(F);
+create_group(Host, Group, Opts, odbc) ->
+    SGroup = ejabberd_odbc:escape(Group),
+    SOpts = ejabberd_odbc:encode_term(Opts),
+    F = fun() ->
+                odbc_queries:update_t("sr_group",
+                                      ["name", "opts"],
+                                      [SGroup, SOpts],
+                                      ["name='", SGroup, "'"])
+        end,
+    ejabberd_odbc:sql_transaction(Host, F).
 
 delete_group(Host, Group) ->
+    delete_group(Host, Group, gen_mod:db_type(Host, ?MODULE)).
+
+delete_group(Host, Group, mnesia) ->
     GroupHost = {Group, Host},
     F = fun() ->
                %% Delete the group ...
@@ -437,53 +475,102 @@ delete_group(Host, Group) ->
                                      mnesia:delete_object(UserEntry)
                              end, Users)
        end,
-    mnesia:transaction(F).
+    mnesia:transaction(F);
+delete_group(Host, Group, odbc) ->
+    SGroup = ejabberd_odbc:escape(Group),
+    F = fun() ->
+                ejabberd_odbc:sql_query_t(
+                  ["delete from sr_group where name='", SGroup, "';"]),
+                ejabberd_odbc:sql_query_t(
+                  ["delete from sr_user where grp='", SGroup, "';"])
+        end,
+    ejabberd_odbc:sql_transaction(Host, F).
 
 get_group_opts(Host, Group) ->
+    get_group_opts(Host, Group, gen_mod:db_type(Host, ?MODULE)).
+
+get_group_opts(Host, Group, mnesia) ->
     case catch mnesia:dirty_read(sr_group, {Group, Host}) of
        [#sr_group{opts = Opts}] ->
            Opts;
        _ ->
            error
+    end;
+get_group_opts(Host, Group, odbc) ->
+    SGroup = ejabberd_odbc:escape(Group),
+    case catch ejabberd_odbc:sql_query(
+                 Host, ["select opts from sr_group "
+                        "where name='", SGroup, "';"]) of
+        {selected, ["opts"], [{SOpts}]} ->
+            ejabberd_odbc:decode_term(SOpts);
+        _ ->
+            error
     end.
 
 set_group_opts(Host, Group, Opts) ->
+    set_group_opts(Host, Group, Opts, gen_mod:db_type(Host, ?MODULE)).
+
+set_group_opts(Host, Group, Opts, mnesia) ->
     R = #sr_group{group_host = {Group, Host}, opts = Opts},
     F = fun() ->
                mnesia:write(R)
        end,
-    mnesia:transaction(F).
+    mnesia:transaction(F);
+set_group_opts(Host, Group, Opts, odbc) ->
+    SGroup = ejabberd_odbc:escape(Group),
+    SOpts = ejabberd_odbc:encode_term(Opts),
+    F = fun() ->
+                odbc_queries:update_t("sr_group",
+                                      ["name", "opts"],
+                                      [SGroup, SOpts],
+                                      ["name='", SGroup, "'"])
+        end,
+    ejabberd_odbc:sql_transaction(Host, F).
 
 get_user_groups(US) ->
     Host = element(2, US),
+    DBType = gen_mod:db_type(Host, ?MODULE),
+    get_user_groups(US, Host, DBType) ++ get_special_users_groups(Host).
+
+get_user_groups(US, Host, mnesia) ->
     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 ++ get_special_users_groups(Host).
+    end;
+get_user_groups(US, Host, odbc) ->
+    SJID = make_jid_s(US),
+    case catch ejabberd_odbc:sql_query(
+                 Host, ["select grp from sr_user "
+                        "where jid='", SJID, "';"]) of
+        {selected, ["grp"], Rs} ->
+            [G || {G} <- Rs];
+        _ ->
+            []
+    end.
 
 is_group_enabled(Host1, Group1) ->
     {Host, Group} = split_grouphost(Host1, Group1),
-    case catch mnesia:dirty_read(sr_group, {Group, Host}) of
-       [#sr_group{opts = Opts}] ->
-           not lists:member(disabled, Opts);
-       _ ->
-           false
+    case get_group_opts(Host, Group) of
+        error ->
+            false;
+        Opts ->
+            not lists:member(disabled, Opts)
     end.
 
 %% @spec (Host::string(), Group::string(), Opt::atom(), Default) -> OptValue | Default
 get_group_opt(Host, Group, Opt, Default) ->
-    case catch mnesia:dirty_read(sr_group, {Group, Host}) of
-       [#sr_group{opts = Opts}] ->
+    case get_group_opts(Host, Group) of
+        error ->
+            Default;
+        Opts ->
            case lists:keysearch(Opt, 1, Opts) of
                {value, {_, Val}} ->
                    Val;
                false ->
                    Default
-           end;
-       _ ->
-           Default
+           end
     end.
 
 get_online_users(Host) ->
@@ -522,6 +609,9 @@ get_group_users(Host, Group, GroupOpts) ->
 
 %% @spec (Host::string(), Group::string()) -> [{User::string(), Server::string()}]
 get_group_explicit_users(Host, Group) ->
+    get_group_explicit_users(Host, Group, gen_mod:db_type(Host, ?MODULE)).
+
+get_group_explicit_users(Host, Group, mnesia) ->
     Read = (catch mnesia:dirty_index_read(
                    sr_user,
                    {Group, Host},
@@ -531,6 +621,21 @@ get_group_explicit_users(Host, Group) ->
            [R#sr_user.us || R <- Rs];
        _ ->
            []
+    end;
+get_group_explicit_users(Host, Group, odbc) ->
+    SGroup = ejabberd_odbc:escape(Group),
+    case catch ejabberd_odbc:sql_query(
+                 Host, ["select jid from sr_user "
+                        "where grp='", SGroup, "';"]) of
+        {selected, ["jid"], Rs} ->
+            lists:map(
+              fun({JID}) ->
+                      {U, S, _} = jlib:jid_tolower(
+                                    jlib:string_to_jid(JID)),
+                      {U, S}
+              end, Rs);
+        _ ->
+            []
     end.
 
 get_group_name(Host1, Group1) ->
@@ -581,15 +686,30 @@ get_special_displayed_groups(GroupsOpts) ->
 %% for the list of groups of that server that user is member
 %% get the list of groups displayed
 get_user_displayed_groups(LUser, LServer, GroupsOpts) ->
-    Groups = case catch mnesia:dirty_read(sr_user, {LUser, LServer}) of
-                Rs when is_list(Rs) ->
-                    [{Group, proplists:get_value(Group, GroupsOpts, [])} ||
-                        #sr_user{group_host = {Group, H}} <- Rs, H == LServer];
-                _ ->
-                    []
-            end,
+    Groups = get_user_displayed_groups(LUser, LServer, GroupsOpts,
+                                       gen_mod:db_type(LServer, ?MODULE)),
     displayed_groups(GroupsOpts, Groups).
 
+get_user_displayed_groups(LUser, LServer, GroupsOpts, mnesia) ->
+    case catch mnesia:dirty_read(sr_user, {LUser, LServer}) of
+        Rs when is_list(Rs) ->
+            [{Group, proplists:get_value(Group, GroupsOpts, [])} ||
+                #sr_user{group_host = {Group, H}} <- Rs, H == LServer];
+        _ ->
+            []
+    end;
+get_user_displayed_groups(LUser, LServer, GroupsOpts, odbc) ->
+    SJID = make_jid_s(LUser, LServer),
+    case catch ejabberd_odbc:sql_query(
+                 LServer, ["select grp from sr_user "
+                           "where jid='", SJID, "';"]) of
+        {selected, ["grp"], Rs} ->
+            [{Group, proplists:get_value(Group, GroupsOpts, [])} ||
+                {Group} <- Rs];
+        _ ->
+            []
+    end.
+
 %% @doc Get the list of groups that are displayed to this user
 get_user_displayed_groups(US) ->
     Host = element(2, US),
@@ -607,13 +727,26 @@ get_user_displayed_groups(US) ->
     [Group || Group <- DisplayedGroups1, is_group_enabled(Host, Group)].
 
 is_user_in_group(US, Group, Host) ->
+    is_user_in_group(US, Group, Host, gen_mod:db_type(Host, ?MODULE)).
+
+is_user_in_group(US, Group, Host, mnesia) ->
     case catch mnesia:dirty_match_object(
                 #sr_user{us=US, group_host={Group, Host}}) of
         [] -> lists:member(US, get_group_users(Host, Group));
        _  -> true
+    end;
+is_user_in_group(US, Group, Host, odbc) ->
+    SJID = make_jid_s(US),
+    SGroup = ejabberd_odbc:escape(Group),
+    case catch ejabberd_odbc:sql_query(
+                 Host, ["select * from sr_user where "
+                        "jid='", SJID, "' and grp='", SGroup, "';"]) of
+        {selected, _, []} ->
+            lists:member(US, get_group_users(Host, Group));
+        _ ->
+            true
     end.
 
-
 %% @spec (Host::string(), {User::string(), Server::string()}, Group::string()) -> {atomic, ok}
 add_user_to_group(Host, US, Group) ->
     {LUser, LServer} = US,
@@ -634,13 +767,27 @@ add_user_to_group(Host, US, Group) ->
            push_user_to_displayed(LUser, LServer, Group, Host, both),
            %% Push members of groups that are displayed to this group
            push_displayed_to_user(LUser, LServer, Group, Host, both),
-           R = #sr_user{us = US, group_host = {Group, Host}},
-           F = fun() ->
-                       mnesia:write(R)
-               end,
-           mnesia:transaction(F)
+            add_user_to_group(Host, US, Group, gen_mod:db_type(Host, ?MODULE))
     end.
 
+add_user_to_group(Host, US, Group, mnesia) ->
+    R = #sr_user{us = US, group_host = {Group, Host}},
+    F = fun() ->
+                mnesia:write(R)
+        end,
+    mnesia:transaction(F);
+add_user_to_group(Host, US, Group, odbc) ->
+    SJID = make_jid_s(US),
+    SGroup = ejabberd_odbc:escape(Group),
+    F = fun() ->
+                odbc_queries:update_t(
+                  "sr_user",
+                  ["jid", "grp"],
+                  [SJID, SGroup],
+                  ["jid='", SJID, "' and grp='", SGroup, "'"])
+        end,
+    ejabberd_odbc:sql_transaction(Host, F).
+
 push_displayed_to_user(LUser, LServer, Group, Host, Subscription) ->
     GroupsOpts = groups_with_opts(LServer),
     GroupOpts = proplists:get_value(Group, GroupsOpts, []),
@@ -648,7 +795,6 @@ push_displayed_to_user(LUser, LServer, Group, Host, Subscription) ->
     [push_members_to_user(LUser, LServer, DGroup, Host, Subscription) || DGroup <- DisplayedGroups].
 
 remove_user_from_group(Host, US, Group) ->
-    GroupHost = {Group, Host},
     {LUser, LServer} = US,
     case ejabberd_regexp:run(LUser, "^@.+@$") of
        match ->
@@ -662,11 +808,8 @@ remove_user_from_group(Host, US, Group) ->
                end,
            ?MODULE:set_group_opts(Host, Group, NewGroupOpts);
        nomatch ->
-           R = #sr_user{us = US, group_host = GroupHost},
-           F = fun() ->
-                       mnesia:delete_object(R)
-               end,
-           Result = mnesia:transaction(F),
+            Result = remove_user_from_group(Host, US, Group,
+                                            gen_mod:db_type(Host, ?MODULE)),
            %% Push removal of the old user to members of groups where the group that this user was members was displayed
            push_user_to_displayed(LUser, LServer, Group, Host, remove),
            %% Push removal of members of groups that where displayed to the group which this user has left
@@ -674,6 +817,22 @@ remove_user_from_group(Host, US, Group) ->
            Result
     end.
 
+remove_user_from_group(Host, US, Group, mnesia) ->
+    R = #sr_user{us = US, group_host = {Group, Host}},
+    F = fun() ->
+                mnesia:delete_object(R)
+        end,
+    mnesia:transaction(F);
+remove_user_from_group(Host, US, Group, odbc) ->
+    SJID = make_jid_s(US),
+    SGroup = ejabberd_odbc:escape(Group),
+    F = fun() ->
+                ejabberd_odbc:sql_query_t(
+                  ["delete from sr_user where jid='",
+                   SJID, "' and grp='", SGroup, "';"]),
+                ok
+        end,
+    ejabberd_odbc:sql_transaction(Host, F).
 
 push_members_to_user(LUser, LServer, Group, Host, Subscription) ->
     GroupsOpts = groups_with_opts(LServer),
@@ -1099,14 +1258,6 @@ shared_roster_group_parse_query(Host, Group, Query) ->
            nothing
     end.
 
-%% Get the roster module for Server.
-get_roster_mod(Server) ->
-    case lists:member(mod_roster_odbc,
-                      gen_mod:loaded_modules(Server)) of
-        true -> mod_roster_odbc;
-        false -> mod_roster
-    end.
-
 get_opt(Opts, Opt, Default) ->
     case lists:keysearch(Opt, 1, Opts) of
        {value, {_, Val}} ->
@@ -1125,3 +1276,12 @@ split_grouphost(Host, Group) ->
        [_] ->
            {Host, Group}
     end.
+
+make_jid_s(U, S) ->
+    ejabberd_odbc:escape(
+      jlib:jid_to_string(
+        jlib:jid_tolower(
+          jlib:make_jid(U, S, "")))).
+
+make_jid_s({U, S}) ->
+    make_jid_s(U, S).
diff --git a/src/mod_shared_roster_odbc.erl b/src/mod_shared_roster_odbc.erl
deleted file mode 100644 (file)
index 570e509..0000000
+++ /dev/null
@@ -1,1165 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File    : mod_shared_roster_odbc.erl
-%%% Author  : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : Shared roster management
-%%% Created :  5 Mar 2005 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2012   ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
--module(mod_shared_roster_odbc).
--author('alexey@process-one.net').
-
--behaviour(gen_mod).
-
--export([start/2, stop/1,
-         item_to_xml/1,
-        webadmin_menu/3, webadmin_page/3,
-        get_user_roster/2,
-        get_subscription_lists/3,
-        get_jid_info/4,
-        process_item/2,
-        in_subscription/6,
-        out_subscription/4,
-        user_available/1,
-        unset_presence/4,
-        register_user/2,
-        remove_user/2,
-        list_groups/1,
-        create_group/2,
-        create_group/3,
-        delete_group/2,
-        get_group_opts/2,
-        set_group_opts/3,
-        get_group_users/2,
-        get_group_explicit_users/2,
-        is_user_in_group/3,
-        add_user_to_group/3,
-        remove_user_from_group/3]).
-
--include("ejabberd.hrl").
--include("jlib.hrl").
--include("mod_roster.hrl").
--include("web/ejabberd_http.hrl").
--include("web/ejabberd_web_admin.hrl").
-
-start(Host, _Opts) ->
-    ejabberd_hooks:add(webadmin_menu_host, Host,
-                      ?MODULE, webadmin_menu, 70),
-    ejabberd_hooks:add(webadmin_page_host, Host,
-                      ?MODULE, webadmin_page, 50),
-    ejabberd_hooks:add(roster_get, Host,
-                      ?MODULE, get_user_roster, 70),
-    ejabberd_hooks:add(roster_in_subscription, Host,
-                      ?MODULE, in_subscription, 30),
-    ejabberd_hooks:add(roster_out_subscription, Host,
-                      ?MODULE, out_subscription, 30),
-    ejabberd_hooks:add(roster_get_subscription_lists, Host,
-                      ?MODULE, get_subscription_lists, 70),
-    ejabberd_hooks:add(roster_get_jid_info, Host,
-                      ?MODULE, get_jid_info, 70),
-    ejabberd_hooks:add(roster_process_item, Host,
-                      ?MODULE, process_item, 50),
-    ejabberd_hooks:add(user_available_hook, Host,
-                      ?MODULE, user_available, 50),
-    ejabberd_hooks:add(unset_presence_hook, Host,
-                      ?MODULE, unset_presence, 50),
-    ejabberd_hooks:add(register_user, Host,
-                      ?MODULE, register_user, 50),
-    ejabberd_hooks:add(anonymous_purge_hook, Host,
-                      ?MODULE, remove_user, 50),
-    ejabberd_hooks:add(remove_user, Host,
-                      ?MODULE, remove_user, 50).
-%%ejabberd_hooks:add(remove_user, Host,
-%%            ?MODULE, remove_user, 50),
-
-stop(Host) ->
-    ejabberd_hooks:delete(webadmin_menu_host, Host,
-                         ?MODULE, webadmin_menu, 70),
-    ejabberd_hooks:delete(webadmin_page_host, Host,
-                         ?MODULE, webadmin_page, 50),
-    ejabberd_hooks:delete(roster_get, Host,
-                         ?MODULE, get_user_roster, 70),
-    ejabberd_hooks:delete(roster_in_subscription, Host,
-                         ?MODULE, in_subscription, 30),
-    ejabberd_hooks:delete(roster_out_subscription, Host,
-                         ?MODULE, out_subscription, 30),
-    ejabberd_hooks:delete(roster_get_subscription_lists, Host,
-                         ?MODULE, get_subscription_lists, 70),
-    ejabberd_hooks:delete(roster_get_jid_info, Host,
-                         ?MODULE, get_jid_info, 70),
-    ejabberd_hooks:delete(roster_process_item, Host,
-                         ?MODULE, process_item, 50),
-    ejabberd_hooks:delete(user_available_hook, Host,
-                         ?MODULE, user_available, 50),
-    ejabberd_hooks:delete(unset_presence_hook, Host,
-                         ?MODULE, unset_presence, 50),
-    ejabberd_hooks:delete(register_user, Host,
-                         ?MODULE, register_user, 50),
-    ejabberd_hooks:delete(anonymous_purge_hook, Host,
-                         ?MODULE, remove_user, 50),
-    ejabberd_hooks:delete(remove_user, Host,
-                         ?MODULE, remove_user, 50).
-%%ejabberd_hooks:delete(remove_user, Host,
-%%               ?MODULE, remove_user, 50),
-
-
-get_user_roster(Items, US) ->
-    {U, S} = US,
-    DisplayedGroups = get_user_displayed_groups(US),
-    %% Get shared roster users in all groups and remove self: 
-    SRUsers = 
-       lists:foldl(
-         fun(Group, Acc1) ->
-                 GroupName = get_group_name(S, Group),
-                 lists:foldl(
-                   fun(User, Acc2) ->
-                           if User == US -> Acc2;
-                              true -> dict:append(User, 
-                                                  GroupName,
-                                                  Acc2)
-                           end
-                   end, Acc1, get_group_users(S, Group))
-         end, dict:new(), DisplayedGroups),
-
-    %% If partially subscribed users are also in shared roster, show them as
-    %% totally subscribed:
-    {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),
-
-    %% Export items in roster format:
-    ModVcard = get_vcard_module(S),
-    SRItems = [#roster{usj = {U, S, {U1, S1, ""}},
-                      us = US,
-                      jid = {U1, S1, ""},
-                      name = get_rosteritem_name(ModVcard, U1, S1),
-                      subscription = both,
-                      ask = none,
-                      groups = GroupNames} ||
-                 {{U1, S1}, GroupNames} <- dict:to_list(SRUsersRest)],
-    SRItems ++ NewItems1.
-
-get_vcard_module(Server) ->
-    Modules = gen_mod:loaded_modules(Server),
-    [M || M <- Modules,
-         (M == mod_vcard) or (M == mod_vcard_odbc) or (M == mod_vcard_ldap)].
-
-get_rosteritem_name([], _, _) ->
-    "";
-get_rosteritem_name([ModVcard], U, S) ->
-    From = jlib:make_jid("", S, ?MODULE),
-    To = jlib:make_jid(U, S, ""),
-    IQ = {iq,"",get,"vcard-temp","",
-         {xmlelement,"vCard",[{"xmlns","vcard-temp"}],[]}},
-    IQ_Vcard = ModVcard:process_sm_iq(From, To, IQ),
-    try get_rosteritem_name_vcard(IQ_Vcard#iq.sub_el)
-    catch E1:E2 ->
-           ?ERROR_MSG("Error ~p found when trying to get the vCard of ~s@~s "
-                      "in ~p:~n ~p", [E1, U, S, ModVcard, E2]),
-           ""
-    end.
-
-get_rosteritem_name_vcard([]) ->
-    "";
-get_rosteritem_name_vcard([Vcard]) ->
-    case xml:get_path_s(Vcard, [{elem, "NICKNAME"}, cdata]) of
-       "" -> xml:get_path_s(Vcard, [{elem, "FN"}, cdata]);
-       Nickname -> Nickname
-    end.
-
-%% This function rewrites the roster entries when moving or renaming
-%% them in the user contact list.
-process_item(RosterItem, Host) ->
-    USFrom = {UserFrom, ServerFrom} = RosterItem#roster.us,
-    {UserTo, ServerTo, ResourceTo} = RosterItem#roster.jid,
-    NameTo = RosterItem#roster.name,
-    USTo = {UserTo, ServerTo},
-    DisplayedGroups = get_user_displayed_groups(USFrom),
-    CommonGroups = lists:filter(fun(Group) ->
-                                       is_user_in_group(USTo, Group, Host)
-                               end, DisplayedGroups),
-    case CommonGroups of
-       [] -> RosterItem;
-       %% Roster item cannot be removed: We simply reset the original groups:
-       _ when RosterItem#roster.subscription == remove ->
-           GroupNames = lists:map(fun(Group) ->
-                                          get_group_name(Host, Group)
-                                  end, CommonGroups),
-           RosterItem#roster{subscription = both, ask = none,
-                             groups=[GroupNames]};
-       %% Both users have at least a common shared group,
-       %% So each user can see the other
-       _ ->
-           %% Check if the list of groups of the new roster item
-           %% include at least a new one
-           case lists:subtract(RosterItem#roster.groups, CommonGroups) of
-                %% If it doesn't, then remove this user from any
-                %% existing roster groups.
-               [] ->
-                    %% Remove pending subscription by setting it
-                    %% unsubscribed.
-                    Mod = get_roster_mod(ServerFrom),
-
-                    %% Remove pending out subscription
-                    Mod:out_subscription(UserTo, ServerTo,
-                                         jlib:make_jid(UserFrom, ServerFrom, ""),
-                                         unsubscribe),
-
-                    %% Remove pending in subscription
-                    Mod:in_subscription(aaaa, UserFrom, ServerFrom,
-                                        jlib:make_jid(UserTo, ServerTo, ""),
-                                        unsubscribe, ""),
-
-                    %% But we're still subscribed, so respond as such.
-                   RosterItem#roster{subscription = both, ask = none};
-               %% If so, it means the user wants to add that contact
-               %% to his personal roster
-               PersonalGroups ->
-                   %% Store roster items in From and To rosters
-                   set_new_rosteritems(UserFrom, ServerFrom,
-                                       UserTo, ServerTo, ResourceTo, NameTo,
-                                       PersonalGroups)
-           end
-    end.
-
-build_roster_record(User1, Server1, User2, Server2, Name2, Groups) ->
-    USR2 = {User2, Server2, ""},
-    #roster{usj = {User1, Server1, USR2},
-           us = {User1, Server1},
-           jid = USR2,
-           name = Name2,
-           subscription = both,
-           ask = none,
-           groups = Groups
-          }.
-
-set_new_rosteritems(UserFrom, ServerFrom,
-                   UserTo, ServerTo, ResourceTo, NameTo, GroupsFrom) ->
-    Mod = get_roster_mod(ServerFrom),
-
-    RIFrom = build_roster_record(UserFrom, ServerFrom,
-                                UserTo, ServerTo, NameTo, GroupsFrom),
-    set_item(UserFrom, ServerFrom, ResourceTo, RIFrom),
-    JIDTo = jlib:make_jid(UserTo, ServerTo, ""),
-
-    JIDFrom = jlib:make_jid(UserFrom, ServerFrom, ""),
-    RITo = build_roster_record(UserTo, ServerTo,
-                              UserFrom, ServerFrom, UserFrom,[]),
-    set_item(UserTo, ServerTo, "", RITo),
-
-    %% From requests
-    Mod:out_subscription(UserFrom, ServerFrom, JIDTo, subscribe),
-    Mod:in_subscription(aaa, UserTo, ServerTo, JIDFrom, subscribe, ""),
-
-    %% To accepts
-    Mod:out_subscription(UserTo, ServerTo, JIDFrom, subscribed),
-    Mod:in_subscription(aaa, UserFrom, ServerFrom, JIDTo, subscribed, ""),
-
-    %% To requests
-    Mod:out_subscription(UserTo, ServerTo, JIDFrom, subscribe),
-    Mod:in_subscription(aaa, UserFrom, ServerFrom, JIDTo, subscribe, ""),
-
-    %% From accepts
-    Mod:out_subscription(UserFrom, ServerFrom, JIDTo, subscribed),
-    Mod:in_subscription(aaa, UserTo, ServerTo, JIDFrom, subscribed, ""),
-
-    RIFrom.
-
-set_item(User, Server, Resource, Item) ->
-    ResIQ = #iq{type = set, xmlns = ?NS_ROSTER,
-               id = "push" ++ randoms:get_string(),
-               sub_el = [{xmlelement, "query",
-                          [{"xmlns", ?NS_ROSTER}],
-                          [mod_roster:item_to_xml(Item)]}]},
-    ejabberd_router:route(
-      jlib:make_jid(User, Server, Resource),
-      jlib:make_jid("", Server, ""),
-      jlib:iq_to_xml(ResIQ)).
-
-
-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, _Reason) ->
-    process_subscription(in, User, Server, JID, Type, Acc).
-
-out_subscription(UserFrom, ServerFrom, JIDTo, unsubscribed) ->
-    Mod = get_roster_mod(ServerFrom),
-
-    %% Remove pending out subscription
-    #jid{luser = UserTo, lserver = ServerTo} = JIDTo,
-    JIDFrom = jlib:make_jid(UserFrom, UserTo, ""),
-    Mod:out_subscription(UserTo, ServerTo, JIDFrom, unsubscribe),
-
-    %% Remove pending in subscription
-    Mod:in_subscription(aaaa, UserFrom, ServerFrom, JIDTo, unsubscribe, ""),
-
-    process_subscription(out, UserFrom, ServerFrom, JIDTo, unsubscribed, false);
-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) ->
-    case ejabberd_odbc:sql_query(
-           Host, ["select name from sr_group;"]) of
-        {selected, ["name"], Rs} ->
-            [G || {G} <- Rs];
-        _ ->
-            []
-    end.
-
-groups_with_opts(Host) ->
-    case ejabberd_odbc:sql_query(
-           Host, ["select name, opts from sr_group;"]) of
-        {selected, ["name", "opts"], Rs} ->
-            [{G, ejabberd_odbc:decode_term(Opts)} || {G, Opts} <- Rs];
-        _ ->
-            []
-    end.
-
-create_group(Host, Group) ->
-    create_group(Host, Group, []).
-
-create_group(Host, Group, Opts) ->
-    SGroup = ejabberd_odbc:escape(Group),
-    SOpts = ejabberd_odbc:encode_term(Opts),
-    F = fun() ->
-                odbc_queries:update_t("sr_group",
-                                      ["name", "opts"],
-                                      [SGroup, SOpts],
-                                      ["name='", SGroup, "'"])
-        end,
-    ejabberd_odbc:sql_transaction(Host, F).
-
-delete_group(Host, Group) ->
-    SGroup = ejabberd_odbc:escape(Group),
-    F = fun() ->
-                ejabberd_odbc:sql_query_t(
-                  ["delete from sr_group where name='", SGroup, "';"]),
-                ejabberd_odbc:sql_query_t(
-                  ["delete from sr_user where grp='", SGroup, "';"])
-        end,
-    ejabberd_odbc:sql_transaction(Host, F).
-
-get_group_opts(Host, Group) ->
-    SGroup = ejabberd_odbc:escape(Group),
-    case catch ejabberd_odbc:sql_query(
-                 Host, ["select opts from sr_group "
-                        "where name='", SGroup, "';"]) of
-        {selected, ["opts"], [{SOpts}]} ->
-            ejabberd_odbc:decode_term(SOpts);
-        _ ->
-            error
-    end.
-
-set_group_opts(Host, Group, Opts) ->
-    SGroup = ejabberd_odbc:escape(Group),
-    SOpts = ejabberd_odbc:encode_term(Opts),
-    F = fun() ->
-                odbc_queries:update_t("sr_group",
-                                      ["name", "opts"],
-                                      [SGroup, SOpts],
-                                      ["name='", SGroup, "'"])
-        end,
-    ejabberd_odbc:sql_transaction(Host, F).
-
-get_user_groups(US) ->
-    Host = element(2, US),
-    SJID = make_jid_s(US),
-    case catch ejabberd_odbc:sql_query(
-                 Host, ["select grp from sr_user "
-                        "where jid='", SJID, "';"]) of
-        {selected, ["grp"], Rs} ->
-            [G || {G} <- Rs];
-        _ ->
-            []
-    end ++ get_special_users_groups(Host).
-
-is_group_enabled(Host1, Group1) ->
-    {Host, Group} = split_grouphost(Host1, Group1),
-    SGroup = ejabberd_odbc:escape(Group),
-    case catch ejabberd_odbc:sql_query(
-                 Host, ["select opts from sr_group "
-                        "where name='", SGroup, "';"]) of
-        {selected, ["opts"], [{SOpts}]} ->
-            Opts = ejabberd_odbc:decode_term(SOpts),
-            not lists:member(disabled, Opts);
-        _ ->
-            false
-    end.
-
-%% @spec (Host::string(), Group::string(), Opt::atom(), Default) -> OptValue | Default
-get_group_opt(Host, Group, Opt, Default) ->
-    SGroup = ejabberd_odbc:escape(Group),
-    case catch ejabberd_odbc:sql_query(
-                 Host, ["select opts from sr_group "
-                        "where name='", SGroup, "';"]) of
-        {selected, ["opts"], [{SOpts}]} ->
-            Opts = ejabberd_odbc:decode_term(SOpts),
-           case lists:keysearch(Opt, 1, Opts) of
-               {value, {_, Val}} ->
-                   Val;
-               false ->
-                   Default
-           end;
-       _ ->
-           Default
-    end.
-
-get_online_users(Host) ->
-    lists:usort([{U, S} || {U, S, _} <- ejabberd_sm:get_vh_session_list(Host)]).
-
-get_group_users(Host1, Group1) ->
-    {Host, Group} = split_grouphost(Host1, Group1),
-    case get_group_opt(Host, Group, all_users, false) of
-       true ->
-           ejabberd_auth:get_vh_registered_users(Host);
-       false ->
-           []
-    end ++
-    case get_group_opt(Host, Group, online_users, false) of
-       true ->
-           get_online_users(Host);
-       false ->
-           []
-    end ++
-    get_group_explicit_users(Host, Group).
-
-get_group_users(Host, Group, GroupOpts) ->
-    case proplists:get_value(all_users, GroupOpts, false) of
-       true ->
-           ejabberd_auth:get_vh_registered_users(Host);
-       false ->
-           []
-    end ++
-    case proplists:get_value(online_users, GroupOpts, false) of
-       true ->
-           get_online_users(Host);
-       false ->
-           []
-    end ++
-    get_group_explicit_users(Host, Group).
-
-%% @spec (Host::string(), Group::string()) -> [{User::string(), Server::string()}]
-get_group_explicit_users(Host, Group) ->
-    SGroup = ejabberd_odbc:escape(Group),
-    case catch ejabberd_odbc:sql_query(
-                 Host, ["select jid from sr_user "
-                        "where grp='", SGroup, "';"]) of
-        {selected, ["jid"], Rs} ->
-            lists:map(
-              fun({JID}) ->
-                      {U, S, _} = jlib:jid_tolower(
-                                    jlib:string_to_jid(JID)),
-                      {U, S}
-              end, Rs);
-        _ ->
-            []
-    end.
-
-get_group_name(Host1, Group1) ->
-    {Host, Group} = split_grouphost(Host1, Group1),
-    get_group_opt(Host, Group, name, Group).
-
-%% Get list of names of groups that have @all@/@online@/etc in the memberlist
-get_special_users_groups(Host) ->
-    lists:filter(
-      fun(Group) ->
-             get_group_opt(Host, Group, all_users, false)
-                 orelse get_group_opt(Host, Group, online_users, false)
-      end,
-      list_groups(Host)).
-
-%% Get list of names of groups that have @online@ in the memberlist
-get_special_users_groups_online(Host) ->
-    lists:filter(
-      fun(Group) ->
-             get_group_opt(Host, Group, online_users, false)
-      end,
-      list_groups(Host)).
-
-%% Given two lists of groupnames and their options,
-%% return the list of displayed groups to the second list
-displayed_groups(GroupsOpts, SelectedGroupsOpts) ->
-    DisplayedGroups =
-       lists:usort(
-         lists:flatmap(
-           fun({_Group, Opts}) ->
-                   [G || G <- proplists:get_value(displayed_groups, Opts, []),
-                         not lists:member(disabled, Opts)]
-           end, SelectedGroupsOpts)),
-    [G || G <- DisplayedGroups,
-         not lists:member(disabled, proplists:get_value(G, GroupsOpts, []))].
-
-%% Given a list of group names with options,
-%% for those that have @all@ in memberlist,
-%% get the list of groups displayed
-get_special_displayed_groups(GroupsOpts) ->
-    Groups = lists:filter(
-              fun({_Group, Opts}) ->
-                      proplists:get_value(all_users, Opts, false)
-              end, GroupsOpts),
-    displayed_groups(GroupsOpts, Groups).
-
-%% Given a username and server, and a list of group names with options,
-%% for the list of groups of that server that user is member
-%% get the list of groups displayed
-get_user_displayed_groups(LUser, LServer, GroupsOpts) ->
-    SJID = make_jid_s(LUser, LServer),
-    Groups = case catch ejabberd_odbc:sql_query(
-                          LServer, ["select grp from sr_user "
-                                    "where jid='", SJID, "';"]) of
-                 {selected, ["grp"], Rs} ->
-                     [{Group, proplists:get_value(Group, GroupsOpts, [])} ||
-                         {Group} <- Rs];
-                 _ ->
-                     []
-             end,
-    displayed_groups(GroupsOpts, Groups).
-
-%% @doc Get the list of groups that are displayed to this user
-get_user_displayed_groups(US) ->
-    Host = element(2, US),
-    DisplayedGroups1 =
-       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)].
-
-is_user_in_group(US, Group, Host) ->
-    SJID = make_jid_s(US),
-    SGroup = ejabberd_odbc:escape(Group),
-    case catch ejabberd_odbc:sql_query(
-                 Host, ["select * from sr_user where "
-                        "jid='", SJID, "' and grp='", SGroup, "';"]) of
-        {selected, _, []} ->
-            lists:member(US, get_group_users(Host, Group));
-        _ ->
-            true
-    end.
-
-%% @spec (Host::string(), {User::string(), Server::string()}, Group::string()) -> {atomic, ok}
-add_user_to_group(Host, US, Group) ->
-    {LUser, LServer} = US,
-    case ejabberd_regexp:run(LUser, "^@.+@$") of
-       match ->
-           GroupOpts = ?MODULE:get_group_opts(Host, Group),
-           MoreGroupOpts =
-               case LUser of
-                   "@all@" -> [{all_users, true}];
-                   "@online@" -> [{online_users, true}];
-                   _ -> []
-               end,
-            ?MODULE:set_group_opts(
-             Host, Group,
-             GroupOpts ++ MoreGroupOpts);
-       nomatch ->
-           %% Push this new user to members of groups where this group is displayed
-           push_user_to_displayed(LUser, LServer, Group, Host, both),
-           %% Push members of groups that are displayed to this group
-           push_displayed_to_user(LUser, LServer, Group, Host, both),
-            SJID = make_jid_s(US),
-            SGroup = ejabberd_odbc:escape(Group),
-            F = fun() ->
-                        odbc_queries:update_t(
-                          "sr_user",
-                          ["jid", "grp"],
-                          [SJID, SGroup],
-                          ["jid='", SJID, "' and grp='", SGroup, "'"])
-                end,
-            ejabberd_odbc:sql_transaction(Host, F)
-    end.
-
-push_displayed_to_user(LUser, LServer, Group, Host, Subscription) ->
-    GroupsOpts = groups_with_opts(LServer),
-    GroupOpts = proplists:get_value(Group, GroupsOpts, []),
-    DisplayedGroups = proplists:get_value(displayed_groups, GroupOpts, []),
-    [push_members_to_user(LUser, LServer, DGroup, Host, Subscription) || DGroup <- DisplayedGroups].
-
-remove_user_from_group(Host, US, Group) ->
-    {LUser, LServer} = US,
-    case ejabberd_regexp:run(LUser, "^@.+@$") of
-       match ->
-           GroupOpts = ?MODULE:get_group_opts(Host, Group),
-           NewGroupOpts =
-               case LUser of
-                   "@all@" ->
-                       lists:filter(fun(X) -> X/={all_users,true} end, GroupOpts);
-                   "@online@" ->
-                       lists:filter(fun(X) -> X/={online_users,true} end, GroupOpts)
-               end,
-           ?MODULE:set_group_opts(Host, Group, NewGroupOpts);
-       nomatch ->
-            SJID = make_jid_s(US),
-            SGroup = ejabberd_odbc:escape(Group),
-            F = fun() ->
-                        ejabberd_odbc:sql_query_t(
-                          ["delete from sr_user where jid='",
-                           SJID, "' and grp='", SGroup, "';"]),
-                        ok
-                end,
-            Result = ejabberd_odbc:sql_transaction(Host, F),
-           %% Push removal of the old user to members of groups where the group that this user was members was displayed
-           push_user_to_displayed(LUser, LServer, Group, Host, remove),
-           %% Push removal of members of groups that where displayed to the group which this user has left
-           push_displayed_to_user(LUser, LServer, Group, Host, remove),
-           Result
-    end.
-
-
-push_members_to_user(LUser, LServer, Group, Host, Subscription) ->
-    GroupsOpts = groups_with_opts(LServer),
-    GroupOpts = proplists:get_value(Group, GroupsOpts, []),
-    GroupName = proplists:get_value(name, GroupOpts, Group),
-    Members = get_group_users(Host, Group),
-    lists:foreach(
-      fun({U, S}) ->
-             push_roster_item(LUser, LServer, U, S, GroupName, Subscription)
-      end, Members).
-
-register_user(User, Server) ->
-    %% Get list of groups where this user is member
-    Groups = get_user_groups({User, Server}),
-    %% Push this user to members of groups where is displayed a group which this user is member
-    [push_user_to_displayed(User, Server, Group, Server, both) || Group <- Groups].
-
-remove_user(User, Server) ->
-    push_user_to_members(User, Server, remove).
-
-push_user_to_members(User, Server, Subscription) ->
-    LUser = jlib:nodeprep(User),
-    LServer = jlib:nameprep(Server),
-    GroupsOpts = groups_with_opts(LServer),
-    SpecialGroups = get_special_displayed_groups(GroupsOpts),
-    UserGroups = get_user_displayed_groups(LUser, LServer, GroupsOpts),
-    lists:foreach(
-      fun(Group) ->
-             remove_user_from_group(LServer, {LUser, LServer}, Group),
-             GroupOpts = proplists:get_value(Group, GroupsOpts, []),
-             GroupName = proplists:get_value(name, GroupOpts, Group),
-             lists:foreach(
-               fun({U, S}) ->
-                       push_roster_item(U, S, LUser, LServer, GroupName, Subscription)
-               end, get_group_users(LServer, Group, GroupOpts))
-      end, lists:usort(SpecialGroups++UserGroups)).
-
-push_user_to_displayed(LUser, LServer, Group, Host, Subscription) ->
-    GroupsOpts = groups_with_opts(Host),
-    GroupOpts = proplists:get_value(Group, GroupsOpts, []),
-    GroupName = proplists:get_value(name, GroupOpts, Group),
-    DisplayedToGroupsOpts = displayed_to_groups(Group, Host),
-    [push_user_to_group(LUser, LServer, GroupD, Host, GroupName, Subscription) || {GroupD, _Opts} <- DisplayedToGroupsOpts].
-
-push_user_to_group(LUser, LServer, Group, Host, GroupName, Subscription) ->
-    lists:foreach(
-      fun({U, S})  when (U == LUser) and (S == LServer) -> ok;
-         ({U, S}) ->
-             push_roster_item(U, S, LUser, LServer, GroupName, Subscription)
-      end, get_group_users(Host, Group)).
-
-%% Get list of groups to which this group is displayed
-displayed_to_groups(GroupName, LServer) ->
-    GroupsOpts = groups_with_opts(LServer),
-    lists:filter(
-      fun({_Group, Opts}) ->
-             lists:member(GroupName, proplists:get_value(displayed_groups, Opts, []))
-      end, GroupsOpts).
-
-push_item(User, Server, From, Item) ->
-    %% It was
-    %%  ejabberd_sm:route(jlib:make_jid("", "", ""),
-    %%                    jlib:make_jid(User, Server, "")
-    %% why?
-    ejabberd_sm:route(From, jlib:make_jid(User, Server, ""),
-                     {xmlelement, "broadcast", [],
-                      [{item,
-                        Item#roster.jid,
-                        Item#roster.subscription}]}),
-    Stanza = jlib:iq_to_xml(
-              #iq{type = set, xmlns = ?NS_ROSTER,
-                  id = "push" ++ randoms:get_string(),
-                  sub_el = [{xmlelement, "query",
-                             [{"xmlns", ?NS_ROSTER}],
-                             [item_to_xml(Item)]}]}),
-    lists:foreach(
-      fun(Resource) ->
-             JID = jlib:make_jid(User, Server, Resource),
-             ejabberd_router:route(JID, JID, Stanza)
-      end, ejabberd_sm:get_user_resources(User, Server)).
-
-push_roster_item(User, Server, ContactU, ContactS, GroupName, Subscription) ->
-    Item = #roster{usj = {User, Server, {ContactU, ContactS, ""}},
-                  us = {User, Server},
-                  jid = {ContactU, ContactS, ""},
-                  name = "",
-                  subscription = Subscription,
-                  ask = none,
-                  groups = [GroupName]},
-    push_item(User, Server, jlib:make_jid("", Server, ""), Item).
-
-item_to_xml(Item) ->
-    Attrs1 = [{"jid", jlib:jid_to_string(Item#roster.jid)}],
-    Attrs2 = case Item#roster.name of
-                "" ->
-                    Attrs1;
-                Name ->
-                    [{"name", Name} | Attrs1]
-            end,
-    Attrs3 = case Item#roster.subscription of
-                none ->
-                    [{"subscription", "none"} | Attrs2];
-                from ->
-                    [{"subscription", "from"} | Attrs2];
-                to ->
-                    [{"subscription", "to"} | Attrs2];
-                both ->
-                    [{"subscription", "both"} | Attrs2];
-                remove ->
-                    [{"subscription", "remove"} | Attrs2]
-            end,
-    Attrs4 = case ask_to_pending(Item#roster.ask) of
-                out ->
-                    [{"ask", "subscribe"} | Attrs3];
-                both ->
-                    [{"ask", "subscribe"} | Attrs3];
-                _ ->
-                    Attrs3
-            end,
-    SubEls1 = lists:map(fun(G) ->
-                               {xmlelement, "group", [], [{xmlcdata, G}]}
-                       end, Item#roster.groups),
-    SubEls = SubEls1 ++ Item#roster.xs,
-    {xmlelement, "item", Attrs4, SubEls}.
-
-ask_to_pending(subscribe) -> out;
-ask_to_pending(unsubscribe) -> none;
-ask_to_pending(Ask) -> Ask.
-
-user_available(New) ->
-    LUser = New#jid.luser,
-    LServer = New#jid.lserver,
-    Resources = ejabberd_sm:get_user_resources(LUser, LServer),
-    ?DEBUG("user_available for ~p @ ~p (~p resources)",
-          [LUser, LServer, length(Resources)]),
-    case length(Resources) of
-       %% first session for this user
-       1 ->
-           %% This is a simplification - we ignore he 'display'
-           %% property - @online@ is always reflective.
-           OnlineGroups = get_special_users_groups_online(LServer),
-           lists:foreach(
-             fun(OG) ->
-                     ?DEBUG("user_available: pushing  ~p @ ~p grp ~p",
-                            [LUser, LServer, OG ]),
-                     push_user_to_displayed(LUser, LServer, OG, LServer, both)
-             end, OnlineGroups);
-       _ ->
-           ok
-    end.
-
-unset_presence(LUser, LServer, Resource, Status) ->
-    Resources = ejabberd_sm:get_user_resources(LUser, LServer),
-    ?DEBUG("unset_presence for ~p @ ~p / ~p -> ~p (~p resources)",
-          [LUser, LServer, Resource, Status, length(Resources)]),
-    %% if user has no resources left...
-    case length(Resources) of
-       0 ->
-           %% This is a simplification - we ignore he 'display'
-           %% property - @online@ is always reflective.
-           OnlineGroups = get_special_users_groups_online(LServer),
-           %% for each of these groups...
-           lists:foreach(
-             fun(OG) ->
-                     %% Push removal of the old user to members of groups
-                     %% where the group that this uwas members was displayed
-                     push_user_to_displayed(LUser, LServer, OG, LServer, remove),
-                     %% Push removal of members of groups that where
-                     %% displayed to the group which thiuser has left
-                     push_displayed_to_user(LUser, LServer, OG, LServer,remove)
-             end, OnlineGroups);
-       _ ->
-           ok
-    end.
-
-%%---------------------
-%% Web Admin
-%%---------------------
-
-webadmin_menu(Acc, _Host, Lang) ->
-    [{"shared-roster", ?T("Shared Roster Groups")} | Acc].
-
-webadmin_page(_, Host,
-             #request{us = _US,
-                      path = ["shared-roster"],
-                      q = Query,
-                      lang = Lang} = _Request) ->
-    Res = list_shared_roster_groups(Host, Query, Lang),
-    {stop, Res};
-
-webadmin_page(_, Host,
-             #request{us = _US,
-                      path = ["shared-roster", Group],
-                      q = Query,
-                      lang = Lang} = _Request) ->
-    Res = shared_roster_group(Host, Group, Query, Lang),
-    {stop, Res};
-
-webadmin_page(Acc, _, _) -> Acc.
-
-list_shared_roster_groups(Host, Query, Lang) ->
-    Res = list_sr_groups_parse_query(Host, Query),
-    SRGroups = ?MODULE:list_groups(Host),
-    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")])
-                      ]
-                     )]
-                )]),
-    ?H1GL(?T("Shared Roster Groups"), "modsharedroster", "mod_shared_roster") ++
-       case Res of
-           ok -> [?XREST("Submitted")];
-           error -> [?XREST("Bad format")];
-           nothing -> []
-       end ++
-       [?XAE("form", [{"action", ""}, {"method", "post"}],
-             [FGroups,
-              ?BR,
-              ?INPUTT("submit", "delete", "Delete Selected")
-             ])
-       ].
-
-list_sr_groups_parse_query(Host, Query) ->
-    case lists:keysearch("addnew", 1, Query) of
-       {value, _} ->
-           list_sr_groups_parse_addnew(Host, Query);
-       _ ->
-           case lists:keysearch("delete", 1, Query) of
-               {value, _} ->
-                   list_sr_groups_parse_delete(Host, Query);
-               _ ->
-                   nothing
-           end
-    end.
-
-list_sr_groups_parse_addnew(Host, Query) ->
-    case lists:keysearch("namenew", 1, Query) of
-       {value, {_, Group}} when Group /= "" ->
-           ?MODULE:create_group(Host, Group),
-           ok;
-       _ ->
-           error
-    end.
-
-list_sr_groups_parse_delete(Host, Query) ->
-    SRGroups = ?MODULE:list_groups(Host),
-    lists:foreach(
-      fun(Group) ->
-             case lists:member({"selected", Group}, Query) of
-                 true ->
-                     ?MODULE:delete_group(Host, Group);
-                 _ ->
-                     ok
-             end
-      end, SRGroups),
-    ok.
-
-
-shared_roster_group(Host, Group, Query, Lang) ->
-    Res = shared_roster_group_parse_query(Host, Group, Query),
-    GroupOpts = ?MODULE:get_group_opts(Host, Group),
-    Name = get_opt(GroupOpts, name, ""),
-    Description = get_opt(GroupOpts, description, ""),
-    AllUsers = get_opt(GroupOpts, all_users, false),
-    OnlineUsers = get_opt(GroupOpts, online_users, false),
-    %%Disabled = false,
-    DisplayedGroups = get_opt(GroupOpts, displayed_groups, []),
-    Members = ?MODULE:get_group_explicit_users(Host, Group),
-    FMembers =
-       if
-           AllUsers ->
-               "@all@\n";
-           true ->
-               []
-       end ++
-       if
-           OnlineUsers ->
-               "@online@\n";
-           true ->
-               []
-       end ++
-       [[us_to_list(Member), $\n] || Member <- Members],
-    FDisplayedGroups = [[DG, $\n] || DG <- DisplayedGroups],
-    DescNL = length(ejabberd_regexp:split(Description, "\n")),
-    FGroup =
-       ?XAE("table", [{"class", "withtextareas"}],
-            [?XE("tbody",
-                 [?XE("tr",
-                      [?XCT("td", "Name:"),
-                       ?XE("td", [?INPUT("text", "name", Name)])
-                      ]
-                     ),
-                  ?XE("tr",
-                      [?XCT("td", "Description:"),
-                       ?XE("td", [
-                                  ?TEXTAREA("description", integer_to_list(lists:max([3, DescNL])), "20", Description)
-                                 ]
-                          )
-                      ]
-                     ),
-                  ?XE("tr",
-                      [?XCT("td", "Members:"),
-                       ?XE("td", [
-                                  ?TEXTAREA("members", integer_to_list(lists:max([3, length(FMembers)])), "20", FMembers)
-                                 ]
-                          )
-                      ]
-                     ),
-                  ?XE("tr",
-                      [?XCT("td", "Displayed Groups:"),
-                       ?XE("td", [
-                                  ?TEXTAREA("dispgroups", integer_to_list(lists:max([3, length(FDisplayedGroups)])), "20", FDisplayedGroups)
-                                 ]
-                          )
-                      ]
-                     )]
-                )]),
-    ?H1GL(?T("Shared Roster Groups"), "modsharedroster", "mod_shared_roster") ++
-       [?XC("h2", ?T("Group ") ++ Group)] ++
-       case Res of
-           ok -> [?XREST("Submitted")];
-           error -> [?XREST("Bad format")];
-           nothing -> []
-       end ++
-       [?XAE("form", [{"action", ""}, {"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,
-
-           OldMembers = ?MODULE:get_group_explicit_users(
-                          Host, Group),
-           SJIDs = string:tokens(SMembers, ", \r\n"),
-           NewMembers =
-               lists:foldl(
-                 fun(_SJID, error) -> error;
-                    (SJID, USs) ->
-                         case SJID of
-                             "@all@" ->
-                                 USs;
-                             "@online@" ->
-                                 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
-                 end, [], SJIDs),
-           AllUsersOpt =
-               case lists:member("@all@", SJIDs) of
-                   true -> [{all_users, true}];
-                   false -> []
-               end,
-           OnlineUsersOpt =
-               case lists:member("@online@", SJIDs) of
-                   true -> [{online_users, true}];
-                   false -> []
-               end,
-
-           ?MODULE:set_group_opts(
-             Host, Group,
-             NameOpt ++ DispGroupsOpt ++ DescriptionOpt ++ AllUsersOpt ++ OnlineUsersOpt),
-
-           if
-               NewMembers == error -> error;
-               true ->
-                   AddedMembers = NewMembers -- OldMembers,
-                   RemovedMembers = OldMembers -- NewMembers,
-                   lists:foreach(
-                     fun(US) ->
-                             ?MODULE:remove_user_from_group(
-                               Host, US, Group)
-                     end, RemovedMembers),
-                   lists:foreach(
-                     fun(US) ->
-                             ?MODULE:add_user_to_group(
-                               Host, US, Group)
-                     end, AddedMembers),
-                   ok
-           end;
-       _ ->
-           nothing
-    end.
-
-%% Get the roster module for Server.
-get_roster_mod(Server) ->
-    case lists:member(mod_roster_odbc,
-                      gen_mod:loaded_modules(Server)) of
-        true -> mod_roster_odbc;
-        false -> mod_roster
-    end.
-
-get_opt(Opts, Opt, Default) ->
-    case lists:keysearch(Opt, 1, Opts) of
-       {value, {_, Val}} ->
-           Val;
-       false ->
-           Default
-    end.
-
-us_to_list({User, Server}) ->
-    jlib:jid_to_string({User, Server, ""}).
-
-split_grouphost(Host, Group) ->
-    case string:tokens(Group, "@") of
-       [GroupName, HostName] ->
-           {HostName, GroupName};
-       [_] ->
-           {Host, Group}
-    end.
-
-make_jid_s(U, S) ->
-    ejabberd_odbc:escape(
-      jlib:jid_to_string(
-        jlib:jid_tolower(
-          jlib:make_jid(U, S, "")))).
-
-make_jid_s({U, S}) ->
-    make_jid_s(U, S).
index b796375f5192fd34e97350a0a6345fde9294cdb3..6381ec55c9d513c7fdf89685c187350e5243313c 100644 (file)
@@ -1,7 +1,7 @@
 %%%----------------------------------------------------------------------
 %%% File    : mod_vcard.erl
 %%% Author  : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : Vcard management in Mnesia
+%%% Purpose : Vcard management
 %%% Created :  2 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
 %%%
 %%%
 -define(PROCNAME, ejabberd_mod_vcard).
 
 start(Host, Opts) ->
-    mnesia:create_table(vcard, [{disc_only_copies, [node()]},
-                               {attributes, record_info(fields, vcard)}]),
-    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),
-    mnesia:add_table_index(vcard_search, lgiven),
-    mnesia:add_table_index(vcard_search, lmiddle),
-    mnesia:add_table_index(vcard_search, lnickname),
-    mnesia:add_table_index(vcard_search, lbday),
-    mnesia:add_table_index(vcard_search, lctry),
-    mnesia:add_table_index(vcard_search, llocality),
-    mnesia:add_table_index(vcard_search, lemail),
-    mnesia:add_table_index(vcard_search, lorgname),
-    mnesia:add_table_index(vcard_search, lorgunit),
-
+    case gen_mod:db_type(Opts) of
+        mnesia ->
+            mnesia:create_table(vcard,
+                                [{disc_only_copies, [node()]},
+                                 {attributes,
+                                  record_info(fields, vcard)}]),
+            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),
+            mnesia:add_table_index(vcard_search, lgiven),
+            mnesia:add_table_index(vcard_search, lmiddle),
+            mnesia:add_table_index(vcard_search, lnickname),
+            mnesia:add_table_index(vcard_search, lbday),
+            mnesia:add_table_index(vcard_search, lctry),
+            mnesia:add_table_index(vcard_search, llocality),
+            mnesia:add_table_index(vcard_search, lemail),
+            mnesia:add_table_index(vcard_search, lorgname),
+            mnesia:add_table_index(vcard_search, lorgunit);
+        _ ->
+            ok
+    end,
     ejabberd_hooks:add(remove_user, Host,
                       ?MODULE, remove_user, 50),
     IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
@@ -183,19 +190,45 @@ process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
            end;
        get ->
            #jid{luser = LUser, lserver = LServer} = To,
-           US = {LUser, LServer},
-           F = fun() ->
-                       mnesia:read({vcard, US})
-               end,
-           Els = case mnesia:transaction(F) of
-                     {atomic, Rs} ->
-                         lists:map(fun(R) ->
-                                           R#vcard.vcard
-                                   end, Rs);
-                     {aborted, _Reason} ->
-                         []
-                 end,
-           IQ#iq{type = result, sub_el = Els}
+            case get_vcard(LUser, LServer) of
+                error ->
+                    IQ#iq{type = error,
+                         sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]};
+                Els ->
+                    IQ#iq{type = result, sub_el = Els}
+            end
+    end.
+
+get_vcard(LUser, LServer) ->
+    get_vcard(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
+
+get_vcard(LUser, LServer, mnesia) ->
+    US = {LUser, LServer},
+    F = fun() ->
+                mnesia:read({vcard, US})
+        end,
+    case mnesia:transaction(F) of
+        {atomic, Rs} ->
+            lists:map(fun(R) ->
+                              R#vcard.vcard
+                      end, Rs);
+        {aborted, _Reason} ->
+            error
+    end;
+get_vcard(LUser, LServer, odbc) ->
+    Username = ejabberd_odbc:escape(LUser),
+    case catch odbc_queries:get_vcard(LServer, Username) of
+        {selected, ["vcard"], [{SVCARD}]} ->
+            case xml_stream:parse_element(SVCARD) of
+                {error, _Reason} ->
+                    error;
+                VCARD ->
+                    [VCARD]
+            end;
+        {selected, ["vcard"], []} ->
+            [];
+        _ ->
+            error
     end.
 
 set_vcard(User, LServer, VCARD) ->
@@ -231,8 +264,6 @@ set_vcard(User, LServer, VCARD) ->
     LOrgName  = string2lower(OrgName),
     LOrgUnit  = string2lower(OrgUnit),
 
-    US = {LUser, LServer},
-
     if
        (LUser     == error) or
        (LFN       == error) or
@@ -248,26 +279,66 @@ set_vcard(User, LServer, VCARD) ->
        (LOrgUnit  == error) ->
            {error, badarg};
        true ->
-           F = fun() ->
-               mnesia:write(#vcard{us = US, vcard = VCARD}),
-               mnesia:write(
-                 #vcard_search{us        = US,
-                               user      = {User, LServer},
-                               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,
-           mnesia:transaction(F),
+            case gen_mod:db_type(LServer, ?MODULE) of
+                mnesia ->
+                    US = {LUser, LServer},
+                    F = fun() ->
+                                mnesia:write(#vcard{us = US, vcard = VCARD}),
+                                mnesia:write(
+                                  #vcard_search{us        = US,
+                                                user      = {User, LServer},
+                                                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,
+                    mnesia:transaction(F);
+                odbc ->
+                    Username = ejabberd_odbc:escape(User),
+                    LUsername = ejabberd_odbc:escape(LUser),
+                    SVCARD = ejabberd_odbc:escape(
+                               xml:element_to_binary(VCARD)),
+                    
+                    SFN = ejabberd_odbc:escape(FN),
+                    SLFN = ejabberd_odbc:escape(LFN),
+                    SFamily = ejabberd_odbc:escape(Family),
+                    SLFamily = ejabberd_odbc:escape(LFamily),
+                    SGiven = ejabberd_odbc:escape(Given),
+                    SLGiven = ejabberd_odbc:escape(LGiven),
+                    SMiddle = ejabberd_odbc:escape(Middle),
+                    SLMiddle = ejabberd_odbc:escape(LMiddle),
+                    SNickname = ejabberd_odbc:escape(Nickname),
+                    SLNickname = ejabberd_odbc:escape(LNickname),
+                    SBDay = ejabberd_odbc:escape(BDay),
+                    SLBDay = ejabberd_odbc:escape(LBDay),
+                    SCTRY = ejabberd_odbc:escape(CTRY),
+                    SLCTRY = ejabberd_odbc:escape(LCTRY),
+                    SLocality = ejabberd_odbc:escape(Locality),
+                    SLLocality = ejabberd_odbc:escape(LLocality),
+                    SEMail = ejabberd_odbc:escape(EMail),
+                    SLEMail = ejabberd_odbc:escape(LEMail),
+                    SOrgName = ejabberd_odbc:escape(OrgName),
+                    SLOrgName = ejabberd_odbc:escape(LOrgName),
+                    SOrgUnit = ejabberd_odbc:escape(OrgUnit),
+                    SLOrgUnit = ejabberd_odbc:escape(LOrgUnit),
+                    
+                    odbc_queries:set_vcard(LServer, LUsername, SBDay, SCTRY, SEMail,
+                                           SFN, SFamily, SGiven, SLBDay, SLCTRY,
+                                           SLEMail, SLFN, SLFamily, SLGiven,
+                                           SLLocality, SLMiddle, SLNickname,
+                                           SLOrgName, SLOrgUnit, SLocality,
+                                           SMiddle, SNickname, SOrgName,
+                                           SOrgUnit, SVCARD, Username)
+            end,
            ejabberd_hooks:run(vcard_set, LServer, [LUser, LServer, VCARD])
     end.
 
@@ -481,14 +552,34 @@ search_result(Lang, JID, ServerHost, Data) ->
        ?TLFIELD("text-single", "Email", "email"),
        ?TLFIELD("text-single", "Organization Name", "orgname"),
        ?TLFIELD("text-single", "Organization Unit", "orgunit")
-      ]}] ++ lists:map(fun record_to_item/1, search(ServerHost, Data)).
+      ]}] ++ lists:map(fun(R) -> record_to_item(ServerHost, R) end,
+                      search(ServerHost, Data)).
 
 -define(FIELD(Var, Val),
        {xmlelement, "field", [{"var", Var}],
         [{xmlelement, "value", [],
           [{xmlcdata, Val}]}]}).
 
-record_to_item(R) ->
+record_to_item(LServer, {Username, FN, Family, Given, Middle,
+                        Nickname, BDay, CTRY, Locality,
+                        EMail, OrgName, OrgUnit}) ->
+    {xmlelement, "item", [],
+     [
+       ?FIELD("jid",      Username ++ "@" ++ LServer),
+       ?FIELD("fn",       FN),
+       ?FIELD("last",     Family),
+       ?FIELD("first",    Given),
+       ?FIELD("middle",   Middle),
+       ?FIELD("nick",     Nickname),
+       ?FIELD("bday",     BDay),
+       ?FIELD("ctry",     CTRY),
+       ?FIELD("locality", Locality),
+       ?FIELD("email",    EMail),
+       ?FIELD("orgname",  OrgName),
+       ?FIELD("orgunit",  OrgUnit)
+      ]
+    };
+record_to_item(_LServer, #vcard_search{} = R) ->
     {User, Server} = R#vcard_search.user,
     {xmlelement, "item", [],
      [
@@ -509,9 +600,13 @@ record_to_item(R) ->
 
 
 search(LServer, Data) ->
-    MatchSpec = make_matchspec(LServer, Data),
+    DBType = gen_mod:db_type(LServer, ?MODULE), 
+    MatchSpec = make_matchspec(LServer, Data, DBType),
     AllowReturnAll = gen_mod:get_module_opt(LServer, ?MODULE,
                                            allow_return_all, false),
+    search(LServer, MatchSpec, AllowReturnAll, DBType).
+
+search(LServer, MatchSpec, AllowReturnAll, mnesia) ->
     if
        (MatchSpec == #vcard_search{_ = '_'}) and (not AllowReturnAll) ->
            [];
@@ -535,17 +630,58 @@ search(LServer, Data) ->
                            lists:sublist(Rs, ?JUD_MATCHES)
                    end
            end
+    end;
+search(LServer, MatchSpec, AllowReturnAll, odbc) ->
+        if
+       (MatchSpec == "") and (not AllowReturnAll) ->
+           [];
+       true ->
+           Limit = case gen_mod:get_module_opt(LServer, ?MODULE,
+                                               matches, ?JUD_MATCHES) of
+                       infinity ->
+                           "";
+                       Val when is_integer(Val) and (Val > 0) ->
+                           [" LIMIT ", integer_to_list(Val)];
+                       Val ->
+                           ?ERROR_MSG("Illegal option value ~p. "
+                                      "Default value ~p substituted.",
+                                      [{matches, Val}, ?JUD_MATCHES]),
+                           [" LIMIT ", integer_to_list(?JUD_MATCHES)]
+                   end,
+           case catch ejabberd_odbc:sql_query(
+                        LServer,
+                        ["select username, fn, family, given, middle, "
+                         "       nickname, bday, ctry, locality, "
+                         "       email, orgname, orgunit from vcard_search ",
+                         MatchSpec, Limit, ";"]) of
+               {selected, ["username", "fn", "family", "given", "middle",
+                           "nickname", "bday", "ctry", "locality",
+                           "email", "orgname", "orgunit"],
+                Rs} when is_list(Rs) ->
+                   Rs;
+               Error ->
+                   ?ERROR_MSG("~p", [Error]),
+                   []
+           end
     end.
 
-
-make_matchspec(LServer, Data) ->
+make_matchspec(LServer, Data, mnesia) ->
     GlobMatch = #vcard_search{_ = '_'},
-    Match = filter_fields(Data, GlobMatch, LServer),
-    Match.
+    Match = filter_fields(Data, GlobMatch, LServer, mnesia),
+    Match;
+make_matchspec(LServer, Data, odbc) ->
+    filter_fields(Data, "", LServer, odbc).
 
-filter_fields([], Match, _LServer) ->
+filter_fields([], Match, _LServer, mnesia) ->
     Match;
-filter_fields([{SVar, [Val]} | Ds], Match, LServer)
+filter_fields([], Match, _LServer, odbc) ->
+    case Match of
+       "" ->
+           "";
+       _ ->
+           [" where ", Match]
+    end;
+filter_fields([{SVar, [Val]} | Ds], Match, LServer, mnesia)
   when is_list(Val) and (Val /= "") ->
     LVal = string2lower(Val),
     NewMatch = case SVar of
@@ -571,9 +707,46 @@ filter_fields([{SVar, [Val]} | Ds], Match, LServer)
                    "orgunit"  -> Match#vcard_search{lorgunit  = make_val(LVal)};
                   _          -> Match
               end,
-    filter_fields(Ds, NewMatch, LServer);
-filter_fields([_ | Ds], Match, LServer) ->
-    filter_fields(Ds, Match, LServer).
+    filter_fields(Ds, NewMatch, LServer, mnesia);
+filter_fields([{SVar, [Val]} | Ds], Match, LServer, odbc)
+  when is_list(Val) and (Val /= "") ->
+    LVal = string2lower(Val),
+    NewMatch = case SVar of
+                   "user"     -> make_val(Match, "lusername", LVal);
+                   "fn"       -> make_val(Match, "lfn",       LVal);
+                   "last"     -> make_val(Match, "lfamily",   LVal);
+                   "first"    -> make_val(Match, "lgiven",    LVal);
+                   "middle"   -> make_val(Match, "lmiddle",   LVal);
+                   "nick"     -> make_val(Match, "lnickname", LVal);
+                   "bday"     -> make_val(Match, "lbday",     LVal);
+                   "ctry"     -> make_val(Match, "lctry",     LVal);
+                   "locality" -> make_val(Match, "llocality", LVal);
+                   "email"    -> make_val(Match, "lemail",    LVal);
+                   "orgname"  -> make_val(Match, "lorgname",  LVal);
+                   "orgunit"  -> make_val(Match, "lorgunit",  LVal);
+                  _          -> Match
+              end,
+    filter_fields(Ds, NewMatch, LServer, odbc);
+filter_fields([_ | Ds], Match, LServer, DBType) ->
+    filter_fields(Ds, Match, LServer, DBType).
+
+make_val(Match, Field, Val) ->
+    Condition =
+       case lists:suffix("*", Val) of
+           true ->
+               Val1 = lists:sublist(Val, length(Val) - 1),
+               SVal = ejabberd_odbc:escape_like(Val1) ++ "%",
+               [Field, " LIKE '", SVal, "'"];
+           _ ->
+               SVal = ejabberd_odbc:escape(Val),
+               [Field, " = '", SVal, "'"]
+       end,
+    case Match of
+       "" ->
+           Condition;
+       _ ->
+           [Match, " and ", Condition]
+    end.
 
 make_val(Val) ->
     case lists:suffix("*", Val) of
@@ -679,13 +852,21 @@ reindex_vcards() ->
 remove_user(User, Server) ->
     LUser = jlib:nodeprep(User),
     LServer = jlib:nameprep(Server),
+    remove_user(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
+
+remove_user(LUser, LServer, mnesia) ->
     US = {LUser, LServer},
     F = fun() ->
                mnesia:delete({vcard, US}),
                mnesia:delete({vcard_search, US})
        end,
-    mnesia:transaction(F).
-
+    mnesia:transaction(F);
+remove_user(LUser, LServer, odbc) ->
+    Username = ejabberd_odbc:escape(LUser),
+    ejabberd_odbc:sql_transaction(
+      LServer,
+      [["delete from vcard where username='", Username, "';"],
+       ["delete from vcard_search where lusername='", Username, "';"]]).
 
 update_tables() ->
     update_vcard_table(),
diff --git a/src/mod_vcard_odbc.erl b/src/mod_vcard_odbc.erl
deleted file mode 100644 (file)
index f472dcb..0000000
+++ /dev/null
@@ -1,659 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File    : mod_vcard.erl
-%%% Author  : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : vCard support via ODBC
-%%% Created :  2 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2012   ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
--module(mod_vcard_odbc).
--author('alexey@process-one.net').
-
--behaviour(gen_mod).
-
--export([start/2, init/3, stop/1,
-        get_sm_features/5,
-        process_local_iq/3,
-        process_sm_iq/3,
-        %reindex_vcards/0,
-        remove_user/2]).
-
--include("ejabberd.hrl").
--include("jlib.hrl").
-
-
--define(JUD_MATCHES, 30).
--define(PROCNAME, ejabberd_mod_vcard).
-
-start(Host, Opts) ->
-    ejabberd_hooks:add(remove_user, Host,
-                      ?MODULE, remove_user, 50),
-    IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
-    gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_VCARD,
-                                 ?MODULE, process_local_iq, IQDisc),
-    gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_VCARD,
-                                 ?MODULE, process_sm_iq, IQDisc),
-    ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 50),
-    MyHost = gen_mod:get_opt_host(Host, Opts, "vjud.@HOST@"),
-    Search = gen_mod:get_opt(search, Opts, true),
-    register(gen_mod:get_module_proc(Host, ?PROCNAME),
-            spawn(?MODULE, init, [MyHost, Host, Search])).
-
-
-init(Host, ServerHost, Search) ->
-    case Search of
-       false ->
-           loop(Host, ServerHost);
-       _ ->
-           ejabberd_router:register_route(Host),
-           loop(Host, ServerHost)
-    end.
-
-loop(Host, ServerHost) ->
-    receive
-       {route, From, To, Packet} ->
-           case catch do_route(ServerHost, From, To, Packet) of
-               {'EXIT', Reason} ->
-                   ?ERROR_MSG("~p", [Reason]);
-               _ ->
-                   ok
-           end,
-           loop(Host, ServerHost);
-       stop ->
-           ejabberd_router:unregister_route(Host),
-           ok;
-       _ ->
-           loop(Host, ServerHost)
-    end.
-
-stop(Host) ->
-    ejabberd_hooks:delete(remove_user, Host,
-                         ?MODULE, remove_user, 50),
-    gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_VCARD),
-    gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_VCARD),
-    ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 50),
-    Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
-    Proc ! stop,
-    {wait, Proc}.
-
-get_sm_features({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
-    Acc;
-
-get_sm_features(Acc, _From, _To, Node, _Lang) ->
-    case Node of
-       [] ->
-           case Acc of
-               {result, Features} ->
-                   {result, [?NS_VCARD | Features]};
-               empty ->
-                   {result, [?NS_VCARD]}
-           end;
-       _ ->
-           Acc
-     end.
-
-process_local_iq(_From, _To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
-    case Type of
-       set ->
-           IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
-       get ->
-           IQ#iq{type = result,
-                 sub_el = [{xmlelement, "vCard",
-                            [{"xmlns", ?NS_VCARD}],
-                            [{xmlelement, "FN", [],
-                              [{xmlcdata, "ejabberd"}]},
-                             {xmlelement, "URL", [],
-                              [{xmlcdata, ?EJABBERD_URI}]},
-                             {xmlelement, "DESC", [],
-                              [{xmlcdata,
-                                translate:translate(
-                                  Lang,
-                                  "Erlang Jabber Server") ++
-                                  "\nCopyright (c) 2002-2012 ProcessOne"}]},
-                             {xmlelement, "BDAY", [],
-                              [{xmlcdata, "2002-11-16"}]}
-                            ]}]}
-    end.
-
-
-process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
-    case Type of
-       set ->
-           #jid{user = User, lserver = LServer} = From,
-           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, lserver = LServer} = To,
-           Username = ejabberd_odbc:escape(LUser),
-           case catch odbc_queries:get_vcard(LServer, Username) of
-               {selected, ["vcard"], [{SVCARD}]} ->
-                   case xml_stream:parse_element(SVCARD) of
-                       {error, _Reason} ->
-                           IQ#iq{type = error,
-                                 sub_el = [SubEl, ?ERR_SERVICE_UNAVAILABLE]};
-                       VCARD ->
-                           IQ#iq{type = result, sub_el = [VCARD]}
-                   end;
-               {selected, ["vcard"], []} ->
-                   IQ#iq{type = result, sub_el = []};
-               _ ->
-                   IQ#iq{type = error,
-                         sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]}
-           end
-    end.
-
-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]),
-    Middle   = xml:get_path_s(VCARD, [{elem, "N"}, {elem, "MIDDLE"},    cdata]),
-    Nickname = xml:get_path_s(VCARD, [{elem, "NICKNAME"},               cdata]),
-    BDay     = xml:get_path_s(VCARD, [{elem, "BDAY"},                   cdata]),
-    CTRY     = xml:get_path_s(VCARD, [{elem, "ADR"}, {elem, "CTRY"},    cdata]),
-    Locality = xml:get_path_s(VCARD, [{elem, "ADR"}, {elem, "LOCALITY"},cdata]),
-    EMail1   = xml:get_path_s(VCARD, [{elem, "EMAIL"}, {elem, "USERID"},cdata]),
-    EMail2   = xml:get_path_s(VCARD, [{elem, "EMAIL"},                  cdata]),
-    OrgName  = xml:get_path_s(VCARD, [{elem, "ORG"}, {elem, "ORGNAME"}, cdata]),
-    OrgUnit  = xml:get_path_s(VCARD, [{elem, "ORG"}, {elem, "ORGUNIT"}, cdata]),
-    EMail = case EMail1 of
-               "" ->
-                   EMail2;
-               _ ->
-                   EMail1
-           end,
-
-    LUser     = jlib:nodeprep(User),
-    LFN       = string2lower(FN),
-    LFamily   = string2lower(Family),
-    LGiven    = string2lower(Given),
-    LMiddle   = string2lower(Middle),
-    LNickname = string2lower(Nickname),
-    LBDay     = string2lower(BDay),
-    LCTRY     = string2lower(CTRY),
-    LLocality = string2lower(Locality),
-    LEMail    = string2lower(EMail),
-    LOrgName  = string2lower(OrgName),
-    LOrgUnit  = string2lower(OrgUnit),
-
-    if
-       (LUser     == error) or
-       (LFN       == error) or
-       (LFamily   == error) or
-       (LGiven    == error) or
-       (LMiddle   == error) or
-       (LNickname == error) or
-       (LBDay     == error) or
-       (LCTRY     == error) or
-       (LLocality == error) or
-       (LEMail    == error) or
-       (LOrgName  == error) or
-       (LOrgUnit  == error) ->
-           {error, badarg};
-       true ->
-           Username = ejabberd_odbc:escape(User),
-           LUsername = ejabberd_odbc:escape(LUser),
-           SVCARD = ejabberd_odbc:escape(
-                      xml:element_to_binary(VCARD)),
-
-           SFN = ejabberd_odbc:escape(FN),
-           SLFN = ejabberd_odbc:escape(LFN),
-           SFamily = ejabberd_odbc:escape(Family),
-           SLFamily = ejabberd_odbc:escape(LFamily),
-           SGiven = ejabberd_odbc:escape(Given),
-           SLGiven = ejabberd_odbc:escape(LGiven),
-           SMiddle = ejabberd_odbc:escape(Middle),
-           SLMiddle = ejabberd_odbc:escape(LMiddle),
-           SNickname = ejabberd_odbc:escape(Nickname),
-           SLNickname = ejabberd_odbc:escape(LNickname),
-           SBDay = ejabberd_odbc:escape(BDay),
-           SLBDay = ejabberd_odbc:escape(LBDay),
-           SCTRY = ejabberd_odbc:escape(CTRY),
-           SLCTRY = ejabberd_odbc:escape(LCTRY),
-           SLocality = ejabberd_odbc:escape(Locality),
-           SLLocality = ejabberd_odbc:escape(LLocality),
-           SEMail = ejabberd_odbc:escape(EMail),
-           SLEMail = ejabberd_odbc:escape(LEMail),
-           SOrgName = ejabberd_odbc:escape(OrgName),
-           SLOrgName = ejabberd_odbc:escape(LOrgName),
-           SOrgUnit = ejabberd_odbc:escape(OrgUnit),
-           SLOrgUnit = ejabberd_odbc:escape(LOrgUnit),
-
-           odbc_queries:set_vcard(LServer, LUsername, SBDay, SCTRY, SEMail,
-                                  SFN, SFamily, SGiven, SLBDay, SLCTRY,
-                                  SLEMail, SLFN, SLFamily, SLGiven,
-                                  SLLocality, SLMiddle, SLNickname,
-                                  SLOrgName, SLOrgUnit, SLocality,
-                                  SMiddle, SNickname, SOrgName,
-                                  SOrgUnit, SVCARD, Username),
-
-           ejabberd_hooks:run(vcard_set, LServer, [LUser, LServer, VCARD])
-    end.
-
-string2lower(String) ->
-    case stringprep:tolower(String) of
-       Lower when is_list(Lower) -> Lower;
-       error -> string:to_lower(String)
-    end.
-
--define(TLFIELD(Type, Label, Var),
-       {xmlelement, "field", [{"type", Type},
-                              {"label", translate:translate(Lang, Label)},
-                              {"var", Var}], []}).
-
-
--define(FORM(JID),
-       [{xmlelement, "instructions", [],
-         [{xmlcdata, translate:translate(Lang, "You need an x:data capable client to search")}]},
-        {xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "form"}],
-         [{xmlelement, "title", [],
-           [{xmlcdata, translate:translate(Lang, "Search users in ") ++
-             jlib:jid_to_string(JID)}]},
-          {xmlelement, "instructions", [],
-           [{xmlcdata, translate:translate(Lang, "Fill in the form to search "
-                                           "for any matching Jabber User "
-                                           "(Add * to the end of field to "
-                                           "match substring)")}]},
-          ?TLFIELD("text-single", "User", "user"),
-          ?TLFIELD("text-single", "Full Name", "fn"),
-          ?TLFIELD("text-single", "Name", "first"),
-          ?TLFIELD("text-single", "Middle Name", "middle"),
-          ?TLFIELD("text-single", "Family Name", "last"),
-          ?TLFIELD("text-single", "Nickname", "nick"),
-          ?TLFIELD("text-single", "Birthday", "bday"),
-          ?TLFIELD("text-single", "Country", "ctry"),
-          ?TLFIELD("text-single", "City", "locality"),
-          ?TLFIELD("text-single", "Email", "email"),
-          ?TLFIELD("text-single", "Organization Name", "orgname"),
-          ?TLFIELD("text-single", "Organization Unit", "orgunit")
-         ]}]).
-
-do_route(ServerHost, From, To, Packet) ->
-    #jid{user = User, resource = Resource} = To,
-    if
-       (User /= "") or (Resource /= "") ->
-           Err = jlib:make_error_reply(Packet, ?ERR_SERVICE_UNAVAILABLE),
-           ejabberd_router:route(To, From, Err);
-       true ->
-           IQ = jlib:iq_query_info(Packet),
-           case IQ of
-               #iq{type = Type, xmlns = ?NS_SEARCH, lang = Lang, sub_el = SubEl} ->
-                   case Type of
-                       set ->
-                           XDataEl = find_xdata_el(SubEl),
-                           case XDataEl of
-                               false ->
-                                   Err = jlib:make_error_reply(
-                                           Packet, ?ERR_BAD_REQUEST),
-                                   ejabberd_router:route(To, From, Err);
-                               _ ->
-                                   XData = jlib:parse_xdata_submit(XDataEl),
-                                   case XData of
-                                       invalid ->
-                                           Err = jlib:make_error_reply(
-                                                   Packet,
-                                                   ?ERR_BAD_REQUEST),
-                                           ejabberd_router:route(To, From,
-                                                                 Err);
-                                       _ ->
-                                           ResIQ =
-                                               IQ#iq{
-                                                 type = result,
-                                                 sub_el =
-                                                 [{xmlelement,
-                                                   "query",
-                                                   [{"xmlns", ?NS_SEARCH}],
-                                                   [{xmlelement, "x",
-                                                     [{"xmlns", ?NS_XDATA},
-                                                      {"type", "result"}],
-                                                     search_result(Lang, To, ServerHost, XData)
-                                                    }]}]},
-                                           ejabberd_router:route(
-                                             To, From, jlib:iq_to_xml(ResIQ))
-                                   end
-                           end;
-                       get ->
-                           ResIQ = IQ#iq{type = result,
-                                         sub_el = [{xmlelement,
-                                                    "query",
-                                                    [{"xmlns", ?NS_SEARCH}],
-                                                    ?FORM(To)
-                                                   }]},
-                           ejabberd_router:route(To,
-                                                 From,
-                                                 jlib:iq_to_xml(ResIQ))
-                   end;
-               #iq{type = Type, xmlns = ?NS_DISCO_INFO, lang = Lang} ->
-                   case Type of
-                       set ->
-                           Err = jlib:make_error_reply(
-                                   Packet, ?ERR_NOT_ALLOWED),
-                           ejabberd_router:route(To, From, Err);
-                       get ->
-                           Info = ejabberd_hooks:run_fold(
-                                    disco_info, ServerHost, [],
-                                    [ServerHost, ?MODULE, "", ""]),
-                           ResIQ =
-                               IQ#iq{type = result,
-                                     sub_el = [{xmlelement,
-                                                "query",
-                                                [{"xmlns", ?NS_DISCO_INFO}],
-                                                [{xmlelement, "identity",
-                                                  [{"category", "directory"},
-                                                   {"type", "user"},
-                                                   {"name",
-                                                    translate:translate(Lang, "vCard User Search")}],
-                                                  []},
-                                                 {xmlelement, "feature",
-                                                  [{"var", ?NS_SEARCH}], []},
-                                                 {xmlelement, "feature",
-                                                  [{"var", ?NS_VCARD}], []}
-                                                ] ++ Info
-                                               }]},
-                           ejabberd_router:route(To,
-                                                 From,
-                                                 jlib:iq_to_xml(ResIQ))
-                   end;
-               #iq{type = Type, xmlns = ?NS_DISCO_ITEMS} ->
-                   case Type of
-                       set ->
-                           Err = jlib:make_error_reply(
-                                   Packet, ?ERR_NOT_ALLOWED),
-                           ejabberd_router:route(To, From, Err);
-                       get ->
-                           ResIQ =
-                               IQ#iq{type = result,
-                                     sub_el = [{xmlelement,
-                                                "query",
-                                                [{"xmlns", ?NS_DISCO_ITEMS}],
-                                                []}]},
-                           ejabberd_router:route(To,
-                                                 From,
-                                                 jlib:iq_to_xml(ResIQ))
-                   end;
-               #iq{type = get, xmlns = ?NS_VCARD, lang = Lang} ->
-                   ResIQ =
-                       IQ#iq{type = result,
-                             sub_el = [{xmlelement,
-                                        "vCard",
-                                        [{"xmlns", ?NS_VCARD}],
-                                        iq_get_vcard(Lang)}]},
-                   ejabberd_router:route(To,
-                                         From,
-                                         jlib:iq_to_xml(ResIQ));
-               _ ->
-                   Err = jlib:make_error_reply(Packet,
-                                               ?ERR_SERVICE_UNAVAILABLE),
-                   ejabberd_router:route(To, From, Err)
-           end
-    end.
-
-iq_get_vcard(Lang) ->
-    [{xmlelement, "FN", [],
-      [{xmlcdata, "ejabberd/mod_vcard"}]},
-     {xmlelement, "URL", [],
-      [{xmlcdata, ?EJABBERD_URI}]},
-     {xmlelement, "DESC", [],
-      [{xmlcdata, translate:translate(
-                   Lang,
-                   "ejabberd vCard module") ++
-                   "\nCopyright (c) 2003-2012 ProcessOne"}]}].
-
-find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
-    find_xdata_el1(SubEls).
-
-find_xdata_el1([]) ->
-    false;
-find_xdata_el1([{xmlelement, Name, Attrs, SubEls} | Els]) ->
-    case xml:get_attr_s("xmlns", Attrs) of
-       ?NS_XDATA ->
-           {xmlelement, Name, Attrs, SubEls};
-       _ ->
-           find_xdata_el1(Els)
-    end;
-find_xdata_el1([_ | Els]) ->
-    find_xdata_el1(Els).
-
--define(LFIELD(Label, Var),
-       {xmlelement, "field", [{"label", translate:translate(Lang, Label)},
-                              {"var", Var}], []}).
-
-search_result(Lang, JID, ServerHost, Data) ->
-    [{xmlelement, "title", [],
-      [{xmlcdata, translate:translate(Lang, "Search Results for ") ++
-       jlib:jid_to_string(JID)}]},
-     {xmlelement, "reported", [],
-      [?TLFIELD("text-single", "Jabber ID", "jid"),
-       ?TLFIELD("text-single", "Full Name", "fn"),
-       ?TLFIELD("text-single", "Name", "first"),
-       ?TLFIELD("text-single", "Middle Name", "middle"),
-       ?TLFIELD("text-single", "Family Name", "last"),
-       ?TLFIELD("text-single", "Nickname", "nick"),
-       ?TLFIELD("text-single", "Birthday", "bday"),
-       ?TLFIELD("text-single", "Country", "ctry"),
-       ?TLFIELD("text-single", "City", "locality"),
-       ?TLFIELD("text-single", "Email", "email"),
-       ?TLFIELD("text-single", "Organization Name", "orgname"),
-       ?TLFIELD("text-single", "Organization Unit", "orgunit")
-      ]}] ++ lists:map(fun(R) -> record_to_item(ServerHost, R) end,
-                      search(ServerHost, Data)).
-
--define(FIELD(Var, Val),
-       {xmlelement, "field", [{"var", Var}],
-        [{xmlelement, "value", [],
-          [{xmlcdata, Val}]}]}).
-
-
-record_to_item(LServer, {Username, FN, Family, Given, Middle,
-                        Nickname, BDay, CTRY, Locality,
-                        EMail, OrgName, OrgUnit}) ->
-    {xmlelement, "item", [],
-     [
-       ?FIELD("jid",      Username ++ "@" ++ LServer),
-       ?FIELD("fn",       FN),
-       ?FIELD("last",     Family),
-       ?FIELD("first",    Given),
-       ?FIELD("middle",   Middle),
-       ?FIELD("nick",     Nickname),
-       ?FIELD("bday",     BDay),
-       ?FIELD("ctry",     CTRY),
-       ?FIELD("locality", Locality),
-       ?FIELD("email",    EMail),
-       ?FIELD("orgname",  OrgName),
-       ?FIELD("orgunit",  OrgUnit)
-      ]
-     }.
-
-
-search(LServer, Data) ->
-    MatchSpec = make_matchspec(LServer, Data),
-    AllowReturnAll = gen_mod:get_module_opt(LServer, ?MODULE,
-                                           allow_return_all, false),
-    if
-       (MatchSpec == "") and (not AllowReturnAll) ->
-           [];
-       true ->
-           Limit = case gen_mod:get_module_opt(LServer, ?MODULE,
-                                               matches, ?JUD_MATCHES) of
-                       infinity ->
-                           "";
-                       Val when is_integer(Val) and (Val > 0) ->
-                           [" LIMIT ", integer_to_list(Val)];
-                       Val ->
-                           ?ERROR_MSG("Illegal option value ~p. "
-                                      "Default value ~p substituted.",
-                                      [{matches, Val}, ?JUD_MATCHES]),
-                           [" LIMIT ", integer_to_list(?JUD_MATCHES)]
-                   end,
-           case catch ejabberd_odbc:sql_query(
-                        LServer,
-                        ["select username, fn, family, given, middle, "
-                         "       nickname, bday, ctry, locality, "
-                         "       email, orgname, orgunit from vcard_search ",
-                         MatchSpec, Limit, ";"]) of
-               {selected, ["username", "fn", "family", "given", "middle",
-                           "nickname", "bday", "ctry", "locality",
-                           "email", "orgname", "orgunit"],
-                Rs} when is_list(Rs) ->
-                   Rs;
-               Error ->
-                   ?ERROR_MSG("~p", [Error]),
-                   []
-           end
-    end.
-
-
-make_matchspec(LServer, Data) ->
-    filter_fields(Data, "", LServer).
-
-filter_fields([], Match, _LServer) ->
-    case Match of
-       "" ->
-           "";
-       _ ->
-           [" where ", Match]
-    end;
-filter_fields([{SVar, [Val]} | Ds], Match, LServer)
-  when is_list(Val) and (Val /= "") ->
-    LVal = string2lower(Val),
-    NewMatch = case SVar of
-                   "user"     -> make_val(Match, "lusername", LVal);
-                   "fn"       -> make_val(Match, "lfn",       LVal);
-                   "last"     -> make_val(Match, "lfamily",   LVal);
-                   "first"    -> make_val(Match, "lgiven",    LVal);
-                   "middle"   -> make_val(Match, "lmiddle",   LVal);
-                   "nick"     -> make_val(Match, "lnickname", LVal);
-                   "bday"     -> make_val(Match, "lbday",     LVal);
-                   "ctry"     -> make_val(Match, "lctry",     LVal);
-                   "locality" -> make_val(Match, "llocality", LVal);
-                   "email"    -> make_val(Match, "lemail",    LVal);
-                   "orgname"  -> make_val(Match, "lorgname",  LVal);
-                   "orgunit"  -> make_val(Match, "lorgunit",  LVal);
-                  _          -> Match
-              end,
-    filter_fields(Ds, NewMatch, LServer);
-filter_fields([_ | Ds], Match, LServer) ->
-    filter_fields(Ds, Match, LServer).
-
-make_val(Match, Field, Val) ->
-    Condition =
-       case lists:suffix("*", Val) of
-           true ->
-               Val1 = lists:sublist(Val, length(Val) - 1),
-               SVal = ejabberd_odbc:escape_like(Val1) ++ "%",
-               [Field, " LIKE '", SVal, "'"];
-           _ ->
-               SVal = ejabberd_odbc:escape(Val),
-               [Field, " = '", SVal, "'"]
-       end,
-    case Match of
-       "" ->
-           Condition;
-       _ ->
-           [Match, " and ", Condition]
-    end.
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%set_vcard_t(R, _) ->
-%    US = R#vcard.us,
-%    User  = US,
-%    VCARD = R#vcard.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]),
-%    Middle   = xml:get_path_s(VCARD, [{elem, "N"}, {elem, "MIDDLE"},    cdata]),
-%    Nickname = xml:get_path_s(VCARD, [{elem, "NICKNAME"},               cdata]),
-%    BDay     = xml:get_path_s(VCARD, [{elem, "BDAY"},                   cdata]),
-%    CTRY     = xml:get_path_s(VCARD, [{elem, "ADR"}, {elem, "CTRY"},    cdata]),
-%    Locality = xml:get_path_s(VCARD, [{elem, "ADR"}, {elem, "LOCALITY"},cdata]),
-%    EMail    = xml:get_path_s(VCARD, [{elem, "EMAIL"},                  cdata]),
-%    OrgName  = xml:get_path_s(VCARD, [{elem, "ORG"}, {elem, "ORGNAME"}, cdata]),
-%    OrgUnit  = xml:get_path_s(VCARD, [{elem, "ORG"}, {elem, "ORGUNIT"}, cdata]),
-%
-%    {LUser, _LServer} = US,
-%    LFN       = stringprep:tolower(FN),
-%    LFamily   = stringprep:tolower(Family),
-%    LGiven    = stringprep:tolower(Given),
-%    LMiddle   = stringprep:tolower(Middle),
-%    LNickname = stringprep:tolower(Nickname),
-%    LBDay     = stringprep:tolower(BDay),
-%    LCTRY     = stringprep:tolower(CTRY),
-%    LLocality = stringprep:tolower(Locality),
-%    LEMail    = stringprep:tolower(EMail),
-%    LOrgName  = stringprep:tolower(OrgName),
-%    LOrgUnit  = stringprep:tolower(OrgUnit),
-%
-%    if
-%      (LUser     == error) or
-%      (LFN       == error) or
-%      (LFamily   == error) or
-%      (LGiven    == error) or
-%      (LMiddle   == error) or
-%      (LNickname == error) or
-%      (LBDay     == error) or
-%      (LCTRY     == error) or
-%      (LLocality == error) or
-%      (LEMail    == error) or
-%      (LOrgName  == error) or
-%      (LOrgUnit  == error) ->
-%          {error, badarg};
-%      true ->
-%          mnesia:write(
-%            #vcard_search{us        = US,
-%                          user      = User,     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.
-%
-%
-%reindex_vcards() ->
-%    F = fun() ->
-%              mnesia:foldl(fun set_vcard_t/2, [], vcard)
-%      end,
-%    mnesia:transaction(F).
-
-
-remove_user(User, Server) ->
-    LUser = jlib:nodeprep(User),
-    LServer = jlib:nameprep(Server),
-    Username = ejabberd_odbc:escape(LUser),
-    ejabberd_odbc:sql_transaction(
-      LServer,
-      [["delete from vcard where username='", Username, "';"],
-       ["delete from vcard_search where lusername='", Username, "';"]]).
index 38503c4c390e85eb47830a7b5eb7eec5aa70c412..3ee632aaabae6088bfdcfe2a527be05be2c53ba4 100644 (file)
 %% gen_mod callbacks
 %%====================================================================
 
-start(Host, _Opts) ->
-    mnesia:create_table(vcard_xupdate,
-                        [{disc_copies, [node()]},
-                         {attributes, record_info(fields, vcard_xupdate)}]),
+start(Host, Opts) ->
+    case gen_mod:db_type(Opts) of
+        mnesia ->
+            mnesia:create_table(vcard_xupdate,
+                                [{disc_copies, [node()]},
+                                 {attributes,
+                                  record_info(fields, vcard_xupdate)}]);
+        _ ->
+            ok
+    end,
     ejabberd_hooks:add(c2s_update_presence, Host,
                       ?MODULE, update_presence, 100),
     ejabberd_hooks:add(vcard_set, Host,
@@ -68,28 +74,66 @@ vcard_set(LUser, LServer, VCARD) ->
     ejabberd_sm:force_update_presence(US).
 
 %%====================================================================
-%% Mnesia storage
+%% Storage
 %%====================================================================
 
 add_xupdate(LUser, LServer, Hash) ->
+    add_xupdate(LUser, LServer, Hash, gen_mod:db_type(LServer, ?MODULE)).
+
+add_xupdate(LUser, LServer, Hash, mnesia) ->
     F = fun() ->
                 mnesia:write(#vcard_xupdate{us = {LUser, LServer}, hash = Hash})
         end,
-    mnesia:transaction(F).
+    mnesia:transaction(F);
+add_xupdate(LUser, LServer, Hash, odbc) ->
+    Username = ejabberd_odbc:escape(LUser),
+    SHash = ejabberd_odbc:escape(Hash),
+    F = fun() ->
+                odbc_queries:update_t(
+                  "vcard_xupdate",
+                  ["username", "hash"],
+                  [Username, SHash],
+                  ["username='", Username, "'"])
+        end,
+    ejabberd_odbc:sql_transaction(LServer, F).
 
 get_xupdate(LUser, LServer) ->
+    get_xupdate(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
+
+get_xupdate(LUser, LServer, mnesia) ->
     case mnesia:dirty_read(vcard_xupdate, {LUser, LServer}) of
         [#vcard_xupdate{hash = Hash}] ->
             Hash;
         _ ->
             undefined
+    end;
+get_xupdate(LUser, LServer, odbc) ->
+    Username = ejabberd_odbc:escape(LUser),
+    case ejabberd_odbc:sql_query(
+           LServer, ["select hash from vcard_xupdate "
+                     "where username='", Username, "';"]) of
+        {selected, ["hash"], [{Hash}]} ->
+            Hash;
+        _ ->
+            undefined
     end.
 
 remove_xupdate(LUser, LServer) ->
+    remove_xupdate(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
+
+remove_xupdate(LUser, LServer, mnesia) ->
     F = fun() ->
                 mnesia:delete({vcard_xupdate, {LUser, LServer}})
         end,
-    mnesia:transaction(F).
+    mnesia:transaction(F);
+remove_xupdate(LUser, LServer, odbc) ->
+    Username = ejabberd_odbc:escape(LUser),
+    F = fun() ->
+                ejabberd_odbc:sql_query_t(
+                  ["delete from vcard_xupdate where "
+                   "username='", Username, "';"])
+        end,
+    ejabberd_odbc:sql_transaction(LServer, F).
 
 %%%----------------------------------------------------------------------
 %%% Presence stanza rebuilding
diff --git a/src/mod_vcard_xupdate_odbc.erl b/src/mod_vcard_xupdate_odbc.erl
deleted file mode 100644 (file)
index b6a5adf..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File    : mod_vcard_xupdate_odbc.erl
-%%% Author  : Igor Goryachev <igor@goryachev.org>
-%%% Purpose : Add avatar hash in presence on behalf of client (XEP-0153)
-%%% Created : 9 Mar 2007 by Igor Goryachev <igor@goryachev.org>
-%%%----------------------------------------------------------------------
-
--module(mod_vcard_xupdate_odbc).
-
--behaviour(gen_mod).
-
-%% gen_mod callbacks
--export([start/2,
-         stop/1]).
-
-%% hooks
--export([update_presence/3,
-        vcard_set/3]).
-
--include("ejabberd.hrl").
--include("jlib.hrl").
-
-%%====================================================================
-%% gen_mod callbacks
-%%====================================================================
-
-start(Host, _Opts) ->
-    ejabberd_hooks:add(c2s_update_presence, Host,
-                      ?MODULE, update_presence, 100),
-    ejabberd_hooks:add(vcard_set, Host,
-                      ?MODULE, vcard_set, 100),
-    ok.
-
-stop(Host) ->
-    ejabberd_hooks:delete(c2s_update_presence, Host,
-                         ?MODULE, update_presence, 100),
-    ejabberd_hooks:delete(vcard_set, Host,
-                         ?MODULE, vcard_set, 100),
-    ok.
-
-%%====================================================================
-%% Hooks
-%%====================================================================
-
-update_presence({xmlelement, "presence", Attrs, _Els} = Packet, User, Host) ->
-    case xml:get_attr_s("type", Attrs) of
-        [] ->
-           presence_with_xupdate(Packet, User, Host);
-        _ ->
-            Packet
-    end;
-update_presence(Packet, _User, _Host) ->
-    Packet.
-
-vcard_set(LUser, LServer, VCARD) ->
-    US = {LUser, LServer},
-    case xml:get_path_s(VCARD, [{elem, "PHOTO"}, {elem, "BINVAL"}, cdata]) of
-       [] ->
-           remove_xupdate(LUser, LServer);
-       BinVal ->
-           add_xupdate(LUser, LServer, sha:sha(jlib:decode_base64(BinVal)))
-    end,
-    ejabberd_sm:force_update_presence(US).
-
-%%====================================================================
-%% ODBC storage
-%%====================================================================
-
-add_xupdate(LUser, LServer, Hash) ->
-    Username = ejabberd_odbc:escape(LUser),
-    SHash = ejabberd_odbc:escape(Hash),
-    F = fun() ->
-                odbc_queries:update_t(
-                  "vcard_xupdate",
-                  ["username", "hash"],
-                  [Username, SHash],
-                  ["username='", Username, "'"])
-        end,
-    ejabberd_odbc:sql_transaction(LServer, F).
-
-get_xupdate(LUser, LServer) ->
-    Username = ejabberd_odbc:escape(LUser),
-    case ejabberd_odbc:sql_query(
-           LServer, ["select hash from vcard_xupdate "
-                     "where username='", Username, "';"]) of
-        {selected, ["hash"], [{Hash}]} ->
-            Hash;
-        _ ->
-            undefined
-    end.
-
-remove_xupdate(LUser, LServer) ->
-    Username = ejabberd_odbc:escape(LUser),
-    F = fun() ->
-                ejabberd_odbc:sql_query_t(
-                  ["delete from vcard_xupdate where "
-                   "username='", Username, "';"])
-        end,
-    ejabberd_odbc:sql_transaction(LServer, F).
-
-%%%----------------------------------------------------------------------
-%%% Presence stanza rebuilding
-%%%----------------------------------------------------------------------
-
-presence_with_xupdate({xmlelement, "presence", Attrs, Els}, User, Host) ->
-    XPhotoEl = build_xphotoel(User, Host),
-    Els2 = presence_with_xupdate2(Els, [], XPhotoEl),
-    {xmlelement, "presence", Attrs, Els2}.
-
-presence_with_xupdate2([], Els2, XPhotoEl) ->
-    lists:reverse([XPhotoEl | Els2]);
-%% This clause assumes that the x element contains only the XMLNS attribute:
-presence_with_xupdate2([{xmlelement, "x", [{"xmlns", ?NS_VCARD_UPDATE}], _}
-                       | Els], Els2, XPhotoEl) ->
-    presence_with_xupdate2(Els, Els2, XPhotoEl);
-presence_with_xupdate2([El | Els], Els2, XPhotoEl) ->
-    presence_with_xupdate2(Els, [El | Els2], XPhotoEl).
-
-build_xphotoel(User, Host) ->
-    Hash = get_xupdate(User, Host),
-    PhotoSubEls = case Hash of
-                     Hash when is_list(Hash) ->
-                         [{xmlcdata, Hash}];
-                     _ ->
-                         []
-                 end,
-    PhotoEl = [{xmlelement, "photo", [], PhotoSubEls}],
-    {xmlelement, "x", [{"xmlns", ?NS_VCARD_UPDATE}], PhotoEl}.
index 447926e261a31a6afd5d3a62dc98fcc8f877db9a..59e9b0313656f0c39ca958f40f58732ffc29472f 100644 (file)
@@ -1564,7 +1564,6 @@ list_users_in_diapason(Host, Diap, Lang, URLFunc) ->
     [list_given_users(Host, Sub, "../../", Lang, URLFunc)].
 
 list_given_users(Host, Users, Prefix, Lang, URLFunc) ->
-    ModLast = get_lastactivity_module(Host),
     ModOffline = get_offlinemsg_module(Host),
     ?XE("table",
        [?XE("thead",
@@ -1583,7 +1582,7 @@ list_given_users(Host, Users, Prefix, Lang, URLFunc) ->
                       FLast =
                           case ejabberd_sm:get_user_resources(User, Server) of
                               [] ->
-                                  case ModLast:get_last_info(User, Server) of
+                                  case mod_last:get_last_info(User, Server) of
                                       not_found ->
                                           ?T("Never");
                                       {ok, Shift, _Status} ->
@@ -1618,22 +1617,17 @@ get_offlinemsg_length(ModOffline, User, Server) ->
     end.
 
 get_offlinemsg_module(Server) ->
-    case [mod_offline, mod_offline_odbc] -- gen_mod:loaded_modules(Server) of
-        [mod_offline, mod_offline_odbc] -> none;
-        [mod_offline_odbc] -> mod_offline;
-        [mod_offline] -> mod_offline_odbc
-    end.
-
-get_lastactivity_module(Server) ->
-    case lists:member(mod_last, gen_mod:loaded_modules(Server)) of
-        true -> mod_last;
-        _ -> mod_last_odbc
+    case gen_mod:is_loaded(Server, mod_offline) of
+        true ->
+            mod_offline;
+        false ->
+            none
     end.
 
 get_lastactivity_menuitem_list(Server) ->
-    case get_lastactivity_module(Server) of
-        mod_last -> [{"last-activity", "Last Activity"}];
-        mod_last_odbc -> []
+    case gen_mod:db_type(Server, mod_last) of
+        mnesia -> [{"last-activity", "Last Activity"}];
+        _ -> []
     end.
 
 us_to_list({User, Server}) ->
@@ -1735,10 +1729,9 @@ user_info(User, Server, Query, Lang) ->
     UserItems = ejabberd_hooks:run_fold(webadmin_user, LServer, [],
                                        [User, Server, Lang]),
     %% Code copied from list_given_users/5:
-    ModLast = get_lastactivity_module(Server),
     LastActivity = case ejabberd_sm:get_user_resources(User, Server) of
                       [] ->
-                          case ModLast:get_last_info(User, Server) of
+                          case mod_last:get_last_info(User, Server) of
                               not_found ->
                                   ?T("Never");
                               {ok, Shift, _Status} ->