[packages/ejabberd] - rel 1; new mod_logdb patch from author; works here

arekm arekm at pld-linux.org
Tue Apr 18 10:45:51 CEST 2017


commit 3f23be8ea2d661e0d674fd2dc60562bda1670ccb
Author: Arkadiusz Miśkiewicz <arekm at maven.pl>
Date:   Tue Apr 18 10:45:44 2017 +0200

    - rel 1; new mod_logdb patch from author; works here

 ejabberd-mod_logdb.patch | 1548 ++++++++++++++++------------------------------
 ejabberd.spec            |    5 +-
 2 files changed, 526 insertions(+), 1027 deletions(-)
---
diff --git a/ejabberd.spec b/ejabberd.spec
index b26cde2..1da09ba 100644
--- a/ejabberd.spec
+++ b/ejabberd.spec
@@ -10,7 +10,7 @@ Summary:	Fault-tolerant distributed Jabber/XMPP server
 Summary(pl.UTF-8):	Odporny na awarie rozproszony serwer Jabbera/XMPP
 Name:		ejabberd
 Version:	17.04
-Release:	0.3
+Release:	1
 License:	GPL
 Group:		Applications/Communications
 Source0:	http://www.process-one.net/downloads/ejabberd/%{version}/%{name}-%{version}.tgz
@@ -64,7 +64,8 @@ Source29:       ejabberd-xmpp-20170322.tar.gz
 #
 Patch0:		%{name}-paths.patch
 Patch1:		%{name}-config.patch
-# https://github.com/paleg/ejabberd/tree/16.04-mod_logdb
+# https://paleg.github.io/mod_logdb/
+# https://github.com/paleg/ejabberd/tree/17.04-mod_logdb
 Patch3:		%{name}-mod_logdb.patch
 Patch4:		%{name}-no_sslv3_or_3des.patch
 URL:		http://www.ejabberd.im/
diff --git a/ejabberd-mod_logdb.patch b/ejabberd-mod_logdb.patch
index 64ff911..e2eaef6 100644
--- a/ejabberd-mod_logdb.patch
+++ b/ejabberd-mod_logdb.patch
@@ -130,19 +130,32 @@ index 568ac092..3a324ed1 100644
 +{"Drop", "Видаляти"}.
 +{"Do not drop", "Не видаляти"}.
 +{"Drop messages on user removal", "Видаляти повідомлення під час видалення користувача"}.
+diff --git a/rebar.config b/rebar.config
+index aef3a017..b35db36f 100644
+--- a/rebar.config
++++ b/rebar.config
+@@ -31,8 +31,8 @@
+         {jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "0.14.8"}}},
+         {p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.1"}}},
+         {luerl, ".*", {git, "https://github.com/rvirding/luerl", {tag, "v0.2"}}},
+-        {if_var_true, mysql, {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql",
+-                                               {tag, "1.0.2"}}}},
++        {if_var_true, mysql, {p1_mysql, ".*", {git, "https://github.com/paleg/p1_mysql",
++                                               {branch, "multi"}}}},
+         {if_var_true, pgsql, {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql",
+                                                {tag, "1.1.2"}}}},
+         {if_var_true, sqlite, {sqlite3, ".*", {git, "https://github.com/processone/erlang-sqlite3",
 diff --git a/src/gen_logdb.erl b/src/gen_logdb.erl
 new file mode 100644
-index 00000000..06a894b4
+index 00000000..8bad1129
 --- /dev/null
 +++ b/src/gen_logdb.erl
-@@ -0,0 +1,164 @@
+@@ -0,0 +1,162 @@
 +%%%----------------------------------------------------------------------
 +%%% File    : gen_logdb.erl
-+%%% Author  : Oleg Palij (mailto,xmpp:o.palij at gmail.com)
++%%% Author  : Oleg Palij (mailto:o.palij at gmail.com)
 +%%% Purpose : Describes generic behaviour for mod_logdb backends.
-+%%% Version : trunk
-+%%% Id      : $Id: gen_logdb.erl 1273 2009-02-05 18:12:57Z malik $
-+%%% Url     : http://www.dp.uz.gov.ua/o.palij/mod_logdb/
++%%% Url     : https://paleg.github.io/mod_logdb/
 +%%%----------------------------------------------------------------------
 +
 +-module(gen_logdb).
@@ -302,17 +315,15 @@ index 00000000..06a894b4
 +   undefined.
 diff --git a/src/mod_logdb.erl b/src/mod_logdb.erl
 new file mode 100644
-index 00000000..79e2d3d3
+index 00000000..d5983820
 --- /dev/null
 +++ b/src/mod_logdb.erl
-@@ -0,0 +1,2174 @@
+@@ -0,0 +1,1952 @@
 +%%%----------------------------------------------------------------------
 +%%% File    : mod_logdb.erl
-+%%% Author  : Oleg Palij (mailto,xmpp:o.palij at gmail.com)
++%%% Author  : Oleg Palij (mailto:o.palij at gmail.com)
 +%%% Purpose : Frontend for log user messages to db
-+%%% Version : trunk
-+%%% Id      : $Id: mod_logdb.erl 1360 2009-07-30 06:00:14Z malik $
-+%%% Url     : http://www.dp.uz.gov.ua/o.palij/mod_logdb/
++%%% Url     : https://paleg.github.io/mod_logdb/
 +%%%----------------------------------------------------------------------
 +
 +-module(mod_logdb).
@@ -324,24 +335,23 @@ index 00000000..79e2d3d3
 +% supervisor
 +-export([start_link/2]).
 +% gen_mod
-+-export([start/2,stop/1,mod_opt_type/1]).
++-export([start/2, stop/1,
++         mod_opt_type/1,
++         depends/2, reload/3]).
 +% gen_server
-+-export([code_change/3,handle_call/3,handle_cast/2,handle_info/2,init/1,terminate/2]).
++-export([code_change/3,
++         handle_call/3, handle_cast/2, handle_info/2,
++         init/1, terminate/2]).
 +% hooks
-+-export([send_packet/4, receive_packet/5, remove_user/2]).
++-export([send_packet/1, receive_packet/1, offline_message/1, remove_user/2]).
 +-export([get_local_identity/5,
 +         get_local_features/5,
 +         get_local_items/5,
 +         adhoc_local_items/4,
 +         adhoc_local_commands/4
-+%         get_sm_identity/5,
-+%         get_sm_features/5,
-+%         get_sm_items/5,
-+%         adhoc_sm_items/4,
-+%         adhoc_sm_commands/4]).
 +        ]).
 +% ejabberdctl
-+-export([rebuild_stats/3,
++-export([rebuild_stats/1,
 +         copy_messages/1, copy_messages_ctl/3, copy_messages_int_tc/1]).
 +%
 +-export([get_vhost_stats/1, get_vhost_stats_at/2,
@@ -369,9 +379,9 @@ index 00000000..79e2d3d3
 +
 +-include("mod_logdb.hrl").
 +-include("ejabberd.hrl").
++-include("xmpp.hrl").
 +-include("mod_roster.hrl").
-+-include("jlib.hrl").
-+-include("ejabberd_ctl.hrl").
++-include("ejabberd_commands.hrl").
 +-include("adhoc.hrl").
 +-include("ejabberd_web_admin.hrl").
 +-include("ejabberd_http.hrl").
@@ -400,7 +410,14 @@ index 00000000..79e2d3d3
 +         worker,
 +         [?MODULE]},
 +    % add child to ejabberd_sup
-+    supervisor:start_child(ejabberd_sup, ChildSpec).
++    supervisor:start_child(ejabberd_gen_mod_sup, ChildSpec).
++
++depends(_Host, _Opts) ->
++    [].
++
++reload(_Host, _NewOpts, _OldOpts) ->
++    % TODO
++    ok.
 +
 +% supervisor starts gen_server
 +start_link(VHost, Opts) ->
@@ -417,7 +434,7 @@ index 00000000..79e2d3d3
 +               {value, _} -> DBsRaw
 +          end,
 +    VHostDB = gen_mod:get_opt(vhosts, Opts, fun(A) -> A end, [{VHost, mnesia}]),
-+    % 10 is default becouse of using in clustered environment
++    % 10 is default because of using in clustered environment
 +    PollUsersSettings = gen_mod:get_opt(poll_users_settings, Opts, fun(A) -> A end, 10),
 +
 +    {DBName, DBOpts} =
@@ -459,13 +476,10 @@ index 00000000..79e2d3d3
 +    ejabberd_hooks:delete(remove_user, VHost, ?MODULE, remove_user, 90),
 +    ejabberd_hooks:delete(user_send_packet, VHost, ?MODULE, send_packet, 90),
 +    ejabberd_hooks:delete(user_receive_packet, VHost, ?MODULE, receive_packet, 90),
-+    %ejabberd_hooks:delete(adhoc_sm_commands, VHost, ?MODULE, adhoc_sm_commands, 110),
-+    %ejabberd_hooks:delete(adhoc_sm_items, VHost, ?MODULE, adhoc_sm_items, 110),
++    ejabberd_hooks:delete(offline_message_hook, VHost, ?MODULE, offline_message, 40),
++
 +    ejabberd_hooks:delete(adhoc_local_commands, VHost, ?MODULE, adhoc_local_commands, 50),
 +    ejabberd_hooks:delete(adhoc_local_items, VHost, ?MODULE, adhoc_local_items, 50),
-+    %ejabberd_hooks:delete(disco_sm_identity, VHost, ?MODULE, get_sm_identity, 50),
-+    %ejabberd_hooks:delete(disco_sm_features, VHost, ?MODULE, get_sm_features, 50),
-+    %ejabberd_hooks:delete(disco_sm_items, VHost, ?MODULE, get_sm_items, 50),
 +    ejabberd_hooks:delete(disco_local_identity, VHost, ?MODULE, get_local_identity, 50),
 +    ejabberd_hooks:delete(disco_local_features, VHost, ?MODULE, get_local_features, 50),
 +    ejabberd_hooks:delete(disco_local_items, VHost, ?MODULE, get_local_items, 50),
@@ -477,14 +491,7 @@ index 00000000..79e2d3d3
 +
 +    ?MYDEBUG("Removed hooks for ~p", [VHost]),
 +
-+    %ejabberd_ctl:unregister_commands(VHost, [{"rebuild_stats", "rebuild mod_logdb module stats for vhost"}], ?MODULE, rebuild_stats),
-+    %Supported_backends = lists:flatmap(fun({Backend, _Opts}) ->
-+    %                                        [atom_to_list(Backend), " "]
-+    %                                   end, State#state.dbs),
-+    %ejabberd_ctl:unregister_commands(
-+    %                       VHost,
-+    %                       [{"copy_messages backend", "copy messages from backend to current backend. backends could be: " ++ Supported_backends }],
-+    %                       ?MODULE, copy_messages_ctl),
++    ejabberd_commands:unregister_commands(get_commands_spec()),
 +    ?MYDEBUG("Unregistered commands for ~p", [VHost]).
 +
 +stop(VHost) ->
@@ -492,8 +499,20 @@ index 00000000..79e2d3d3
 +    %gen_server:call(Proc, {cleanup}),
 +    %?MYDEBUG("Cleanup in stop finished!!!!", []),
 +    %timer:sleep(10000),
-+    ok = supervisor:terminate_child(ejabberd_sup, Proc),
-+    ok = supervisor:delete_child(ejabberd_sup, Proc).
++    ok = supervisor:terminate_child(ejabberd_gen_mod_sup, Proc),
++    ok = supervisor:delete_child(ejabberd_gen_mod_sup, Proc).
++
++get_commands_spec() ->
++    [#ejabberd_commands{name = rebuild_stats, tags = [logdb],
++            desc = "Rebuild mod_logdb stats for given host",
++            module = ?MODULE, function = rebuild_stats,
++            args = [{host, binary}],
++            result = {res, rescode}},
++     #ejabberd_commands{name = copy_messages, tags = [logdb],
++            desc = "Copy logdb messages from given backend to current backend for given host",
++            module = ?MODULE, function = copy_messages_ctl,
++            args = [{host, binary}, {backend, binary}, {date, binary}],
++            result = {res, rescode}}].
 +
 +mod_opt_type(dbs) ->
 +    fun (A) when is_list(A) -> A end;
@@ -652,10 +671,10 @@ index 00000000..79e2d3d3
 +    DBMod:rebuild_stats(VHost),
 +    {noreply, State};
 +handle_cast({copy_messages, Backend}, State) ->
-+    spawn(?MODULE, copy_messages, [[State, Backend]]),
++    spawn(?MODULE, copy_messages, [[State, Backend, []]]),
 +    {noreply, State};
 +handle_cast({copy_messages, Backend, Date}, State) ->
-+    spawn(?MODULE, copy_messages, [[State, Backend, Date]]),
++    spawn(?MODULE, copy_messages, [[State, Backend, [binary_to_list(Date)]]]),
 +    {noreply, State};
 +handle_cast(Msg, State) ->
 +    ?INFO_MSG("Got cast Msg:~p, State:~p", [Msg, State]),
@@ -714,17 +733,13 @@ index 00000000..79e2d3d3
 +           ejabberd_hooks:add(remove_user, VHost, ?MODULE, remove_user, 90),
 +           ejabberd_hooks:add(user_send_packet, VHost, ?MODULE, send_packet, 90),
 +           ejabberd_hooks:add(user_receive_packet, VHost, ?MODULE, receive_packet, 90),
++           ejabberd_hooks:add(offline_message_hook, VHost, ?MODULE, offline_message, 40),
 +
++           ejabberd_hooks:add(adhoc_local_commands, VHost, ?MODULE, adhoc_local_commands, 50),
 +           ejabberd_hooks:add(disco_local_items, VHost, ?MODULE, get_local_items, 50),
-+           ejabberd_hooks:add(disco_local_features, VHost, ?MODULE, get_local_features, 50),
 +           ejabberd_hooks:add(disco_local_identity, VHost, ?MODULE, get_local_identity, 50),
-+           %ejabberd_hooks:add(disco_sm_items, VHost, ?MODULE, get_sm_items, 50),
-+           %ejabberd_hooks:add(disco_sm_features, VHost, ?MODULE, get_sm_features, 50),
-+           %ejabberd_hooks:add(disco_sm_identity, VHost, ?MODULE, get_sm_identity, 50),
++           ejabberd_hooks:add(disco_local_features, VHost, ?MODULE, get_local_features, 50),
 +           ejabberd_hooks:add(adhoc_local_items, VHost, ?MODULE, adhoc_local_items, 50),
-+           ejabberd_hooks:add(adhoc_local_commands, VHost, ?MODULE, adhoc_local_commands, 50),
-+           %ejabberd_hooks:add(adhoc_sm_items, VHost, ?MODULE, adhoc_sm_items, 50),
-+           %ejabberd_hooks:add(adhoc_sm_commands, VHost, ?MODULE, adhoc_sm_commands, 50),
 +
 +           ejabberd_hooks:add(webadmin_menu_host, VHost, ?MODULE, webadmin_menu, 70),
 +           ejabberd_hooks:add(webadmin_user, VHost, ?MODULE, webadmin_user, 50),
@@ -733,17 +748,7 @@ index 00000000..79e2d3d3
 +
 +           ?MYDEBUG("Added hooks for ~p", [VHost]),
 +
-+           %ejabberd_ctl:register_commands(
-+           %                VHost,
-+           %                [{"rebuild_stats", "rebuild mod_logdb module stats for vhost"}],
-+           %                ?MODULE, rebuild_stats),
-+           %Supported_backends = lists:flatmap(fun({Backend, _Opts}) ->
-+           %                                       [atom_to_list(Backend), " "]
-+           %                                   end, State#state.dbs),
-+           %ejabberd_ctl:register_commands(
-+           %                VHost,
-+           %                [{"copy_messages backend", "copy messages from backend to current backend. backends could be: " ++ Supported_backends }],
-+           %                ?MODULE, copy_messages_ctl),
++           ejabberd_commands:register_commands(get_commands_spec()),
 +           ?MYDEBUG("Registered commands for ~p", [VHost]),
 +
 +           NewState=State#state{monref = MonRef, backendPid=SPid, purgeRef=TrefPurge, pollRef=TrefPoll},
@@ -805,21 +810,32 @@ index 00000000..79e2d3d3
 +%
 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 +% TODO: change to/from to list as sql stores it as list
-+send_packet(P, _C2SState, Owner, Peer) ->
++send_packet({Pkt, #{jid := Owner} = C2SState}) ->
++    VHost = Owner#jid.lserver,
++    Peer = xmpp:get_to(Pkt),
++    %?MYDEBUG("send_packet. Peer=~p, Owner=~p", [Peer, Owner]),
++    Proc = gen_mod:get_module_proc(VHost, ?PROCNAME),
++    gen_server:cast(Proc, {addlog, to, Owner, Peer, Pkt}),
++    {Pkt, C2SState}.
++
++receive_packet({Pkt, #{jid := Owner} = C2SState}) ->
 +    VHost = Owner#jid.lserver,
++    Peer = xmpp:get_from(Pkt),
++    %?MYDEBUG("receive_packet. Pkt=~p", [Pkt]),
 +    Proc = gen_mod:get_module_proc(VHost, ?PROCNAME),
-+    gen_server:cast(Proc, {addlog, to, Owner, Peer, P}),
-+    P.
++    gen_server:cast(Proc, {addlog, from, Owner, Peer, Pkt}),
++    {Pkt, C2SState}.
 +
-+receive_packet(P, _C2SState, _JID, Peer, Owner) ->
++offline_message({_Action, #message{from = Peer, to = Owner} = Pkt} = Acc) ->
 +    VHost = Owner#jid.lserver,
++    %?MYDEBUG("offline_message. Pkt=~p", [Pkt]),
 +    Proc = gen_mod:get_module_proc(VHost, ?PROCNAME),
-+    gen_server:cast(Proc, {addlog, from, Owner, Peer, P}),
-+    P.
++    gen_server:cast(Proc, {addlog, from, Owner, Peer, Pkt}),
++    Acc.
 +
 +remove_user(User, Server) ->
-+    LUser = jlib:nodeprep(User),
-+    LServer = jlib:nameprep(Server),
++    LUser = jid:nodeprep(User),
++    LServer = jid:nameprep(Server),
 +    Proc = gen_mod:get_module_proc(LServer, ?PROCNAME),
 +    gen_server:cast(Proc, {remove_user, LUser}).
 +
@@ -828,23 +844,20 @@ index 00000000..79e2d3d3
 +% ejabberdctl
 +%
 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-+rebuild_stats(_Val, VHost, ["rebuild_stats"]) ->
++rebuild_stats(VHost) ->
 +    Proc = gen_mod:get_module_proc(VHost, ?PROCNAME),
 +    gen_server:cast(Proc, {rebuild_stats}),
-+    {stop, ?STATUS_SUCCESS};
-+rebuild_stats(Val, _VHost, _Args) ->
-+    Val.
++    ok.
 +
-+copy_messages_ctl(_Val, VHost, ["copy_messages", Backend]) ->
++copy_messages_ctl(VHost, Backend, <<"all">>) ->
 +    Proc = gen_mod:get_module_proc(VHost, ?PROCNAME),
 +    gen_server:cast(Proc, {copy_messages, Backend}),
-+    {stop, ?STATUS_SUCCESS};
-+copy_messages_ctl(_Val, VHost, ["copy_messages", Backend, Date]) ->
++    ok;
++copy_messages_ctl(VHost, Backend, Date) ->
 +    Proc = gen_mod:get_module_proc(VHost, ?PROCNAME),
 +    gen_server:cast(Proc, {copy_messages, Backend, Date}),
-+    {stop, ?STATUS_SUCCESS};
-+copy_messages_ctl(Val, _VHost, _Args) ->
-+    Val.
++    ok.
++
 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 +%
 +% misc operations
@@ -853,68 +866,44 @@ index 00000000..79e2d3d3
 +
 +% handle_cast({addlog, E}, _)
 +% raw packet -> #msg
-+packet_parse(Owner, Peer, Packet, Direction, State) ->
-+    case fxml:get_subtag(Packet, <<"body">>) of
-+         false ->
-+           ignore;
-+         Body_xml ->
-+           Message_type =
-+              case fxml:get_tag_attr_s(<<"type">>, Packet) of
-+                   <<"error">> ->   throw(ignore);
-+                   []          ->   <<"normal">>;
-+                   MType       ->   MType
-+              end,
-+
-+           case Message_type of
-+                <<"groupchat">> when State#state.groupchat == send, Direction == to ->
-+                   ok;
-+                <<"groupchat">> when State#state.groupchat == send, Direction == from ->
-+                   throw(ignore);
-+                <<"groupchat">> when State#state.groupchat == half ->
-+                   Rooms = ets:match(muc_online_room, '$1'),
-+                   Ni=lists:foldl(fun([{muc_online_room, {GName, GHost}, Pid}], Names) ->
-+                                   case gen_fsm:sync_send_all_state_event(Pid, {get_jid_nick,Owner}) of
-+                                        [] -> Names;
-+                                        Nick ->
-+                                           lists:append(Names, [jlib:jid_to_string({GName, GHost, Nick})])
-+                                   end
-+                                  end, [], Rooms),
-+                   case lists:member(jlib:jid_to_string(Peer), Ni) of
-+                        true when Direction == from ->
-+                          throw(ignore);
-+                        _ ->
-+                          ok
-+                   end;
-+                <<"groupchat">> when State#state.groupchat == none ->
-+                   throw(ignore);
-+                _ ->
-+                   ok
-+           end,
++packet_parse(_Owner, _Peer, #message{type = error}, _Direction, _State) ->
++    ignore;
++packet_parse(_Owner, _Peer, #message{meta = #{sm_copy := true}}, _Direction, _State) ->
++    ignore;
++packet_parse(_Owner, _Peer, #message{meta = #{from_offline := true}}, _Direction, _State) ->
++    ignore;
++packet_parse(Owner, Peer, #message{body = Body, subject = Subject, type = Type}, Direction, State) ->
++    %?MYDEBUG("Owner=~p, Peer=~p, Direction=~p", [Owner, Peer, Direction]),
++    %?MYDEBUG("Body=~p, Subject=~p, Type=~p", [Body, Subject, Type]),
++    SubjectText = xmpp:get_text(Subject),
++    BodyText = xmpp:get_text(Body),
++    if (SubjectText == <<"">>) and (BodyText == <<"">>) ->
++        throw(ignore);
++       true -> ok
++    end,
 +
-+           Message_body = fxml:get_tag_cdata(Body_xml),
-+           Message_subject =
-+              case fxml:get_subtag(Packet, <<"subject">>) of
-+                   false ->
-+                     <<"">>;
-+                   Subject_xml ->
-+                     fxml:get_tag_cdata(Subject_xml)
-+              end,
++    case Type of
++         groupchat when State#state.groupchat == send, Direction == to ->
++            ok;
++         groupchat when State#state.groupchat == send, Direction == from ->
++            throw(ignore);
++         groupchat when State#state.groupchat == none ->
++            throw(ignore);
++         _ ->
++            ok
++    end,
 +
-+           OwnerName = stringprep:tolower(Owner#jid.user),
-+           PName = stringprep:tolower(Peer#jid.user),
-+           PServer = stringprep:tolower(Peer#jid.server),
-+           PResource = Peer#jid.resource,
-+
-+           #msg{timestamp     = get_timestamp(),
-+                owner_name    = OwnerName,
-+                peer_name     = PName,
-+                peer_server   = PServer,
-+                peer_resource = PResource,
-+                direction     = Direction,
-+                type          = Message_type,
-+                subject       = Message_subject,
-+                body          = Message_body}
-+    end.
++    #msg{timestamp     = get_timestamp(),
++         owner_name    = stringprep:tolower(Owner#jid.user),
++         peer_name     = stringprep:tolower(Peer#jid.user),
++         peer_server   = stringprep:tolower(Peer#jid.server),
++         peer_resource = Peer#jid.resource,
++         direction     = Direction,
++         type          = misc:atom_to_binary(Type),
++         subject       = SubjectText,
++         body          = BodyText};
++packet_parse(_, _, _, _, _) ->
++    ignore.
 +
 +% called from handle_cast({addlog, _}, _) -> true (log messages) | false (do not log messages)
 +filter(Owner, Peer, State) ->
@@ -1065,7 +1054,7 @@ index 00000000..79e2d3d3
 +         {value, _} ->
 +             PMsgs = lists:filter(
 +                              fun(Msg) ->
-+                                   ID = jlib:encode_base64(term_to_binary(Msg#msg.timestamp)),
++                                   ID = misc:encode_base64(term_to_binary(Msg#msg.timestamp)),
 +                                   lists:member({<<"selected">>, ID}, Query)
 +                              end, Msgs),
 +             Proc = gen_mod:get_module_proc(VHost, ?PROCNAME),
@@ -1080,7 +1069,7 @@ index 00000000..79e2d3d3
 +             Dates = get_dates(VHost),
 +             PDates = lists:filter(
 +                              fun(Date) ->
-+                                   ID = jlib:encode_base64( << User/binary, (iolist_to_binary(Date))/binary >> ),
++                                   ID = misc:encode_base64( << User/binary, (iolist_to_binary(Date))/binary >> ),
 +                                   lists:member({<<"selected">>, ID}, Query)
 +                              end, Dates),
 +             Proc = gen_mod:get_module_proc(VHost, ?PROCNAME),
@@ -1107,7 +1096,7 @@ index 00000000..79e2d3d3
 +             Dates = get_dates(VHost),
 +             PDates = lists:filter(
 +                              fun(Date) ->
-+                                   ID = jlib:encode_base64( << VHost/binary, (iolist_to_binary(Date))/binary >> ),
++                                   ID = misc:encode_base64( << VHost/binary, (iolist_to_binary(Date))/binary >> ),
 +                                   lists:member({<<"selected">>, ID}, Query)
 +                              end, Dates),
 +             Proc = gen_mod:get_module_proc(VHost, ?PROCNAME),
@@ -1131,7 +1120,7 @@ index 00000000..79e2d3d3
 +         {value, _} ->
 +             PStats = lists:filter(
 +                              fun({User, _Count}) ->
-+                                   ID = jlib:encode_base64( << (iolist_to_binary(User))/binary, VHost/binary >> ),
++                                   ID = misc:encode_base64( << (iolist_to_binary(User))/binary, VHost/binary >> ),
 +                                   lists:member({<<"selected">>, ID}, Query)
 +                              end, Stats),
 +             Proc = gen_mod:get_module_proc(VHost, ?PROCNAME),
@@ -1151,11 +1140,9 @@ index 00000000..79e2d3d3
 +             nothing
 +    end.
 +
-+copy_messages([#state{vhost=VHost}=State, From]) ->
-+    ?INFO_MSG("Going to copy messages from ~p for ~p", [From, VHost]),
-+
++copy_messages([#state{vhost=VHost}=State, From, DatesIn]) ->
 +    {FromDBName, FromDBOpts} =
-+         case lists:keysearch(list_to_atom(From), 1, State#state.dbs) of
++         case lists:keysearch(misc:binary_to_atom(From), 1, State#state.dbs) of
 +              {value, {FN, FO}} ->
 +                 {FN, FO};
 +              false ->
@@ -1167,34 +1154,24 @@ index 00000000..79e2d3d3
 +
 +    {ok, _FromPid} = FromDBMod:start(VHost, FromDBOpts),
 +
-+    Dates = FromDBMod:get_dates(VHost),
++    Dates = case DatesIn of
++                 [] -> FromDBMod:get_dates(VHost);
++                 _ -> DatesIn
++            end,
++
 +    DatesLength = length(Dates),
 +
-+    lists:foldl(fun(Date, Acc) ->
-+                   case copy_messages_int([FromDBMod, State#state.dbmod, VHost, Date]) of
-+                        ok ->
-+                          ?INFO_MSG("Copied messages at ~p (~p/~p)", [Date, Acc, DatesLength]);
-+                        Value ->
-+                          ?ERROR_MSG("Failed to copy messages at ~p (~p/~p): ~p", [Date, Acc, DatesLength, Value]),
-+                          FromDBMod:stop(VHost),
-+                          throw(error)
-+                   end,
-+                   Acc + 1
-+                  end, 1, Dates),
-+    ?INFO_MSG("Copied messages from ~p",  [From]),
-+    FromDBMod:stop(VHost);
-+copy_messages([#state{vhost=VHost}=State, From, Date]) ->
-+    {value, {FromDBName, FromDBOpts}} = lists:keysearch(list_to_atom(From), 1, State#state.dbs),
-+    FromDBMod = list_to_atom(atom_to_list(?MODULE) ++ "_" ++ atom_to_list(FromDBName)),
-+    {ok, _FromPid} = FromDBMod:start(VHost, FromDBOpts),
-+    case catch copy_messages_int([FromDBMod, State#state.dbmod, VHost, Date]) of
-+         {'exit', Reason} ->
-+           ?ERROR_MSG("Failed to copy messages at ~p: ~p", [Date, Reason]);
-+         ok ->
-+           ?INFO_MSG("Copied messages at ~p", [Date]);
-+         Value ->
-+           ?ERROR_MSG("Failed to copy messages at ~p: ~p", [Date, Value])
-+    end,
++    catch lists:foldl(fun(Date, Acc) ->
++                        case catch copy_messages_int([FromDBMod, State#state.dbmod, VHost, Date]) of
++                            ok ->
++                                ?INFO_MSG("Copied messages at ~p (~p/~p)", [Date, Acc, DatesLength]);
++                            Value ->
++                                ?ERROR_MSG("Failed to copy messages at ~p (~p/~p): ~p", [Date, Acc, DatesLength, Value]),
++                                throw(error)
++                        end,
++                        Acc + 1
++                      end, 1, Dates),
++    ?INFO_MSG("copy_messages from ~p finished",  [From]),
 +    FromDBMod:stop(VHost).
 +
 +copy_messages_int([FromDBMod, ToDBMod, VHost, Date]) ->
@@ -1208,10 +1185,10 @@ index 00000000..79e2d3d3
 +copy_messages_int_tc([FromDBMod, ToDBMod, VHost, Date]) ->
 +    ?INFO_MSG("Going to copy messages from ~p for ~p at ~p", [FromDBMod, VHost, Date]),
 +
-+    ok = FromDBMod:rebuild_stats_at(VHost, binary_to_list(Date)),
++    ok = FromDBMod:rebuild_stats_at(VHost, Date),
 +    catch mod_logdb:rebuild_stats_at(VHost, Date),
-+    {ok, FromStats} = FromDBMod:get_vhost_stats_at(VHost, binary_to_list(Date)),
-+    ToStats = case mod_logdb:get_vhost_stats_at(VHost, Date) of
++    {ok, FromStats} = FromDBMod:get_vhost_stats_at(VHost, Date),
++    ToStats = case mod_logdb:get_vhost_stats_at(VHost, iolist_to_binary(Date)) of
 +                   {ok, Stats} -> Stats;
 +                   {error, _} -> []
 +              end,
@@ -1222,13 +1199,20 @@ index 00000000..79e2d3d3
 +    StatsLength = length(FromStats),
 +
 +    CopyFun = if
-+                                                   % destination table is empty
-+                FromDBMod /= mod_logdb_mnesia_old, ToStats == [] ->
++                % destination table is empty
++                ToStats == [] ->
 +                    fun({User, _Count}, Acc) ->
-+                        {ok, Msgs} = FromDBMod:get_user_messages_at(binary_to_list(User), VHost, binary_to_list(Date)),
++                        {ok, Msgs} = FromDBMod:get_user_messages_at(User, VHost, Date),
 +                        MAcc =
 +                          lists:foldl(fun(Msg, MFAcc) ->
-+                                          ok = ToDBMod:log_message(VHost, Msg),
++                                          MsgBinary = Msg#msg{owner_name=iolist_to_binary(User),
++                                                              peer_name=iolist_to_binary(Msg#msg.peer_name),
++                                                              peer_server=iolist_to_binary(Msg#msg.peer_server),
++                                                              peer_resource=iolist_to_binary(Msg#msg.peer_resource),
++                                                              type=iolist_to_binary(Msg#msg.type),
++                                                              subject=iolist_to_binary(Msg#msg.subject),
++                                                              body=iolist_to_binary(Msg#msg.body)},
++                                          ok = ToDBMod:log_message(VHost, MsgBinary),
 +                                          MFAcc + 1
 +                                      end, 0, Msgs),
 +                        NewAcc = Acc + 1,
@@ -1236,10 +1220,10 @@ index 00000000..79e2d3d3
 +                        %timer:sleep(100),
 +                        NewAcc
 +                    end;
-+                                                   % destination table is not empty
-+                FromDBMod /= mod_logdb_mnesia_old, ToStats /= [] ->
++                % destination table is not empty
++                true ->
 +                    fun({User, _Count}, Acc) ->
-+                        {ok, ToMsgs} = ToDBMod:get_user_messages_at(binary_to_list(User), VHost, binary_to_list(Date)),
++                        {ok, ToMsgs} = ToDBMod:get_user_messages_at(User, VHost, Date),
 +                        lists:foreach(fun(#msg{timestamp=Tst}) when length(Tst) == 16 ->
 +                                            ets:insert(mod_logdb_temp, {Tst});
 +                                         % mysql, pgsql removes final zeros after decimal point
@@ -1248,12 +1232,19 @@ index 00000000..79e2d3d3
 +                                            [T] = io_lib:format("~.5f", [F]),
 +                                            ets:insert(mod_logdb_temp, {T})
 +                                      end, ToMsgs),
-+                        {ok, Msgs} = FromDBMod:get_user_messages_at(binary_to_list(User), VHost, binary_to_list(Date)),
++                        {ok, Msgs} = FromDBMod:get_user_messages_at(User, VHost, Date),
 +                        MAcc =
 +                          lists:foldl(fun(#msg{timestamp=ToTimestamp} = Msg, MFAcc) ->
 +                                          case ets:member(mod_logdb_temp, ToTimestamp) of
 +                                               false ->
-+                                                  ok = ToDBMod:log_message(VHost, Msg),
++                                                  MsgBinary = Msg#msg{owner_name=iolist_to_binary(User),
++                                                                      peer_name=iolist_to_binary(Msg#msg.peer_name),
++                                                                      peer_server=iolist_to_binary(Msg#msg.peer_server),
++                                                                      peer_resource=iolist_to_binary(Msg#msg.peer_resource),
++                                                                      type=iolist_to_binary(Msg#msg.type),
++                                                                      subject=iolist_to_binary(Msg#msg.subject),
++                                                                      body=iolist_to_binary(Msg#msg.body)},
++                                                  ok = ToDBMod:log_message(VHost, MsgBinary),
 +                                                  ets:insert(mod_logdb_temp, {ToTimestamp}),
 +                                                  MFAcc + 1;
 +                                               true ->
@@ -1265,79 +1256,8 @@ index 00000000..79e2d3d3
 +                        ?INFO_MSG("Copied ~p messages for ~p (~p/~p) at ~p", [MAcc, User, NewAcc, StatsLength, Date]),
 +                        %timer:sleep(100),
 +                        NewAcc
-+                    end;
-+                % copying from mod_logmnesia
-+                true ->
-+                    fun({User, _Count}, Acc) ->
-+                        ToStats =
-+                           case ToDBMod:get_user_messages_at(binary_to_list(User), VHost, binary_to_list(Date)) of
-+                                {ok, []} ->
-+                                  ok;
-+                                {ok, ToMsgs} ->
-+                                  lists:foreach(fun(#msg{timestamp=Tst}) when length(Tst) == 16 ->
-+                                                     ets:insert(mod_logdb_temp, {Tst});
-+                                                   % mysql, pgsql removes final zeros after decimal point
-+                                                   (#msg{timestamp=Tst}) when length(Tst) < 15 ->
-+                                                     {F, _} = string:to_float(Tst++".0"),
-+                                                     [T] = io_lib:format("~.5f", [F]),
-+                                                     ets:insert(mod_logdb_temp, {T})
-+                                                end, ToMsgs);
-+                                {error, _} ->
-+                                  ok
-+                           end,
-+                        {ok, Msgs} = FromDBMod:get_user_messages_at(binary_to_list(User), VHost, binary_to_list(Date)),
-+
-+                        MAcc =
-+                          lists:foldl(
-+                            fun({msg, TU, TS, TR, FU, FS, FR, Type, Subj, Body, Timest},
-+                                MFAcc) ->
-+                                  [Timestamp] = if is_float(Timest) == true ->
-+                                                     io_lib:format("~.5f", [Timest]);
-+                                                   % early versions of mod_logmnesia
-+                                                   is_integer(Timest) == true ->
-+                                                     io_lib:format("~.5f", [Timest-719528*86400.0]);
-+                                                   true ->
-+                                                     ?ERROR_MSG("Incorrect timestamp ~p", [Timest]),
-+                                                     throw(error)
-+                                                end,
-+                                  case ets:member(mod_logdb_temp, Timestamp) of
-+                                       false ->
-+                                          if
-+                                           % from
-+                                           TS == VHost ->
-+                                             TMsg = #msg{timestamp=Timestamp,
-+                                                         owner_name=TU,
-+                                                         peer_name=FU, peer_server=FS, peer_resource=FR,
-+                                                         direction=from,
-+                                                         type=Type,
-+                                                         subject=Subj, body=Body},
-+                                             ok = ToDBMod:log_message(VHost, TMsg);
-+                                           true -> ok
-+                                         end,
-+                                         if
-+                                           % to
-+                                           FS == VHost ->
-+                                             FMsg = #msg{timestamp=Timestamp,
-+                                                         owner_name=FU,
-+                                                         peer_name=TU, peer_server=TS, peer_resource=TR,
-+                                                         direction=to,
-+                                                         type=Type,
-+                                                         subject=Subj, body=Body},
-+                                             ok = ToDBMod:log_message(VHost, FMsg);
-+                                           true -> ok
-+                                         end,
-+                                         ets:insert(mod_logdb_temp, {Timestamp}),
-+                                         MFAcc + 1;
-+                                       true -> % not ets:member
-+                                          MFAcc
-+                                   end % case
-+                          end, 0, Msgs), % foldl
-+                        NewAcc = Acc + 1,
-+                        ?INFO_MSG("Copied ~p messages for ~p (~p/~p) at ~p", [MAcc, User, NewAcc, StatsLength, Date]),
-+                        %timer:sleep(100),
-+                        NewAcc
-+                    end % fun
-+              end, % if FromDBMod /= mod_logdb_mnesia_old
++                    end
++              end,
 +
 +    if
 +      FromStats == [] ->
@@ -1346,7 +1266,7 @@ index 00000000..79e2d3d3
 +        ?INFO_MSG("Stats are equal at ~p", [Date]);
 +      FromStatsS /= ToStatsS ->
 +        lists:foldl(CopyFun, 0, FromStats),
-+        ok = ToDBMod:rebuild_stats_at(VHost, binary_to_list(Date))
++        ok = ToDBMod:rebuild_stats_at(VHost, Date)
 +        %timer:sleep(1000)
 +    end,
 +
@@ -1396,22 +1316,19 @@ index 00000000..79e2d3d3
 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 +-define(ITEMS_RESULT(Allow, LNode, Fallback),
 +    case Allow of
-+        deny ->
-+            Fallback;
++        deny -> Fallback;
 +        allow ->
 +            case get_local_items(LServer, LNode,
-+                                 jlib:jid_to_string(To), Lang) of
-+                {result, Res} ->
-+                    {result, Res};
-+                {error, Error} ->
-+                    {error, Error}
++                                 jid:encode(To), Lang) of
++                {result, Res} -> {result, Res};
++                {error, Error} -> {error, Error}
 +            end
 +    end).
 +
-+get_local_items(Acc, From, #jid{lserver = LServer} = To, <<"">>, Lang) ->
++get_local_items(Acc, From, #jid{lserver = LServer} = To,
++                <<"">>, Lang) ->
 +    case gen_mod:is_loaded(LServer, mod_adhoc) of
-+        false ->
-+            Acc;
++        false -> Acc;
 +        _ ->
 +            Items = case Acc of
 +                         {result, Its} -> Its;
@@ -1422,7 +1339,7 @@ index 00000000..79e2d3d3
 +            if
 +              AllowUser == allow; AllowAdmin == allow ->
 +                case get_local_items(LServer, [],
-+                                     jlib:jid_to_string(To), Lang) of
++                                     jid:encode(To), Lang) of
 +                     {result, Res} ->
 +                        {result, Items ++ Res};
 +                     {error, _Error} ->
@@ -1432,24 +1349,25 @@ index 00000000..79e2d3d3
 +                {result, Items}
 +            end
 +    end;
-+get_local_items(Acc, From, #jid{lserver = LServer} = To, Node, Lang) ->
++get_local_items(Acc, From, #jid{lserver = LServer} = To,
++                Node, Lang) ->
 +    case gen_mod:is_loaded(LServer, mod_adhoc) of
-+        false ->
-+            Acc;
++        false -> Acc;
 +        _ ->
-+            LNode = str:tokens(Node, <<"/">>),
++            LNode = tokenize(Node),
 +            AllowAdmin = acl:match_rule(LServer, mod_logdb_admin, From),
++            Err = xmpp:err_forbidden(<<"Denied by ACL">>, Lang),
 +            case LNode of
 +                 [<<"mod_logdb">>] ->
-+                      ?ITEMS_RESULT(AllowAdmin, LNode, {error, ?ERR_FORBIDDEN});
++                      ?ITEMS_RESULT(AllowAdmin, LNode, {error, Err});
 +                 [<<"mod_logdb_users">>] ->
-+                      ?ITEMS_RESULT(AllowAdmin, LNode, {error, ?ERR_FORBIDDEN});
++                      ?ITEMS_RESULT(AllowAdmin, LNode, {error, Err});
 +                 [<<"mod_logdb_users">>, <<$@, _/binary>>] ->
-+                      ?ITEMS_RESULT(AllowAdmin, LNode, {error, ?ERR_FORBIDDEN});
++                      ?ITEMS_RESULT(AllowAdmin, LNode, {error, Err});
 +                 [<<"mod_logdb_users">>, _User] ->
-+                      ?ITEMS_RESULT(AllowAdmin, LNode, {error, ?ERR_FORBIDDEN});
++                      ?ITEMS_RESULT(AllowAdmin, LNode, {error, Err});
 +                 [<<"mod_logdb_settings">>] ->
-+                      ?ITEMS_RESULT(AllowAdmin, LNode, {error, ?ERR_FORBIDDEN});
++                      ?ITEMS_RESULT(AllowAdmin, LNode, {error, Err});
 +                 _ ->
 +                      Acc
 +            end
@@ -1458,11 +1376,14 @@ index 00000000..79e2d3d3
 +-define(T(Lang, Text), translate:translate(Lang, Text)).
 +
 +-define(NODE(Name, Node),
-+    #xmlel{name = <<"item">>,
-+           attrs =
-+           [{<<"jid">>, Server}, {<<"name">>, ?T(Lang, Name)},
-+            {<<"node">>, Node}],
-+           children = []}).
++    #disco_item{jid = jid:make(Server),
++            node = Node,
++            name = ?T(Lang, Name)}).
++
++-define(NS_ADMINX(Sub),
++    <<(?NS_ADMIN)/binary, "#", Sub/binary>>).
++
++tokenize(Node) -> str:tokens(Node, <<"/#">>).
 +
 +get_local_items(_Host, [], Server, Lang) ->
 +    {result,
@@ -1473,29 +1394,22 @@ index 00000000..79e2d3d3
 +     [?NODE(<<"Messages logging engine users">>, <<"mod_logdb_users">>),
 +      ?NODE(<<"Messages logging engine settings">>, <<"mod_logdb_settings">>)]
 +    };
-+get_local_items(Host, [<<"mod_logdb_users">>], Server, Lang) ->
-+    {result, get_all_vh_users(Host, Server, Lang)};
++get_local_items(Host, [<<"mod_logdb_users">>], Server, _Lang) ->
++    {result, get_all_vh_users(Host, Server)};
 +get_local_items(Host, [<<"mod_logdb_users">>, <<$@, Diap/binary>>], Server, Lang) ->
-+    case catch ejabberd_auth:get_vh_registered_users(Host) of
-+        {'EXIT', _Reason} ->
-+            ?ERR_INTERNAL_SERVER_ERROR;
-+        Users ->
-+            SUsers = lists:sort([{S, U} || {U, S} <- Users]),
-+            case catch begin
-+                           [S1, S2] = ejabberd_regexp:split(Diap, <<"-">>),
-+                           N1 = jlib:binary_to_integer(S1),
-+                           N2 = jlib:binary_to_integer(S2),
-+                           Sub = lists:sublist(SUsers, N1, N2 - N1 + 1),
-+                           lists:map(fun({S, U}) ->
-+                                      ?NODE(<< U/binary, "@", S/binary >>,
-+                                            << (iolist_to_binary("mod_logdb_users/"))/binary, U/binary, "@", S/binary >>)
-+                                     end, Sub)
-+                       end of
-+                {'EXIT', _Reason} ->
-+                    ?ERR_NOT_ACCEPTABLE;
-+                Res ->
-+                    {result, Res}
-+            end
++    Users = ejabberd_auth:get_vh_registered_users(Host),
++    SUsers = lists:sort([{S, U} || {U, S} <- Users]),
++    try
++        [S1, S2] = ejabberd_regexp:split(Diap, <<"-">>),
++        N1 = binary_to_integer(S1),
++        N2 = binary_to_integer(S2),
++        Sub = lists:sublist(SUsers, N1, N2 - N1 + 1),
++        {result, lists:map(fun({S, U}) ->
++                               ?NODE(<< U/binary, "@", S/binary >>,
++                                     << (iolist_to_binary("mod_logdb_users/"))/binary, U/binary, "@", S/binary >>)
++                           end, Sub)}
++    catch _:_ ->
++        xmpp:err_not_acceptable()
 +    end;
 +get_local_items(_Host, [<<"mod_logdb_users">>, _User], _Server, _Lang) ->
 +    {result, []};
@@ -1503,35 +1417,36 @@ index 00000000..79e2d3d3
 +    {result, []};
 +get_local_items(_Host, Item, _Server, _Lang) ->
 +    ?MYDEBUG("asked for items in ~p", [Item]),
-+    {error, ?ERR_ITEM_NOT_FOUND}.
++    {error, xmpp:err_item_not_found()}.
 +
-+-define(INFO_RESULT(Allow, Feats),
++-define(INFO_RESULT(Allow, Feats, Lang),
 +    case Allow of
-+      deny -> {error, ?ERR_FORBIDDEN};
++      deny -> {error, xmpp:err_forbidden(<<"Denied by ACL">>, Lang)};
 +      allow -> {result, Feats}
 +    end).
 +
-+get_local_features(Acc, From, #jid{lserver = LServer} = _To, Node, _Lang) ->
++get_local_features(Acc, From,
++                   #jid{lserver = LServer} = _To, Node, Lang) ->
 +    case gen_mod:is_loaded(LServer, mod_adhoc) of
 +        false ->
 +            Acc;
 +        _ ->
-+            LNode = str:tokens(Node, <<"/">>),
++            LNode = tokenize(Node),
 +            AllowUser = acl:match_rule(LServer, mod_logdb, From),
 +            AllowAdmin = acl:match_rule(LServer, mod_logdb_admin, From),
 +            case LNode of
 +                 [<<"mod_logdb">>] when AllowUser == allow; AllowAdmin == allow ->
-+                    ?INFO_RESULT(allow, [?NS_COMMANDS]);
++                    ?INFO_RESULT(allow, [?NS_COMMANDS], Lang);
 +                 [<<"mod_logdb">>] ->
-+                    ?INFO_RESULT(deny, [?NS_COMMANDS]);
++                    ?INFO_RESULT(deny, [?NS_COMMANDS], Lang);
 +                 [<<"mod_logdb_users">>] ->
-+                    ?INFO_RESULT(AllowAdmin, []);
++                    ?INFO_RESULT(AllowAdmin, [], Lang);
 +                 [<<"mod_logdb_users">>, [$@ | _]] ->
-+                    ?INFO_RESULT(AllowAdmin, []);
++                    ?INFO_RESULT(AllowAdmin, [], Lang);
 +                 [<<"mod_logdb_users">>, _User] ->
-+                    ?INFO_RESULT(AllowAdmin, [?NS_COMMANDS]);
++                    ?INFO_RESULT(AllowAdmin, [?NS_COMMANDS], Lang);
 +                 [<<"mod_logdb_settings">>] ->
-+                    ?INFO_RESULT(AllowAdmin, [?NS_COMMANDS]);
++                    ?INFO_RESULT(AllowAdmin, [?NS_COMMANDS], Lang);
 +                 [] ->
 +                    Acc;
 +                 _ ->
@@ -1540,87 +1455,65 @@ index 00000000..79e2d3d3
 +    end.
 +
 +-define(INFO_IDENTITY(Category, Type, Name, Lang),
-+    [#xmlel{name = <<"identity">>,
-+        attrs =
-+            [{<<"category">>, Category}, {<<"type">>, Type},
-+             {<<"name">>, ?T(Lang, Name)}],
-+        children = []}]).
++    [#identity{category = Category, type = Type, name = ?T(Lang, Name)}]).
 +
 +-define(INFO_COMMAND(Name, Lang),
 +    ?INFO_IDENTITY(<<"automation">>, <<"command-node">>,
 +               Name, Lang)).
 +
 +get_local_identity(Acc, _From, _To, Node, Lang) ->
-+    LNode = str:tokens(Node, <<"/">>),
++    LNode = tokenize(Node),
 +    case LNode of
 +         [<<"mod_logdb">>] ->
 +            ?INFO_COMMAND(<<"Messages logging engine">>, Lang);
 +         [<<"mod_logdb_users">>] ->
 +            ?INFO_COMMAND(<<"Messages logging engine users">>, Lang);
-+         [<<"mod_logdb_users">>, [$@ | _]] ->
-+            Acc;
 +         [<<"mod_logdb_users">>, User] ->
 +            ?INFO_COMMAND(User, Lang);
 +         [<<"mod_logdb_settings">>] ->
 +            ?INFO_COMMAND(<<"Messages logging engine settings">>, Lang);
-+         [] ->
-+            Acc;
 +         _ ->
 +            Acc
 +    end.
 +
-+%get_sm_items(Acc, From, To, Node, Lang) ->
-+%    ?MYDEBUG("get_sm_items Acc=~p From=~p To=~p Node=~p Lang=~p", [Acc, From, To, Node, Lang]),
-+%    Acc.
-+
-+%get_sm_features(Acc, From, To, Node, Lang) ->
-+%    ?MYDEBUG("get_sm_features Acc=~p From=~p To=~p Node=~p Lang=~p", [Acc, From, To, Node, Lang]),
-+%    Acc.
-+
-+%get_sm_identity(Acc, From, To, Node, Lang) ->
-+%    ?MYDEBUG("get_sm_identity Acc=~p From=~p To=~p Node=~p Lang=~p", [Acc, From, To, Node, Lang]),
-+%    Acc.
-+
-+adhoc_local_items(Acc, From, #jid{lserver = LServer, server = Server} = To,
-+                  Lang) ->
++adhoc_local_items(Acc, From,
++                  #jid{lserver = LServer, server = Server} = To, Lang) ->
++    % TODO: case acl:match_rule(LServer, ???, From) of
 +    Items = case Acc of
 +                {result, Its} -> Its;
 +                empty -> []
 +            end,
-+    Nodes = recursively_get_local_items(LServer, <<"">>, Server, Lang),
++    Nodes = recursively_get_local_items(LServer,
++                                        <<"">>, Server, Lang),
 +    Nodes1 = lists:filter(
-+               fun(N) ->
-+                        Nd = fxml:get_tag_attr_s("node", N),
++               fun(#disco_item{node = Nd}) ->
 +                        F = get_local_features([], From, To, Nd, Lang),
 +                        case F of
-+                            {result, [?NS_COMMANDS]} ->
-+                                true;
-+                            _ ->
-+                                false
++                            {result, [?NS_COMMANDS]} -> true;
++                            _ -> false
 +                        end
 +               end, Nodes),
 +    {result, Items ++ Nodes1}.
 +
-+recursively_get_local_items(_LServer, <<"mod_logdb_users">>, _Server, _Lang) ->
++recursively_get_local_items(_LServer,
++                            <<"mod_logdb_users">>, _Server, _Lang) ->
 +    [];
-+recursively_get_local_items(LServer, Node, Server, Lang) ->
-+    LNode = str:tokens(Node, <<"/">>),
-+    Items = case get_local_items(LServer, LNode, Server, Lang) of
-+                {result, Res} ->
-+                    Res;
-+                {error, _Error} ->
-+                    []
++recursively_get_local_items(LServer,
++                            Node, Server, Lang) ->
++    LNode = tokenize(Node),
++    Items = case get_local_items(LServer, LNode,
++                                 Server, Lang) of
++                {result, Res} -> Res;
++                {error, _Error} -> []
 +            end,
 +    Nodes = lists:flatten(
 +      lists:map(
-+        fun(N) ->
-+                S = fxml:get_tag_attr_s("jid", N),
-+                Nd = fxml:get_tag_attr_s("node", N),
-+                if (S /= Server) or (Nd == "") ->
++        fun(#disco_item{jid = #jid{server = S}, node = Nd} = Item) ->
++                if (S /= Server) or (Nd == <<"">>) ->
 +                    [];
 +                true ->
-+                    [N, recursively_get_local_items(
-+                          LServer, Nd, Server, Lang)]
++                    [Item, recursively_get_local_items(
++                            LServer, Nd, Server, Lang)]
 +                end
 +        end, Items)),
 +    Nodes.
@@ -1628,19 +1521,21 @@ index 00000000..79e2d3d3
 +-define(COMMANDS_RESULT(Allow, From, To, Request),
 +    case Allow of
 +        deny ->
-+            {error, ?ERR_FORBIDDEN};
++            {error, xmpp:err_forbidden(<<"Denied by ACL">>, Lang)};
 +        allow ->
 +            adhoc_local_commands(From, To, Request)
 +    end).
 +
 +adhoc_local_commands(Acc, From, #jid{lserver = LServer} = To,
-+                     #adhoc_request{node = Node} = Request) ->
-+    LNode = str:tokens(Node, <<"/">>),
++                     #adhoc_command{node = Node, lang = Lang} = Request) ->
++    LNode = tokenize(Node),
 +    AllowUser = acl:match_rule(LServer, mod_logdb, From),
 +    AllowAdmin = acl:match_rule(LServer, mod_logdb_admin, From),
 +    case LNode of
 +         [<<"mod_logdb">>] when AllowUser == allow; AllowAdmin == allow ->
 +             ?COMMANDS_RESULT(allow, From, To, Request);
++         [<<"mod_logdb_users">>, <<$@, _/binary>>] when AllowAdmin == allow ->
++             Acc;
 +         [<<"mod_logdb_users">>, _User] when AllowAdmin == allow ->
 +             ?COMMANDS_RESULT(allow, From, To, Request);
 +         [<<"mod_logdb_settings">>] when AllowAdmin == allow ->
@@ -1650,110 +1545,90 @@ index 00000000..79e2d3d3
 +    end.
 +
 +adhoc_local_commands(From, #jid{lserver = LServer} = _To,
-+                     #adhoc_request{lang = Lang,
++                     #adhoc_command{lang = Lang,
 +                                    node = Node,
-+                                    sessionid = SessionID,
++                                    sid = SessionID,
 +                                    action = Action,
 +                                    xdata = XData} = Request) ->
-+    LNode = str:tokens(Node, <<"/">>),
++    LNode = tokenize(Node),
 +    %% 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">> ->
++    ActionIsExecute = Action == execute orelse Action == complete,
++    if Action == cancel ->
 +            %% User cancels request
-+            adhoc:produce_response(
-+              Request,
-+              #adhoc_response{status = canceled});
-+       XData == false, ActionIsExecute ->
++            #adhoc_command{status = canceled, lang = Lang,
++                           node = Node, sid = SessionID};
++       XData == undefined, ActionIsExecute ->
 +            %% User requests form
-+            case get_form(LServer, LNode, From, Lang) of
++            case get_form(LServer, LNode, Lang) of
 +                {result, Form} ->
-+                    adhoc:produce_response(
++                    xmpp_util:make_adhoc_response(
 +                      Request,
-+                      #adhoc_response{status = executing,
-+                                      elements = Form});
++                      #adhoc_command{status = executing,
++                                     xdata = Form});
 +                {error, Error} ->
 +                    {error, Error}
 +            end;
-+       XData /= false, ActionIsExecute ->
++       XData /= undefined, ActionIsExecute ->
 +            %% User returns form.
-+            case jlib:parse_xdata_submit(XData) of
-+                invalid ->
-+                    {error, ?ERR_BAD_REQUEST};
-+                Fields ->
-+                    case catch set_form(From, LServer, LNode, Lang, Fields) of
-+                        {result, _Res} ->
-+                            adhoc:produce_response(
-+                              #adhoc_response{lang = Lang,
-+                                              node = Node,
-+                                              sessionid = SessionID,
-+                                              status = completed});
-+                        {'EXIT', _} -> {error, ?ERR_BAD_REQUEST};
-+                        {error, Error} -> {error, Error}
-+                    end
++            case catch set_form(From, LServer, LNode, Lang, XData) of
++                {result, Res} ->
++                    xmpp_util:make_adhoc_response(
++                      Request,
++                      #adhoc_command{xdata = Res, status = completed});
++                {'EXIT', _} -> {error, xmpp:err_bad_request()};
++                {error, Error} -> {error, Error}
 +            end;
 +       true ->
-+            {error, ?ERR_BAD_REQUEST}
++            {error, xmpp:err_bad_request(<<"Unexpected action">>, Lang)}
 +    end.
 +
-+-define(LISTLINE(Label, Value),
-+                 #xmlel{name = <<"option">>,
-+                        attrs = [{<<"label">>, ?T(Lang, Label)}],
-+                        children = [#xmlel{name = <<"value">>, attrs = [],
-+                                           children = [{xmlcdata, Value}]
-+                                   }]}).
-+-define(DEFVAL(Value), #xmlel{name = <<"value">>, attrs = [],
-+                              children = [{xmlcdata, Value}]}).
++-define(TVFIELD(Type, Var, Val),
++    #xdata_field{type = Type, var = Var, values = [Val]}).
++
++-define(HFIELD(),
++    ?TVFIELD(hidden, <<"FORM_TYPE">>, (?NS_ADMIN))).
 +
 +get_user_form(LUser, LServer, Lang) ->
-+    %From = jlib:jid_to_string(jlib:jid_remove_resource(Jid)),
++    ?MYDEBUG("get_user_form ~p ~p", [LUser, LServer]),
++    %From = jid:encode(jid:remove_resource(Jid)),
 +    #user_settings{dolog_default=DLD,
 +                   dolog_list=DLL,
 +                   donotlog_list=DNLL} = get_user_settings(LUser, LServer),
-+    {result,
-+     [#xmlel{name = <<"x">>,
-+             attrs = [{<<"xmlns">>, ?NS_XDATA}],
-+             children = [
-+                #xmlel{name = <<"title">>, attrs = [],
-+                   children =
-+                    [{xmlcdata,
-+                      ?T(Lang, <<"Messages logging engine settings">>)}]},
-+                #xmlel{name = <<"instructions">>, attrs = [],
-+                   children =
-+                    [{xmlcdata,
-+                      << (?T(Lang, <<"Set logging preferences">>))/binary, (iolist_to_binary(": "))/binary,
-+                          LUser/binary, "@", LServer/binary >> }]},
-+                #xmlel{name = <<"field">>,
-+                       attrs = [{<<"type">>, <<"list-single">>},
-+                                {<<"label">>, ?T(Lang, <<"Default">>)},
-+                                {<<"var">>, <<"dolog_default">>}],
-+                       children =
-+                        [?DEFVAL(jlib:atom_to_binary(DLD)),
-+                         ?LISTLINE(<<"Log Messages">>, <<"true">>),
-+                         ?LISTLINE(<<"Do Not Log Messages">>, <<"false">>)
-+                        ]},
-+                #xmlel{name = <<"field">>,
-+                       attrs = [{<<"type">>, <<"text-multi">>},
-+                                {<<"label">>, ?T(Lang, <<"Log Messages">>)},
-+                                {<<"var">>, <<"dolog_list">>}],
-+                       children = [#xmlel{name = <<"value">>, attrs = [],
-+                                          children = [{xmlcdata, iolist_to_binary(list_to_string(DLL))}]}
-+                                  ]
-+                      },
-+                #xmlel{name = <<"field">>,
-+                       attrs = [{<<"type">>, <<"text-multi">>},
-+                                {<<"label">>, ?T(Lang, <<"Do Not Log Messages">>)},
-+                                {<<"var">>, <<"donotlog_list">>}],
-+                       children = [#xmlel{name = <<"value">>, attrs = [],
-+                                          children = [{xmlcdata, iolist_to_binary(list_to_string(DNLL))}]}
-+                                  ]
-+                      }
-+             ]}]}.
++    Fs = [
++          #xdata_field{
++             type = 'list-single',
++             label = ?T(Lang, <<"Default">>),
++             var = <<"dolog_default">>,
++             values = [misc:atom_to_binary(DLD)],
++             options = [#xdata_option{label = ?T(Lang, <<"Log Messages">>),
++                                      value = <<"true">>},
++                        #xdata_option{label = ?T(Lang, <<"Do Not Log Messages">>),
++                                      value = <<"false">>}]},
++          #xdata_field{
++             type = 'text-multi',
++             label = ?T(Lang, <<"Log Messages">>),
++             var = <<"dolog_list">>,
++             values = DLL},
++          #xdata_field{
++             type = 'text-multi',
++             label = ?T(Lang, <<"Do Not Log Messages">>),
++             var = <<"donotlog_list">>,
++             values = DNLL}
++         ],
++    {result, #xdata{
++                title = ?T(Lang, <<"Messages logging engine settings">>),
++                type = form,
++                instructions = [<< (?T(Lang, <<"Set logging preferences">>))/binary,
++                                               (iolist_to_binary(": "))/binary,
++                                               LUser/binary, "@", LServer/binary >>],
++                fields = [?HFIELD()|
++                          Fs]}}.
 +
 +get_settings_form(Host, Lang) ->
++    ?MYDEBUG("get_settings_form ~p ~p", [Host, Lang]),
 +    #state{dbmod=_DBMod,
 +           dbs=_DBs,
 +           dolog_default=DLD,
@@ -1763,108 +1638,73 @@ index 00000000..79e2d3d3
 +           drop_messages_on_user_removal=MRemoval,
 +           poll_users_settings=PollTime} = mod_logdb:get_module_settings(Host),
 +
-+    %Backends = lists:map(fun({Backend, _Opts}) ->
-+    %                         ?LISTLINE(jlib:atom_to_binary(Backend), jlib:atom_to_binary(Backend))
-+    %                     end, DBs),
-+    %DB = iolist_to_binary(lists:sublist(atom_to_list(DBMod), length(atom_to_list(?MODULE)) + 2, length(atom_to_list(DBMod)))),
-+    %DBsL = lists:append([?DEFVAL(DB)], Backends),
-+
 +    PurgeDays =
 +       case PurgeDaysT of
 +            never -> <<"never">>;
 +            Num when is_integer(Num) -> integer_to_binary(Num);
 +            _ -> <<"unknown">>
 +       end,
-+    {result,
-+     [#xmlel{name = <<"x">>,
-+             attrs = [{<<"xmlns">>, ?NS_XDATA}],
-+             children = [#xmlel{name = <<"title">>, attrs = [],
-+                                children =
-+                                 [{xmlcdata,
-+                                   <<(?T(Lang, <<"Messages logging engine settings">>))/binary,
-+                                     (iolist_to_binary(" (run-time)"))/binary >>}]},
-+                         #xmlel{name = <<"instructions">>, attrs = [],
-+                                children =
-+                                 [{xmlcdata, ?T(Lang, <<"Set run-time settings">>)}]},
-+%                         #xmlel{name = <<"field">>,
-+%                                attrs = [{<<"type">>, <<"list-single">>},
-+%                                         {<<"label">>, ?T(Lang, <<"Backend">>)},
-+%                                         {<<"var">>, <<"backend">>}],
-+%                                children = DBsL},
-+%                         #xmlel{name = <<"field">>,
-+%                                attrs = [{<<"type">>, <<"text-multi">>},
-+%                                         {<<"label">>, ?T(Lang, <<"dbs">>)},
-+%                                         {<<"var">>, <<"dbs">>}],
-+%                                children = [#xmlel{name = <<"value">>, attrs = [],
-+%                                                   children = [{xmlcdata, iolist_to_binary(lists:flatten(io_lib:format("~p.",[DBs])))}]}
-+%                                           ]
-+%                               },
-+                         #xmlel{name = <<"field">>,
-+                                attrs = [{<<"type">>, <<"list-single">>},
-+                                         {<<"label">>, ?T(Lang, <<"Default">>)},
-+                                         {<<"var">>, <<"dolog_default">>}],
-+                                children =
-+                                 [?DEFVAL(jlib:atom_to_binary(DLD)),
-+                                  ?LISTLINE(<<"Log Messages">>, <<"true">>),
-+                                  ?LISTLINE(<<"Do Not Log Messages">>, <<"false">>)
-+                                 ]},
-+                         #xmlel{name = <<"field">>,
-+                                attrs = [{<<"type">>, <<"list-single">>},
-+                                         {<<"label">>, ?T(Lang, <<"Drop messages on user removal">>)},
-+                                         {<<"var">>, <<"drop_messages_on_user_removal">>}],
-+                                children =
-+                                 [?DEFVAL(jlib:atom_to_binary(MRemoval)),
-+                                  ?LISTLINE(<<"Drop">>, <<"true">>),
-+                                  ?LISTLINE(<<"Do not drop">>, <<"false">>)
-+                                 ]},
-+                         #xmlel{name = <<"field">>,
-+                                attrs = [{<<"type">>, <<"list-single">>},
-+                                         {<<"label">>, ?T(Lang, <<"Groupchat messages logging">>)},
-+                                         {<<"var">>, <<"groupchat">>}],
-+                                children =
-+                                 [?DEFVAL(jlib:atom_to_binary(GroupChat)),
-+                                  ?LISTLINE(<<"all">>, <<"all">>),
-+                                  ?LISTLINE(<<"none">>, <<"none">>),
-+                                  ?LISTLINE(<<"send">>, <<"send">>),
-+                                  ?LISTLINE(<<"half">>, <<"half">>)
-+                                 ]},
-+                         #xmlel{name = <<"field">>,
-+                                attrs = [{<<"type">>, <<"text-multi">>},
-+                                         {<<"label">>, ?T(Lang, <<"Jids/Domains to ignore">>)},
-+                                         {<<"var">>, <<"ignore_list">>}],
-+                                children = [#xmlel{name = <<"value">>, attrs = [],
-+                                                   children = [{xmlcdata, iolist_to_binary(list_to_string(IgnoreJids))}]}
-+                                           ]
-+                               },
-+                         #xmlel{name = <<"field">>,
-+                                attrs = [{<<"type">>, <<"text-single">>},
-+                                         {<<"label">>, ?T(Lang, <<"Purge messages older than (days)">>)},
-+                                         {<<"var">>, <<"purge_older_days">>}],
-+                                children = [#xmlel{name = <<"value">>, attrs = [],
-+                                                   children = [{xmlcdata, iolist_to_binary(PurgeDays)}]}
-+                                           ]
-+                               },
-+                         #xmlel{name = <<"field">>,
-+                                attrs = [{<<"type">>, <<"text-single">>},
-+                                         {<<"label">>, ?T(Lang, <<"Poll users settings (seconds)">>)},
-+                                         {<<"var">>, <<"poll_users_settings">>}],
-+                                children = [#xmlel{name = <<"value">>, attrs = [],
-+                                                   children = [{xmlcdata, integer_to_binary(PollTime)}]}
-+                                           ]
-+                               }
-+                        ]}
-+     ]}.
-+
-+%get_form(_Host, [<<"mod_logdb">>], #jid{luser = LUser, lserver = LServer} = _Jid, Lang) ->
-+%    get_user_form(LUser, LServer, Lang);
-+get_form(_Host, [<<"mod_logdb_users">>, User], _JidFrom, Lang) ->
-+    #jid{luser=LUser, lserver=LServer} = jlib:string_to_jid(User),
++    Fs = [
++          #xdata_field{
++             type = 'list-single',
++             label = ?T(Lang, <<"Default">>),
++             var = <<"dolog_default">>,
++             values = [misc:atom_to_binary(DLD)],
++             options = [#xdata_option{label = ?T(Lang, <<"Log Messages">>),
++                                      value = <<"true">>},
++                        #xdata_option{label = ?T(Lang, <<"Do Not Log Messages">>),
++                                      value = <<"false">>}]},
++          #xdata_field{
++             type = 'list-single',
++             label = ?T(Lang, <<"Drop messages on user removal">>),
++             var = <<"drop_messages_on_user_removal">>,
++             values = [misc:atom_to_binary(MRemoval)],
++             options = [#xdata_option{label = ?T(Lang, <<"Drop">>),
++                                      value = <<"true">>},
++                        #xdata_option{label = ?T(Lang, <<"Do not drop">>),
++                                      value = <<"false">>}]},
++          #xdata_field{
++             type = 'list-single',
++             label = ?T(Lang, <<"Groupchat messages logging">>),
++             var = <<"groupchat">>,
++             values = [misc:atom_to_binary(GroupChat)],
++             options = [#xdata_option{label = ?T(Lang, <<"all">>),
++                                      value = <<"all">>},
++                        #xdata_option{label = ?T(Lang, <<"none">>),
++                                      value = <<"none">>},
++                        #xdata_option{label = ?T(Lang, <<"send">>),
++                                      value = <<"send">>}]},
++          #xdata_field{
++             type = 'text-multi',
++             label = ?T(Lang, <<"Jids/Domains to ignore">>),
++             var = <<"ignore_list">>,
++             values = IgnoreJids},
++          #xdata_field{
++             type = 'text-single',
++             label = ?T(Lang, <<"Purge messages older than (days)">>),
++             var = <<"purge_older_days">>,
++             values = [iolist_to_binary(PurgeDays)]},
++          #xdata_field{
++             type = 'text-single',
++             label = ?T(Lang, <<"Poll users settings (seconds)">>),
++             var = <<"poll_users_settings">>,
++             values = [integer_to_binary(PollTime)]}
++         ],
++    {result, #xdata{
++                title = ?T(Lang, <<"Messages logging engine settings (run-time)">>),
++                instructions = [?T(Lang, <<"Set run-time settings">>)],
++                type = form,
++                fields = [?HFIELD()|
++                          Fs]}}.
++
++get_form(_Host, [<<"mod_logdb_users">>, User], Lang) ->
++    #jid{luser=LUser, lserver=LServer} = jid:decode(User),
 +    get_user_form(LUser, LServer, Lang);
-+get_form(Host, [<<"mod_logdb_settings">>], _JidFrom, Lang) ->
++get_form(Host, [<<"mod_logdb_settings">>], Lang) ->
 +    get_settings_form(Host, Lang);
-+get_form(_Host, Command, _, _Lang) ->
++get_form(_Host, Command, _Lang) ->
 +    ?MYDEBUG("asked for form ~p", [Command]),
-+    {error, ?ERR_SERVICE_UNAVAILABLE}.
++    {error, xmpp:err_service_unavailable()}.
 +
 +check_log_list([]) ->
 +    ok;
@@ -1876,11 +1716,9 @@ index 00000000..79e2d3d3
 +         {_, _} -> ok
 +    end,
 +    % this check for Head to be valid jid
-+    case jlib:string_to_jid(Head) of
-+         error ->
-+            throw(error);
-+         _ ->
-+            check_log_list(Tail)
++    case catch jid:decode(Head) of
++         {'EXIT', _Reason} -> throw(error);
++         _ -> check_log_list(Tail)
 +    end.
 +
 +check_ignore_list([]) ->
@@ -1894,112 +1732,81 @@ index 00000000..79e2d3d3
 +         {_, _} -> ok;
 +         nomatch -> throw(error)
 +    end,
++    Jid2Test = case Head of
++                    << $@, _Rest/binary >> ->  << "a", Head/binary >>;
++                    Jid -> Jid
++               end,
 +    % this check for Head to be valid jid
-+    case jlib:string_to_jid(Head) of
-+         error ->
-+            % this check for Head to be valid domain "@domain.org"
-+            case Head of
-+                 << $@, Rest/binary >> ->
-+                    % TODO: this allows spaces and special characters in Head. May be change to nodeprep?
-+                    case jlib:nameprep(Rest) of
-+                         error -> throw(error);
-+                         _ -> check_ignore_list(Tail)
-+                    end;
-+                 _ -> throw(error)
-+            end;
-+         _ ->
-+            check_ignore_list(Tail)
++    case catch jid:decode(Jid2Test) of
++         {'EXIT', _Reason} -> throw(error);
++         _ -> check_ignore_list(Tail)
 +    end.
 +
++get_value(Field, XData) -> hd(get_values(Field, XData)).
++
++get_values(Field, XData) ->
++    xmpp_util:get_xdata_values(Field, XData).
++
 +parse_users_settings(XData) ->
-+    DLD = case lists:keysearch(<<"dolog_default">>, 1, XData) of
-+               {value, {_, [String]}} when String == <<"true">>; String == <<"false">> ->
-+                 list_to_bool(String);
-+               _ ->
-+                 throw(bad_request)
-+          end,
-+    DLL = case lists:keysearch(<<"dolog_list">>, 1, XData) of
-+               false ->
-+                 throw(bad_request);
-+               {value, {_, [[]]}} ->
-+                 [];
-+               {value, {_, List1}} ->
-+                 case catch check_log_list(List1) of
-+                      error ->
-+                         throw(bad_request);
-+                      ok ->
-+                         List1
-+                 end
-+          end,
-+    DNLL = case lists:keysearch(<<"donotlog_list">>, 1, XData) of
-+               false ->
-+                 throw(bad_request);
-+               {value, {_, [[]]}} ->
-+                 [];
-+               {value, {_, List2}} ->
-+                 case catch check_log_list(List2) of
-+                      error ->
-+                         throw(bad_request);
-+                      ok ->
-+                         List2
-+                 end
++    DLD = case get_value(<<"dolog_default">>, XData) of
++               ValueDLD when ValueDLD == <<"true">>;
++                             ValueDLD == <<"false">> ->
++                  list_to_bool(ValueDLD);
++              _ -> throw(bad_request)
 +          end,
++
++    ListDLL = get_values(<<"dolog_list">>, XData),
++    DLL = case catch check_log_list(ListDLL) of
++                  ok -> ListDLL;
++                  error -> throw(bad_request)
++             end,
++
++    ListDNLL = get_values(<<"donotlog_list">>, XData),
++    DNLL = case catch check_log_list(ListDNLL) of
++                  ok -> ListDNLL;
++                  error -> throw(bad_request)
++             end,
++
 +    #user_settings{dolog_default=DLD,
 +                   dolog_list=DLL,
 +                   donotlog_list=DNLL}.
 +
 +parse_module_settings(XData) ->
-+    DLD = case lists:keysearch(<<"dolog_default">>, 1, XData) of
-+               {value, {_, [Str1]}} when Str1 == <<"true">>; Str1 == <<"false">> ->
-+                 list_to_bool(Str1);
-+               _ ->
-+                 throw(bad_request)
-+          end,
-+    MRemoval = case lists:keysearch(<<"drop_messages_on_user_removal">>, 1, XData) of
-+               {value, {_, [Str5]}} when Str5 == <<"true">>; Str5 == <<"false">> ->
-+                 list_to_bool(Str5);
-+               _ ->
-+                 throw(bad_request)
++    DLD = case get_value(<<"dolog_default">>, XData) of
++               ValueDLD when ValueDLD == <<"true">>;
++                             ValueDLD == <<"false">> ->
++                   list_to_bool(ValueDLD);
++               _ -> throw(bad_request)
 +          end,
-+    GroupChat = case lists:keysearch(<<"groupchat">>, 1, XData) of
-+                     {value, {_, [Str2]}} when Str2 == <<"none">>;
-+                                               Str2 == <<"all">>;
-+                                               Str2 == <<"send">>;
-+                                               Str2 == <<"half">> ->
-+                       jlib:binary_to_atom(Str2);
-+                     _ ->
-+                       throw(bad_request)
++    MRemoval = case get_value(<<"drop_messages_on_user_removal">>, XData) of
++                    ValueMRemoval when ValueMRemoval == <<"true">>;
++                                       ValueMRemoval == <<"false">> ->
++                        list_to_bool(ValueMRemoval);
++                    _ -> throw(bad_request)
++               end,
++    GroupChat = case get_value(<<"groupchat">>, XData) of
++                     ValueGroupChat when ValueGroupChat == <<"none">>;
++                                         ValueGroupChat == <<"all">>;
++                                         ValueGroupChat == <<"send">> ->
++                         misc:binary_to_atom(ValueGroupChat);
++                     _ -> throw(bad_request)
 +                end,
-+    Ignore = case lists:keysearch(<<"ignore_list">>, 1, XData) of
-+                  {value, {_, List}} ->
-+                    case catch check_ignore_list(List) of
-+                         ok ->
-+                            List;
-+                         error ->
-+                            throw(bad_request)
-+                    end;
-+                  _ ->
-+                    throw(bad_request)
++    ListIgnore = get_values(<<"ignore_list">>, XData),
++    Ignore = case catch check_ignore_list(ListIgnore) of
++                  ok -> ListIgnore;
++                  error -> throw(bad_request)
 +             end,
-+    Purge = case lists:keysearch(<<"purge_older_days">>, 1, XData) of
-+                 {value, {_, [<<"never">>]}} ->
-+                   never;
-+                 {value, {_, [Str3]}} ->
-+                   case catch binary_to_integer(Str3) of
-+                        {'EXIT', {badarg, _}} -> throw(bad_request);
-+                        Int1 -> Int1
-+                   end;
-+                 _ ->
-+                   throw(bad_request)
++    Purge = case get_value(<<"purge_older_days">>, XData) of
++                 <<"never">> -> never;
++                 ValuePurge ->
++                    case catch binary_to_integer(ValuePurge) of
++                         IntValuePurge when is_integer(IntValuePurge) -> IntValuePurge;
++                         _ -> throw(bad_request)
++                    end
 +            end,
-+    Poll = case lists:keysearch(<<"poll_users_settings">>, 1, XData) of
-+                {value, {_, [Str4]}} ->
-+                  case catch binary_to_integer(Str4) of
-+                       {'EXIT', {badarg, _}} -> throw(bad_request);
-+                       Int2 -> Int2
-+                  end;
-+                _ ->
-+                  throw(bad_request)
++    Poll = case catch binary_to_integer(get_value(<<"poll_users_settings">>, XData)) of
++                IntValuePoll when is_integer(IntValuePoll) -> IntValuePoll;
++                _ -> throw(bad_request)
 +           end,
 +    #state{dolog_default=DLD,
 +           groupchat=GroupChat,
@@ -2008,65 +1815,47 @@ index 00000000..79e2d3d3
 +           drop_messages_on_user_removal=MRemoval,
 +           poll_users_settings=Poll}.
 +
-+set_form(From, _Host, [<<"mod_logdb">>], _Lang, XData) ->
-+    #jid{luser=LUser, lserver=LServer} = From,
++set_form(_From, _Host, [<<"mod_logdb_users">>, User], Lang, XData) ->
++    #jid{luser=LUser, lserver=LServer} = jid:decode(User),
++    Txt = "Parse user settings failed",
 +    case catch parse_users_settings(XData) of
 +         bad_request ->
-+            {error, ?ERR_BAD_REQUEST};
-+         {'EXIT', Reason} ->
-+            ?ERROR_MSG("Failed to set form ~p", [Reason]),
-+            {error, ?ERR_BAD_REQUEST};
-+         UserSettings ->
-+            case mod_logdb:set_user_settings(LUser, LServer, UserSettings) of
-+                 ok ->
-+                    {result, []};
-+                 error ->
-+                    {error, ?ERR_INTERNAL_SERVER_ERROR}
-+            end
-+    end;
-+set_form(_From, _Host, [<<"mod_logdb_users">>, User], _Lang, XData) ->
-+    #jid{luser=LUser, lserver=LServer} = jlib:string_to_jid(User),
-+    case catch parse_users_settings(XData) of
-+         bad_request -> {error, ?ERR_BAD_REQUEST};
++            ?ERROR_MSG("Failed to set user form: bad_request", []),
++            {error, xmpp:err_bad_request(Txt, Lang)};
 +         {'EXIT', Reason} ->
-+            ?ERROR_MSG("Failed to set form ~p", [Reason]),
-+            {error, ?ERR_BAD_REQUEST};
++            ?ERROR_MSG("Failed to set user form ~p", [Reason]),
++            {error, xmpp:err_bad_request(Txt, Lang)};
 +         UserSettings ->
 +            case mod_logdb:set_user_settings(LUser, LServer, UserSettings) of
 +                 ok ->
-+                    {result, []};
++                    {result, undefined};
 +                 error ->
-+                    {error, ?ERR_INTERNAL_SERVER_ERROR}
++                    {error, xmpp:err_internal_server_error()}
 +            end
 +    end;
-+set_form(_From, Host, [<<"mod_logdb_settings">>], _Lang, XData) ->
++set_form(_From, Host, [<<"mod_logdb_settings">>], Lang, XData) ->
++    Txt = "Parse module settings failed",
 +    case catch parse_module_settings(XData) of
-+         bad_request -> {error, ?ERR_BAD_REQUEST};
++         bad_request ->
++            ?ERROR_MSG("Failed to set settings form: bad_request", []),
++            {error, xmpp:err_bad_request(Txt, Lang)};
 +         {'EXIT', Reason} ->
-+            ?ERROR_MSG("Failed to set form ~p", [Reason]),
-+            {error, ?ERR_BAD_REQUEST};
++            ?ERROR_MSG("Failed to set settings form ~p", [Reason]),
++            {error, xmpp:err_bad_request(Txt, Lang)};
 +         Settings ->
 +            case mod_logdb:set_module_settings(Host, Settings) of
 +                 ok ->
-+                    {result, []};
++                    {result, undefined};
 +                 error ->
-+                    {error, ?ERR_INTERNAL_SERVER_ERROR}
++                    {error, xmpp:err_internal_server_error()}
 +            end
 +    end;
 +set_form(From, _Host, Node, _Lang, XData) ->
-+    User = jlib:jid_to_string(jlib:jid_remove_resource(From)),
++    User = jid:encode(jid:remove_resource(From)),
 +    ?MYDEBUG("set form for ~p at ~p XData=~p", [User, Node, XData]),
-+    {error, ?ERR_SERVICE_UNAVAILABLE}.
-+
-+%adhoc_sm_items(Acc, From, To, Request) ->
-+%    ?MYDEBUG("adhoc_sm_items Acc=~p From=~p To=~p Request=~p", [Acc, From, To, Request]),
-+%    Acc.
-+%
-+%adhoc_sm_commands(Acc, From, To, Request) ->
-+%    ?MYDEBUG("adhoc_sm_commands Acc=~p From=~p To=~p Request=~p", [Acc, From, To, Request]),
-+%    Acc.
++    {error, xmpp:err_service_unavailable()}.
 +
-+get_all_vh_users(Host, Server, Lang) ->
++get_all_vh_users(Host, Server) ->
 +    case catch ejabberd_auth:get_vh_registered_users(Host) of
 +        {'EXIT', _Reason} ->
 +            [];
@@ -2075,19 +1864,19 @@ index 00000000..79e2d3d3
 +            case length(SUsers) of
 +                N when N =< 100 ->
 +                    lists:map(fun({S, U}) ->
-+                                      ?NODE(<< U/binary, "@", S/binary >>,
-+                                            << (iolist_to_binary("mod_logdb_users/"))/binary, U/binary, "@", S/binary >>)
-+                              end,
-+                              SUsers);
++                                  #disco_item{jid = jid:make(Server),
++                                              node = <<"mod_logdb_users/", U/binary, $@, S/binary>>,
++                                              name = << U/binary, "@", S/binary >>}
++                              end, SUsers);
 +                N ->
-+                    NParts = trunc(math:sqrt(N * 0.618)) + 1,
++                    NParts = trunc(math:sqrt(N * 6.17999999999999993783e-1)) + 1,
 +                    M = trunc(N / NParts) + 1,
 +                    lists:map(fun(K) ->
 +                                      L = K + M - 1,
 +                                      Node = <<"@",
-+                                               (iolist_to_binary(integer_to_list(K)))/binary,
++                                               (integer_to_binary(K))/binary,
 +                                               "-",
-+                                               (iolist_to_binary(integer_to_list(L)))/binary
++                                               (integer_to_binary(L))/binary
 +                                             >>,
 +                                      {FS, FU} = lists:nth(K, SUsers),
 +                                      {LS, LU} =
@@ -2098,7 +1887,9 @@ index 00000000..79e2d3d3
 +                                          <<FU/binary, "@", FS/binary,
 +                                           " -- ",
 +                                           LU/binary, "@", LS/binary>>,
-+                                      ?NODE(Name, << (iolist_to_binary("mod_logdb_users/"))/binary, Node/binary >>)
++                                      #disco_item{jid = jid:make(Host),
++                                                  node = <<"mod_logdb_users/", Node/binary>>,
++                                                  name = Name}
 +                              end, lists:seq(1, N, M))
 +            end
 +    end.
@@ -2189,7 +1980,7 @@ index 00000000..79e2d3d3
 +         {ok, Dates} ->
 +              Fun = fun({Date, Count}) ->
 +                         DateBin = iolist_to_binary(Date),
-+                         ID = jlib:encode_base64( << Server/binary, DateBin/binary >> ),
++                         ID = misc:encode_base64( << Server/binary, DateBin/binary >> ),
 +                         ?XE(<<"tr">>,
 +                          [?XAE(<<"td">>, [{<<"class">>, <<"valign">>}],
 +                            [?INPUT(<<"checkbox">>, <<"selected">>, ID)]),
@@ -2242,7 +2033,7 @@ index 00000000..79e2d3d3
 +                   end,
 +             Fun = fun({User, Count}) ->
 +                         UserBin = iolist_to_binary(User),
-+                         ID = jlib:encode_base64( << UserBin/binary, Server/binary >> ),
++                         ID = misc:encode_base64( << UserBin/binary, Server/binary >> ),
 +                         ?XE(<<"tr">>,
 +                          [?XAE(<<"td">>, [{<<"class">>, <<"valign">>}],
 +                            [?INPUT(<<"checkbox">>, <<"selected">>, ID)]),
@@ -2273,7 +2064,7 @@ index 00000000..79e2d3d3
 +   end.
 +
 +user_messages_stats(User, Server, Query, Lang) ->
-+    Jid = jlib:jid_to_string({User, Server, ""}),
++    Jid = jid:encode({User, Server, ""}),
 +
 +    Res = case catch user_messages_parse_query(User, Server, Query) of
 +               {'EXIT', Reason} ->
@@ -2297,7 +2088,7 @@ index 00000000..79e2d3d3
 +        {ok, Dates} ->
 +            Fun = fun({Date, Count}) ->
 +                      DateBin = iolist_to_binary(Date),
-+                      ID = jlib:encode_base64( << User/binary, DateBin/binary >> ),
++                      ID = misc:encode_base64( << User/binary, DateBin/binary >> ),
 +                      ?XE(<<"tr">>,
 +                       [?XAE(<<"td">>, [{<<"class">>, <<"valign">>}],
 +                         [?INPUT(<<"checkbox">>, <<"selected">>, ID)]),
@@ -2338,7 +2129,7 @@ index 00000000..79e2d3d3
 +    end.
 +
 +user_messages_stats_at(User, Server, Query, Lang, Date) ->
-+   Jid = jlib:jid_to_string({User, Server, ""}),
++   Jid = jid:encode({User, Server, ""}),
 +
 +   {Time, Value} = timer:tc(mod_logdb, get_user_messages_at, [User, Server, Date]),
 +   ?INFO_MSG("get_user_messages_at(~p,~p,~p) elapsed ~p sec", [User, Server, Date, Time/1000000]),
@@ -2365,7 +2156,7 @@ index 00000000..79e2d3d3
 +           UR = ejabberd_hooks:run_fold(roster_get, Server, [], [{User, Server}]),
 +           UserRoster =
 +                 lists:map(fun(Item) ->
-+                              {jlib:jid_to_string(Item#roster.jid), Item#roster.name}
++                              {jid:encode(Item#roster.jid), Item#roster.name}
 +                          end, UR),
 +
 +           UniqUsers = lists:foldl(fun(#msg{peer_name=PName, peer_server=PServer}, List) ->
@@ -2380,7 +2171,7 @@ index 00000000..79e2d3d3
 +           CheckedUsers = case lists:keysearch(<<"filter">>, 1, Query) of
 +                           {value, _} ->
 +                              lists:filter(fun(UFUser) ->
-+                                                ID = jlib:encode_base64(term_to_binary(UFUser)),
++                                                ID = misc:encode_base64(term_to_binary(UFUser)),
 +                                                lists:member({<<"selected">>, ID}, Query)
 +                                           end, UniqUsers);
 +                           false -> []
@@ -2388,7 +2179,7 @@ index 00000000..79e2d3d3
 +
 +           % UniqUsers in html (noone selected -> everyone selected)
 +           Users = lists:map(fun(UHUser) ->
-+                                ID = jlib:encode_base64(term_to_binary(UHUser)),
++                                ID = misc:encode_base64(term_to_binary(UHUser)),
 +                                Input = case lists:member(UHUser, CheckedUsers) of
 +                                         true -> [?INPUTC(<<"checkbox">>, <<"selected">>, ID)];
 +                                         false when CheckedUsers == [] -> [?INPUTC(<<"checkbox">>, <<"selected">>, ID)];
@@ -2436,7 +2227,7 @@ index 00000000..79e2d3d3
 +                                   PName++"@"++PServer;
 +                              N -> N
 +                         end,
-+                      ID = jlib:encode_base64(term_to_binary(Timestamp)),
++                      ID = misc:encode_base64(term_to_binary(Timestamp)),
 +                      ?XE(<<"tr">>,
 +                       [?XE(<<"td">>, [?INPUT(<<"checkbox">>, <<"selected">>, ID)]),
 +                        ?XC(<<"td">>, iolist_to_binary(convert_timestamp(Timestamp))),
@@ -2482,17 +2273,15 @@ index 00000000..79e2d3d3
 +    end.
 diff --git a/src/mod_logdb.hrl b/src/mod_logdb.hrl
 new file mode 100644
-index 00000000..d44f0df5
+index 00000000..49791f4e
 --- /dev/null
 +++ b/src/mod_logdb.hrl
-@@ -0,0 +1,35 @@
+@@ -0,0 +1,33 @@
 +%%%----------------------------------------------------------------------
 +%%% File    : mod_logdb.hrl
-+%%% Author  : Oleg Palij (mailto,xmpp:o.palij at gmail.com)
++%%% Author  : Oleg Palij (mailto:o.palij at gmail.com)
 +%%% Purpose :
-+%%% Version : trunk
-+%%% Id      : $Id: mod_logdb.hrl 1273 2009-02-05 18:12:57Z malik $
-+%%% Url     : http://www.dp.uz.gov.ua/o.palij/mod_logdb/
++%%% Url     : https://paleg.github.io/mod_logdb/
 +%%%----------------------------------------------------------------------
 +
 +-define(logdb_debug, true).
@@ -2523,17 +2312,15 @@ index 00000000..d44f0df5
 +                          {<<"checked">>, <<"true">>}])).
 diff --git a/src/mod_logdb_mnesia.erl b/src/mod_logdb_mnesia.erl
 new file mode 100644
-index 00000000..a8ae7664
+index 00000000..ea167d88
 --- /dev/null
 +++ b/src/mod_logdb_mnesia.erl
-@@ -0,0 +1,557 @@
+@@ -0,0 +1,555 @@
 +%%%----------------------------------------------------------------------
 +%%% File    : mod_logdb_mnesia.erl
-+%%% Author  : Oleg Palij (mailto,xmpp:o.palij at gmail.com)
++%%% Author  : Oleg Palij (mailto:o.palij at gmail.com)
 +%%% Purpose : mnesia backend for mod_logdb
-+%%% Version : trunk
-+%%% Id      : $Id: mod_logdb_mnesia.erl 1273 2009-02-05 18:12:57Z malik $
-+%%% Url     : http://www.dp.uz.gov.ua/o.palij/mod_logdb/
++%%% Url     : https://paleg.github.io/mod_logdb/
 +%%%----------------------------------------------------------------------
 +
 +-module(mod_logdb_mnesia).
@@ -2900,9 +2687,9 @@ index 00000000..a8ae7664
 +                    Table = term_to_binary(ATable),
 +                    case ejabberd_regexp:run( Table, << VHost/binary, <<"$">>/binary >> ) of
 +                         match ->
-+                            case re:run(Table, "_[0-9]+-[0-9]+-[0-9]+_") of
++                            case re:run(Table, "[0-9]+-[0-9]+-[0-9]+") of
 +                                 {match, [{S, E}]} ->
-+                                     lists:append(Dates, [lists:sublist(binary_to_list(Table), S+2, E-2)]);
++                                     lists:append(Dates, [lists:sublist(binary_to_list(Table), S+1, E)]);
 +                                 nomatch ->
 +                                     Dates
 +                            end;
@@ -3084,284 +2871,17 @@ index 00000000..a8ae7664
 +               {type, bag},
 +               {attributes, record_info(fields, msg)},
 +               {record_name, msg}]).
-diff --git a/src/mod_logdb_mnesia_old.erl b/src/mod_logdb_mnesia_old.erl
-new file mode 100644
-index 00000000..e962d9a8
---- /dev/null
-+++ b/src/mod_logdb_mnesia_old.erl
-@@ -0,0 +1,259 @@
-+%%%----------------------------------------------------------------------
-+%%% File    : mod_logdb_mnesia_old.erl
-+%%% Author  : Oleg Palij (mailto,xmpp:o.palij at gmail.com)
-+%%% Purpose : mod_logmnesia backend for mod_logdb (should be used only for copy_tables functionality)
-+%%% Version : trunk
-+%%% Id      : $Id: mod_logdb_mnesia_old.erl 1273 2009-02-05 18:12:57Z malik $
-+%%% Url     : http://www.dp.uz.gov.ua/o.palij/mod_logdb/
-+%%%----------------------------------------------------------------------
-+
-+-module(mod_logdb_mnesia_old).
-+-author('o.palij at gmail.com').
-+
-+-include("ejabberd.hrl").
-+-include("jlib.hrl").
-+-include("logger.hrl").
-+
-+-behaviour(gen_logdb).
-+
-+-export([start/2, stop/1,
-+         log_message/2,
-+         rebuild_stats/1,
-+         rebuild_stats_at/2,
-+         rebuild_stats_at1/2,
-+         delete_messages_by_user_at/3, delete_all_messages_by_user_at/3, delete_messages_at/2,
-+         get_vhost_stats/1, get_vhost_stats_at/2, get_user_stats/2, get_user_messages_at/3,
-+         get_dates/1,
-+         get_users_settings/1, get_user_settings/2, set_user_settings/3,
-+         drop_user/2]).
-+
-+-record(stats, {user, server, table, count}).
-+-record(msg,   {to_user, to_server, to_resource, from_user, from_server, from_resource, id, type, subject, body, timestamp}).
-+
-+tables_prefix() -> "messages_".
-+% stats_table should not start with tables_prefix(VHost) !
-+% i.e. lists:prefix(tables_prefix(VHost), atom_to_list(stats_table())) must be /= true
-+stats_table() -> list_to_atom("messages-stats").
-+% table name as atom from Date
-+-define(ATABLE(Date), list_to_atom(tables_prefix() ++ Date)).
-+-define(LTABLE(Date), tables_prefix() ++ Date).
-+
-+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-+%
-+% gen_logdb callbacks
-+%
-+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-+start(_Opts, _VHost) ->
-+   case mnesia:system_info(is_running) of
-+        yes ->
-+          ok = create_stats_table(),
-+          {ok, ok};
-+        no ->
-+          ?ERROR_MSG("Mnesia not running", []),
-+          error;
-+        Status ->
-+          ?ERROR_MSG("Mnesia status: ~p", [Status]),
-+          error
-+   end.
-+
-+stop(_VHost) ->
-+   ok.
-+
-+log_message(_VHost, _Msg) ->
-+   error.
-+
-+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-+%
-+% gen_logdb callbacks (maintaince)
-+%
-+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-+rebuild_stats(_VHost) ->
-+     ok.
-+
-+rebuild_stats_at(VHost, Date) ->
-+    Table = ?LTABLE(Date),
-+    {Time, Value}=timer:tc(?MODULE, rebuild_stats_at1, [VHost, Table]),
-+    ?INFO_MSG("rebuild_stats_at ~p elapsed ~p sec: ~p~n", [Date, Time/1000000, Value]),
-+    Value.
-+rebuild_stats_at1(VHost, Table) ->
-+    CFun = fun(Msg, Stats) ->
-+               To = Msg#msg.to_user ++ "@" ++ Msg#msg.to_server,
-+               Stats_to = if
-+                            Msg#msg.to_server == VHost ->
-+                               case lists:keysearch(To, 1, Stats) of
-+                                    {value, {Who_to, Count_to}} ->
-+                                       lists:keyreplace(To, 1, Stats, {Who_to, Count_to + 1});
-+                                    false ->
-+                                        lists:append(Stats, [{To, 1}])
-+                               end;
-+                            true ->
-+                               Stats
-+                          end,
-+               From = Msg#msg.from_user ++ "@" ++ Msg#msg.from_server,
-+               Stats_from = if
-+                              Msg#msg.from_server == VHost  ->
-+                                 case lists:keysearch(From, 1, Stats_to) of
-+                                      {value, {Who_from, Count_from}} ->
-+                                         lists:keyreplace(From, 1, Stats_to, {Who_from, Count_from + 1});
-+                                      false ->
-+                                         lists:append(Stats_to, [{From, 1}])
-+                                 end;
-+                              true ->
-+                                 Stats_to
-+                            end,
-+               Stats_from
-+           end,
-+    DFun = fun(#stats{table=STable, server=Server} = Stat, _Acc)
-+                when STable == Table, Server == VHost ->
-+                 mnesia:delete_object(stats_table(), Stat, write);
-+              (_Stat, _Acc) -> ok
-+           end,
-+    case mnesia:transaction(fun() ->
-+                               mnesia:write_lock_table(list_to_atom(Table)),
-+                               mnesia:write_lock_table(stats_table()),
-+                               % Calc stats for VHost at Date
-+                               AStats = mnesia:foldl(CFun, [], list_to_atom(Table)),
-+                               % Delete all stats for VHost at Date
-+                               mnesia:foldl(DFun, [], stats_table()),
-+                               % Write new calc'ed stats
-+                               lists:foreach(fun({Who, Count}) ->
-+                                                 Jid = jlib:string_to_jid(Who),
-+                                                 JUser = Jid#jid.user,
-+                                                 WStat = #stats{user=JUser, server=VHost, table=Table, count=Count},
-+                                                 mnesia:write(stats_table(), WStat, write)
-+                                             end, AStats)
-+                            end) of
-+         {aborted, Reason} ->
-+              ?ERROR_MSG("Failed to rebuild_stats_at for ~p at ~p: ~p", [VHost, Table, Reason]),
-+              error;
-+         {atomic, _} ->
-+              ok
-+    end.
-+
-+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-+%
-+% gen_logdb callbacks (delete)
-+%
-+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-+delete_messages_by_user_at(_VHost, _Msgs, _Date) ->
-+    error.
-+
-+delete_all_messages_by_user_at(_User, _VHost, _Date) ->
-+    error.
-+
-+delete_messages_at(VHost, Date) ->
-+   Table = list_to_atom(tables_prefix() ++ Date),
-+
-+   DFun = fun(#msg{to_server=To_server, from_server=From_server}=Msg, _Acc)
-+                when To_server == VHost; From_server == VHost ->
-+                   mnesia:delete_object(Table, Msg, write);
-+             (_Msg, _Acc) -> ok
-+          end,
-+
-+   case mnesia:transaction(fun() ->
-+                            mnesia:foldl(DFun, [], Table)
-+                           end) of
-+        {aborted, Reason} ->
-+            ?ERROR_MSG("Failed to delete_messages_at for ~p at ~p: ~p", [VHost, Date, Reason]),
-+            error;
-+        {atomic, _} ->
-+            ok
-+   end.
-+
-+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-+%
-+% gen_logdb callbacks (get)
-+%
-+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-+get_vhost_stats(_VHost) ->
-+    {error, "does not emplemented"}.
-+
-+get_vhost_stats_at(VHost, Date) ->
-+    Fun = fun() ->
-+             Pat = #stats{user='$1', server=VHost, table=tables_prefix()++Date, count = '$2'},
-+             mnesia:select(stats_table(), [{Pat, [], [['$1', '$2']]}])
-+          end,
-+    case mnesia:transaction(Fun) of
-+         {atomic, Result} ->
-+                   RFun = fun([User, Count]) ->
-+                             {User, Count}
-+                          end,
-+                   {ok, lists:reverse(lists:keysort(2, lists:map(RFun, Result)))};
-+         {aborted, Reason} -> {error, Reason}
-+    end.
-+
-+get_user_stats(_User, _VHost) ->
-+    {error, "does not emplemented"}.
-+
-+get_user_messages_at(User, VHost, Date) ->
-+    Table_name = tables_prefix() ++ Date,
-+    case mnesia:transaction(fun() ->
-+                               Pat_to = #msg{to_user=User, to_server=VHost, _='_'},
-+                               Pat_from = #msg{from_user=User, from_server=VHost,  _='_'},
-+                               mnesia:select(list_to_atom(Table_name),
-+                                             [{Pat_to, [], ['$_']},
-+                                              {Pat_from, [], ['$_']}])
-+                       end) of
-+          {atomic, Result} ->
-+                   Msgs = lists:map(fun(#msg{to_user=To_user, to_server=To_server, to_resource=To_res,
-+                                             from_user=From_user, from_server=From_server, from_resource=From_res,
-+                                             type=Type,
-+                                             subject=Subj,
-+                                             body=Body, timestamp=Timestamp} = _Msg) ->
-+                                        Subject = case Subj of
-+                                                       "None" -> "";
-+                                                       _ -> Subj
-+                                                  end,
-+                                        {msg, To_user, To_server, To_res, From_user, From_server, From_res, Type, Subject, Body, Timestamp}
-+                                    end, Result),
-+                   {ok, Msgs};
-+          {aborted, Reason} ->
-+                   {error, Reason}
-+    end.
-+
-+get_dates(_VHost) ->
-+    Tables = mnesia:system_info(tables),
-+    MessagesTables =
-+        lists:filter(fun(Table) ->
-+                         lists:prefix(tables_prefix(), atom_to_list(Table))
-+                     end,
-+                     Tables),
-+    lists:map(fun(Table) ->
-+                  lists:sublist(atom_to_list(Table),
-+                                length(tables_prefix())+1,
-+                                length(atom_to_list(Table)))
-+              end,
-+              MessagesTables).
-+
-+get_users_settings(_VHost) ->
-+    {ok, []}.
-+get_user_settings(_User, _VHost) ->
-+    {ok, []}.
-+set_user_settings(_User, _VHost, _Set) ->
-+    ok.
-+drop_user(_User, _VHost) ->
-+    ok.
-+
-+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-+%
-+% internal
-+%
-+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-+% called from db_logon/2
-+create_stats_table() ->
-+    SName = stats_table(),
-+    case mnesia:create_table(SName,
-+                             [{disc_only_copies, [node()]},
-+                              {type, bag},
-+                              {attributes, record_info(fields, stats)},
-+                              {record_name, stats}
-+                             ]) of
-+         {atomic, ok} ->
-+             ?INFO_MSG("Created stats table", []),
-+             ok;
-+         {aborted, {already_exists, _}} ->
-+             ok;
-+         {aborted, Reason} ->
-+             ?ERROR_MSG("Failed to create stats table: ~p", [Reason]),
-+             error
-+    end.
 diff --git a/src/mod_logdb_mysql.erl b/src/mod_logdb_mysql.erl
 new file mode 100644
-index 00000000..c805c920
+index 00000000..09036211
 --- /dev/null
 +++ b/src/mod_logdb_mysql.erl
-@@ -0,0 +1,1055 @@
+@@ -0,0 +1,1052 @@
 +%%%----------------------------------------------------------------------
 +%%% File    : mod_logdb_mysql.erl
-+%%% Author  : Oleg Palij (mailto,xmpp:o.palij at gmail.com)
++%%% Author  : Oleg Palij (mailto:o.palij at gmail.com)
 +%%% Purpose : MySQL backend for mod_logdb
-+%%% Version : trunk
-+%%% Id      : $Id: mod_logdb_mysql.erl 1360 2009-07-30 06:00:14Z malik $
-+%%% Url     : http://www.dp.uz.gov.ua/o.palij/mod_logdb/
++%%% Url     : https://paleg.github.io/mod_logdb/
 +%%%----------------------------------------------------------------------
 +
 +-module(mod_logdb_mysql).
@@ -3905,14 +3425,13 @@ index 00000000..c805c920
 +get_dates_int(DBRef, VHost) ->
 +    case sql_query_internal(DBRef, ["SHOW TABLES"]) of
 +         {data, Tables} ->
++            Reg = "^" ++ lists:sublist(prefix(),2,length(prefix())) ++ ".*" ++ escape_vhost(VHost),
 +            lists:foldl(fun([Table], Dates) ->
-+                           Reg = lists:sublist(prefix(),2,length(prefix())) ++ ".*" ++ escape_vhost(VHost),
 +                           case re:run(Table, Reg) of
-+                                {match, [{1, _}]} ->
-+                                   ?MYDEBUG("matched ~p against ~p", [Table, Reg]),
-+                                   case re:run(Table,"[0-9]+-[0-9]+-[0-9]+") of
++                                {match, _} ->
++                                   case re:run(Table, "[0-9]+-[0-9]+-[0-9]+") of
 +                                        {match, [{S, E}]} ->
-+                                            lists:append(Dates, [lists:sublist(Table,S,E)]);
++                                            lists:append(Dates, [lists:sublist(Table, S+1, E)]);
 +                                        nomatch ->
 +                                            Dates
 +                                   end;
@@ -4412,17 +3931,15 @@ index 00000000..c805c920
 +    {error, Reason}.
 diff --git a/src/mod_logdb_mysql5.erl b/src/mod_logdb_mysql5.erl
 new file mode 100644
-index 00000000..04a546ea
+index 00000000..b6025a3d
 --- /dev/null
 +++ b/src/mod_logdb_mysql5.erl
-@@ -0,0 +1,983 @@
+@@ -0,0 +1,981 @@
 +%%%----------------------------------------------------------------------
 +%%% File    : mod_logdb_mysql5.erl
-+%%% Author  : Oleg Palij (mailto,xmpp:o.palij at gmail.com)
++%%% Author  : Oleg Palij (mailto:o.palij at gmail.com)
 +%%% Purpose : MySQL 5 backend for mod_logdb
-+%%% Version : trunk
-+%%% Id      : $Id: mod_logdb_mysql5.erl 1360 2009-07-30 06:00:14Z malik $
-+%%% Url     : http://www.dp.uz.gov.ua/o.palij/mod_logdb/
++%%% Url     : https://paleg.github.io/mod_logdb/
 +%%%----------------------------------------------------------------------
 +
 +-module(mod_logdb_mysql5).
@@ -4904,13 +4421,13 @@ index 00000000..04a546ea
 +get_dates_int(DBRef, VHost) ->
 +    case sql_query_internal(DBRef, ["SHOW TABLES"]) of
 +         {data, Tables} ->
++            Reg = "^" ++ lists:sublist(prefix(),2,length(prefix())) ++ ".*" ++ escape_vhost(VHost),
 +            lists:foldl(fun([Table], Dates) ->
-+                           Reg = lists:sublist(prefix(),2,length(prefix())) ++ ".*" ++ escape_vhost(VHost),
 +                           case re:run(Table, Reg) of
-+                                {match, [{1, _}]} ->
-+                                   case re:run(Table,"[0-9]+-[0-9]+-[0-9]+") of
++                                {match, _} ->
++                                   case re:run(Table, "[0-9]+-[0-9]+-[0-9]+") of
 +                                        {match, [{S, E}]} ->
-+                                            lists:append(Dates, [lists:sublist(Table,S,E)]);
++                                            lists:append(Dates, [lists:sublist(Table, S+1, E)]);
 +                                        nomatch ->
 +                                            Dates
 +                                   end;
@@ -5401,20 +4918,18 @@ index 00000000..04a546ea
 +END;", [logmessage_name(VHost),UName,UName,UName,UName,SName,SName,RName,RName,UName,UName,SName,RName,StName,StName]).
 diff --git a/src/mod_logdb_pgsql.erl b/src/mod_logdb_pgsql.erl
 new file mode 100644
-index 00000000..29bed039
+index 00000000..61a71fff
 --- /dev/null
 +++ b/src/mod_logdb_pgsql.erl
-@@ -0,0 +1,1108 @@
+@@ -0,0 +1,1106 @@
 +% {ok, DBRef} = pgsql:connect([{host, "127.0.0.1"}, {database, "logdb"}, {user, "logdb"}, {password, "logdb"}, {port, 5432}, {as_binary, true}]).
 +% Schema = "test".
 +% pgsql:squery(DBRef, "CREATE TABLE test.\"logdb_stats_test\" (owner_id INTEGER, peer_name_id INTEGER, peer_server_id INTEGER, at VARCHAR(20), count integer);" ).
 +%%%----------------------------------------------------------------------
 +%%% File    : mod_logdb_pgsql.erl
-+%%% Author  : Oleg Palij (mailto,xmpp:o.palij at gmail.com)
++%%% Author  : Oleg Palij (mailto:o.palij at gmail.com)
 +%%% Purpose : Posgresql backend for mod_logdb
-+%%% Version : trunk
-+%%% Id      : $Id: mod_logdb_pgsql.erl 1360 2009-07-30 06:00:14Z malik $
-+%%% Url     : http://www.dp.uz.gov.ua/o.palij/mod_logdb/
++%%% Url     : https://paleg.github.io/mod_logdb/
 +%%%----------------------------------------------------------------------
 +
 +-module(mod_logdb_pgsql).
@@ -5906,7 +5421,7 @@ index 00000000..29bed039
 +            lists:foldl(fun({_Schema, Table, _Type, _Owner}, Dates) ->
 +                             case re:run(Table,"[0-9]+-[0-9]+-[0-9]+") of
 +                                  {match, [{S, E}]} ->
-+                                      lists:append(Dates, [lists:sublist(Table,S,E)]);
++                                      lists:append(Dates, [lists:sublist(Table, S+1, E)]);
 +                                  nomatch ->
 +                                      Dates
 +                             end
@@ -6513,37 +6028,20 @@ index 00000000..29bed039
 +get_result(Rez) ->
 +    {error, undefined, Rez}.
 +
-diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl
-index c9c78575..4db4895a 100644
---- a/src/mod_muc_room.erl
-+++ b/src/mod_muc_room.erl
-@@ -538,6 +538,12 @@ handle_sync_event({process_item_change,
- 	NSD ->
- 	    {reply, {ok, NSD}, StateName, NSD}
-     end;
-+handle_sync_event({get_jid_nick, Jid}, _From, StateName, StateData) ->
-+    R = case (?DICT):find(jlib:jid_tolower(Jid), StateData#state.users) of
-+	    error -> [];
-+	    {ok, {user, _, Nick, _, _}} -> Nick
-+        end,
-+    {reply, R, StateName, StateData};
- handle_sync_event(get_subscribers, _From, StateName, StateData) ->
-     JIDs = lists:map(fun jid:make/1,
- 		     ?DICT:fetch_keys(StateData#state.subscribers)),
 diff --git a/src/mod_roster.erl b/src/mod_roster.erl
-index b3a627f7..761e234d 100644
+index cf281528..c3a5c92a 100644
 --- a/src/mod_roster.erl
 +++ b/src/mod_roster.erl
-@@ -62,6 +62,8 @@
+@@ -65,6 +65,8 @@
  
- -include("ejabberd_web_admin.hrl").
+ -define(SETS, gb_sets).
  
 +-include("mod_logdb.hrl").
 +
  -export_type([subscription/0]).
  
  -callback init(binary(), gen_mod:opts()) -> any().
-@@ -941,6 +943,14 @@ user_roster(User, Server, Query, Lang) ->
+@@ -943,6 +945,14 @@ user_roster(User, Server, Query, Lang) ->
  				  Query),
      Items = get_roster(LUser, LServer),
      SItems = lists:sort(Items),
@@ -6558,7 +6056,7 @@ index b3a627f7..761e234d 100644
      FItems = case SItems of
  	       [] -> [?CT(<<"None">>)];
  	       _ ->
-@@ -998,7 +1008,33 @@ user_roster(User, Server, Query, Lang) ->
+@@ -1000,7 +1010,33 @@ user_roster(User, Server, Query, Lang) ->
  							  [?INPUTT(<<"submit">>,
  								   <<"remove",
  								     (ejabberd_web_admin:term_to_id(R#roster.jid))/binary>>,
@@ -6566,7 +6064,7 @@ index b3a627f7..761e234d 100644
 +								     <<"Remove">>)]),
 +                             case gen_mod:is_loaded(Server, mod_logdb) of
 +                                  true ->
-+                                     Peer = jlib:jid_to_string(R#roster.jid),
++                                     Peer = jid:encode(R#roster.jid),
 +                                     A = lists:member(Peer, Settings#user_settings.dolog_list),
 +                                     B = lists:member(Peer, Settings#user_settings.donotlog_list),
 +                                     {Name, Value} =
@@ -6593,9 +6091,9 @@ index b3a627f7..761e234d 100644
  					end,
  					SItems)))])]
  	     end,
-@@ -1123,9 +1159,42 @@ user_roster_item_parse_query(User, Server, Items,
- 										=
- 										[]}]}}),
+@@ -1107,9 +1143,42 @@ user_roster_item_parse_query(User, Server, Items,
+ 					    sub_els = [#roster_query{
+ 							  items = [RosterItem]}]}),
  				      throw(submitted);
 -				  false -> ok
 -				end
@@ -6604,13 +6102,13 @@ index b3a627f7..761e234d 100644
 +                    case lists:keysearch(
 +                           <<"donotlog", (ejabberd_web_admin:term_to_id(JID))/binary>>, 1, Query) of
 +                        {value, _} ->
-+                             Peer = jlib:jid_to_string(JID),
++                             Peer = jid:encode(JID),
 +                             Settings = mod_logdb:get_user_settings(User, Server),
 +                             DNLL = case lists:member(Peer, Settings#user_settings.donotlog_list) of
 +                                         false -> lists:append(Settings#user_settings.donotlog_list, [Peer]);
 +                                         true -> Settings#user_settings.donotlog_list
 +                                    end,
-+                             DLL = lists:delete(jlib:jid_to_string(JID), Settings#user_settings.dolog_list),
++                             DLL = lists:delete(jid:encode(JID), Settings#user_settings.dolog_list),
 +                             Sett = Settings#user_settings{donotlog_list=DNLL, dolog_list=DLL},
 +                             % TODO: check returned value
 +                             ok = mod_logdb:set_user_settings(User, Server, Sett),
@@ -6619,13 +6117,13 @@ index b3a627f7..761e234d 100644
 +                           case lists:keysearch(
 +                                  <<"dolog", (ejabberd_web_admin:term_to_id(JID))/binary>>, 1, Query) of
 +                               {value, _} ->
-+                                  Peer = jlib:jid_to_string(JID),
++                                  Peer = jid:encode(JID),
 +                                  Settings = mod_logdb:get_user_settings(User, Server),
 +                                  DLL = case lists:member(Peer, Settings#user_settings.dolog_list) of
 +                                             false -> lists:append(Settings#user_settings.dolog_list, [Peer]);
 +                                             true -> Settings#user_settings.dolog_list
 +                                        end,
-+                                  DNLL = lists:delete(jlib:jid_to_string(JID), Settings#user_settings.donotlog_list),
++                                  DNLL = lists:delete(jid:encode(JID), Settings#user_settings.donotlog_list),
 +                                  Sett = Settings#user_settings{donotlog_list=DNLL, dolog_list=DLL},
 +                                  % TODO: check returned value
 +                                  ok = mod_logdb:set_user_settings(User, Server, Sett),
================================================================

---- gitweb:

http://git.pld-linux.org/gitweb.cgi/packages/ejabberd.git/commitdiff/3f23be8ea2d661e0d674fd2dc60562bda1670ccb



More information about the pld-cvs-commit mailing list