]> git.pld-linux.org Git - packages/ejabberd.git/blobdiff - ejabberd-mod_logdb.patch
- up to 19.09.1
[packages/ejabberd.git] / ejabberd-mod_logdb.patch
index 8e9c93701655c7ac4fca920b2b7f6d29f65b399d..449c1c3e1362d0e0866e269517846f7325809fa5 100644 (file)
---- src/mod_logdb.erl.orig     2009-02-05 19:21:29.000000000 +0200
-+++ src/mod_logdb.erl  2009-02-05 19:19:51.000000000 +0200
-@@ -0,0 +1,2094 @@
+From 9a2ed8d2b20ef052b71b065e686cc049d18999ac Mon Sep 17 00:00:00 2001
+From: Oleh Palii <o.palij@gmail.com>
+Date: Sat, 31 Aug 2019 11:04:57 +0300
+Subject: [PATCH 1/3] apply mod_logdb to 19.08
+
+---
+ priv/msgs/nl.msg         |   14 +
+ priv/msgs/pl.msg         |   26 +
+ priv/msgs/ru.msg         |   30 +
+ priv/msgs/uk.msg         |   30 +
+ rebar.config             |    4 +-
+ src/gen_logdb.erl        |  162 ++++
+ src/mod_logdb.erl        | 1951 ++++++++++++++++++++++++++++++++++++++
+ src/mod_logdb.hrl        |   33 +
+ src/mod_logdb_mnesia.erl |  553 +++++++++++
+ src/mod_logdb_mysql.erl  | 1050 ++++++++++++++++++++
+ src/mod_logdb_mysql5.erl |  979 +++++++++++++++++++
+ src/mod_logdb_pgsql.erl  | 1104 +++++++++++++++++++++
+ src/mod_roster.erl       |   77 +-
+ 13 files changed, 6007 insertions(+), 6 deletions(-)
+ create mode 100644 src/gen_logdb.erl
+ create mode 100644 src/mod_logdb.erl
+ create mode 100644 src/mod_logdb.hrl
+ create mode 100644 src/mod_logdb_mnesia.erl
+ create mode 100644 src/mod_logdb_mysql.erl
+ create mode 100644 src/mod_logdb_mysql5.erl
+ create mode 100644 src/mod_logdb_pgsql.erl
+
+diff --git a/priv/msgs/nl.msg b/priv/msgs/nl.msg
+index 8009d529ff..fd3d3b0ebb 100644
+--- a/priv/msgs/nl.msg
++++ b/priv/msgs/nl.msg
+@@ -345,3 +345,17 @@
+ {"Your contact offline message queue is full. The message has been discarded.","Te veel offline berichten voor dit contactpersoon. Het bericht is niet opgeslagen."}.
+ {"Your Jabber account was successfully created.","Uw Jabber-account is succesvol gecreeerd."}.
+ {"Your Jabber account was successfully deleted.","Uw Jabber-account is succesvol verwijderd."}.
++% mod_logdb
++{"Users Messages", "Gebruikersberichten"}.
++{"Date", "Datum"}.
++{"Count", "Aantal"}.
++{"Logged messages for ~s", "Gelogde berichten van ~s"}.
++{"Logged messages for ~s at ~s", "Gelogde berichten van ~s op ~s"}.
++{" at ", " op "}.
++{"No logged messages for ~s", "Geen gelogde berichten van ~s"}.
++{"No logged messages for ~s at ~s", "Geen gelogde berichten van ~s op ~s"}.
++{"Date, Time", "Datum en tijd"}.
++{"Direction: Jid", "Richting: Jabber ID"}.
++{"Subject", "Onderwerp"}.
++{"Body", "Berichtveld"}.
++{"Messages", "Berichten"}.
+diff --git a/priv/msgs/pl.msg b/priv/msgs/pl.msg
+index 2ca75b259c..fffae5742e 100644
+--- a/priv/msgs/pl.msg
++++ b/priv/msgs/pl.msg
+@@ -444,3 +444,29 @@
+ {"You're not allowed to create nodes","Nie masz uprawnień do tworzenia węzłów"}.
+ {"Your Jabber account was successfully created.","Twoje konto zostało stworzone."}.
+ {"Your Jabber account was successfully deleted.","Twoje konto zostało usunięte."}.
++% mod_logdb
++{"Users Messages", "Wiadomości użytkownika"}.
++{"Date", "Data"}.
++{"Count", "Liczba"}.
++{"Logged messages for ~s", "Zapisane wiadomości dla ~s"}.
++{"Logged messages for ~s at", "Zapisane wiadomości dla ~s o ~s"}.
++{" at ", " o "}.
++{"No logged messages for ~s", "Brak zapisanych wiadomości dla ~s"}.
++{"No logged messages for ~s at ~s", "Brak zapisanych wiadomości dla ~s o ~s"}.
++{"Date, Time", "Data, Godzina"}.
++{"Direction: Jid", "Kierunek: Jid"}.
++{"Subject", "Temat"}.
++{"Body", "Treść"}.
++{"Messages","Wiadomości"}.
++{"Filter Selected", "Odfiltruj zaznaczone"}.
++{"Do Not Log Messages", "Nie zapisuj wiadomości"}.
++{"Log Messages", "Zapisuj wiadomości"}.
++{"Messages logging engine", "System zapisywania historii rozmów"}.
++{"Default", "Domyślne"}.
++{"Set logging preferences", "Ustaw preferencje zapisywania"}.
++{"Messages logging engine settings", "Ustawienia systemu logowania"}.
++{"Set run-time settings", "Zapisz ustawienia systemu logowania"}.
++{"Groupchat messages logging", "Zapisywanie rozmów z konferencji"}.
++{"Jids/Domains to ignore", "JID/Domena która ma być ignorowana"}.
++{"Purge messages older than (days)", "Usuń wiadomości starsze niż (w dniach)"}.
++{"Poll users settings (seconds)", "Czas aktualizacji preferencji użytkowników (sekundy)"}.
+diff --git a/priv/msgs/ru.msg b/priv/msgs/ru.msg
+index f7dff97ea1..42be5d4f15 100644
+--- a/priv/msgs/ru.msg
++++ b/priv/msgs/ru.msg
+@@ -507,3 +507,33 @@
+ {"Your Jabber account was successfully created.","Ваш Jabber-аккаунт был успешно создан."}.
+ {"Your Jabber account was successfully deleted.","Ваш Jabber-аккаунт был успешно удален."}.
+ {"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","Ваши запросы на добавление в контакт-лист, а также сообщения к ~s блокируются. Для снятия блокировки перейдите по ссылке ~s"}.
++% mod_logdb.erl
++{"Users Messages", "Сообщения пользователей"}.
++{"Date", "Дата"}.
++{"Count", "Количество"}.
++{"Logged messages for ~s", "Сохранённые cообщения для ~s"}.
++{"Logged messages for ~s at ~s", "Сохранённые cообщения для ~s за ~s"}.
++{" at ", " за "}.
++{"No logged messages for ~s", "Отсутствуют сообщения для ~s"}.
++{"No logged messages for ~s at ~s", "Отсутствуют сообщения для ~s за ~s"}.
++{"Date, Time", "Дата, Время"}.
++{"Direction: Jid", "Направление: Jid"}.
++{"Subject", "Тема"}.
++{"Body", "Текст"}.
++{"Messages", "Сообщения"}.
++{"Filter Selected", "Отфильтровать выделенные"}.
++{"Do Not Log Messages", "Не сохранять сообщения"}.
++{"Log Messages", "Сохранять сообщения"}.
++{"Messages logging engine", "Система логирования сообщений"}.
++{"Default", "По умолчанию"}.
++{"Set logging preferences", "Задайте настройки логирования"}.
++{"Messages logging engine users", "Пользователи системы логирования сообщений"}.
++{"Messages logging engine settings", "Настройки системы логирования сообщений"}.
++{"Set run-time settings", "Задайте текущие настройки"}.
++{"Groupchat messages logging", "Логирование сообщений типа groupchat"}.
++{"Jids/Domains to ignore", "Игнорировать следующие jids/домены"}.
++{"Purge messages older than (days)", "Удалять сообщения старее чем (дни)"}.
++{"Poll users settings (seconds)", "Обновлять настройки пользователей через (секунд)"}.
++{"Drop", "Удалять"}.
++{"Do not drop", "Не удалять"}.
++{"Drop messages on user removal", "Удалять сообщения при удалении пользователя"}.
+diff --git a/priv/msgs/uk.msg b/priv/msgs/uk.msg
+index 0fbc336d51..c0b90047fa 100644
+--- a/priv/msgs/uk.msg
++++ b/priv/msgs/uk.msg
+@@ -349,3 +349,33 @@
+ {"Your contact offline message queue is full. The message has been discarded.","Черга повідомлень, що не були доставлені, переповнена. Повідомлення не було збережено."}.
+ {"Your Jabber account was successfully created.","Ваш Jabber-акаунт було успішно створено."}.
+ {"Your Jabber account was successfully deleted.","Ваш Jabber-акаунт було успішно видалено."}.
++% mod_logdb
++{"Users Messages", "Повідомлення користувачів"}.
++{"Date", "Дата"}.
++{"Count", "Кількість"}.
++{"Logged messages for ~s", "Збережені повідомлення для ~s"}.
++{"Logged messages for ~s at ~s", "Збережені повідомлення для ~s за ~s"}.
++{" at ", " за "}.
++{"No logged messages for ~s", "Відсутні повідомлення для ~s"}.
++{"No logged messages for ~s at ~s", "Відсутні повідомлення для ~s за ~s"}.
++{"Date, Time", "Дата, Час"}.
++{"Direction: Jid", "Напрямок: Jid"}.
++{"Subject", "Тема"}.
++{"Body", "Текст"}.
++{"Messages", "Повідомлення"}.
++{"Filter Selected", "Відфільтрувати виділені"}.
++{"Do Not Log Messages", "Не зберігати повідомлення"}.
++{"Log Messages", "Зберігати повідомлення"}.
++{"Messages logging engine", "Система збереження повідомлень"}.
++{"Default", "За замовчуванням"}.
++{"Set logging preferences", "Вкажіть налагоджування збереження повідомлень"}.
++{"Messages logging engine users", "Користувачі системи збереження повідомлень"}.
++{"Messages logging engine settings", "Налагоджування системи збереження повідомлень"}.
++{"Set run-time settings", "Вкажіть поточні налагоджування"}.
++{"Groupchat messages logging", "Збереження повідомлень типу groupchat"}.
++{"Jids/Domains to ignore", "Ігнорувати наступні jids/домени"}.
++{"Purge messages older than (days)", "Видаляти повідомлення старіші ніж (дні)"}.
++{"Poll users settings (seconds)", "Оновлювати налагоджування користувачів кожні (секунд)"}.
++{"Drop", "Видаляти"}.
++{"Do not drop", "Не видаляти"}.
++{"Drop messages on user removal", "Видаляти повідомлення під час видалення користувача"}.
+diff --git a/rebar.config b/rebar.config
+index e05fe84e6e..c3b87afd28 100644
+--- a/rebar.config
++++ b/rebar.config
+@@ -35,8 +35,8 @@
+         {mqtree, ".*", {git, "https://github.com/processone/mqtree", {tag, "1.0.4"}}},
+         {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.29"}}}},
+         {if_var_true, sip, {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.30"}}}},
+-        {if_var_true, mysql, {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql",
+-                                               {tag, "1.0.11"}}}},
++        {if_var_true, mysql, {p1_mysql, ".*", {git, "https://github.com/paleg/p1_mysql",
++                                               {tag, "1.0.11_multi"}}}},
+         {if_var_true, pgsql, {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql",
+                                                {tag, "1.1.8"}}}},
+         {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 0000000000..8bad112969
+--- /dev/null
++++ b/src/gen_logdb.erl
+@@ -0,0 +1,162 @@
++%%%----------------------------------------------------------------------
++%%% File    : gen_logdb.erl
++%%% Author  : Oleg Palij (mailto:o.palij@gmail.com)
++%%% Purpose : Describes generic behaviour for mod_logdb backends.
++%%% Url     : https://paleg.github.io/mod_logdb/
++%%%----------------------------------------------------------------------
++
++-module(gen_logdb).
++-author('o.palij@gmail.com').
++
++-export([behaviour_info/1]).
++
++behaviour_info(callbacks) ->
++   [
++    % called from handle_info(start, _)
++    % it should logon database and return reference to started instance
++    % start(VHost, Opts) -> {ok, SPid} | error
++    %  Options - list of options to connect to db
++    %    Types: Options = list() -> [] |
++    %                              [{user, "logdb"},
++    %                               {pass, "1234"},
++    %                               {db, "logdb"}] | ...
++    %          VHost = list() -> "jabber.example.org"
++    {start, 2},
++
++    % called from cleanup/1
++    % it should logoff database and do cleanup
++    % stop(VHost)
++    %    Types: VHost = list() -> "jabber.example.org"
++    {stop, 1},
++
++    % called from handle_call({addlog, _}, _, _)
++    % it should log messages to database
++    % log_message(VHost, Msg) -> ok | error
++    %    Types:
++    %          VHost = list() -> "jabber.example.org"
++    %          Msg = record() -> #msg
++    {log_message, 2},
++
++    % called from ejabberdctl rebuild_stats
++    % it should rebuild stats table (if used) for vhost
++    % rebuild_stats(VHost)
++    %    Types:
++    %          VHost = list() -> "jabber.example.org"
++    {rebuild_stats, 1},
++
++    % it should rebuild stats table (if used) for vhost at Date
++    % rebuild_stats_at(VHost, Date)
++    %    Types:
++    %          VHost = list() -> "jabber.example.org"
++    %          Date = list() -> "2007-02-12"
++    {rebuild_stats_at, 2},
++
++    % called from user_messages_at_parse_query/5
++    % it should delete selected user messages at date
++    % delete_messages_by_user_at(VHost, Msgs, Date) -> ok | error
++    %    Types:
++    %          VHost = list() -> "jabber.example.org"
++    %          Msgs = list() -> [ #msg1, msg2, ... ]
++    %          Date = list() -> "2007-02-12"
++    {delete_messages_by_user_at, 3},
++
++    % called from user_messages_parse_query/4 | vhost_messages_at_parse_query/4
++    % it should delete all user messages at date
++    % delete_all_messages_by_user_at(User, VHost, Date) -> ok | error
++    %    Types:
++    %          User = list() -> "admin"
++    %          VHost = list() -> "jabber.example.org"
++    %          Date = list() -> "2007-02-12"
++    {delete_all_messages_by_user_at, 3},
++
++    % called from vhost_messages_parse_query/3
++    % it should delete messages for vhost at date and update stats
++    % delete_messages_at(VHost, Date) -> ok | error
++    %    Types:
++    %          VHost = list() -> "jabber.example.org"
++    %          Date = list() -> "2007-02-12"
++    {delete_messages_at, 2},
++
++    % called from ejabberd_web_admin:vhost_messages_stats/3
++    % it should return sorted list of count of messages by dates for vhost
++    % get_vhost_stats(VHost) -> {ok, [{Date1, Msgs_count1}, {Date2, Msgs_count2}, ... ]} |
++    %                           {error, Reason}
++    %    Types:
++    %          VHost = list() -> "jabber.example.org"
++    %          DateN = list() -> "2007-02-12"
++    %          Msgs_countN = number() -> 241
++    {get_vhost_stats, 1},
++
++    % called from ejabberd_web_admin:vhost_messages_stats_at/4
++    % it should return sorted list of count of messages by users at date for vhost
++    % get_vhost_stats_at(VHost, Date) -> {ok, [{User1, Msgs_count1}, {User2, Msgs_count2}, ....]} |
++    %                                    {error, Reason}
++    %    Types:
++    %          VHost = list() -> "jabber.example.org"
++    %          Date = list() -> "2007-02-12"
++    %          UserN = list() -> "admin"
++    %          Msgs_countN = number() -> 241
++    {get_vhost_stats_at, 2},
++
++    % called from ejabberd_web_admin:user_messages_stats/4
++    % it should return sorted list of count of messages by date for user at vhost
++    % get_user_stats(User, VHost) -> {ok, [{Date1, Msgs_count1}, {Date2, Msgs_count2}, ...]} |
++    %                                {error, Reason}
++    %    Types:
++    %          User = list() -> "admin"
++    %          VHost = list() -> "jabber.example.org"
++    %          DateN = list() -> "2007-02-12"
++    %          Msgs_countN = number() -> 241
++    {get_user_stats, 2},
++
++    % called from ejabberd_web_admin:user_messages_stats_at/5
++    % it should return all user messages at date
++    % get_user_messages_at(User, VHost, Date) -> {ok, Msgs} | {error, Reason}
++    %    Types:
++    %          User = list() -> "admin"
++    %          VHost = list() -> "jabber.example.org"
++    %          Date = list() -> "2007-02-12"
++    %          Msgs = list() -> [ #msg1, msg2, ... ]
++    {get_user_messages_at, 3},
++
++    % called from many places
++    % it should return list of dates for vhost
++    % get_dates(VHost) -> [Date1, Date2, ... ]
++    %    Types:
++    %          VHost = list() -> "jabber.example.org"
++    %          DateN = list() -> "2007-02-12"
++    {get_dates, 1},
++
++    % called from start
++    % it should return list with users settings for VHost in db
++    % get_users_settings(VHost) -> [#user_settings1, #user_settings2, ... ] | error
++    %    Types:
++    %          VHost = list() -> "jabber.example.org"
++    {get_users_settings, 1},
++
++    % called from many places
++    % it should return User settings at VHost from db
++    % get_user_settings(User, VHost) -> error | {ok, #user_settings}
++    %    Types:
++    %          User = list() -> "admin"
++    %          VHost = list() -> "jabber.example.org"
++    {get_user_settings, 2},
++
++    % called from web admin
++    % it should set User settings at VHost
++    % set_user_settings(User, VHost, #user_settings) -> ok | error
++    %    Types:
++    %          User = list() -> "admin"
++    %          VHost = list() -> "jabber.example.org"
++    {set_user_settings, 3},
++
++    % called from remove_user (ejabberd hook)
++    % it should remove user messages and settings at VHost
++    % drop_user(User, VHost) -> ok | error
++    %    Types:
++    %          User = list() -> "admin"
++    %          VHost = list() -> "jabber.example.org"
++    {drop_user, 2}
++   ];
++behaviour_info(_) ->
++   undefined.
+diff --git a/src/mod_logdb.erl b/src/mod_logdb.erl
+new file mode 100644
+index 0000000000..bf0240d139
+--- /dev/null
++++ b/src/mod_logdb.erl
+@@ -0,0 +1,1951 @@
 +%%%----------------------------------------------------------------------
 +%%% File    : mod_logdb.erl
-+%%% Author  : Oleg Palij (mailto,xmpp:o.palij@gmail.com)
++%%% Author  : Oleg Palij (mailto:o.palij@gmail.com)
 +%%% Purpose : Frontend for log user messages to db
-+%%% Version : trunk
-+%%% Id      : $Id$
-+%%% Url     : http://www.dp.uz.gov.ua/o.palij/mod_logdb/
++%%% Url     : https://paleg.github.io/mod_logdb/
 +%%%----------------------------------------------------------------------
 +
 +-module(mod_logdb).
 +% supervisor
 +-export([start_link/2]).
 +% gen_mod
-+-export([start/2,stop/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/3, receive_packet/4, offline_packet/3, 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_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,
 +         user_messages_stats_at/5]).
 +
 +-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("web/ejabberd_web_admin.hrl").
-+-include("web/ejabberd_http.hrl").
++-include("ejabberd_web_admin.hrl").
++-include("ejabberd_http.hrl").
++-include("logger.hrl").
 +
 +-define(PROCNAME, ejabberd_mod_logdb).
 +% gen_server call timeout
 +
 +-record(state, {vhost, dbmod, backendPid, monref, purgeRef, pollRef, dbopts, dbs, dolog_default, ignore_jids, groupchat, purge_older_days, poll_users_settings, drop_messages_on_user_removal}).
 +
-+ets_settings_table(VHost) -> list_to_atom("ets_logdb_settings_" ++ VHost).
++ets_settings_table(VHost) -> list_to_atom("ets_logdb_settings_" ++ binary_to_list(VHost)).
 +
 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 +%
 +         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) ->
 +
 +init([VHost, Opts]) ->
 +    process_flag(trap_exit, true),
-+    DBs = gen_mod:get_opt(dbs, Opts, [{mnesia, []}]),
-+    VHostDB = gen_mod:get_opt(vhosts, Opts, [{VHost, mnesia}]),
-+    % 10 is default becouse of using in clustered environment
-+    PollUsersSettings = gen_mod:get_opt(poll_users_settings, Opts, 10),
++    DBsRaw = gen_mod:get_opt(dbs, Opts, fun(A) -> A end, [{mnesia, []}]),
++    DBs = case lists:keysearch(mnesia, 1, DBsRaw) of
++               false -> lists:append(DBsRaw, [{mnesia,[]}]);
++               {value, _} -> DBsRaw
++          end,
++    VHostDB = gen_mod:get_opt(vhosts, Opts, fun(A) -> A end, [{VHost, mnesia}]),
++    % 10 is default because of using in clustered environment
++    PollUsersSettings = gen_mod:get_opt(poll_users_settings, Opts, fun(A) -> A end, 10),
 +
-+    {value,{_, DBName}} = lists:keysearch(VHost, 1, VHostDB),
-+    {value, {DBName, DBOpts}} = lists:keysearch(DBName, 1, DBs),
++    {DBName, DBOpts} =
++         case lists:keysearch(VHost, 1, VHostDB) of
++              false ->
++                 ?WARNING_MSG("There is no logging backend defined for '~s', switching to mnesia", [VHost]),
++                 {mnesia, []};
++              {value,{_, DBNameResult}} ->
++                 case lists:keysearch(DBNameResult, 1, DBs) of
++                      false ->
++                        ?WARNING_MSG("There is no such logging backend '~s' defined for '~s', switching to mnesia", [DBNameResult, VHost]),
++                        {mnesia, []};
++                      {value, {_, DBOptsResult}} ->
++                        {DBNameResult, DBOptsResult}
++                 end
++         end,
 +
-+    ?MYDEBUG("Starting mod_logdb for ~p with ~p backend", [VHost, DBName]),
++    ?MYDEBUG("Starting mod_logdb for '~s' with '~s' backend", [VHost, DBName]),
 +
 +    DBMod = list_to_atom(atom_to_list(?MODULE) ++ "_" ++ atom_to_list(DBName)),
 +
 +                dbopts=DBOpts,
 +                % dbs used for convert messages from one backend to other
 +                dbs=DBs,
-+                dolog_default=gen_mod:get_opt(dolog_default, Opts, true),
-+                drop_messages_on_user_removal=gen_mod:get_opt(drop_messages_on_user_removal, Opts, true),
-+                ignore_jids=gen_mod:get_opt(ignore_jids, Opts, []),
-+                groupchat=gen_mod:get_opt(groupchat, Opts, none),
-+                purge_older_days=gen_mod:get_opt(purge_older_days, Opts, never),
++                dolog_default=gen_mod:get_opt(dolog_default, Opts, fun(A) -> A end, true),
++                drop_messages_on_user_removal=gen_mod:get_opt(drop_messages_on_user_removal, Opts, fun(A) -> A end, true),
++                ignore_jids=gen_mod:get_opt(ignore_jids, Opts, fun(A) -> A end, []),
++                groupchat=gen_mod:get_opt(groupchat, Opts, fun(A) -> A end, none),
++                purge_older_days=gen_mod:get_opt(purge_older_days, Opts, fun(A) -> A end, never),
 +                poll_users_settings=PollUsersSettings}}.
 +
-+cleanup(#state{vhost=VHost} = State) ->
++cleanup(#state{vhost=VHost} = _State) ->
 +    ?MYDEBUG("Stopping ~s for ~p", [?MODULE, VHost]),
 +
 +    %ets:delete(ets_settings_table(VHost)),
 +    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(offline_message_hook, VHost, ?MODULE, offline_packet, 10),
-+    %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(adhoc_local_commands, VHost, ?MODULE, adhoc_local_commands, 110),
-+    ejabberd_hooks:delete(adhoc_local_items, VHost, ?MODULE, adhoc_local_items, 110),
-+    %ejabberd_hooks:delete(disco_sm_identity, VHost, ?MODULE, get_sm_identity, 110),
-+    %ejabberd_hooks:delete(disco_sm_features, VHost, ?MODULE, get_sm_features, 110),
-+    %ejabberd_hooks:delete(disco_sm_items, VHost, ?MODULE, get_sm_items, 110),
-+    ejabberd_hooks:delete(disco_local_identity, VHost, ?MODULE, get_local_identity, 110),
-+    ejabberd_hooks:delete(disco_local_features, VHost, ?MODULE, get_local_features, 110),
-+    ejabberd_hooks:delete(disco_local_items, VHost, ?MODULE, get_local_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_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),
 +
 +    ejabberd_hooks:delete(webadmin_menu_host, VHost, ?MODULE, webadmin_menu, 70),
 +    ejabberd_hooks:delete(webadmin_user, VHost, ?MODULE, webadmin_user, 50),
 +
 +    ?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) ->
 +    %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;
++mod_opt_type(vhosts) ->
++    fun (A) when is_list(A) -> A end;
++mod_opt_type(poll_users_settings) ->
++    fun (I) when is_integer(I) -> I end;
++mod_opt_type(groupchat) ->
++    fun (all) -> all;
++        (send) -> send;
++        (none) -> none
++    end;
++mod_opt_type(dolog_default) ->
++    fun (B) when is_boolean(B) -> B end;
++mod_opt_type(ignore_jids) ->
++    fun (A) when is_list(A) -> A end;
++mod_opt_type(purge_older_days) ->
++    fun (I) when is_integer(I) -> I end;
++mod_opt_type(_) ->
++    [dbs, vhosts, poll_users_settings, groupchat, dolog_default, ignore_jids, purge_older_days].
 +
 +handle_call({cleanup}, _From, State) ->
 +    cleanup(State),
 +% ejabberd_web_admin callbacks
 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 +handle_call({delete_messages_by_user_at, PMsgs, Date}, _From, #state{dbmod=DBMod, vhost=VHost}=State) ->
-+    Reply = DBMod:delete_messages_by_user_at(VHost, PMsgs, Date),
++    Reply = DBMod:delete_messages_by_user_at(VHost, PMsgs, binary_to_list(Date)),
 +    {reply, Reply, State};
 +handle_call({delete_all_messages_by_user_at, User, Date}, _From, #state{dbmod=DBMod, vhost=VHost}=State) ->
-+    Reply = DBMod:delete_all_messages_by_user_at(User, VHost, Date),
++    Reply = DBMod:delete_all_messages_by_user_at(binary_to_list(User), VHost, binary_to_list(Date)),
 +    {reply, Reply, State};
 +handle_call({delete_messages_at, Date}, _From, #state{dbmod=DBMod, vhost=VHost}=State) ->
 +    Reply = DBMod:delete_messages_at(VHost, Date),
 +    Reply = DBMod:get_vhost_stats(VHost),
 +    {reply, Reply, State};
 +handle_call({get_vhost_stats_at, Date}, _From, #state{dbmod=DBMod, vhost=VHost}=State) ->
-+    Reply = DBMod:get_vhost_stats_at(VHost, Date),
++    Reply = DBMod:get_vhost_stats_at(VHost, binary_to_list(Date)),
 +    {reply, Reply, State};
 +handle_call({get_user_stats, User}, _From, #state{dbmod=DBMod, vhost=VHost}=State) ->
-+    Reply = DBMod:get_user_stats(User, VHost),
++    Reply = DBMod:get_user_stats(binary_to_list(User), VHost),
 +    {reply, Reply, State};
 +handle_call({get_user_messages_at, User, Date}, _From, #state{dbmod=DBMod, vhost=VHost}=State) ->
-+    Reply = DBMod:get_user_messages_at(User, VHost, Date),
++    Reply = DBMod:get_user_messages_at(binary_to_list(User), VHost, binary_to_list(Date)),
 +    {reply, Reply, State};
 +handle_call({get_user_settings, User}, _From, #state{dbmod=_DBMod, vhost=VHost}=State) ->
 +    Reply = case ets:match_object(ets_settings_table(VHost),
 +       case ets:match_object(ets_settings_table(VHost),
 +                             #user_settings{owner_name=User, _='_'}) of
 +            [Set] ->
-+                ?MYDEBUG("Settings is equal", []),
 +                ok;
 +            _ ->
-+                case DBMod:set_user_settings(User, VHost, Set) of
++                case DBMod:set_user_settings(binary_to_list(User), VHost, Set) of
 +                     error ->
 +                       error;
 +                     ok ->
 +handle_cast({remove_user, User}, #state{dbmod=DBMod, vhost=VHost}=State) ->
 +    case State#state.drop_messages_on_user_removal of
 +         true ->
-+           DBMod:drop_user(User, VHost),
++           DBMod:drop_user(binary_to_list(User), VHost),
 +           ?INFO_MSG("Launched ~s@~s removal", [User, VHost]);
 +         false ->
 +           ?INFO_MSG("Message removing is disabled. Keeping messages for ~s@~s", [User, VHost])
 +    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]),
 +           {stop, db_connection_failed, State};
 +         {ok, SPid} ->
 +           ?INFO_MSG("~p connection established", [DBMod]),
-+           
++
 +           MonRef = erlang:monitor(process, SPid),
 +
 +           ets:new(ets_settings_table(VHost), [named_table,public,set,{keypos, #user_settings.owner_name}]),
-+           {ok, DoLog} = DBMod:get_users_settings(VHost),
++           DoLog = case DBMod:get_users_settings(VHost) of
++                        {ok, Settings} -> [Sett#user_settings{owner_name = iolist_to_binary(Sett#user_settings.owner_name)} || Sett <- Settings];
++                        {error, _Reason} -> []
++                   end,
 +           ets:insert(ets_settings_table(VHost), DoLog),
 +
 +           TrefPurge = set_purge_timer(State#state.purge_older_days),
 +           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_packet, 10),
-+
-+           ejabberd_hooks:add(disco_local_items, VHost, ?MODULE, get_local_items, 110),
-+           ejabberd_hooks:add(disco_local_features, VHost, ?MODULE, get_local_features, 110),
-+           ejabberd_hooks:add(disco_local_identity, VHost, ?MODULE, get_local_identity, 110),
-+           %ejabberd_hooks:add(disco_sm_items, VHost, ?MODULE, get_sm_items, 110),
-+           %ejabberd_hooks:add(disco_sm_features, VHost, ?MODULE, get_sm_features, 110),
-+           %ejabberd_hooks:add(disco_sm_identity, VHost, ?MODULE, get_sm_identity, 110),
-+           ejabberd_hooks:add(adhoc_local_items, VHost, ?MODULE, adhoc_local_items, 110),
-+           ejabberd_hooks:add(adhoc_local_commands, VHost, ?MODULE, adhoc_local_commands, 110),
-+           %ejabberd_hooks:add(adhoc_sm_items, VHost, ?MODULE, adhoc_sm_items, 110),
-+           %ejabberd_hooks:add(adhoc_sm_commands, VHost, ?MODULE, adhoc_sm_commands, 110),
++           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_identity, VHost, ?MODULE, get_local_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(webadmin_menu_host, VHost, ?MODULE, webadmin_menu, 70),
 +           ejabberd_hooks:add(webadmin_user, VHost, ?MODULE, webadmin_user, 50),
 +
 +           ?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},
 +%
 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 +% TODO: change to/from to list as sql stores it as list
-+send_packet(Owner, Peer, P) ->
++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, P}).
++    gen_server:cast(Proc, {addlog, to, Owner, Peer, Pkt}),
++    {Pkt, C2SState}.
 +
-+offline_packet(Peer, Owner, P) ->
++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, from, Owner, Peer, P}).
++    gen_server:cast(Proc, {addlog, from, Owner, Peer, Pkt}),
++    {Pkt, C2SState}.
 +
-+receive_packet(_JID, Peer, Owner, P) -> 
++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}).
++    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}).
 +
 +% 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
 +
 +% handle_cast({addlog, E}, _)
 +% raw packet -> #msg
-+packet_parse(Owner, Peer, Packet, Direction, State) ->
-+    case xml:get_subtag(Packet, "body") of
-+         false ->
-+           ignore;
-+         Body_xml ->
-+           Message_type =
-+              case xml:get_tag_attr_s("type", Packet) of
-+                   [] -> "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 = xml:get_tag_cdata(Body_xml),
-+           Message_subject =
-+              case xml:get_subtag(Packet, "subject") of
-+                   false ->
-+                     "";
-+                   Subject_xml ->
-+                     xml: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) ->
-+    OwnerStr = Owner#jid.luser++"@"++Owner#jid.lserver,
-+    OwnerServ = "@"++Owner#jid.lserver,
-+    PeerStr = Peer#jid.luser++"@"++Peer#jid.lserver,
-+    PeerServ = "@"++Peer#jid.lserver,
++    OwnerBin = << (Owner#jid.luser)/binary, "@", (Owner#jid.lserver)/binary >>,
++    OwnerServ = << "@", (Owner#jid.lserver)/binary >>,
++    PeerBin = << (Peer#jid.luser)/binary, "@", (Peer#jid.lserver)/binary >>,
++    PeerServ = << "@", (Peer#jid.lserver)/binary >>,
 +
 +    LogTo = case ets:match_object(ets_settings_table(State#state.vhost),
 +                                  #user_settings{owner_name=Owner#jid.luser, _='_'}) of
 +                 [#user_settings{dolog_default=Default,
 +                                 dolog_list=DLL,
 +                                 donotlog_list=DNLL}] ->
-+                      A = lists:member(PeerStr, DLL),
-+                      B = lists:member(PeerStr, DNLL),
++
++                      A = lists:member(PeerBin, DLL),
++                      B = lists:member(PeerBin, DNLL),
 +                      if
 +                        A -> true;
 +                        B -> false;
 +                        true -> State#state.dolog_default
 +                      end;
 +                 _ -> State#state.dolog_default
-+          end,
-+
-+    lists:all(fun(O) -> O end, 
-+              [not lists:member(OwnerStr, State#state.ignore_jids),
-+               not lists:member(PeerStr, State#state.ignore_jids),
++            end,
++    lists:all(fun(O) -> O end,
++              [not lists:member(OwnerBin, State#state.ignore_jids),
++               not lists:member(PeerBin, State#state.ignore_jids),
 +               not lists:member(OwnerServ, State#state.ignore_jids),
 +               not lists:member(PeerServ, State#state.ignore_jids),
 +               LogTo]).
 +    DateDiff = list_to_integer(Days)*24*60*60,
 +    ?MYDEBUG("Purging tables older than ~s days", [Days]),
 +    lists:foreach(fun(Date) ->
-+                    {ok, [Year, Month, Day]} = regexp:split(Date, "[^0-9]+"),
-+                    DateInSec = calendar:datetime_to_gregorian_seconds({{list_to_integer(Year), list_to_integer(Month), list_to_integer(Day)}, {0,0,1}}),
++                    [Year, Month, Day] = ejabberd_regexp:split(iolist_to_binary(Date), <<"[^0-9]+">>),
++                    DateInSec = calendar:datetime_to_gregorian_seconds({{binary_to_integer(Year), binary_to_integer(Month), binary_to_integer(Day)}, {0,0,1}}),
 +                    if
 +                     (DateNow - DateInSec) > DateDiff ->
 +                        gen_server:call(Proc, {delete_messages_at, Date});
-+                     true -> 
++                     true ->
 +                        ?MYDEBUG("Skipping messages at ~p", [Date])
 +                    end
 +              end, Dates).
 +sort_stats(Stats) ->
 +    % Stats = [{"2003-4-15",1}, {"2006-8-18",1}, ... ]
 +    CFun = fun({TableName, Count}) ->
-+                 {ok, [Year, Month, Day]} = regexp:split(TableName, "[^0-9]+"),
-+                 { calendar:datetime_to_gregorian_seconds({{list_to_integer(Year), list_to_integer(Month), list_to_integer(Day)}, {0,0,1}}), Count }
++                 [Year, Month, Day] = ejabberd_regexp:split(iolist_to_binary(TableName), <<"[^0-9]+">>),
++                 { calendar:datetime_to_gregorian_seconds({{binary_to_integer(Year), binary_to_integer(Month), binary_to_integer(Day)}, {0,0,1}}), Count }
 +           end,
 +    % convert to [{63364377601,1}, {63360662401,1}, ... ]
 +    CStats = lists:map(CFun, Stats),
 +%
 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 +user_messages_at_parse_query(VHost, Date, Msgs, Query) ->
-+    case lists:keysearch("delete", 1, Query) of
++    case lists:keysearch(<<"delete">>, 1, Query) of
 +         {value, _} ->
 +             PMsgs = lists:filter(
 +                              fun(Msg) ->
-+                                   ID = jlib:encode_base64(binary_to_list(term_to_binary(Msg#msg.timestamp))),
-+                                   lists:member({"selected", ID}, Query)
++                                   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),
 +             gen_server:call(Proc, {delete_messages_by_user_at, PMsgs, Date}, ?CALL_TIMEOUT);
 +    end.
 +
 +user_messages_parse_query(User, VHost, Query) ->
-+    case lists:keysearch("delete", 1, Query) of
++    case lists:keysearch(<<"delete">>, 1, Query) of
 +         {value, _} ->
 +             Dates = get_dates(VHost),
 +             PDates = lists:filter(
 +                              fun(Date) ->
-+                                   ID = jlib:encode_base64(binary_to_list(term_to_binary(User++Date))),
-+                                   lists:member({"selected", ID}, Query)
++                                   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),
 +             Rez = lists:foldl(
 +                          fun(Date, Acc) ->
 +                              lists:append(Acc,
 +                                           [gen_server:call(Proc,
-+                                                            {delete_all_messages_by_user_at, User, Date},
++                                                            {delete_all_messages_by_user_at, User, iolist_to_binary(Date)},
 +                                                            ?CALL_TIMEOUT)])
 +                          end, [], PDates),
 +             case lists:member(error, Rez) of
 +    end.
 +
 +vhost_messages_parse_query(VHost, Query) ->
-+    case lists:keysearch("delete", 1, Query) of
++    case lists:keysearch(<<"delete">>, 1, Query) of
 +         {value, _} ->
 +             Dates = get_dates(VHost),
 +             PDates = lists:filter(
 +                              fun(Date) ->
-+                                   ID = jlib:encode_base64(binary_to_list(term_to_binary(VHost++Date))),
-+                                   lists:member({"selected", ID}, Query)
++                                   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),
 +             Rez = lists:foldl(fun(Date, Acc) ->
 +    end.
 +
 +vhost_messages_at_parse_query(VHost, Date, Stats, Query) ->
-+    case lists:keysearch("delete", 1, Query) of
++    case lists:keysearch(<<"delete">>, 1, Query) of
 +         {value, _} ->
 +             PStats = lists:filter(
 +                              fun({User, _Count}) ->
-+                                   ID = jlib:encode_base64(binary_to_list(term_to_binary(User++VHost))),
-+                                   lists:member({"selected", ID}, Query)
++                                   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),
 +             Rez = lists:foldl(fun({User, _Count}, Acc) ->
 +                                   lists:append(Acc, [gen_server:call(Proc,
 +                                                                      {delete_all_messages_by_user_at,
-+                                                                       User, Date},
++                                                                       iolist_to_binary(User), iolist_to_binary(Date)},
 +                                                                      ?CALL_TIMEOUT)])
 +                               end, [], PStats),
 +             case lists:member(error, Rez) of
 +             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 ->
 +    FromDBMod = list_to_atom(atom_to_list(?MODULE) ++ "_" ++ atom_to_list(FromDBName)),
 +
 +    {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]) ->
 +
 +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, Date),
 +    catch mod_logdb:rebuild_stats_at(VHost, Date),
 +    {ok, FromStats} = FromDBMod:get_vhost_stats_at(VHost, Date),
-+    ToStats = case mod_logdb:get_vhost_stats_at(VHost, Date) of
++    ToStats = case mod_logdb:get_vhost_stats_at(VHost, iolist_to_binary(Date)) of
 +                   {ok, Stats} -> Stats;
 +                   {error, _} -> []
 +              end,
 +    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(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,
 +                        %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(User, VHost, Date),
 +                        lists:foreach(fun(#msg{timestamp=Tst}) when length(Tst) == 16 ->
 +                          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 ->
 +                        ?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(User, VHost, 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(User, VHost, 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 == [] ->
 +
 +    ok.
 +
-+list_to_bool(Num) ->
++list_to_bool(Num) when is_binary(Num) ->
++    list_to_bool(binary_to_list(Num));
++list_to_bool(Num) when is_list(Num) ->
 +    case lists:member(Num, ["t", "true", "y", "yes", "1"]) of
 +         true ->
 +           true;
 +list_to_string([]) ->
 +    "";
 +list_to_string(List) when is_list(List) ->
-+    Str = lists:flatmap(fun(Elm) -> Elm ++ "\n" end, List),
++    Str = lists:flatmap(fun(Elm) when is_binary(Elm) ->
++                              binary_to_list(Elm) ++ "\n";
++                           (Elm) when is_list(Elm) ->
++                              Elm ++ "\n"
++                        end, List),
 +    lists:sublist(Str, length(Str)-1).
 +
 +string_to_list(null) ->
 +string_to_list([]) ->
 +    [];
 +string_to_list(String) ->
-+    {ok, List} = regexp:split(String, "\n"),
-+    List.
++    ejabberd_regexp:split(iolist_to_binary(String), <<"\n">>).
 +
 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 +%
 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 +-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;
 +            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} ->
 +                {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 = string: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});
-+                 ["mod_logdb_users"] ->
-+                      ?ITEMS_RESULT(AllowAdmin, LNode, {error, ?ERR_FORBIDDEN});
-+                 ["mod_logdb_users", [$@ | _]] ->
-+                      ?ITEMS_RESULT(AllowAdmin, LNode, {error, ?ERR_FORBIDDEN});
-+                 ["mod_logdb_users", _User] ->
-+                      ?ITEMS_RESULT(AllowAdmin, LNode, {error, ?ERR_FORBIDDEN});
-+                 ["mod_logdb_settings"] ->
-+                      ?ITEMS_RESULT(AllowAdmin, LNode, {error, ?ERR_FORBIDDEN});
++                 [<<"mod_logdb">>] ->
++                      ?ITEMS_RESULT(AllowAdmin, LNode, {error, Err});
++                 [<<"mod_logdb_users">>] ->
++                      ?ITEMS_RESULT(AllowAdmin, LNode, {error, Err});
++                 [<<"mod_logdb_users">>, <<$@, _/binary>>] ->
++                      ?ITEMS_RESULT(AllowAdmin, LNode, {error, Err});
++                 [<<"mod_logdb_users">>, _User] ->
++                      ?ITEMS_RESULT(AllowAdmin, LNode, {error, Err});
++                 [<<"mod_logdb_settings">>] ->
++                      ?ITEMS_RESULT(AllowAdmin, LNode, {error, Err});
 +                 _ ->
 +                      Acc
 +            end
 +    end.
 +
++-define(T(Lang, Text), translate:translate(Lang, Text)).
++
 +-define(NODE(Name, Node),
-+        {xmlelement, "item",
-+         [{"jid", Server},
-+          {"name", translate:translate(Lang, Name)},
-+          {"node", Node}], []}).
++    #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,
-+     [?NODE("Messages logging engine", "mod_logdb")]
++     [?NODE(<<"Messages logging engine">>, <<"mod_logdb">>)]
 +    };
-+get_local_items(_Host, ["mod_logdb"], Server, Lang) ->
++get_local_items(_Host, [<<"mod_logdb">>], Server, Lang) ->
 +    {result,
-+     [?NODE("Messages logging engine users", "mod_logdb_users"),
-+      ?NODE("Messages logging engine settings", "mod_logdb_settings")]
++     [?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", [$@ | Diap]], Server, Lang) ->
-+    case catch ejabberd_auth:dirty_get_registered_users() of
-+        {'EXIT', _Reason} ->
-+            ?ERR_INTERNAL_SERVER_ERROR;
-+        Users ->
-+            SUsers = lists:sort([{S, U} || {U, S} <- Users]),
-+            case catch begin
-+                           {ok, [S1, S2]} = regexp:split(Diap, "-"),
-+                           N1 = list_to_integer(S1),
-+                           N2 = list_to_integer(S2),
-+                           Sub = lists:sublist(SUsers, N1, N2 - N1 + 1),
-+                           lists:map(fun({S, U}) ->
-+                                      ?NODE(U ++ "@" ++ S, "mod_logdb_users/" ++ U ++ "@" ++ S)
-+                                     end, Sub)
-+                       end of
-+                {'EXIT', _Reason} ->
-+                    ?ERR_NOT_ACCEPTABLE;
-+                Res ->
-+                    {result, Res}
-+            end
++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) ->
++    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) ->
++get_local_items(_Host, [<<"mod_logdb_users">>, _User], _Server, _Lang) ->
 +    {result, []};
-+get_local_items(_Host, ["mod_logdb_settings"], _Server, _Lang) ->
++get_local_items(_Host, [<<"mod_logdb_settings">>], _Server, _Lang) ->
 +    {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};
-+        allow ->
-+            {result, Feats}
++      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 = string: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]);
-+                 ["mod_logdb"] ->
-+                    ?INFO_RESULT(deny, [?NS_COMMANDS]);
-+                 ["mod_logdb_users"] ->
-+                    ?INFO_RESULT(AllowAdmin, []);
-+                 ["mod_logdb_users", [$@ | _]] ->
-+                    ?INFO_RESULT(AllowAdmin, []);
-+                 ["mod_logdb_users", _User] ->
-+                    ?INFO_RESULT(AllowAdmin, [?NS_COMMANDS]);
-+                 ["mod_logdb_settings"] ->
-+                    ?INFO_RESULT(AllowAdmin, [?NS_COMMANDS]);
++                 [<<"mod_logdb">>] when AllowUser == allow; AllowAdmin == allow ->
++                    ?INFO_RESULT(allow, [?NS_COMMANDS], Lang);
++                 [<<"mod_logdb">>] ->
++                    ?INFO_RESULT(deny, [?NS_COMMANDS], Lang);
++                 [<<"mod_logdb_users">>] ->
++                    ?INFO_RESULT(AllowAdmin, [], Lang);
++                 [<<"mod_logdb_users">>, [$@ | _]] ->
++                    ?INFO_RESULT(AllowAdmin, [], Lang);
++                 [<<"mod_logdb_users">>, _User] ->
++                    ?INFO_RESULT(AllowAdmin, [?NS_COMMANDS], Lang);
++                 [<<"mod_logdb_settings">>] ->
++                    ?INFO_RESULT(AllowAdmin, [?NS_COMMANDS], Lang);
 +                 [] ->
 +                    Acc;
 +                 _ ->
-+                    %?MYDEBUG("asked for ~p features: ~p", [LNode, Allow]),
 +                    Acc
 +            end
 +    end.
 +
 +-define(INFO_IDENTITY(Category, Type, Name, Lang),
-+        [{xmlelement, "identity",
-+          [{"category", Category},
-+           {"type", Type},
-+           {"name", translate:translate(Lang, Name)}], []}]).
++    [#identity{category = Category, type = Type, name = ?T(Lang, Name)}]).
 +
 +-define(INFO_COMMAND(Name, Lang),
-+        ?INFO_IDENTITY("automation", "command-node", Name, Lang)).
++    ?INFO_IDENTITY(<<"automation">>, <<"command-node">>,
++               Name, Lang)).
 +
 +get_local_identity(Acc, _From, _To, Node, Lang) ->
-+    LNode = string: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] ->
++         [<<"mod_logdb">>] ->
++            ?INFO_COMMAND(<<"Messages logging engine">>, Lang);
++         [<<"mod_logdb_users">>] ->
++            ?INFO_COMMAND(<<"Messages logging engine users">>, Lang);
++         [<<"mod_logdb_users">>, User] ->
 +            ?INFO_COMMAND(User, Lang);
-+         ["mod_logdb_settings"] ->
-+            ?INFO_COMMAND("Messages logging engine settings", Lang);
-+         [] ->
-+            Acc;
++         [<<"mod_logdb_settings">>] ->
++            ?INFO_COMMAND(<<"Messages logging engine settings">>, Lang);
 +         _ ->
 +            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 = xml: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 = string: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 = xml:get_tag_attr_s("jid", N),
-+                Nd = xml: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.
 +-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 = string: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 ->
++         [<<"mod_logdb">>] when AllowUser == allow; AllowAdmin == allow ->
 +             ?COMMANDS_RESULT(allow, From, To, Request);
-+         ["mod_logdb_users", _User] when AllowAdmin == allow ->
++         [<<"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 ->
++         [<<"mod_logdb_settings">>] when AllowAdmin == allow ->
 +             ?COMMANDS_RESULT(allow, From, To, Request);
 +         _ ->
 +             Acc
 +    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 = string: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 set_form(From, LServer, LNode, Lang, Fields) of
-+                        {result, _Res} ->
-+                            adhoc:produce_response(
-+                              #adhoc_response{lang = Lang,
-+                                              node = Node,
-+                                              sessionid = SessionID,
-+                                              status = completed});
-+                        {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}
++       true ->
++            {error, xmpp:err_bad_request(<<"Unexpected action">>, Lang)}
 +    end.
 +
-+-define(LISTLINE(Label, Value),
-+                 {xmlelement, "option", [{"label", Label}],
-+                  [{xmlelement, "value", [], [{xmlcdata, Value}]}]}).
-+-define(DEFVAL(Value), {xmlelement, "value", [], [{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, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
-+               [{xmlelement, "title", [],
-+                 [{xmlcdata,
-+                   translate:translate(
-+                     Lang, "Messages logging engine settings")}]},
-+                {xmlelement, "instructions", [],
-+                 [{xmlcdata,
-+                   translate:translate(
-+                     Lang, "Set logging preferences")++ ": " ++ LUser ++ "@" ++ LServer}]},
-+                % default to log
-+                {xmlelement, "field", [{"type", "list-single"},
-+                                       {"label",
-+                                        translate:translate(Lang, "Default")},
-+                                       {"var", "dolog_default"}],
-+                 [?DEFVAL(atom_to_list(DLD)),
-+                  ?LISTLINE(translate:translate(Lang, "Log Messages"), "true"),
-+                  ?LISTLINE(translate:translate(Lang, "Do Not Log Messages"), "false")
-+                 ]},
-+                % do log list
-+                {xmlelement, "field", [{"type", "text-multi"},
-+                                       {"label",
-+                                        translate:translate(
-+                                          Lang, "Log Messages")},
-+                                       {"var", "dolog_list"}],
-+                 [{xmlelement, "value", [], [{xmlcdata, list_to_string(DLL)}]}]},
-+                % do not log list
-+                {xmlelement, "field", [{"type", "text-multi"},
-+                                       {"label",
-+                                        translate:translate(
-+                                          Lang, "Do Not Log Messages")},
-+                                       {"var", "donotlog_list"}],
-+                 [{xmlelement, "value", [], [{xmlcdata, 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) ->
-+    #state{dbmod=DBMod,
-+           dbs=DBs,
++    ?MYDEBUG("get_settings_form ~p ~p", [Host, Lang]),
++    #state{dbmod=_DBMod,
++           dbs=_DBs,
 +           dolog_default=DLD,
 +           ignore_jids=IgnoreJids,
 +           groupchat=GroupChat,
 +           drop_messages_on_user_removal=MRemoval,
 +           poll_users_settings=PollTime} = mod_logdb:get_module_settings(Host),
 +
-+    Backends = lists:map(fun({Backend, _Opts}) ->
-+                             ?LISTLINE(atom_to_list(Backend), atom_to_list(Backend))
-+                         end, DBs),
-+    DB = 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_list(Num);
-+            _ -> "unknown"
++            never -> <<"never">>;
++            Num when is_integer(Num) -> integer_to_binary(Num);
++            _ -> <<"unknown">>
 +       end,
-+    {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
-+               [{xmlelement, "title", [],
-+                 [{xmlcdata,
-+                   translate:translate(
-+                     Lang, "Messages logging engine settings") ++ " (run-time)"}]},
-+                {xmlelement, "instructions", [],
-+                 [{xmlcdata,
-+                   translate:translate(
-+                     Lang, "Set run-time settings")}]},
-+                % backends
-+                {xmlelement, "field", [{"type", "list-single"},
-+                                  {"label",
-+                                      translate:translate(Lang, "Backend")},
-+                                  {"var", "backend"}],
-+                 DBsL},
-+                % dbs
-+                {xmlelement, "field", [{"type", "text-multi"},
-+                                       {"label",
-+                                        translate:translate(
-+                                          Lang, "dbs")},
-+                                       {"var", "dbs"}],
-+                 [{xmlelement, "value", [], [{xmlcdata, lists:flatten(io_lib:format("~p.",[DBs]))}]}]},
-+                % default to log
-+                {xmlelement, "field", [{"type", "list-single"},
-+                                       {"label",
-+                                        translate:translate(Lang, "Default")},
-+                                       {"var", "dolog_default"}],
-+                 [?DEFVAL(atom_to_list(DLD)),
-+                  ?LISTLINE(translate:translate(Lang, "Log Messages"), "true"),
-+                  ?LISTLINE(translate:translate(Lang, "Do Not Log Messages"), "false")
-+                 ]},
-+                % drop_messages_on_user_removal
-+                {xmlelement, "field", [{"type", "list-single"},
-+                                       {"label",
-+                                        translate:translate(Lang, "Drop messages on user removal")},
-+                                       {"var", "drop_messages_on_user_removal"}],
-+                 [?DEFVAL(atom_to_list(MRemoval)),
-+                  ?LISTLINE(translate:translate(Lang, "Drop"), "true"),
-+                  ?LISTLINE(translate:translate(Lang, "Do not drop"), "false")
-+                 ]},
-+                % groupchat
-+                {xmlelement, "field", [{"type", "list-single"},
-+                                       {"label",
-+                                        translate:translate(Lang, "Groupchat messages logging")},
-+                                       {"var", "groupchat"}],
-+                 [?DEFVAL(atom_to_list(GroupChat)),
-+                  ?LISTLINE("all", "all"),
-+                  ?LISTLINE("none", "none"),
-+                  ?LISTLINE("send", "send"),
-+                  ?LISTLINE("half", "half")
-+                 ]},
-+                % ignore_jids
-+                {xmlelement, "field", [{"type", "text-multi"},
-+                                       {"label",
-+                                        translate:translate(
-+                                          Lang, "Jids/Domains to ignore")},
-+                                       {"var", "ignore_list"}],
-+                 [{xmlelement, "value", [], [{xmlcdata, list_to_string(IgnoreJids)}]}]},
-+                % purge older days
-+                {xmlelement, "field", [{"type", "text-single"},
-+                                       {"label",
-+                                        translate:translate(
-+                                          Lang, "Purge messages older than (days)")},
-+                                       {"var", "purge_older_days"}],
-+                 [{xmlelement, "value", [], [{xmlcdata, PurgeDays}]}]},
-+                % poll users settings
-+                {xmlelement, "field", [{"type", "text-single"},
-+                                       {"label",
-+                                        translate:translate(
-+                                          Lang, "Poll users settings (seconds)")},
-+                                       {"var", "poll_users_settings"}],
-+                 [{xmlelement, "value", [], [{xmlcdata, integer_to_list(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;
++check_log_list([<<>>]) ->
++    ok;
 +check_log_list([Head | Tail]) ->
-+    case lists:member($@, Head) of
-+         true -> ok;
-+         false -> throw(error)
++    case binary:match(Head, <<$@>>) of
++         nomatch -> throw(error);
++         {_, _} -> ok
 +    end,
 +    % this check for Head to be valid jid
-+    case jlib:string_to_jid(Head) of
-+         error ->
-+            throw(error);
-+         _ ->
-+            check_log_list(Tail)
-+    end;
-+check_log_list([]) ->
-+    ok.
++    case catch jid:decode(Head) of
++         {'EXIT', _Reason} -> throw(error);
++         _ -> check_log_list(Tail)
++    end.
 +
++check_ignore_list([]) ->
++    ok;
++check_ignore_list([<<>>]) ->
++    ok;
++check_ignore_list([<<>> | Tail]) ->
++    check_ignore_list(Tail);
 +check_ignore_list([Head | Tail]) ->
-+    case lists:member($@, Head) of
-+         true -> ok;
-+         false -> throw(error)
++    case binary:match(Head, <<$@>>) of
++         {_, _} -> 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 lists:nth(1, Head) of
-+                 $@ ->
-+                    % TODO: this allows spaces and special characters in Head. May be change to nodeprep?
-+                    case jlib:nameprep(lists:delete($@, Head)) of
-+                         error -> throw(error);
-+                         _ -> check_log_list(Tail)
-+                    end;
-+                 _ -> throw(error)
-+            end;
-+         _ ->
-+            check_ignore_list(Tail)
-+    end;
-+check_ignore_list([]) ->
-+    ok.
++    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)
++    DLD = case get_value(<<"dolog_default">>, XData) of
++               ValueDLD when ValueDLD == <<"true">>;
++                             ValueDLD == <<"false">> ->
++                   list_to_bool(ValueDLD);
++               _ -> 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)
-+          end,
-+    GroupChat = case lists:keysearch("groupchat", 1, XData) of
-+                     {value, {_, [Str2]}} when Str2 == "none";
-+                                               Str2 == "all";
-+                                               Str2 == "send";
-+                                               Str2 == "half" ->
-+                       list_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 list_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 list_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,
 +           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};
-+         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 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 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} ->
 +            [];
 +            case length(SUsers) of
 +                N when N =< 100 ->
 +                    lists:map(fun({S, U}) ->
-+                                      ?NODE(U ++ "@" ++ S, "mod_logdb_users/" ++ U ++ "@" ++ S)
++                                  #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 =
-+                                          "@" ++ integer_to_list(K) ++
-+                                          "-" ++ integer_to_list(L),
++                                      Node = <<"@",
++                                               (integer_to_binary(K))/binary,
++                                               "-",
++                                               (integer_to_binary(L))/binary
++                                             >>,
 +                                      {FS, FU} = lists:nth(K, SUsers),
 +                                      {LS, LU} =
 +                                          if L < N -> lists:nth(L, SUsers);
 +                                             true -> lists:last(SUsers)
 +                                          end,
 +                                      Name =
-+                                          FU ++ "@" ++ FS ++
-+                                          " -- " ++
-+                                          LU ++ "@" ++ LS,
-+                                      ?NODE(Name, "mod_logdb_users/" ++ Node)
++                                          <<FU/binary, "@", FS/binary,
++                                           " -- ",
++                                           LU/binary, "@", LS/binary>>,
++                                      #disco_item{jid = jid:make(Host),
++                                                  node = <<"mod_logdb_users/", Node/binary>>,
++                                                  name = Name}
 +                              end, lists:seq(1, N, M))
 +            end
 +    end.
 +%
 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 +webadmin_menu(Acc, _Host, Lang) ->
-+    [{"messages", ?T("Users Messages")} | Acc].
++    [{<<"messages">>, ?T(<<"Users Messages">>)} | Acc].
 +
 +webadmin_user(Acc, User, Server, Lang) ->
 +    Sett = get_user_settings(User, Server),
 +    Log =
 +      case Sett#user_settings.dolog_default of
 +           false ->
-+              ?INPUTT("submit", "dolog", "Log Messages");
++              ?INPUTT(<<"submit">>, <<"dolog">>, <<"Log Messages">>);
 +           true ->
-+              ?INPUTT("submit", "donotlog", "Do Not Log Messages");
++              ?INPUTT(<<"submit">>, <<"donotlog">>, <<"Do Not Log Messages">>);
 +           _ -> []
 +      end,
-+    Acc ++ [?XE("h3", [?ACT("messages/", "Messages"), ?C(" "), Log])].
++    Acc ++ [?XE(<<"h3">>, [?ACT(<<"messages/">>, <<"Messages">>), ?C(<<" ">>), Log])].
 +
 +webadmin_page(_, Host,
-+              #request{path = ["messages"],
++              #request{path = [<<"messages">>],
 +                       q = Query,
-+                       lang = Lang}) when is_list(Host) ->
++                       lang = Lang}) ->
 +    Res = vhost_messages_stats(Host, Query, Lang),
 +    {stop, Res};
 +webadmin_page(_, Host,
-+              #request{path = ["messages", Date],
++              #request{path = [<<"messages">>, Date],
 +                       q = Query,
-+                       lang = Lang}) when is_list(Host) ->
++                       lang = Lang}) ->
 +    Res = vhost_messages_stats_at(Host, Query, Lang, Date),
 +    {stop, Res};
 +webadmin_page(_, Host,
-+              #request{path = ["user", U, "messages"],
++              #request{path = [<<"user">>, U, <<"messages">>],
 +                       q = Query,
 +                       lang = Lang}) ->
 +    Res = user_messages_stats(U, Host, Query, Lang),
 +    {stop, Res};
 +webadmin_page(_, Host,
-+              #request{path = ["user", U, "messages", Date],
++              #request{path = [<<"user">>, U, <<"messages">>, Date],
 +                       q = Query,
 +                       lang = Lang}) ->
 +    Res = mod_logdb:user_messages_stats_at(U, Host, Query, Lang, Date),
 +    {stop, Res};
-+webadmin_page(Acc, _, _) -> Acc.
++webadmin_page(Acc, _Host, _R) -> Acc.
 +
-+user_parse_query(_, "dolog", User, Server, _Query) ->
++user_parse_query(_, <<"dolog">>, User, Server, _Query) ->
 +    Sett = get_user_settings(User, Server),
 +    % TODO: check returned value
 +    set_user_settings(User, Server, Sett#user_settings{dolog_default=true}),
 +    {stop, ok};
-+user_parse_query(_, "donotlog", User, Server, _Query) ->
++user_parse_query(_, <<"donotlog">>, User, Server, _Query) ->
 +    Sett = get_user_settings(User, Server),
 +    % TODO: check returned value
 +    set_user_settings(User, Server, Sett#user_settings{dolog_default=false}),
 +    case Value of
 +         {'EXIT', CReason} ->
 +              ?ERROR_MSG("Failed to get_vhost_stats: ~p", [CReason]),
-+              [?XC("h1", ?T("Error occupied while fetching list"))];
++              [?XC(<<"h1">>, ?T(<<"Error occupied while fetching list">>))];
 +         {error, GReason} ->
 +              ?ERROR_MSG("Failed to get_vhost_stats: ~p", [GReason]),
-+              [?XC("h1", ?T("Error occupied while fetching list"))];
++              [?XC(<<"h1">>, ?T(<<"Error occupied while fetching list">>))];
 +         {ok, []} ->
-+              [?XC("h1", ?T("No logged messages for ") ++ Server)];
++              [?XC(<<"h1">>, list_to_binary(io_lib:format(?T(<<"No logged messages for ~s">>), [Server])))];
 +         {ok, Dates} ->
 +              Fun = fun({Date, Count}) ->
-+                         ID = jlib:encode_base64(binary_to_list(term_to_binary(Server++Date))),
-+                         ?XE("tr",
-+                          [?XE("td", [?INPUT("checkbox", "selected", ID)]),
-+                           ?XE("td", [?AC(Date, Date)]),
-+                           ?XC("td", integer_to_list(Count))
++                         DateBin = iolist_to_binary(Date),
++                         ID = misc:encode_base64( << Server/binary, DateBin/binary >> ),
++                         ?XE(<<"tr">>,
++                          [?XAE(<<"td">>, [{<<"class">>, <<"valign">>}],
++                            [?INPUT(<<"checkbox">>, <<"selected">>, ID)]),
++                           ?XE(<<"td">>, [?AC(DateBin, DateBin)]),
++                           ?XC(<<"td">>, integer_to_binary(Count))
 +                          ])
 +                    end,
-+              [?XC("h1", ?T("Logged messages for ") ++ Server)] ++
++
++              [?XC(<<"h1">>, list_to_binary(io_lib:format(?T(<<"Logged messages for ~s">>), [Server])))] ++
 +               case Res of
-+                    ok -> [?CT("Submitted"), ?P];
-+                    error -> [?CT("Bad format"), ?P];
++                    ok -> [?CT(<<"Submitted">>), ?P];
++                    error -> [?CT(<<"Bad format">>), ?P];
 +                    nothing -> []
 +               end ++
-+               [?XAE("form", [{"action", ""}, {"method", "post"}],
-+                [?XE("table",
-+                 [?XE("thead",
-+                  [?XE("tr",
-+                   [?X("td"),
-+                    ?XCT("td", "Date"),
-+                    ?XCT("td", "Count")
++               [?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
++                [?XE(<<"table">>,
++                 [?XE(<<"thead">>,
++                  [?XE(<<"tr">>,
++                   [?X(<<"td">>),
++                    ?XCT(<<"td">>, <<"Date">>),
++                    ?XCT(<<"td">>, <<"Count">>)
 +                   ])]),
-+                  ?XE("tbody",
++                  ?XE(<<"tbody">>,
 +                      lists:map(Fun, Dates)
 +                     )]),
 +                  ?BR,
-+                  ?INPUTT("submit", "delete", "Delete Selected")
++                  ?INPUTT(<<"submit">>, <<"delete">>, <<"Delete Selected">>)
 +                ])]
 +   end.
 +
 +   case Value of
 +        {'EXIT', CReason} ->
 +             ?ERROR_MSG("Failed to get_vhost_stats_at: ~p", [CReason]),
-+             [?XC("h1", ?T("Error occupied while fetching list"))];
++             [?XC(<<"h1">>, ?T(<<"Error occupied while fetching list">>))];
 +        {error, GReason} ->
 +             ?ERROR_MSG("Failed to get_vhost_stats_at: ~p", [GReason]),
-+             [?XC("h1", ?T("Error occupied while fetching list"))];
++             [?XC(<<"h1">>, ?T(<<"Error occupied while fetching list">>))];
 +        {ok, []} ->
-+             [?XC("h1", ?T("No logged messages for ") ++ Server ++ ?T(" at ") ++ Date)];
-+        {ok, Users} ->
-+             Res = case catch vhost_messages_at_parse_query(Server, Date, Users, Query) of
++             [?XC(<<"h1">>, list_to_binary(io_lib:format(?T(<<"No logged messages for ~s at ~s">>), [Server, Date])))];
++        {ok, Stats} ->
++             Res = case catch vhost_messages_at_parse_query(Server, Date, Stats, Query) of
 +                        {'EXIT', Reason} ->
 +                            ?ERROR_MSG("~p", [Reason]),
 +                            error;
 +                        VResult -> VResult
 +                   end,
 +             Fun = fun({User, Count}) ->
-+                         ID = jlib:encode_base64(binary_to_list(term_to_binary(User++Server))),
-+                         ?XE("tr",
-+                          [?XE("td", [?INPUT("checkbox", "selected", ID)]),
-+                           ?XE("td", [?AC("../user/"++User++"/messages/"++Date, User)]),
-+                           ?XC("td", integer_to_list(Count))
++                         UserBin = iolist_to_binary(User),
++                         ID = misc:encode_base64( << UserBin/binary, Server/binary >> ),
++                         ?XE(<<"tr">>,
++                          [?XAE(<<"td">>, [{<<"class">>, <<"valign">>}],
++                            [?INPUT(<<"checkbox">>, <<"selected">>, ID)]),
++                           ?XE(<<"td">>, [?AC(<< <<"../user/">>/binary, UserBin/binary, <<"/messages/">>/binary, Date/binary >>, UserBin)]),
++                           ?XC(<<"td">>, integer_to_binary(Count))
 +                          ])
 +                   end,
-+             [?XC("h1", ?T("Logged messages for ") ++ Server ++ ?T(" at ") ++ Date)] ++
++             [?XC(<<"h1">>, list_to_binary(io_lib:format(?T(<<"Logged messages for ~s at ~s">>), [Server, Date])))] ++
 +              case Res of
-+                    ok -> [?CT("Submitted"), ?P];
-+                    error -> [?CT("Bad format"), ?P];
++                    ok -> [?CT(<<"Submitted">>), ?P];
++                    error -> [?CT(<<"Bad format">>), ?P];
 +                    nothing -> []
 +              end ++
-+              [?XAE("form", [{"action", ""}, {"method", "post"}],
-+                [?XE("table",
-+                 [?XE("thead",
-+                  [?XE("tr",
-+                   [?X("td"),
-+                    ?XCT("td", "User"),
-+                    ?XCT("td", "Count")
++              [?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
++                [?XE(<<"table">>,
++                 [?XE(<<"thead">>,
++                  [?XE(<<"tr">>,
++                   [?X(<<"td">>),
++                    ?XCT(<<"td">>, <<"User">>),
++                    ?XCT(<<"td">>, <<"Count">>)
 +                   ])]),
-+                  ?XE("tbody",
-+                      lists:map(Fun, Users)
++                  ?XE(<<"tbody">>,
++                      lists:map(Fun, Stats)
 +                     )]),
 +                  ?BR,
-+                  ?INPUTT("submit", "delete", "Delete Selected")
++                  ?INPUTT(<<"submit">>, <<"delete">>, <<"Delete Selected">>)
 +                ])]
 +   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} ->
 +   case Value of
 +        {'EXIT', CReason} ->
 +            ?ERROR_MSG("Failed to get_user_stats: ~p", [CReason]),
-+            [?XC("h1", ?T("Error occupied while fetching days"))];
++            [?XC(<<"h1">>, ?T(<<"Error occupied while fetching days">>))];
 +        {error, GReason} ->
 +            ?ERROR_MSG("Failed to get_user_stats: ~p", [GReason]),
-+            [?XC("h1", ?T("Error occupied while fetching days"))];
++            [?XC(<<"h1">>, ?T(<<"Error occupied while fetching days">>))];
 +        {ok, []} ->
-+            [?XC("h1", ?T("No logged messages for ") ++ Jid)];
++            [?XC(<<"h1">>, list_to_binary(io_lib:format(?T(<<"No logged messages for ~s">>), [Jid])))];
 +        {ok, Dates} ->
 +            Fun = fun({Date, Count}) ->
-+                      ID = jlib:encode_base64(binary_to_list(term_to_binary(User++Date))),
-+                      ?XE("tr",
-+                       [?XE("td", [?INPUT("checkbox", "selected", ID)]),
-+                        ?XE("td", [?AC(Date, Date)]),
-+                        ?XC("td", integer_to_list(Count))
++                      DateBin = iolist_to_binary(Date),
++                      ID = misc:encode_base64( << User/binary, DateBin/binary >> ),
++                      ?XE(<<"tr">>,
++                       [?XAE(<<"td">>, [{<<"class">>, <<"valign">>}],
++                         [?INPUT(<<"checkbox">>, <<"selected">>, ID)]),
++                        ?XE(<<"td">>, [?AC(DateBin, DateBin)]),
++                        ?XC(<<"td">>, iolist_to_binary(integer_to_list(Count)))
 +                       ])
-+                       %[?AC(Date, Date ++ " (" ++ integer_to_list(Count) ++ ")"), ?BR]
 +                  end,
-+            [?XC("h1", ?T("Logged messages for ") ++ Jid)] ++
++            [?XC(<<"h1">>, list_to_binary(io_lib:format(?T("Logged messages for ~s"), [Jid])))] ++
 +             case Res of
-+                   ok -> [?CT("Submitted"), ?P];
-+                   error -> [?CT("Bad format"), ?P];
++                   ok -> [?CT(<<"Submitted">>), ?P];
++                   error -> [?CT(<<"Bad format">>), ?P];
 +                   nothing -> []
 +             end ++
-+             [?XAE("form", [{"action", ""}, {"method", "post"}],
-+              [?XE("table",
-+               [?XE("thead",
-+                [?XE("tr",
-+                 [?X("td"),
-+                  ?XCT("td", "Date"),
-+                  ?XCT("td", "Count")
++             [?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
++              [?XE(<<"table">>,
++               [?XE(<<"thead">>,
++                [?XE(<<"tr">>,
++                 [?X(<<"td">>),
++                  ?XCT(<<"td">>, <<"Date">>),
++                  ?XCT(<<"td">>, <<"Count">>)
 +                 ])]),
-+                ?XE("tbody",
++                ?XE(<<"tbody">>,
 +                    lists:map(Fun, Dates)
 +                   )]),
 +                ?BR,
-+                ?INPUTT("submit", "delete", "Delete Selected")
++                ?INPUTT(<<"submit">>, <<"delete">>, <<"Delete Selected">>)
 +              ])]
 +    end.
 +
 +    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]),
 +   case Value of
 +        {'EXIT', CReason} ->
 +           ?ERROR_MSG("Failed to get_user_messages_at: ~p", [CReason]),
-+           [?XC("h1", ?T("Error occupied while fetching messages"))];
++           [?XC(<<"h1">>, ?T(<<"Error occupied while fetching messages">>))];
 +        {error, GReason} ->
 +           ?ERROR_MSG("Failed to get_user_messages_at: ~p", [GReason]),
-+           [?XC("h1", ?T("Error occupied while fetching messages"))];
++           [?XC(<<"h1">>, ?T(<<"Error occupied while fetching messages">>))];
 +        {ok, []} ->
-+           [?XC("h1", ?T("No logged messages for ") ++ Jid ++ ?T(" at ") ++ Date)];
++           [?XC(<<"h1">>, list_to_binary(io_lib:format(?T(<<"No logged messages for ~s at ~s">>), [Jid, Date])))];
 +        {ok, User_messages} ->
 +           Res =  case catch user_messages_at_parse_query(Server,
-+                                                                    Date,
-+                                                                    User_messages,
-+                                                                    Query) of
++                                                          Date,
++                                                          User_messages,
++                                                          Query) of
 +                       {'EXIT', Reason} ->
 +                            ?ERROR_MSG("~p", [Reason]),
 +                            error;
 +           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}
-+                           end, UR),
++                              {jid:encode(Item#roster.jid), Item#roster.name}
++                          end, UR),
 +
 +           UniqUsers = lists:foldl(fun(#msg{peer_name=PName, peer_server=PServer}, List) ->
 +                                 ToAdd = PName++"@"++PServer,
 +                               end, [], User_messages),
 +
 +           % Users to filter (sublist of UniqUsers)
-+           CheckedUsers = case lists:keysearch("filter", 1, Query) of
++           CheckedUsers = case lists:keysearch(<<"filter">>, 1, Query) of
 +                           {value, _} ->
 +                              lists:filter(fun(UFUser) ->
-+                                                ID = jlib:encode_base64(binary_to_list(term_to_binary(UFUser))),
-+                                                lists:member({"selected", ID}, Query)
++                                                ID = misc:encode_base64(term_to_binary(UFUser)),
++                                                lists:member({<<"selected">>, ID}, Query)
 +                                           end, UniqUsers);
 +                           false -> []
 +                         end,
 +
 +           % UniqUsers in html (noone selected -> everyone selected)
 +           Users = lists:map(fun(UHUser) ->
-+                                ID = jlib:encode_base64(binary_to_list(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)];
-+                                         false -> [?INPUT("checkbox", "selected", ID)]
++                                         true -> [?INPUTC(<<"checkbox">>, <<"selected">>, ID)];
++                                         false when CheckedUsers == [] -> [?INPUTC(<<"checkbox">>, <<"selected">>, ID)];
++                                         false -> [?INPUT(<<"checkbox">>, <<"selected">>, ID)]
 +                                        end,
 +                                Nick =
 +                                   case search_user_nick(UHUser, UserRoster) of
-+                                        nothing -> "";
-+                                        N -> " ("++ N ++")"
++                                        nothing -> <<"">>;
++                                        N -> iolist_to_binary( " ("++ N ++")" )
 +                                   end,
-+                                ?XE("tr",
-+                                 [?XE("td", Input),
-+                                  ?XC("td", UHUser++Nick)])
++                                ?XE(<<"tr">>,
++                                 [?XE(<<"td">>, Input),
++                                  ?XC(<<"td">>, iolist_to_binary(UHUser++Nick))])
 +                             end, lists:sort(UniqUsers)),
 +           % Messages to show (based on Users)
 +           User_messages_filtered = case CheckedUsers of
 +                               peer_name=PName, peer_server=PServer, peer_resource=PRes,
 +                               type=Type,
 +                               body=Body}) ->
-+                      TextRaw = case Subject of
-+                                     "" -> Body;
-+                                     _ -> [?T("Subject"),": ",Subject,"<br>", Body]
-+                                end,
-+                      ID = jlib:encode_base64(binary_to_list(term_to_binary(Timestamp))),
-+                      % replace \n with <br>
-+                      Text = lists:map(fun(10) -> "<br>";
-+                                           (A) -> A
-+                                        end, TextRaw),
++                      Text = case Subject of
++                                  "" -> iolist_to_binary(Body);
++                                  _ -> iolist_to_binary([binary_to_list(?T(<<"Subject">>)) ++ ": " ++ Subject ++ "\n" ++ Body])
++                             end,
 +                      Resource = case PRes of
 +                                      [] -> [];
 +                                      undefined -> [];
 +                                   PName++"@"++PServer;
 +                              N -> N
 +                         end,
-+                      ?XE("tr",
-+                       [?XE("td", [?INPUT("checkbox", "selected", ID)]),
-+                        ?XC("td", convert_timestamp(Timestamp)),
-+                        ?XC("td", atom_to_list(Direction)++": "++UserNick),
-+                        ?XC("td", Text)])
++                      ID = misc:encode_base64(term_to_binary(Timestamp)),
++                      ?XE(<<"tr">>,
++                       [?XE(<<"td">>, [?INPUT(<<"checkbox">>, <<"selected">>, ID)]),
++                        ?XC(<<"td">>, iolist_to_binary(convert_timestamp(Timestamp))),
++                        ?XC(<<"td">>, iolist_to_binary(atom_to_list(Direction)++": "++UserNick)),
++                        ?XE(<<"td">>, [?XC(<<"pre">>, Text)])])
 +                 end,
 +           % Filtered user messages in html
 +           Msgs = lists:map(Msgs_Fun, lists:sort(User_messages_filtered)),
 +
-+           [?XC("h1", ?T("Logged messages for ") ++ Jid ++ ?T(" at ") ++ Date)] ++
++           [?XC(<<"h1">>, list_to_binary(io_lib:format(?T(<<"Logged messages for ~s at ~s">>), [Jid, Date])))] ++
 +            case Res of
-+                 ok -> [?CT("Submitted"), ?P];
-+                 error -> [?CT("Bad format"), ?P];
++                 ok -> [?CT(<<"Submitted">>), ?P];
++                 error -> [?CT(<<"Bad format">>), ?P];
 +                 nothing -> []
 +            end ++
-+            [?XAE("form", [{"action", ""}, {"method", "post"}],
-+             [?XE("table",
-+                  [?XE("thead",
-+                       [?X("td"),
-+                        ?XCT("td", "User")
++            [?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
++             [?XE(<<"table">>,
++                  [?XE(<<"thead">>,
++                       [?X(<<"td">>),
++                        ?XCT(<<"td">>, <<"User">>)
 +                       ]
 +                      ),
-+                   ?XE("tbody",
++                   ?XE(<<"tbody">>,
 +                        Users
 +                      )]),
-+              ?INPUTT("submit", "filter", "Filter Selected")
++              ?INPUTT(<<"submit">>, <<"filter">>, <<"Filter Selected">>)
 +             ] ++
-+             [?XE("table",
-+                  [?XE("thead",
-+                       [?XE("tr",
-+                        [?X("td"),
-+                         ?XCT("td", "Date, Time"),
-+                         ?XCT("td", "Direction: Jid"),
-+                         ?XCT("td", "Body")
++             [?XE(<<"table">>,
++                  [?XE(<<"thead">>,
++                       [?XE(<<"tr">>,
++                        [?X(<<"td">>),
++                         ?XCT(<<"td">>, <<"Date, Time">>),
++                         ?XCT(<<"td">>, <<"Direction: Jid">>),
++                         ?XCT(<<"td">>, <<"Body">>)
 +                        ])]),
-+                   ?XE("tbody",
++                   ?XE(<<"tbody">>,
 +                        Msgs
 +                      )]),
-+              ?INPUTT("submit", "delete", "Delete Selected"),
++              ?INPUTT(<<"submit">>, <<"delete">>, <<"Delete Selected">>),
 +              ?BR
 +             ]
 +            )]
 +    end.
---- src/mod_logdb.hrl.orig     2009-02-05 19:21:29.000000000 +0200
-+++ src/mod_logdb.hrl  2009-02-05 19:21:02.000000000 +0200
-@@ -0,0 +1,35 @@
+diff --git a/src/mod_logdb.hrl b/src/mod_logdb.hrl
+new file mode 100644
+index 0000000000..49791f4e69
+--- /dev/null
++++ b/src/mod_logdb.hrl
+@@ -0,0 +1,33 @@
 +%%%----------------------------------------------------------------------
 +%%% File    : mod_logdb.hrl
-+%%% Author  : Oleg Palij (mailto,xmpp:o.palij@gmail.com)
++%%% Author  : Oleg Palij (mailto:o.palij@gmail.com)
 +%%% Purpose :
-+%%% Version : trunk
-+%%% Id      : $Id$
-+%%% Url     : http://www.dp.uz.gov.ua/o.palij/mod_logdb/
++%%% Url     : https://paleg.github.io/mod_logdb/
 +%%%----------------------------------------------------------------------
 +
 +-define(logdb_debug, true).
 +                        donotlog_list=[]}).
 +
 +-define(INPUTC(Type, Name, Value),
-+        ?XA("input", [{"type", Type},
-+                      {"name", Name},
-+                      {"value", Value},
-+                      {"checked", "true"}])).
---- src/mod_logdb_mnesia.erl.orig      2009-02-05 19:21:29.000000000 +0200
-+++ src/mod_logdb_mnesia.erl   2009-02-05 19:19:59.000000000 +0200
-@@ -0,0 +1,546 @@
++        ?XA(<<"input">>, [{<<"type">>,    Type},
++                          {<<"name">>,    Name},
++                          {<<"value">>,   Value},
++                          {<<"checked">>, <<"true">>}])).
+diff --git a/src/mod_logdb_mnesia.erl b/src/mod_logdb_mnesia.erl
+new file mode 100644
+index 0000000000..a08d5262c2
+--- /dev/null
++++ b/src/mod_logdb_mnesia.erl
+@@ -0,0 +1,553 @@
 +%%%----------------------------------------------------------------------
 +%%% File    : mod_logdb_mnesia.erl
-+%%% Author  : Oleg Palij (mailto,xmpp:o.palij@gmail.com)
++%%% Author  : Oleg Palij (mailto:o.palij@gmail.com)
 +%%% Purpose : mnesia backend for mod_logdb
-+%%% Version : trunk
-+%%% Id      : $Id$
-+%%% Url     : http://www.dp.uz.gov.ua/o.palij/mod_logdb/
++%%% Url     : https://paleg.github.io/mod_logdb/
 +%%%----------------------------------------------------------------------
 +
 +-module(mod_logdb_mnesia).
 +-author('o.palij@gmail.com').
 +
 +-include("mod_logdb.hrl").
-+-include("ejabberd.hrl").
-+-include("jlib.hrl").
++-include("logger.hrl").
 +
 +-behaviour(gen_logdb).
 +-behaviour(gen_server).
-+   
++
 +% gen_server
 +-export([code_change/3,handle_call/3,handle_cast/2,handle_info/2,init/1,terminate/2]).
 +% gen_mod
 +         get_dates/1,
 +         get_users_settings/1, get_user_settings/2, set_user_settings/3,
 +         drop_user/2]).
-+ 
++
 +-define(PROCNAME, mod_logdb_mnesia).
 +-define(CALL_TIMEOUT, 10000).
-+  
++
 +-record(state, {vhost}).
 +
 +-record(stats, {user, at, count}).
 +   "logdb_".
 +
 +suffix(VHost) ->
-+   "_" ++ VHost.
++   "_" ++ binary_to_list(VHost).
 +
 +stats_table(VHost) ->
 +   list_to_atom(prefix() ++ "stats" ++ suffix(VHost)).
 +    {reply, Reply, State};
 +handle_call({rebuild_stats_at, Date}, _From, #state{vhost=VHost}=State) ->
 +    Reply = rebuild_stats_at_int(VHost, Date),
-+    {reply, Reply, State}; 
++    {reply, Reply, State};
 +handle_call({delete_messages_by_user_at, Msgs, Date}, _From, #state{vhost=VHost}=State) ->
 +    Table = table_name(VHost, Date),
 +    Fun = fun() ->
 +% internals
 +%
 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-+log_message_int(VHost, #msg{timestamp=Timestamp}=Msg) ->
++log_message_int(VHost, #msg{timestamp=Timestamp}=MsgBin) ->
 +    Date = mod_logdb:convert_timestamp_brief(Timestamp),
 +
++    Msg = #msg{timestamp     = MsgBin#msg.timestamp,
++               owner_name    = binary_to_list(MsgBin#msg.owner_name),
++               peer_name     = binary_to_list(MsgBin#msg.peer_name),
++               peer_server   = binary_to_list(MsgBin#msg.peer_server),
++               peer_resource = binary_to_list(MsgBin#msg.peer_resource),
++               direction     = MsgBin#msg.direction,
++               type          = binary_to_list(MsgBin#msg.type),
++               subject       = binary_to_list(MsgBin#msg.subject),
++               body          = binary_to_list(MsgBin#msg.body)},
++
 +    ATable = table_name(VHost, Date),
 +    Fun = fun() ->
 +              mnesia:write_lock_table(ATable),
 +                     ?ERROR_MSG("Failed to log message: ~p", [CReason]),
 +                     error;
 +                  {atomic, ok} ->
-+                     ?MYDEBUG("Created msg table for ~p at ~p", [VHost, Date]),
-+                     log_message_int(VHost, Msg)
++                     ?MYDEBUG("Created msg table for ~s at ~s", [VHost, Date]),
++                     log_message_int(VHost, MsgBin)
 +             end;
 +         {aborted, TReason} ->
 +             ?ERROR_MSG("Failed to log message: ~p", [TReason]),
 +             error;
 +         {atomic, _} ->
-+             ?MYDEBUG("Logged ok for ~p, peer: ~p", [Msg#msg.owner_name++"@"++VHost,
-+                                                    Msg#msg.peer_name++"@"++Msg#msg.peer_server]),
++             ?MYDEBUG("Logged ok for ~s, peer: ~s", [ [Msg#msg.owner_name, <<"@">>, VHost],
++                                                      [Msg#msg.peer_name, <<"@">>, Msg#msg.peer_server] ]),
 +             increment_user_stats(Msg#msg.owner_name, VHost, Date)
 +    end.
 +
 +get_dates_int(VHost) ->
 +    Tables = mnesia:system_info(tables),
 +    lists:foldl(fun(ATable, Dates) ->
-+                    Table = atom_to_list(ATable),
-+                    case regexp:match(Table, VHost++"$") of
-+                         {match, _, _} ->
-+                            case regexp:match(Table,"_[0-9]+-[0-9]+-[0-9]+_") of
-+                                 {match, S, E} ->
-+                                     lists:append(Dates, [lists:sublist(Table,S+1,E-2)]);
++                    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
++                                 {match, [{S, E}]} ->
++                                     lists:append(Dates, [lists:sublist(binary_to_list(Table), S+1, E)]);
 +                                 nomatch ->
 +                                     Dates
 +                            end;
 +    case mnesia:transaction(fun() ->
 +                               mnesia:write_lock_table(Table),
 +                               mnesia:write_lock_table(STable),
++                               % Delete all stats for VHost at Date
++                               mnesia:foldl(DFun, [], STable),
 +                               % Calc stats for VHost at Date
 +                               case mnesia:foldl(CFun, [], Table) of
 +                                    [] -> empty;
 +                                    AStats ->
-+                                      % Delete all stats for VHost at Date
-+                                      mnesia:foldl(DFun, [], STable),
 +                                      % Write new calc'ed stats
 +                                      lists:foreach(fun({Owner, Count}) ->
 +                                                        WStat = #stats{user=Owner, at=Date, count=Count},
 +               {type, bag},
 +               {attributes, record_info(fields, msg)},
 +               {record_name, msg}]).
---- src/mod_logdb_mysql.erl.orig       2009-02-05 19:21:29.000000000 +0200
-+++ src/mod_logdb_mysql.erl    2009-02-05 19:20:23.000000000 +0200
-@@ -0,0 +1,1052 @@
+diff --git a/src/mod_logdb_mysql.erl b/src/mod_logdb_mysql.erl
+new file mode 100644
+index 0000000000..21d65e6578
+--- /dev/null
++++ b/src/mod_logdb_mysql.erl
+@@ -0,0 +1,1050 @@
 +%%%----------------------------------------------------------------------
 +%%% File    : mod_logdb_mysql.erl
-+%%% Author  : Oleg Palij (mailto,xmpp:o.palij@gmail.com)
++%%% Author  : Oleg Palij (mailto:o.palij@gmail.com)
 +%%% Purpose : MySQL backend for mod_logdb
-+%%% Version : trunk
-+%%% Id      : $Id$
-+%%% Url     : http://www.dp.uz.gov.ua/o.palij/mod_logdb/
++%%% Url     : https://paleg.github.io/mod_logdb/
 +%%%----------------------------------------------------------------------
 +
 +-module(mod_logdb_mysql).
 +-author('o.palij@gmail.com').
 +
 +-include("mod_logdb.hrl").
-+-include("ejabberd.hrl").
-+-include("jlib.hrl").
++-include("logger.hrl").
 +
 +-behaviour(gen_logdb).
 +-behaviour(gen_server).
 +% replace "." with "_"
 +escape_vhost(VHost) -> lists:map(fun(46) -> 95;
 +                                    (A) -> A
-+                                 end, VHost).
++                                 end, binary_to_list(VHost)).
 +prefix() ->
 +   "`logdb_".
 +
 +resources_table(VHost) ->
 +   prefix() ++ "resources" ++ suffix(VHost).
 +
-+ets_users_table(VHost) -> list_to_atom("logdb_users_" ++ VHost).
-+ets_servers_table(VHost) -> list_to_atom("logdb_servers_" ++ VHost).
-+ets_resources_table(VHost) -> list_to_atom("logdb_resources_" ++ VHost).
++ets_users_table(VHost) -> list_to_atom("logdb_users_" ++ binary_to_list(VHost)).
++ets_servers_table(VHost) -> list_to_atom("logdb_servers_" ++ binary_to_list(VHost)).
++ets_resources_table(VHost) -> list_to_atom("logdb_resources_" ++ binary_to_list(VHost)).
 +
 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 +%
 +init([VHost, Opts]) ->
 +   crypto:start(),
 +
-+   Server = gen_mod:get_opt(server, Opts, "localhost"),
-+   Port = gen_mod:get_opt(port, Opts, 3306),
-+   DB = gen_mod:get_opt(db, Opts, "logdb"),
-+   User = gen_mod:get_opt(user, Opts, "root"),
-+   Password = gen_mod:get_opt(password, Opts, ""),
++   Server = gen_mod:get_opt(server, Opts, fun(A) -> A end, <<"localhost">>),
++   Port = gen_mod:get_opt(port, Opts, fun(A) -> A end, 3306),
++   DB = gen_mod:get_opt(db, Opts, fun(A) -> A end, <<"logdb">>),
++   User = gen_mod:get_opt(user, Opts, fun(A) -> A end, <<"root">>),
++   Password = gen_mod:get_opt(password, Opts, fun(A) -> A end, <<"">>),
 +
 +   St = #state{vhost=VHost,
 +               server=Server, port=Port, db=DB,
 +                 ?MYDEBUG("MySQL (~p)~n", [Level]),
 +                 ?MYDEBUG(Format, Argument)
 +            end,
-+   mysql_conn:start(Server, Port, DBUser, Password, DB, LogFun).
++   ?INFO_MSG("Opening mysql connection ~s@~s:~p/~s", [DBUser, Server, Port, DB]),
++   p1_mysql_conn:start(binary_to_list(Server), Port,
++                       binary_to_list(DBUser), binary_to_list(Password),
++                       binary_to_list(DB), LogFun).
 +
 +close_mysql_connection(DBRef) ->
 +   ?MYDEBUG("Closing ~p mysql connection", [DBRef]),
-+   mysql_conn:stop(DBRef).
++   catch p1_mysql_conn:stop(DBRef).
 +
 +handle_call({log_message, Msg}, _From, #state{dbref=DBRef, vhost=VHost}=State) ->
 +    Date = convert_timestamp_brief(Msg#msg.timestamp),
 +
 +    Table = messages_table(VHost, Date),
-+    Owner_id = get_user_id(DBRef, VHost, Msg#msg.owner_name),
-+    Peer_name_id = get_user_id(DBRef, VHost, Msg#msg.peer_name),
-+    Peer_server_id = get_server_id(DBRef, VHost, Msg#msg.peer_server),
-+    Peer_resource_id = get_resource_id(DBRef, VHost, Msg#msg.peer_resource),
++    Owner_id = get_user_id(DBRef, VHost, binary_to_list(Msg#msg.owner_name)),
++    Peer_name_id = get_user_id(DBRef, VHost, binary_to_list(Msg#msg.peer_name)),
++    Peer_server_id = get_server_id(DBRef, VHost, binary_to_list(Msg#msg.peer_server)),
++    Peer_resource_id = get_resource_id(DBRef, VHost, binary_to_list(Msg#msg.peer_resource)),
 +
 +    Query = ["INSERT INTO ",Table," ",
 +               "(owner_id,",
 +                 "'", Peer_server_id, "',",
 +                 "'", Peer_resource_id, "',",
 +                 "'", atom_to_list(Msg#msg.direction), "',",
-+                 "'", Msg#msg.type, "',",
-+                 "'", ejabberd_odbc:escape(Msg#msg.subject), "',",
-+                 "'", ejabberd_odbc:escape(Msg#msg.body), "',",
++                 "'", binary_to_list(Msg#msg.type), "',",
++                 "'", binary_to_list( ejabberd_sql:escape(Msg#msg.subject) ), "',",
++                 "'", binary_to_list( ejabberd_sql:escape(Msg#msg.body) ), "',",
 +                 "'", Msg#msg.timestamp, "');"],
 +
 +    Reply =
 +       case sql_query_internal_silent(DBRef, Query) of
 +            {updated, _} ->
-+               ?MYDEBUG("Logged ok for ~p, peer: ~p", [Msg#msg.owner_name++"@"++VHost,
-+                                                       Msg#msg.peer_name++"@"++Msg#msg.peer_server]),
++               ?MYDEBUG("Logged ok for ~s, peer: ~s", [ [Msg#msg.owner_name, <<"@">>, VHost],
++                                                        [Msg#msg.peer_name, <<"@">>, Msg#msg.peer_server] ]),
 +               increment_user_stats(DBRef, Msg#msg.owner_name, Owner_id, VHost, Peer_name_id, Peer_server_id, Date);
 +            {error, Reason} ->
-+               case regexp:match(Reason, "#42S02") of
++               case ejabberd_regexp:run(iolist_to_binary(Reason), <<"#42S02">>) of
 +                    % Table doesn't exist
-+                    {match, _, _} ->
++                    match ->
 +                       case create_msg_table(DBRef, VHost, Date) of
 +                            error ->
 +                               error;
 +                            ok ->
 +                               {updated, _} = sql_query_internal(DBRef, Query),
-+                               increment_user_stats(DBRef, Msg#msg.owner_name, Owner_id, VHost, Peer_name_id, Peer_server_id, Date)
++                               increment_user_stats(DBRef, binary_to_list(Msg#msg.owner_name), Owner_id, VHost, Peer_name_id, Peer_server_id, Date)
 +                       end;
 +                    _ ->
 +                       ?ERROR_MSG("Failed to log message: ~p", [Reason]),
 +    Query = ["SELECT username,dolog_default,dolog_list,donotlog_list ",
 +                "FROM ",settings_table(VHost)," ",
 +             "JOIN ",users_table(VHost)," ON user_id=owner_id;"],
-+    Reply = 
++    Reply =
 +      case sql_query_internal(DBRef, Query) of
 +           {data, Result} ->
 +              {ok, lists:map(fun([Owner, DoLogDef, DoLogL, DoNotLogL]) ->
 +                       ?MYDEBUG("New settings for ~s@~s", [User, VHost]),
 +                       ok;
 +                   {error, Reason} ->
-+                       case regexp:match(Reason, "#23000") of
++                       case ejabberd_regexp:run(iolist_to_binary(Reason), <<"#23000">>) of
 +                            % Already exists
-+                            {match, _, _} ->
++                            match ->
 +                                ok;
 +                             _ ->
 +                                ?ERROR_MSG("Failed setup user ~p@~p: ~p", [User, VHost, Reason]),
 +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 regexp:match(Table, Reg) of
-+                                {match, 1, _} ->
-+                                   ?MYDEBUG("matched ~p against ~p", [Table, Reg]),
-+                                   case regexp:match(Table,"[0-9]+-[0-9]+-[0-9]+") of
-+                                        {match, S, E} ->
-+                                            lists:append(Dates, [lists:sublist(Table,S,E)]);
++                           case re:run(Table, Reg) of
++                                {match, _} ->
++                                   case re:run(Table, "[0-9]+-[0-9]+-[0-9]+") of
++                                        {match, [{S, E}]} ->
++                                            lists:append(Dates, [lists:sublist(Table, S+1, E)]);
 +                                        nomatch ->
 +                                            Dates
 +                                   end;
-+                               
 +                                _ ->
 +                                   Dates
 +                           end
 +            rebuild_all_stats_int(State),
 +            ok;
 +         {error, Reason} ->
-+            case regexp:match(Reason, "#42S01") of
-+                 {match, _, _} ->
++            case ejabberd_regexp:run(iolist_to_binary(Reason), <<"#42S01">>) of
++                 match ->
 +                   ?MYDEBUG("Stats table for ~p already exists", [VHost]),
 +                   CheckQuery = ["SHOW COLUMNS FROM ",SName," LIKE 'peer_%_id';"],
 +                   case sql_query_internal(DBRef, CheckQuery) of
 +                          NewId;
 +                      {error, Reason} ->
 +                          % this can be in clustered environment
-+                          {match, _, _} = regexp:match(Reason, "#23000"),
++                          match = ejabberd_regexp:run(iolist_to_binary(Reason), <<"#23000">>),
 +                          ?ERROR_MSG("Duplicate key name for ~p", [User]),
 +                          {ok, ClID} = get_user_id_from_db(DBRef, VHost, User),
 +                          ClID
 +                Id;
 +             {error, Reason} ->
 +                % this can be in clustered environment
-+                {match, _, _} = regexp:match(Reason, "#23000"),
++                match = ejabberd_regexp:run(iolist_to_binary(Reason), <<"#23000">>),
 +                ?ERROR_MSG("Duplicate key name for ~p", [Server]),
 +                update_servers_from_db(DBRef, VHost),
 +                [[Id1]] = ets:match(ets_servers_table(VHost), {Server, '$1'}),
 +
 +get_resource_id_from_db(DBRef, VHost, Resource) ->
 +  SQuery = ["SELECT resource_id FROM ",resources_table(VHost)," ",
-+               "WHERE resource=\"",ejabberd_odbc:escape(Resource),"\";"],
++               "WHERE resource=\"",binary_to_list(ejabberd_sql:escape(iolist_to_binary(Resource))),"\";"],
 +  case sql_query_internal(DBRef, SQuery) of
 +       % no such resource in db
 +       {data, []} ->
 +              % no such resource in db
 +              {ok, []} ->
 +                 IQuery = ["INSERT INTO ",resources_table(VHost)," ",
-+                              "SET resource=\"",ejabberd_odbc:escape(Resource),"\";"],
++                              "SET resource=\"",binary_to_list(ejabberd_sql:escape(iolist_to_binary(Resource))),"\";"],
 +                 case sql_query_internal_silent(DBRef, IQuery) of
 +                      {updated, _} ->
 +                          {ok, NewId} = get_resource_id_from_db(DBRef, VHost, Resource),
 +                          NewId;
 +                      {error, Reason} ->
 +                          % this can be in clustered environment
-+                          {match, _, _} = regexp:match(Reason, "#23000"),
-+                          ?ERROR_MSG("Duplicate key name for ~p", [Resource]),
++                          match = ejabberd_regexp:run(iolist_to_binary(Reason), <<"#23000">>),
++                          ?ERROR_MSG("Duplicate key name for ~s", [Resource]),
 +                          {ok, ClID} = get_resource_id_from_db(DBRef, VHost, Resource),
 +                          ClID
 +                 end;
 +
 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 +%
-+% SQL internals 
++% SQL internals
 +%
 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 +sql_query_internal(DBRef, Query) ->
 +
 +sql_query_internal_silent(DBRef, Query) ->
 +    ?MYDEBUG("DOING: \"~s\"", [lists:append(Query)]),
-+    get_result(mysql_conn:fetch(DBRef, Query, self(), ?MYSQL_TIMEOUT)).
++    get_result(p1_mysql_conn:fetch(DBRef, Query, self(), ?MYSQL_TIMEOUT)).
 +
 +get_result({updated, MySQLRes}) ->
-+    {updated, mysql:get_result_affected_rows(MySQLRes)};
++    {updated, p1_mysql:get_result_affected_rows(MySQLRes)};
 +get_result({data, MySQLRes}) ->
-+    {data, mysql:get_result_rows(MySQLRes)};
++    {data, p1_mysql:get_result_rows(MySQLRes)};
 +get_result({error, "query timed out"}) ->
 +    {error, "query timed out"};
 +get_result({error, MySQLRes}) ->
-+    Reason = mysql:get_result_reason(MySQLRes),
++    Reason = p1_mysql:get_result_reason(MySQLRes),
 +    {error, Reason}.
---- src/mod_logdb_mysql5.erl.orig      2009-02-05 19:21:29.000000000 +0200
-+++ src/mod_logdb_mysql5.erl   2009-02-05 19:20:14.000000000 +0200
-@@ -0,0 +1,978 @@
+diff --git a/src/mod_logdb_mysql5.erl b/src/mod_logdb_mysql5.erl
+new file mode 100644
+index 0000000000..c05ab958e2
+--- /dev/null
++++ b/src/mod_logdb_mysql5.erl
+@@ -0,0 +1,979 @@
 +%%%----------------------------------------------------------------------
 +%%% File    : mod_logdb_mysql5.erl
-+%%% Author  : Oleg Palij (mailto,xmpp:o.palij@gmail.com)
++%%% Author  : Oleg Palij (mailto:o.palij@gmail.com)
 +%%% Purpose : MySQL 5 backend for mod_logdb
-+%%% Version : trunk
-+%%% Id      : $Id$
-+%%% Url     : http://www.dp.uz.gov.ua/o.palij/mod_logdb/
++%%% Url     : https://paleg.github.io/mod_logdb/
 +%%%----------------------------------------------------------------------
 +
 +-module(mod_logdb_mysql5).
 +-author('o.palij@gmail.com').
 +
 +-include("mod_logdb.hrl").
-+-include("ejabberd.hrl").
-+-include("jlib.hrl").
++-include("logger.hrl").
 +
 +-behaviour(gen_logdb).
 +-behaviour(gen_server).
 +% replace "." with "_"
 +escape_vhost(VHost) -> lists:map(fun(46) -> 95;
 +                                    (A) -> A
-+                                 end, VHost).
++                                 end, binary_to_list(VHost)).
 +prefix() ->
 +   "`logdb_".
 +
 +init([VHost, Opts]) ->
 +   crypto:start(),
 +
-+   Server = gen_mod:get_opt(server, Opts, "localhost"),
-+   Port = gen_mod:get_opt(port, Opts, 3306),
-+   DB = gen_mod:get_opt(db, Opts, "logdb"),
-+   User = gen_mod:get_opt(user, Opts, "root"),
-+   Password = gen_mod:get_opt(password, Opts, ""),
++   Server = gen_mod:get_opt(server, Opts, fun(A) -> A end, <<"localhost">>),
++   Port = gen_mod:get_opt(port, Opts, fun(A) -> A end, 3306),
++   DB = gen_mod:get_opt(db, Opts, fun(A) -> A end, <<"logdb">>),
++   User = gen_mod:get_opt(user, Opts, fun(A) -> A end, <<"root">>),
++   Password = gen_mod:get_opt(password, Opts, fun(A) -> A end, <<"">>),
 +
 +   St = #state{vhost=VHost,
 +               server=Server, port=Port, db=DB,
 +                 ?MYDEBUG("MySQL (~p)~n", [Level]),
 +                 ?MYDEBUG(Format, Argument)
 +            end,
-+   mysql_conn:start(Server, Port, DBUser, Password, DB, [65536, 131072], LogFun).
++   ?INFO_MSG("Opening mysql connection ~s@~s:~p/~s", [DBUser, Server, Port, DB]),
++   p1_mysql_conn:start(binary_to_list(Server), Port,
++                       binary_to_list(DBUser), binary_to_list(Password),
++                       binary_to_list(DB), LogFun).
 +
 +close_mysql_connection(DBRef) ->
 +   ?MYDEBUG("Closing ~p mysql connection", [DBRef]),
-+   mysql_conn:stop(DBRef).
++   catch p1_mysql_conn:stop(DBRef).
 +
 +handle_call({rebuild_stats_at, Date}, _From, #state{dbref=DBRef, vhost=VHost}=State) ->
 +    Reply = rebuild_stats_at_int(DBRef, VHost, Date),
 +    Query = ["SELECT username,dolog_default,dolog_list,donotlog_list ",
 +                "FROM ",settings_table(VHost)," ",
 +             "JOIN ",users_table(VHost)," ON user_id=owner_id;"],
-+    Reply = 
++    Reply =
 +      case sql_query_internal(DBRef, Query) of
 +           {data, Result} ->
 +              {ok, lists:map(fun([Owner, DoLogDef, DoLogL, DoNotLogL]) ->
 +                       ?MYDEBUG("New settings for ~s@~s", [User, VHost]),
 +                       ok;
 +                   {error, Reason} ->
-+                       case regexp:match(Reason, "#23000") of
++                       case ejabberd_regexp:run(iolist_to_binary(Reason), <<"#23000">>) of
 +                            % Already exists
-+                            {match, _, _} ->
++                            match ->
 +                                ok;
 +                             _ ->
 +                                ?ERROR_MSG("Failed setup user ~p@~p: ~p", [User, VHost, Reason]),
 +            Query = [ "CALL ",logmessage_name(VHost)," "
 +                         "('", TableName, "',",
 +                         "'", Date, "',",
-+                         "'", Msg#msg.owner_name, "',",
-+                         "'", Msg#msg.peer_name, "',",
-+                         "'", Msg#msg.peer_server, "',",
-+                         "'", ejabberd_odbc:escape(Msg#msg.peer_resource), "',",
++                         "'", binary_to_list(Msg#msg.owner_name), "',",
++                         "'", binary_to_list(Msg#msg.peer_name), "',",
++                         "'", binary_to_list(Msg#msg.peer_server), "',",
++                         "'", binary_to_list( ejabberd_sql:escape(Msg#msg.peer_resource) ), "',",
 +                         "'", atom_to_list(Msg#msg.direction), "',",
-+                         "'", Msg#msg.type, "',",
-+                         "'", ejabberd_odbc:escape(Msg#msg.subject), "',",
-+                         "'", ejabberd_odbc:escape(Msg#msg.body), "',",
++                         "'", binary_to_list(Msg#msg.type), "',",
++                         "'", binary_to_list( ejabberd_sql:escape(Msg#msg.subject) ), "',",
++                         "'", binary_to_list( ejabberd_sql:escape(Msg#msg.body) ), "',",
 +                         "'", Msg#msg.timestamp, "');"],
 +
 +            case sql_query_internal(DBRef, Query) of
 +                 {updated, _} ->
-+                    ?MYDEBUG("Logged ok for ~p, peer: ~p", [Msg#msg.owner_name++"@"++VHost,
-+                                                            Msg#msg.peer_name++"@"++Msg#msg.peer_server]),
++                    ?MYDEBUG("Logged ok for ~s, peer: ~s", [ [Msg#msg.owner_name, <<"@">>, VHost],
++                                                             [Msg#msg.peer_name, <<"@">>, Msg#msg.peer_server] ]),
 +                    ok;
 +                 {error, _Reason} ->
 +                    error
 +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 regexp:match(Table, Reg) of
-+                                {match, 1, _} ->
-+                                   case regexp:match(Table,"[0-9]+-[0-9]+-[0-9]+") of
-+                                        {match, S, E} ->
-+                                            lists:append(Dates, [lists:sublist(Table,S,E)]);
++                           case re:run(Table, Reg) of
++                                {match, _} ->
++                                   case re:run(Table, "[0-9]+-[0-9]+-[0-9]+") of
++                                        {match, [{S, E}]} ->
++                                            lists:append(Dates, [lists:sublist(Table, S+1, E)]);
 +                                        nomatch ->
 +                                            Dates
 +                                   end;
 +                      Count = sql_query_internal(DBRef, ["SELECT count(*) FROM ",Table,";"]),
 +                      case Count of
 +                        {data, [["0"]]} ->
-+                           {updated, _} = sql_query_internal(DBRef, ["DROP VIEW IF EXISTS ",view_table(VHost,Date),";"]),
 +                           {updated, _} = sql_query_internal(DBRef, ["DROP TABLE ",Table,";"]),
-+                           {updated, _} = sql_query_internal(DBRef, ["LOCK TABLE ",STable," WRITE;"]),
++                           sql_query_internal(DBRef, ["UNLOCK TABLES;"]),
++                           {updated, _} = sql_query_internal(DBRef, ["DROP VIEW IF EXISTS ",view_table(VHost,Date),";"]),
++                           {updated, _} = sql_query_internal(DBRef, ["LOCK TABLE ",STable," WRITE, ",TempTable," WRITE;"]),
 +                           {updated, _} = sql_query_internal(DBRef, DQuery),
 +                           ok;
 +                        _ ->
 +            rebuild_all_stats_int(State),
 +            ok;
 +         {error, Reason} ->
-+            case regexp:match(Reason, "#42S01") of
-+                 {match, _, _} ->
++            case ejabberd_regexp:run(iolist_to_binary(Reason), <<"#42S01">>) of
++                 match ->
 +                   ?MYDEBUG("Stats table for ~p already exists", [VHost]),
 +                   CheckQuery = ["SHOW COLUMNS FROM ",SName," LIKE 'peer_%_id';"],
 +                   case sql_query_internal(DBRef, CheckQuery) of
 +
 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 +%
-+% SQL internals 
++% SQL internals
 +%
 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 +sql_query_internal(DBRef, Query) ->
 +
 +sql_query_internal_silent(DBRef, Query) ->
 +    ?MYDEBUG("DOING: \"~s\"", [lists:append(Query)]),
-+    get_result(mysql_conn:fetch(DBRef, Query, self(), ?MYSQL_TIMEOUT)).
++    get_result(p1_mysql_conn:fetch(DBRef, Query, self(), ?MYSQL_TIMEOUT)).
 +
 +get_result({updated, MySQLRes}) ->
-+    {updated, mysql:get_result_affected_rows(MySQLRes)};
++    {updated, p1_mysql:get_result_affected_rows(MySQLRes)};
 +get_result({data, MySQLRes}) ->
-+    {data, mysql:get_result_rows(MySQLRes)};
++    {data, p1_mysql:get_result_rows(MySQLRes)};
 +get_result({error, "query timed out"}) ->
 +    {error, "query timed out"};
 +get_result({error, MySQLRes}) ->
-+    Reason = mysql:get_result_reason(MySQLRes),
++    Reason = p1_mysql:get_result_reason(MySQLRes),
 +    {error, Reason}.
 +
 +get_user_id(DBRef, VHost, User) ->
 +                   DBIdNew;
 +               {error, Reason} ->
 +                   % this can be in clustered environment
-+                   {match, _, _} = regexp:match(Reason, "#23000"),
++                   match = ejabberd_regexp:run(iolist_to_binary(Reason), <<"#23000">>),
 +                   ?ERROR_MSG("Duplicate key name for ~p", [User]),
 +                   {data, [[ClID]]} = sql_query_internal(DBRef, SQuery),
 +                   ClID
 +    io_lib:format("
 +CREATE PROCEDURE ~s(tablename TEXT, atdate TEXT, owner TEXT, peer_name TEXT, peer_server TEXT, peer_resource TEXT, mdirection VARCHAR(4), mtype VARCHAR(10), msubject TEXT, mbody TEXT, mtimestamp DOUBLE)
 +BEGIN
-+   DECLARE ownerID MEDIUMINT UNSIGNED; 
++   DECLARE ownerID MEDIUMINT UNSIGNED;
 +   DECLARE peer_nameID MEDIUMINT UNSIGNED;
 +   DECLARE peer_serverID MEDIUMINT UNSIGNED;
 +   DECLARE peer_resourceID MEDIUMINT UNSIGNED;
 +   SELECT user_id INTO @ownerID FROM ~s WHERE username=owner;
 +   IF @ownerID IS NULL THEN
 +      INSERT INTO ~s SET username=owner;
-+      SET @ownerID = LAST_INSERT_ID(); 
++      SET @ownerID = LAST_INSERT_ID();
 +   END IF;
 +
 +   SELECT user_id INTO @peer_nameID FROM ~s WHERE username=peer_name;
 +      END IF;
 +   END IF;
 +END;", [logmessage_name(VHost),UName,UName,UName,UName,SName,SName,RName,RName,UName,UName,SName,RName,StName,StName]).
---- src/mod_logdb_pgsql.erl.orig       2009-02-05 19:21:29.000000000 +0200
-+++ src/mod_logdb_pgsql.erl    2009-02-05 19:20:29.000000000 +0200
-@@ -0,0 +1,1078 @@
+diff --git a/src/mod_logdb_pgsql.erl b/src/mod_logdb_pgsql.erl
+new file mode 100644
+index 0000000000..202c6ed4a8
+--- /dev/null
++++ b/src/mod_logdb_pgsql.erl
+@@ -0,0 +1,1104 @@
++% {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@gmail.com)
++%%% Author  : Oleg Palij (mailto:o.palij@gmail.com)
 +%%% Purpose : Posgresql backend for mod_logdb
-+%%% Version : trunk
-+%%% Id      : $Id$
-+%%% Url     : http://www.dp.uz.gov.ua/o.palij/mod_logdb/
++%%% Url     : https://paleg.github.io/mod_logdb/
 +%%%----------------------------------------------------------------------
 +
 +-module(mod_logdb_pgsql).
 +-author('o.palij@gmail.com').
 +
 +-include("mod_logdb.hrl").
-+-include("ejabberd.hrl").
-+-include("jlib.hrl").
++-include("logger.hrl").
 +
 +-behaviour(gen_logdb).
 +-behaviour(gen_server).
 +% replace "." with "_"
 +escape_vhost(VHost) -> lists:map(fun(46) -> 95;
 +                                    (A) -> A
-+                                 end, VHost).
++                                 end, binary_to_list(VHost)).
 +
 +prefix(Schema) ->
 +   Schema ++ ".\"" ++ "logdb_".
 +%
 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 +init([VHost, Opts]) ->
-+   Server = gen_mod:get_opt(server, Opts, "localhost"),
-+   DB = gen_mod:get_opt(db, Opts, "ejabberd_logdb"),
-+   User = gen_mod:get_opt(user, Opts, "root"),
-+   Port = gen_mod:get_opt(port, Opts, 5432),
-+   Password = gen_mod:get_opt(password, Opts, ""),
-+   Schema = gen_mod:get_opt(schema, Opts, "public"),
++   Server = gen_mod:get_opt(server, Opts, fun(A) -> A end, <<"localhost">>),
++   DB = gen_mod:get_opt(db, Opts, fun(A) -> A end, <<"ejabberd_logdb">>),
++   User = gen_mod:get_opt(user, Opts, fun(A) -> A end, <<"root">>),
++   Port = gen_mod:get_opt(port, Opts, fun(A) -> A end, 5432),
++   Password = gen_mod:get_opt(password, Opts, fun(A) -> A end, <<"">>),
++   Schema = binary_to_list(gen_mod:get_opt(schema, Opts, fun(A) -> A end, <<"public">>)),
 +
-+   ?MYDEBUG("Starting pgsql backend for ~p", [VHost]),
++   ?MYDEBUG("Starting pgsql backend for ~s", [VHost]),
 +
 +   St = #state{vhost=VHost,
 +               server=Server, port=Port, db=DB,
 +
 +open_pgsql_connection(#state{server=Server, port=Port, db=DB, schema=Schema,
 +                             user=User, password=Password} = _State) ->
++   ?INFO_MSG("Opening pgsql connection ~s@~s:~p/~s", [User, Server, Port, DB]),
 +   {ok, DBRef} = pgsql:connect(Server, DB, User, Password, Port),
 +   {updated, _} = sql_query_internal(DBRef, ["SET SEARCH_PATH TO ",Schema,";"]),
 +   {ok, DBRef}.
 +                 "('", TableName, "',",
 +                  "'", ViewName, "',",
 +                  "'", Date, "',",
-+                  "'", Msg#msg.owner_name, "',",
-+                  "'", Msg#msg.peer_name, "',",
-+                  "'", Msg#msg.peer_server, "',",
-+                  "'", ejabberd_odbc:escape(Msg#msg.peer_resource), "',",
++                  "'", binary_to_list(Msg#msg.owner_name), "',",
++                  "'", binary_to_list(Msg#msg.peer_name), "',",
++                  "'", binary_to_list(Msg#msg.peer_server), "',",
++                  "'", binary_to_list( ejabberd_sql:escape(Msg#msg.peer_resource) ), "',",
 +                  "'", atom_to_list(Msg#msg.direction), "',",
-+                  "'", Msg#msg.type, "',",
-+                  "'", ejabberd_odbc:escape(Msg#msg.subject), "',",
-+                  "'", ejabberd_odbc:escape(Msg#msg.body), "',",
++                  "'", binary_to_list(Msg#msg.type), "',",
++                  "'", binary_to_list( ejabberd_sql:escape(Msg#msg.subject) ), "',",
++                  "'", binary_to_list( ejabberd_sql:escape(Msg#msg.body) ), "',",
 +                  "'", Msg#msg.timestamp, "');"],
 +
 +    case sql_query_internal_silent(DBRef, Query) of
 +    % TODO: change this
 +         {data, [{"0"}]} ->
-+             ?MYDEBUG("Logged ok for ~p, peer: ~p", [Msg#msg.owner_name++"@"++VHost,
-+                                                     Msg#msg.peer_name++"@"++Msg#msg.peer_server]),
++             ?MYDEBUG("Logged ok for ~s, peer: ~s", [ [Msg#msg.owner_name, <<"@">>, VHost],
++                                                      [Msg#msg.peer_name, <<"@">>, Msg#msg.peer_server] ]),
 +             ok;
 +         {error, _Reason} ->
 +             error
 +                                  dolog_list=string_to_list(DoLogL),
 +                                  donotlog_list=string_to_list(DoNotLogL)}};
 +           {error, Reason} ->
-+              ?ERROR_MSG("Failed to get_user_settings for ~p@~p: ~p", [User, VHost, Reason]),
++              ?ERROR_MSG("Failed to get_user_settings for ~s@~s: ~p", [User, VHost, Reason]),
 +              error
 +      end,
 +    {reply, Reply, State};
 +    case sql_query_internal(DBRef, Query) of
 +         {data, Recs} ->
 +            lists:foldl(fun({_Schema, Table, _Type, _Owner}, Dates) ->
-+                             case regexp:match(Table,"[0-9]+-[0-9]+-[0-9]+") of
-+                                  {match, S, E} ->
-+                                      lists:append(Dates, [lists:sublist(Table,S,E)]);
++                             case re:run(Table,"[0-9]+-[0-9]+-[0-9]+") of
++                                  {match, [{S, E}]} ->
++                                      lists:append(Dates, [lists:sublist(Table, S+1, E)]);
 +                                  nomatch ->
 +                                      Dates
 +                             end
 +
 +    case sql_transaction_internal(DBRef, Fun) of
 +         {atomic, _} ->
-+            ?INFO_MSG("Rebuilded stats for ~p at ~p", [VHost, Date]),
++            ?INFO_MSG("Rebuilded stats for ~s at ~s", [VHost, Date]),
 +            ok;
 +         {aborted, Reason} ->
 +            ?ERROR_MSG("Failed to rebuild stats for ~s table: ~p.", [Date, Reason]),
 +                     {value, {code, "42P07"}} ->
 +                         exists;
 +                     _ ->
-+                         ?ERROR_MSG("Failed to create stats table for ~p: ~p", [VHost, Reason]),
++                         ?ERROR_MSG("Failed to create stats table for ~s: ~p", [VHost, Reason]),
 +                         error
 +                end
 +        end
 +      end,
 +    case sql_transaction_internal(DBRef, Fun) of
 +         {atomic, created} ->
-+            ?MYDEBUG("Created stats table for ~p", [VHost]),
++            ?MYDEBUG("Created stats table for ~s", [VHost]),
 +            rebuild_all_stats_int(State),
 +            ok;
 +         {atomic, exists} ->
-+            ?MYDEBUG("Stats table for ~p already exists", [VHost]),
-+            {match, F, L} = regexp:match(SName, "\".*\""),
-+            QTable = lists:sublist(SName, F+1, L-2),
++            ?MYDEBUG("Stats table for ~s already exists", [VHost]),
++            {match, [{F, L}]} = re:run(SName, "\".*\""),
++            QTable = lists:sublist(SName, F+2, L-2),
 +            OIDQuery = ["SELECT c.oid FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relname='",QTable,"' AND pg_catalog.pg_table_is_visible(c.oid);"],
 +            {data,[{OID}]} = sql_query_internal(DBRef, OIDQuery),
 +            CheckQuery = ["SELECT a.attname FROM pg_catalog.pg_attribute a  WHERE a.attrelid = '",OID,"' AND a.attnum > 0 AND NOT a.attisdropped AND a.attname ~ '^peer_.*_id$';"],
 +            ],
 +    case sql_query_internal_silent(DBRef, Query) of
 +         {updated, _} ->
-+            ?MYDEBUG("Created settings table for ~p", [VHost]),
++            ?MYDEBUG("Created settings table for ~s", [VHost]),
 +            ok;
 +         {error, Reason} ->
 +            case lists:keysearch(code, 1, Reason) of
 +                 {value, {code, "42P07"}} ->
-+                   ?MYDEBUG("Settings table for ~p already exists", [VHost]),
++                   ?MYDEBUG("Settings table for ~s already exists", [VHost]),
 +                   ok;
 +                 _ ->
-+                   ?ERROR_MSG("Failed to create settings table for ~p: ~p", [VHost, Reason]),
++                   ?ERROR_MSG("Failed to create settings table for ~s: ~p", [VHost, Reason]),
 +                   error
 +            end
 +    end.
 +                     {value, {code, "42P07"}} ->
 +                       exists;
 +                     _ ->
-+                       ?ERROR_MSG("Failed to create users table for ~p: ~p", [VHost, Reason]),
++                       ?ERROR_MSG("Failed to create users table for ~s: ~p", [VHost, Reason]),
 +                       error
 +                end
 +        end
 +      end,
 +    case sql_transaction_internal(DBRef, Fun) of
 +         {atomic, created} ->
-+             ?MYDEBUG("Created users table for ~p", [VHost]),
++             ?MYDEBUG("Created users table for ~s", [VHost]),
 +             ok;
-+         {atomic, exists} -> 
-+             ?MYDEBUG("Users table for ~p already exists", [VHost]),
++         {atomic, exists} ->
++             ?MYDEBUG("Users table for ~s already exists", [VHost]),
 +             ok;
 +         {aborted, _} -> error
 +    end.
 +                     {value, {code, "42P07"}} ->
 +                       exists;
 +                     _ ->
-+                       ?ERROR_MSG("Failed to create servers table for ~p: ~p", [VHost, Reason]),
++                       ?ERROR_MSG("Failed to create servers table for ~s: ~p", [VHost, Reason]),
 +                       error
 +                end
 +        end
 +      end,
 +    case sql_transaction_internal(DBRef, Fun) of
 +         {atomic, created} ->
-+            ?MYDEBUG("Created servers table for ~p", [VHost]),
++            ?MYDEBUG("Created servers table for ~s", [VHost]),
 +            ok;
 +         {atomic, exists} ->
-+            ?MYDEBUG("Servers table for ~p already exists", [VHost]),
++            ?MYDEBUG("Servers table for ~s already exists", [VHost]),
 +            ok;
 +         {aborted, _} -> error
 +    end.
 +                         {value, {code, "42P07"}} ->
 +                           exists;
 +                         _ ->
-+                           ?ERROR_MSG("Failed to create users table for ~p: ~p", [VHost, Reason]),
++                           ?ERROR_MSG("Failed to create users table for ~s: ~p", [VHost, Reason]),
 +                           error
 +                    end
 +            end
 +          end,
 +    case sql_transaction_internal(DBRef, Fun) of
 +         {atomic, created} ->
-+             ?MYDEBUG("Created resources table for ~p", [VHost]),
++             ?MYDEBUG("Created resources table for ~s", [VHost]),
 +             ok;
 +         {atomic, exists} ->
-+             ?MYDEBUG("Resources table for ~p already exists", [VHost]),
++             ?MYDEBUG("Resources table for ~s already exists", [VHost]),
 +             ok;
 +         {aborted, _} -> error
 +    end.
 +
-+create_internals(#state{dbref=DBRef, vhost=VHost, schema=Schema}) ->
++create_internals(#state{dbref=DBRef, vhost=VHost, schema=Schema}=State) ->
 +    sql_query_internal(DBRef, ["DROP FUNCTION IF EXISTS ",logmessage_name(VHost,Schema)," (tbname TEXT, atdt TEXT, owner TEXT, peer_name TEXT, peer_server TEXT, peer_resource TEXT, mdirection VARCHAR(4), mtype VARCHAR(9), msubj TEXT, mbody TEXT, mtimestamp DOUBLE PRECISION);"]),
 +    case sql_query_internal(DBRef, [get_logmessage(VHost, Schema)]) of
 +         {updated, _} ->
 +            ?MYDEBUG("Created logmessage for ~p", [VHost]),
 +            ok;
-+         {error, _} ->
-+            error
++         {error, Reason} ->
++            case lists:keysearch(code, 1, Reason) of
++                 {value, {code, "42704"}} ->
++                    ?ERROR_MSG("plpgsql language must be installed into database '~s'. Use CREATE LANGUAGE...", [State#state.db]),
++                    error;
++                 _ ->
++                    error
++            end
 +    end.
 +
 +get_user_id(DBRef, VHost, Schema, User) ->
 +
 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 +%
-+% SQL internals 
++% SQL internals
 +%
 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 +% like do_transaction/2 in mysql_conn.erl (changeset by Yariv Sadan <yarivvv@gmail.com>)
 +    {updated, 1};
 +get_result({ok, ["CREATE FUNCTION"]}) ->
 +    {updated, 1};
-+get_result({ok, [{"SELECT", _Rows, Recs}]}) ->
-+    {data, [list_to_tuple(Rec) || Rec <- Recs]};
++get_result({ok, [{[$S, $E, $L, $E, $C, $T, $  | _Rest], _Rows, Recs}]}) ->
++    Fun = fun(Rec) ->
++              list_to_tuple(
++                  lists:map(fun(Elem) when is_binary(Elem) ->
++                                  binary_to_list(Elem);
++                               (Elem) when is_list(Elem) ->
++                                  Elem;
++                               (Elem) when is_integer(Elem) ->
++                                  integer_to_list(Elem);
++                               (Elem) when is_float(Elem) ->
++                                  float_to_list(Elem);
++                               (Elem) when is_boolean(Elem) ->
++                                  atom_to_list(Elem);
++                               (Elem) ->
++                                  ?ERROR_MSG("Unknown element type ~p", [Elem]),
++                                  Elem
++                            end, Rec))
++          end,
++    Res = lists:map(Fun, Recs),
++    %{data, [list_to_tuple(Rec) || Rec <- Recs]};
++    {data, Res};
 +get_result({ok, ["INSERT " ++ OIDN]}) ->
 +    [_OID, N] = string:tokens(OIDN, " "),
 +    {updated, list_to_integer(N)};
 +get_result(Rez) ->
 +    {error, undefined, Rez}.
 +
---- src/mod_logdb_mnesia_old.erl.orig  2009-02-05 19:21:29.000000000 +0200
-+++ src/mod_logdb_mnesia_old.erl       2009-02-05 19:20:07.000000000 +0200
-@@ -0,0 +1,258 @@
-+%%%----------------------------------------------------------------------
-+%%% File    : mod_logdb_mnesia_old.erl
-+%%% Author  : Oleg Palij (mailto,xmpp:o.palij@gmail.com)
-+%%% Purpose : mod_logmnesia backend for mod_logdb (should be used only for copy_tables functionality)
-+%%% Version : trunk
-+%%% Id      : $Id$
-+%%% Url     : http://www.dp.uz.gov.ua/o.palij/mod_logdb/
-+%%%----------------------------------------------------------------------
-+
-+-module(mod_logdb_mnesia_old).
-+-author('o.palij@gmail.com').
-+
-+-include("ejabberd.hrl").
-+-include("jlib.hrl").
-+
-+-behaviour(gen_logdb).
+diff --git a/src/mod_roster.erl b/src/mod_roster.erl
+index 426589319c..6b51d3c381 100644
+--- a/src/mod_roster.erl
++++ b/src/mod_roster.erl
+@@ -65,6 +65,8 @@
+ -define(ROSTER_ITEM_CACHE, roster_item_cache).
+ -define(ROSTER_VERSION_CACHE, roster_version_cache).
++-include("mod_logdb.hrl").
 +
-+-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]).
+ -type c2s_state() :: ejabberd_c2s:state().
+ -export_type([subscription/0]).
+@@ -943,6 +945,14 @@ user_roster(User, Server, Query, Lang) ->
+                                 Query),
+     Items = get_roster(LUser, LServer),
+     SItems = lists:sort(Items),
 +
-+-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}).
++    Settings = case gen_mod:is_loaded(Server, mod_logdb) of
++         true ->
++             mod_logdb:get_user_settings(User, Server);
++         false ->
++             []
++    end,
 +
-+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).
+     FItems = case SItems of
+              [] -> [?CT(?T("None"))];
+              _ ->
+@@ -1000,7 +1010,33 @@ user_roster(User, Server, Query, Lang) ->
+                                                         [?INPUTT(<<"submit">>,
+                                                                  <<"remove",
+                                                                    (ejabberd_web_admin:term_to_id(R#roster.jid))/binary>>,
+-                                                                 ?T("Remove"))])])
++                                                                 ?T("Remove"))]),
++                              case gen_mod:is_loaded(Server, mod_logdb) of
++                                  true ->
++                                     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} =
++                                       if
++                                         A ->
++                                           {<<"donotlog">>, <<"Do Not Log Messages">>};
++                                         B ->
++                                           {<<"dolog">>, <<"Log Messages">>};
++                                         Settings#user_settings.dolog_default == true ->
++                                           {<<"donotlog">>, <<"Do Not Log Messages">>};
++                                         Settings#user_settings.dolog_default == false ->
++                                           {<<"dolog">>, <<"Log Messages">>}
++                                       end,
++
++                                     ?XAE(<<"td">>, [{<<"class">>, <<"valign">>}],
++                                          [?INPUTT(<<"submit">>,
++                                                   <<Name/binary,
++                                                   (ejabberd_web_admin:term_to_id(R#roster.jid))/binary>>,
++                                                   Value)]);
++                                  false ->
++                                     ?X([])
++                              end
++                           ])
+                                       end,
+                                       SItems)))])]
+            end,
+@@ -1107,9 +1143,42 @@ user_roster_item_parse_query(User, Server, Items,
+                                           sub_els = [#roster_query{
+                                                         items = [RosterItem]}]}),
+                                     throw(submitted);
+-                                false -> ok
+-                              end
+-                        end
++                  false ->
++                    case lists:keysearch(
++                           <<"donotlog", (ejabberd_web_admin:term_to_id(JID))/binary>>, 1, Query) of
++                        {value, _} ->
++                             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(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),
++                             throw(nothing);
++                        false ->
++                           case lists:keysearch(
++                                  <<"dolog", (ejabberd_web_admin:term_to_id(JID))/binary>>, 1, Query) of
++                               {value, _} ->
++                                  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(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),
++                                  throw(nothing);
++                               false ->
++                                   ok
++                           end % dolog
++                    end % donotlog
++                end % remove
++              end % validate
+                 end,
+                 Items),
+     nothing.
+
+From 5043114bc1a74caa522e8a1569b485ccc1808a79 Mon Sep 17 00:00:00 2001
+From: Oleh Palii <o.palij@gmail.com>
+Date: Sat, 31 Aug 2019 15:23:19 +0300
+Subject: [PATCH 2/3] mod_logdb 19.08 adaptation
+
+---
+ src/mod_logdb.erl        | 187 +++++++++++++++++++++++----------------
+ src/mod_logdb_mysql.erl  |  10 +--
+ src/mod_logdb_mysql5.erl |  10 +--
+ src/mod_logdb_pgsql.erl  |  12 +--
+ 4 files changed, 125 insertions(+), 94 deletions(-)
+
+diff --git a/src/mod_logdb.erl b/src/mod_logdb.erl
+index bf0240d139..0b5c2ec687 100644
+--- a/src/mod_logdb.erl
++++ b/src/mod_logdb.erl
+@@ -26,6 +26,7 @@
+ -export([get_local_identity/5,
+          get_local_features/5,
+          get_local_items/5,
++         mod_options/1,
+          adhoc_local_items/4,
+          adhoc_local_commands/4
+         ]).
+@@ -56,6 +57,8 @@
+          user_messages_stats/4,
+          user_messages_stats_at/5]).
++-export([get_opt/3]).
 +
-+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-+%
-+% 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
+ -include("mod_logdb.hrl").
+ -include("xmpp.hrl").
+ -include("mod_roster.hrl").
+@@ -64,6 +67,7 @@
+ -include("ejabberd_web_admin.hrl").
+ -include("ejabberd_http.hrl").
+ -include("logger.hrl").
++-include("translate.hrl").
+ -define(PROCNAME, ejabberd_mod_logdb).
+ % gen_server call timeout
+@@ -73,6 +77,28 @@
+ ets_settings_table(VHost) -> list_to_atom("ets_logdb_settings_" ++ binary_to_list(VHost)).
++-spec tr(binary(), binary()) -> binary().
++tr(Lang, Text) ->
++    translate:translate(Lang, Text).
++
++mod_options(VHost) ->
++    [
++     {dbs, [{mnesia, []}]},
++     {vhosts, [{VHost, mnesia}]},
++     {ignore_jids, []},
++     {groupchat, none},
++     {drop_messages_on_user_removal, true},
++     {purge_older_days, never},
++     {dolog_default, true},
++     {poll_users_settings, 5}
++    ].
++
++get_opt(Opt, Opts, Default) ->
++   case lists:keyfind(Opt, 1, Opts) of
++      false -> Default;
++      {_, Result} -> Result
 +   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
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ %
+ % gen_mod/gen_server callbacks
+@@ -88,7 +114,8 @@ start(VHost, Opts) ->
+          worker,
+          [?MODULE]},
+     % add child to ejabberd_sup
+-    supervisor:start_child(ejabberd_gen_mod_sup, ChildSpec).
++    supervisor:start_child(ejabberd_gen_mod_sup, ChildSpec),
++    ok.
+ depends(_Host, _Opts) ->
+     [].
+@@ -106,14 +133,14 @@ start_link(VHost, Opts) ->
+ init([VHost, Opts]) ->
+     process_flag(trap_exit, true),
+-    DBsRaw = gen_mod:get_opt(dbs, Opts, fun(A) -> A end, [{mnesia, []}]),
++    DBsRaw = gen_mod:get_opt(dbs, Opts),
+     DBs = case lists:keysearch(mnesia, 1, DBsRaw) of
+                false -> lists:append(DBsRaw, [{mnesia,[]}]);
+                {value, _} -> DBsRaw
+           end,
+-    VHostDB = gen_mod:get_opt(vhosts, Opts, fun(A) -> A end, [{VHost, mnesia}]),
++    VHostDB = gen_mod:get_opt(vhosts, Opts),
+     % 10 is default because of using in clustered environment
+-    PollUsersSettings = gen_mod:get_opt(poll_users_settings, Opts, fun(A) -> A end, 10),
++    PollUsersSettings = gen_mod:get_opt(poll_users_settings, Opts),
+     {DBName, DBOpts} =
+          case lists:keysearch(VHost, 1, VHostDB) of
+@@ -139,11 +166,11 @@ init([VHost, Opts]) ->
+                 dbopts=DBOpts,
+                 % dbs used for convert messages from one backend to other
+                 dbs=DBs,
+-                dolog_default=gen_mod:get_opt(dolog_default, Opts, fun(A) -> A end, true),
+-                drop_messages_on_user_removal=gen_mod:get_opt(drop_messages_on_user_removal, Opts, fun(A) -> A end, true),
+-                ignore_jids=gen_mod:get_opt(ignore_jids, Opts, fun(A) -> A end, []),
+-                groupchat=gen_mod:get_opt(groupchat, Opts, fun(A) -> A end, none),
+-                purge_older_days=gen_mod:get_opt(purge_older_days, Opts, fun(A) -> A end, never),
++                dolog_default=gen_mod:get_opt(dolog_default, Opts),
++                drop_messages_on_user_removal=gen_mod:get_opt(drop_messages_on_user_removal, Opts),
++                ignore_jids=gen_mod:get_opt(ignore_jids, Opts),
++                groupchat=gen_mod:get_opt(groupchat, Opts),
++                purge_older_days=gen_mod:get_opt(purge_older_days, Opts),
+                 poll_users_settings=PollUsersSettings}}.
+ cleanup(#state{vhost=VHost} = _State) ->
+@@ -444,7 +471,7 @@ handle_info(scheduled_purging, #state{vhost=VHost, purge_older_days=Days} = Stat
+ % from timer:send_interval/2 (in start handle_info)
+ handle_info(poll_users_settings, #state{dbmod=DBMod, vhost=VHost}=State) ->
+     {ok, DoLog} = DBMod:get_users_settings(VHost),
+-    ?MYDEBUG("DoLog=~p", [DoLog]),
++%    ?MYDEBUG("DoLog=~p", [DoLog]),
+     true = ets:delete_all_objects(ets_settings_table(VHost)),
+     ets:insert(ets_settings_table(VHost), DoLog),
+     {noreply, State};
+@@ -654,8 +681,7 @@ sort_stats(Stats) ->
+ % return float seconds elapsed from "zero hour" as list
+ get_timestamp() ->
+     {MegaSec, Sec, MicroSec} = now(),
+-    [List] = io_lib:format("~.5f", [MegaSec*1000000 + Sec + MicroSec/1000000]),
+-    List.
++    io_lib:format("~.5f", [MegaSec*1000000 + Sec + MicroSec/1000000]).
+ % convert float seconds elapsed from "zero hour" to local time "%Y-%m-%d %H:%M:%S" string
+ convert_timestamp(Seconds) when is_list(Seconds) ->
+@@ -907,7 +933,7 @@ copy_messages_int_tc([FromDBMod, ToDBMod, VHost, Date]) ->
+                                          % mysql, pgsql removes final zeros after decimal point
+                                          (#msg{timestamp=Tst}) when length(Tst) < 16 ->
+                                             {F, _} = string:to_float(Tst++".0"),
+-                                            [T] = io_lib:format("~.5f", [F]),
++                                            T = io_lib:format("~.5f", [F]),
+                                             ets:insert(mod_logdb_temp, {T})
+                                       end, ToMsgs),
+                         {ok, Msgs} = FromDBMod:get_user_messages_at(User, VHost, Date),
+@@ -992,16 +1018,25 @@ string_to_list(String) ->
+ % ad-hoc (copy/pasted from mod_configure.erl)
+ %
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
++-spec get_permission_level(jid()) -> global | vhost.
++get_permission_level(JID) ->
++    case acl:match_rule(global, configure, JID) of
++      allow -> global;
++      deny -> vhost
 +    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.
---- src/gen_logdb.erl.orig     2009-02-05 19:21:29.000000000 +0200
-+++ src/gen_logdb.erl  2009-02-05 19:19:39.000000000 +0200
-@@ -0,0 +1,164 @@
-+%%%----------------------------------------------------------------------
-+%%% File    : gen_logdb.erl
-+%%% Author  : Oleg Palij (mailto,xmpp:o.palij@gmail.com)
-+%%% Purpose : Describes generic behaviour for mod_logdb backends.
-+%%% Version : trunk
-+%%% Id      : $Id$
-+%%% Url     : http://www.dp.uz.gov.ua/o.palij/mod_logdb/
-+%%%----------------------------------------------------------------------
-+
-+-module(gen_logdb).
-+-author('o.palij@gmail.com').
-+
-+-export([behaviour_info/1]).
-+
-+behaviour_info(callbacks) ->
-+   [
-+    % called from handle_info(start, _)
-+    % it should logon database and return reference to started instance
-+    % start(VHost, Opts) -> {ok, SPid} | error
-+    %  Options - list of options to connect to db
-+    %    Types: Options = list() -> [] |
-+    %                              [{user, "logdb"},
-+    %                               {pass, "1234"},
-+    %                               {db, "logdb"}] | ...
-+    %          VHost = list() -> "jabber.example.org"
-+    {start, 2},
-+
-+    % called from cleanup/1
-+    % it should logoff database and do cleanup
-+    % stop(VHost)
-+    %    Types: VHost = list() -> "jabber.example.org"
-+    {stop, 1},
-+
-+    % called from handle_call({addlog, _}, _, _)
-+    % it should log messages to database
-+    % log_message(VHost, Msg) -> ok | error
-+    %    Types:
-+    %          VHost = list() -> "jabber.example.org"
-+    %          Msg = record() -> #msg
-+    {log_message, 2},
-+
-+    % called from ejabberdctl rebuild_stats
-+    % it should rebuild stats table (if used) for vhost
-+    % rebuild_stats(VHost)
-+    %    Types:
-+    %          VHost = list() -> "jabber.example.org"
-+    {rebuild_stats, 1},
-+
-+    % it should rebuild stats table (if used) for vhost at Date
-+    % rebuild_stats_at(VHost, Date)
-+    %    Types:
-+    %          VHost = list() -> "jabber.example.org"
-+    %          Date = list() -> "2007-02-12"
-+    {rebuild_stats_at, 2},
-+
-+    % called from user_messages_at_parse_query/5
-+    % it should delete selected user messages at date
-+    % delete_messages_by_user_at(VHost, Msgs, Date) -> ok | error
-+    %    Types:
-+    %          VHost = list() -> "jabber.example.org"
-+    %          Msgs = list() -> [ #msg1, msg2, ... ]
-+    %          Date = list() -> "2007-02-12"
-+    {delete_messages_by_user_at, 3},
-+
-+    % called from user_messages_parse_query/4 | vhost_messages_at_parse_query/4
-+    % it should delete all user messages at date
-+    % delete_all_messages_by_user_at(User, VHost, Date) -> ok | error
-+    %    Types:
-+    %          User = list() -> "admin"
-+    %          VHost = list() -> "jabber.example.org"
-+    %          Date = list() -> "2007-02-12"
-+    {delete_all_messages_by_user_at, 3},
-+
-+    % called from vhost_messages_parse_query/3
-+    % it should delete messages for vhost at date and update stats
-+    % delete_messages_at(VHost, Date) -> ok | error
-+    %    Types:
-+    %          VHost = list() -> "jabber.example.org"
-+    %          Date = list() -> "2007-02-12"
-+    {delete_messages_at, 2},
-+
-+    % called from ejabberd_web_admin:vhost_messages_stats/3
-+    % it should return sorted list of count of messages by dates for vhost
-+    % get_vhost_stats(VHost) -> {ok, [{Date1, Msgs_count1}, {Date2, Msgs_count2}, ... ]} |
-+    %                           {error, Reason}
-+    %    Types:
-+    %          VHost = list() -> "jabber.example.org"
-+    %          DateN = list() -> "2007-02-12"
-+    %          Msgs_countN = number() -> 241
-+    {get_vhost_stats, 1},
-+
-+    % called from ejabberd_web_admin:vhost_messages_stats_at/4
-+    % it should return sorted list of count of messages by users at date for vhost
-+    % get_vhost_stats_at(VHost, Date) -> {ok, [{User1, Msgs_count1}, {User2, Msgs_count2}, ....]} |
-+    %                                    {error, Reason}
-+    %    Types:
-+    %          VHost = list() -> "jabber.example.org"
-+    %          Date = list() -> "2007-02-12"
-+    %          UserN = list() -> "admin"
-+    %          Msgs_countN = number() -> 241
-+    {get_vhost_stats_at, 2},
-+
-+    % called from ejabberd_web_admin:user_messages_stats/4
-+    % it should return sorted list of count of messages by date for user at vhost
-+    % get_user_stats(User, VHost) -> {ok, [{Date1, Msgs_count1}, {Date2, Msgs_count2}, ...]} |
-+    %                                {error, Reason}
-+    %    Types:
-+    %          User = list() -> "admin"
-+    %          VHost = list() -> "jabber.example.org"
-+    %          DateN = list() -> "2007-02-12"
-+    %          Msgs_countN = number() -> 241
-+    {get_user_stats, 2},
-+
-+    % called from ejabberd_web_admin:user_messages_stats_at/5
-+    % it should return all user messages at date
-+    % get_user_messages_at(User, VHost, Date) -> {ok, Msgs} | {error, Reason}
-+    %    Types:
-+    %          User = list() -> "admin"
-+    %          VHost = list() -> "jabber.example.org"
-+    %          Date = list() -> "2007-02-12"
-+    %          Msgs = list() -> [ #msg1, msg2, ... ]
-+    {get_user_messages_at, 3},
-+
-+    % called from many places
-+    % it should return list of dates for vhost
-+    % get_dates(VHost) -> [Date1, Date2, ... ]
-+    %    Types:
-+    %          VHost = list() -> "jabber.example.org"
-+    %          DateN = list() -> "2007-02-12"
-+    {get_dates, 1},
-+
-+    % called from start
-+    % it should return list with users settings for VHost in db
-+    % get_users_settings(VHost) -> [#user_settings1, #user_settings2, ... ] | error
-+    %    Types:
-+    %          VHost = list() -> "jabber.example.org"
-+    {get_users_settings, 1},
-+
-+    % called from many places
-+    % it should return User settings at VHost from db
-+    % get_user_settings(User, VHost) -> error | {ok, #user_settings}
-+    %    Types:
-+    %          User = list() -> "admin"
-+    %          VHost = list() -> "jabber.example.org"
-+    {get_user_settings, 2},
-+
-+    % called from web admin
-+    % it should set User settings at VHost
-+    % set_user_settings(User, VHost, #user_settings) -> ok | error
-+    %    Types:
-+    %          User = list() -> "admin"
-+    %          VHost = list() -> "jabber.example.org"
-+    {set_user_settings, 3},
-+
-+    % called from remove_user (ejabberd hook)
-+    % it should remove user messages and settings at VHost
-+    % drop_user(User, VHost) -> ok | error
-+    %    Types:
-+    %          User = list() -> "admin"
-+    %          VHost = list() -> "jabber.example.org"
-+    {drop_user, 2}
-+   ];
-+behaviour_info(_) ->
-+   undefined.
---- src/web/ejabberd_web_admin-2.0.3.erl       2009-02-03 08:27:39.000000000 +0200
-+++ src/web/ejabberd_web_admin.erl     2009-02-03 08:40:57.000000000 +0200
-@@ -1514,25 +1514,31 @@
+ -define(ITEMS_RESULT(Allow, LNode, Fallback),
+-    case Allow of
+-        deny -> Fallback;
+-        allow ->
+-            case get_local_items(LServer, LNode,
+-                                 jid:encode(To), Lang) of
+-                {result, Res} -> {result, Res};
+-                {error, Error} -> {error, Error}
+-            end
+-    end).
++      case Allow of
++        deny -> Fallback;
++        allow ->
++            PermLev = get_permission_level(From),
++            case get_local_items({PermLev, LServer}, LNode,
++                                 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) ->
+@@ -1051,15 +1086,13 @@ get_local_items(Acc, From, #jid{lserver = LServer} = To,
+             end
+     end.
  
- user_parse_query(User, Server, Query) ->
--    case lists:keysearch("chpassword", 1, Query) of
--      {value, _} ->
--          case lists:keysearch("password", 1, Query) of
--              {value, {_, undefined}} ->
--                  error;
--              {value, {_, Password}} ->
--                  ejabberd_auth:set_password(User, Server, Password),
--                  ok;
--              _ ->
--                  error
--          end;
--      _ ->
--          case lists:keysearch("removeuser", 1, Query) of
--              {value, _} ->
--                  ejabberd_auth:remove_user(User, Server),
--                  ok;
--              false ->
--                  nothing
--          end
-+    lists:foldl(fun({Action, Value}, Acc) when Acc == nothing ->
-+                      user_parse_query1(Action, User, Server, Query);
-+                   ({Action, Value}, Acc) ->
-+                      Acc
-+                end, nothing, Query).
-+
-+user_parse_query1("password", User, Server, Query) ->
-+    nothing;
-+user_parse_query1("chpassword", User, Server, Query) ->
-+    case lists:keysearch("password", 1, Query) of
-+        {value, {_, undefined}} ->
-+            error;
-+        {value, {_, Password}} ->
-+            ejabberd_auth:set_password(User, Server, Password),
-+            ok;
-+        _ ->
-+            error
-+    end;
-+user_parse_query1("removeuser", User, Server, Query) ->
-+    ejabberd_auth:remove_user(User, Server),
-+    ok;
-+user_parse_query1(Action, User, Server, Query) ->
-+    case ejabberd_hooks:run_fold(webadmin_user_parse_query, Server, [], [Action, User, Server, Query]) of
-+         [] -> nothing;
-+         Res -> Res
+--define(T(Lang, Text), translate:translate(Lang, Text)).
+-
+ -define(NODE(Name, Node),
+-    #disco_item{jid = jid:make(Server),
+-            node = Node,
+-            name = ?T(Lang, Name)}).
++      #disco_item{jid = jid:make(Server),
++                  node = Node,
++                  name = tr(Lang, Name)}).
+ -define(NS_ADMINX(Sub),
+-    <<(?NS_ADMIN)/binary, "#", Sub/binary>>).
++      <<(?NS_ADMIN)/binary, "#", Sub/binary>>).
+ tokenize(Node) -> str:tokens(Node, <<"/#">>).
+@@ -1098,10 +1131,10 @@ get_local_items(_Host, Item, _Server, _Lang) ->
+     {error, xmpp:err_item_not_found()}.
+ -define(INFO_RESULT(Allow, Feats, Lang),
+-    case Allow of
+-      deny -> {error, xmpp:err_forbidden(<<"Denied by ACL">>, Lang)};
+-      allow -> {result, Feats}
+-    end).
++      case Allow of
++        deny -> {error, xmpp:err_forbidden(?T("Access denied by service policy"), Lang)};
++        allow -> {result, Feats}
++      end).
+ get_local_features(Acc, From,
+                    #jid{lserver = LServer} = _To, Node, Lang) ->
+@@ -1133,11 +1166,11 @@ get_local_features(Acc, From,
      end.
  
+ -define(INFO_IDENTITY(Category, Type, Name, Lang),
+-    [#identity{category = Category, type = Type, name = ?T(Lang, Name)}]).
++      [#identity{category = Category, type = Type, name = tr(Lang, Name)}]).
  
---- src/mod_muc/mod_muc_room-2.0.3.erl 2009-02-03 08:27:59.000000000 +0200
-+++ src/mod_muc/mod_muc_room.erl       2009-02-03 08:37:26.000000000 +0200
-@@ -695,6 +695,12 @@
- handle_sync_event({change_config, Config}, _From, StateName, StateData) ->
-     {result, [], NSD} = change_config(Config, StateData),
-     {reply, {ok, NSD#state.config}, StateName, NSD};
-+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(_Event, _From, StateName, StateData) ->
-     Reply = ok,
-     {reply, Reply, StateName, StateData}.
---- src/msgs/uk-2.0.3.msg      2009-01-14 11:54:15.000000000 +0200
-+++ src/msgs/uk.msg    2009-02-03 08:26:20.000000000 +0200
-@@ -388,6 +388,35 @@
- % mod_offline_odbc.erl
- {"Your contact offline message queue is full. The message has been discarded.", "Черга повідомлень, що не були доставлені, переповнена. Повідомлення не було збережено."}.
+ -define(INFO_COMMAND(Name, Lang),
+-    ?INFO_IDENTITY(<<"automation">>, <<"command-node">>,
+-               Name, Lang)).
++      ?INFO_IDENTITY(<<"automation">>, <<"command-node">>,
++                     Name, Lang)).
  
-+% mod_logdb
-+{"Users Messages", "Повідомлення користувачів"}.
-+{"Date", "Дата"}.
-+{"Count", "Кількість"}.
-+{"Logged messages for ", "Збережені повідомлення для "}.
-+{" at ", " за "}.
-+{"No logged messages for ", "Відсутні повідомлення для "}.
-+{"Date, Time", "Дата, Час"}.
-+{"Direction: Jid", "Напрямок: Jid"}.
-+{"Subject", "Тема"}.
-+{"Body", "Текст"}.
-+{"Messages", "Повідомлення"}.
-+{"Filter Selected", "Відфільтрувати виділені"}.
-+{"Do Not Log Messages", "Не зберігати повідомлення"}.
-+{"Log Messages", "Зберігати повідомлення"}.
-+{"Messages logging engine", "Система збереження повідомлень"}.
-+{"Default", "За замовчуванням"}.
-+{"Set logging preferences", "Вкажіть налагоджування збереження повідомлень"}.
-+{"Messages logging engine users", "Користувачі системи збереження повідомлень"}.
-+{"Messages logging engine settings", "Налагоджування системи збереження повідомлень"}.
-+{"Set run-time settings", "Вкажіть поточні налагоджування"}.
-+{"Groupchat messages logging", "Збереження повідомлень типу groupchat"}.
-+{"Jids/Domains to ignore", "Ігнорувати наступні jids/домени"}.
-+{"Purge messages older than (days)", "Видаляти повідомлення старіші ніж (дні)"}.
-+{"Poll users settings (seconds)", "Оновлювати налагоджування користувачів кожні (секунд)"}.
-+{"Drop", "Видаляти"}.
-+{"Do not drop", "Не видаляти"}.
-+{"Drop messages on user removal", "Видаляти повідомлення під час видалення користувача"}.
-+
- % Local Variables:
- % mode: erlang
- % End:
---- src/msgs/ru-2.0.3.msg      2009-01-14 11:54:15.000000000 +0200
-+++ src/msgs/ru.msg    2009-02-03 08:25:31.000000000 +0200
-@@ -388,6 +388,35 @@
- % mod_offline_odbc.erl
- {"Your contact offline message queue is full. The message has been discarded.", "Очередь недоставленных сообщений Вашего адресата переполнена. Сообщение не было сохранено."}.
+ get_local_identity(Acc, _From, _To, Node, Lang) ->
+     LNode = tokenize(Node),
+@@ -1198,10 +1231,8 @@ recursively_get_local_items(LServer,
  
-+% mod_logdb.erl
-+{"Users Messages", "Сообщения пользователей"}.
-+{"Date", "Дата"}.
-+{"Count", "Количество"}.
-+{"Logged messages for ", "Сохранённые cообщения для "}.
-+{" at ", " за "}.
-+{"No logged messages for ", "Отсутствуют сообщения для "}.
-+{"Date, Time", "Дата, Время"}.
-+{"Direction: Jid", "Направление: Jid"}.
-+{"Subject", "Тема"}.
-+{"Body", "Текст"}.
-+{"Messages", "Сообщения"}.
-+{"Filter Selected", "Отфильтровать выделенные"}.
-+{"Do Not Log Messages", "Не сохранять сообщения"}.
-+{"Log Messages", "Сохранять сообщения"}.
-+{"Messages logging engine", "Система логирования сообщений"}.
-+{"Default", "По умолчанию"}.
-+{"Set logging preferences", "Задайте настройки логирования"}.
-+{"Messages logging engine users", "Пользователи системы логирования сообщений"}.
-+{"Messages logging engine settings", "Настройки системы логирования сообщений"}.
-+{"Set run-time settings", "Задайте текущие настройки"}.
-+{"Groupchat messages logging", "Логирование сообщений типа groupchat"}.
-+{"Jids/Domains to ignore", "Игнорировать следующие jids/домены"}.
-+{"Purge messages older than (days)", "Удалять сообщения старее чем (дни)"}.
-+{"Poll users settings (seconds)", "Обновлять настройки пользователей через (секунд)"}.
-+{"Drop", "Удалять"}.
-+{"Do not drop", "Не удалять"}.
-+{"Drop messages on user removal", "Удалять сообщения при удалении пользователя"}.
-+
- % Local Variables:
- % mode: erlang
- % End:
---- src/msgs/pl-2.0.3.msg      2009-01-14 11:54:15.000000000 +0200
-+++ src/msgs/pl.msg    2009-02-03 08:24:33.000000000 +0200
-@@ -408,6 +408,31 @@
- % mod_offline.erl
- {"Your contact offline message queue is full. The message has been discarded.", "Twoja kolejka wiadomoci offline jest pełna. Wiadomoć została odrzucona."}.
+ -define(COMMANDS_RESULT(Allow, From, To, Request),
+     case Allow of
+-        deny ->
+-            {error, xmpp:err_forbidden(<<"Denied by ACL">>, Lang)};
+-        allow ->
+-            adhoc_local_commands(From, To, Request)
++        deny -> {error, xmpp:err_forbidden(?T("Access denied by service policy"), Lang)};
++        allow -> adhoc_local_commands(From, To, Request)
+     end).
  
-+% mod_logdb
-+{"Users Messages", "Wiadomości użytkownika"}.
-+{"Date", "Data"}.
-+{"Count", "Liczba"}.
-+{"Logged messages for ", "Zapisane wiadomości dla "}.
-+{" at ", " o "}.
-+{"No logged messages for ", "Brak zapisanych wiadomości dla "}.
-+{"Date, Time", "Data, Godzina"}.
-+{"Direction: Jid", "Kierunek: Jid"}.
-+{"Subject", "Temat"}.
-+{"Body", "Treść"}.
-+{"Messages","Wiadomości"}.
-+{"Filter Selected", "Odfiltruj zaznaczone"}.
-+{"Do Not Log Messages", "Nie zapisuj wiadomości"}.
-+{"Log Messages", "Zapisuj wiadomości"}.
-+{"Messages logging engine", "System zapisywania historii rozmów"}.
-+{"Default", "Domyślne"}.
-+{"Set logging preferences", "Ustaw preferencje zapisywania"}.
-+{"Messages logging engine settings", "Ustawienia systemu logowania"}.
-+{"Set run-time settings", "Zapisz ustawienia systemu logowania"}.
-+{"Groupchat messages logging", "Zapisywanie rozmów z konferencji"}.
-+{"Jids/Domains to ignore", "JID/Domena która ma być ignorowana"}.
-+{"Purge messages older than (days)", "Usuń wiadomości starsze niż (w dniach)"}.
-+{"Poll users settings (seconds)", "Czas aktualizacji preferencji użytkowników (sekundy)"}.
-+
- % Local Variables:
- % mode: erlang
- % End:
---- src/msgs/nl-2.0.3.msg      2009-01-14 11:54:15.000000000 +0200
-+++ src/msgs/nl.msg    1970-01-01 03:00:00.000000000 +0300
-@@ -379,6 +379,19 @@
- % mod_proxy65/mod_proxy65_service.erl
- {"ejabberd SOCKS5 Bytestreams module", "ejabberd SOCKS5 Bytestreams module"}.
+ adhoc_local_commands(Acc, From, #jid{lserver = LServer} = To,
+@@ -1278,28 +1309,28 @@ get_user_form(LUser, LServer, Lang) ->
+     Fs = [
+           #xdata_field{
+              type = 'list-single',
+-             label = ?T(Lang, <<"Default">>),
++             label = tr(Lang, ?T("Default")),
+              var = <<"dolog_default">>,
+              values = [misc:atom_to_binary(DLD)],
+-             options = [#xdata_option{label = ?T(Lang, <<"Log Messages">>),
++             options = [#xdata_option{label = tr(Lang, ?T("Log Messages")),
+                                       value = <<"true">>},
+-                        #xdata_option{label = ?T(Lang, <<"Do Not Log Messages">>),
++                        #xdata_option{label = tr(Lang, ?T("Do Not Log Messages")),
+                                       value = <<"false">>}]},
+           #xdata_field{
+              type = 'text-multi',
+-             label = ?T(Lang, <<"Log Messages">>),
++             label = tr(Lang, ?T("Log Messages")),
+              var = <<"dolog_list">>,
+              values = DLL},
+           #xdata_field{
+              type = 'text-multi',
+-             label = ?T(Lang, <<"Do Not Log Messages">>),
++             label = tr(Lang, ?T("Do Not Log Messages")),
+              var = <<"donotlog_list">>,
+              values = DNLL}
+          ],
+     {result, #xdata{
+-                title = ?T(Lang, <<"Messages logging engine settings">>),
++                title = tr(Lang, ?T("Messages logging engine settings")),
+                 type = form,
+-                instructions = [<< (?T(Lang, <<"Set logging preferences">>))/binary,
++                instructions = [<< (tr(Lang, ?T("Set logging preferences")))/binary,
+                                                (iolist_to_binary(": "))/binary,
+                                                LUser/binary, "@", LServer/binary >>],
+                 fields = [?HFIELD()|
+@@ -1325,52 +1356,52 @@ get_settings_form(Host, Lang) ->
+     Fs = [
+           #xdata_field{
+              type = 'list-single',
+-             label = ?T(Lang, <<"Default">>),
++             label = tr(Lang, ?T("Default")),
+              var = <<"dolog_default">>,
+              values = [misc:atom_to_binary(DLD)],
+-             options = [#xdata_option{label = ?T(Lang, <<"Log Messages">>),
++             options = [#xdata_option{label = tr(Lang, ?T("Log Messages")),
+                                       value = <<"true">>},
+-                        #xdata_option{label = ?T(Lang, <<"Do Not Log Messages">>),
++                        #xdata_option{label = tr(Lang, ?T("Do Not Log Messages")),
+                                       value = <<"false">>}]},
+           #xdata_field{
+              type = 'list-single',
+-             label = ?T(Lang, <<"Drop messages on user removal">>),
++             label = tr(Lang, ?T("Drop messages on user removal")),
+              var = <<"drop_messages_on_user_removal">>,
+              values = [misc:atom_to_binary(MRemoval)],
+-             options = [#xdata_option{label = ?T(Lang, <<"Drop">>),
++             options = [#xdata_option{label = tr(Lang, ?T("Drop")),
+                                       value = <<"true">>},
+-                        #xdata_option{label = ?T(Lang, <<"Do not drop">>),
++                        #xdata_option{label = tr(Lang, ?T("Do not drop")),
+                                       value = <<"false">>}]},
+           #xdata_field{
+              type = 'list-single',
+-             label = ?T(Lang, <<"Groupchat messages logging">>),
++             label = tr(Lang, ?T("Groupchat messages logging")),
+              var = <<"groupchat">>,
+              values = [misc:atom_to_binary(GroupChat)],
+-             options = [#xdata_option{label = ?T(Lang, <<"all">>),
++             options = [#xdata_option{label = tr(Lang, ?T("all")),
+                                       value = <<"all">>},
+-                        #xdata_option{label = ?T(Lang, <<"none">>),
++                        #xdata_option{label = tr(Lang, ?T("none")),
+                                       value = <<"none">>},
+-                        #xdata_option{label = ?T(Lang, <<"send">>),
++                        #xdata_option{label = tr(Lang, ?T("send")),
+                                       value = <<"send">>}]},
+           #xdata_field{
+              type = 'text-multi',
+-             label = ?T(Lang, <<"Jids/Domains to ignore">>),
++             label = tr(Lang, ?T("Jids/Domains to ignore")),
+              var = <<"ignore_list">>,
+              values = IgnoreJids},
+           #xdata_field{
+              type = 'text-single',
+-             label = ?T(Lang, <<"Purge messages older than (days)">>),
++             label = tr(Lang, ?T("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)">>),
++             label = tr(Lang, ?T("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">>)],
++                title = tr(Lang, ?T("Messages logging engine settings (run-time)")),
++                instructions = [tr(Lang, ?T("Set run-time settings"))],
+                 type = form,
+                 fields = [?HFIELD()|
+                           Fs]}}.
+@@ -1578,7 +1609,7 @@ get_all_vh_users(Host, Server) ->
+ %
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ webadmin_menu(Acc, _Host, Lang) ->
+-    [{<<"messages">>, ?T(<<"Users Messages">>)} | Acc].
++    [{<<"messages">>, tr(Lang, ?T("Users Messages"))} | Acc].
  
-+% mod_logdb
-+{"Users Messages", "Gebruikersberichten"}.
-+{"Date", "Datum"}.
-+{"Count", "Aantal"}.
-+{"Logged messages for ", "Gelogde berichten van "}.
-+{" at ", " op "}.
-+{"No logged messages for ", "Geen gelogde berichten van "}.
-+{"Date, Time", "Datum en tijd"}.
-+{"Direction: Jid", "Richting: Jabber ID"}.
-+{"Subject", "Onderwerp"}.
-+{"Body", "Berichtveld"}.
-+{"Messages", "Berichten"}.
-+
- % Local Variables:
- % mode: erlang
- % End:
---- src/mod_roster-2.0.3.erl   2009-02-03 08:28:12.000000000 +0200
-+++ src/mod_roster.erl 2009-02-03 08:32:14.000000000 +0200
-@@ -48,7 +48,7 @@
- -include("mod_roster.hrl").
- -include("web/ejabberd_http.hrl").
- -include("web/ejabberd_web_admin.hrl").
--
-+-include("mod_logdb.hrl").
+ webadmin_user(Acc, User, Server, Lang) ->
+     Sett = get_user_settings(User, Server),
+@@ -1649,12 +1680,12 @@ vhost_messages_stats(Server, Query, Lang) ->
+     case Value of
+          {'EXIT', CReason} ->
+               ?ERROR_MSG("Failed to get_vhost_stats: ~p", [CReason]),
+-              [?XC(<<"h1">>, ?T(<<"Error occupied while fetching list">>))];
++              [?XC(<<"h1">>, tr(Lang, ?T("Error occupied while fetching list")))];
+          {error, GReason} ->
+               ?ERROR_MSG("Failed to get_vhost_stats: ~p", [GReason]),
+-              [?XC(<<"h1">>, ?T(<<"Error occupied while fetching list">>))];
++              [?XC(<<"h1">>, tr(Lang, ?T("Error occupied while fetching list")))];
+          {ok, []} ->
+-              [?XC(<<"h1">>, list_to_binary(io_lib:format(?T(<<"No logged messages for ~s">>), [Server])))];
++              [?XC(<<"h1">>, list_to_binary(io_lib:format(tr(Lang, ?T("No logged messages for ~s")), [Server])))];
+          {ok, Dates} ->
+               Fun = fun({Date, Count}) ->
+                          DateBin = iolist_to_binary(Date),
+@@ -1667,7 +1698,7 @@ vhost_messages_stats(Server, Query, Lang) ->
+                           ])
+                     end,
  
- start(Host, Opts) ->
-     IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
-@@ -829,6 +829,14 @@
-     Res = user_roster_parse_query(User, Server, Items1, Query),
-     Items = mnesia:dirty_index_read(roster, US, #roster.us),
-     SItems = lists:sort(Items),
-+
-+    Settings = case gen_mod:is_loaded(Server, mod_logdb) of
-+         true ->
-+             mod_logdb:get_user_settings(User, Server);
-+         false ->
-+             []
-+    end,
-+
-     FItems =
-       case SItems of
-           [] ->
-@@ -876,7 +884,33 @@
-                                             [?INPUTT("submit",
-                                                      "remove" ++
-                                                      ejabberd_web_admin:term_to_id(R#roster.jid),
--                                                     "Remove")])])
-+                                                     "Remove")]),
-+                                         case gen_mod:is_loaded(Server, mod_logdb) of
-+                                              true ->
-+                                                 Peer = jlib:jid_to_string(R#roster.jid),
-+                                                 A = lists:member(Peer, Settings#user_settings.dolog_list),
-+                                                 B = lists:member(Peer, Settings#user_settings.donotlog_list),
-+                                                 {Name, Value} =
-+                                                   if
-+                                                     A ->
-+                                                       {"donotlog", "Do Not Log Messages"};
-+                                                     B ->
-+                                                       {"dolog", "Log Messages"};
-+                                                     Settings#user_settings.dolog_default == true ->
-+                                                       {"donotlog", "Do Not Log Messages"};
-+                                                     Settings#user_settings.dolog_default == false ->
-+                                                       {"dolog", "Log Messages"}
-+                                                   end,
-+
-+                                                 ?XAE("td", [{"class", "valign"}],
-+                                                      [?INPUTT("submit",
-+                                                               Name ++
-+                                                               ejabberd_web_admin:term_to_id(R#roster.jid),
-+                                                               Value)]);
-+                                              false ->
-+                                                 ?X([])
-+                                         end
-+                                        ])
-                           end, SItems))])]
-       end,
-     [?XC("h1", ?T("Roster of ") ++ us_to_list(US))] ++
-@@ -958,11 +992,42 @@
-                                                {"subscription", "remove"}],
-                                               []}]}}),
-                             throw(submitted);
--                        false ->
--                            ok
--                    end
--
--            end
-+                          false ->
-+                            case lists:keysearch(
-+                                   "donotlog" ++ ejabberd_web_admin:term_to_id(JID), 1, Query) of
-+                                {value, _} ->
-+                                     Peer = jlib:jid_to_string(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),
-+                                     Sett = Settings#user_settings{donotlog_list=DNLL, dolog_list=DLL},
-+                                     % TODO: check returned value
-+                                     ok = mod_logdb:set_user_settings(User, Server, Sett),
-+                                     throw(nothing);
-+                                false ->
-+                                   case lists:keysearch(
-+                                          "dolog" ++ ejabberd_web_admin:term_to_id(JID), 1, Query) of
-+                                       {value, _} ->
-+                                          Peer = jlib:jid_to_string(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),
-+                                          Sett = Settings#user_settings{donotlog_list=DNLL, dolog_list=DLL},
-+                                          % TODO: check returned value
-+                                          ok = mod_logdb:set_user_settings(User, Server, Sett),
-+                                          throw(nothing);
-+                                       false ->
-+                                           ok
-+                                   end % dolog
-+                            end % donotlog
-+                      end % remove
-+              end % validate
-       end, Items),
-     nothing.
+-              [?XC(<<"h1">>, list_to_binary(io_lib:format(?T(<<"Logged messages for ~s">>), [Server])))] ++
++              [?XC(<<"h1">>, list_to_binary(io_lib:format(tr(Lang, ?T("Logged messages for ~s")), [Server])))] ++
+                case Res of
+                     ok -> [?CT(<<"Submitted">>), ?P];
+                     error -> [?CT(<<"Bad format">>), ?P];
+@@ -1696,12 +1727,12 @@ vhost_messages_stats_at(Server, Query, Lang, Date) ->
+    case Value of
+         {'EXIT', CReason} ->
+              ?ERROR_MSG("Failed to get_vhost_stats_at: ~p", [CReason]),
+-             [?XC(<<"h1">>, ?T(<<"Error occupied while fetching list">>))];
++             [?XC(<<"h1">>, tr(Lang, ?T("Error occupied while fetching list")))];
+         {error, GReason} ->
+              ?ERROR_MSG("Failed to get_vhost_stats_at: ~p", [GReason]),
+-             [?XC(<<"h1">>, ?T(<<"Error occupied while fetching list">>))];
++             [?XC(<<"h1">>, tr(Lang, ?T("Error occupied while fetching list")))];
+         {ok, []} ->
+-             [?XC(<<"h1">>, list_to_binary(io_lib:format(?T(<<"No logged messages for ~s at ~s">>), [Server, Date])))];
++             [?XC(<<"h1">>, list_to_binary(io_lib:format(tr(Lang, ?T("No logged messages for ~s at ~s")), [Server, Date])))];
+         {ok, Stats} ->
+              Res = case catch vhost_messages_at_parse_query(Server, Date, Stats, Query) of
+                         {'EXIT', Reason} ->
+@@ -1719,7 +1750,7 @@ vhost_messages_stats_at(Server, Query, Lang, Date) ->
+                            ?XC(<<"td">>, integer_to_binary(Count))
+                           ])
+                    end,
+-             [?XC(<<"h1">>, list_to_binary(io_lib:format(?T(<<"Logged messages for ~s at ~s">>), [Server, Date])))] ++
++             [?XC(<<"h1">>, list_to_binary(io_lib:format(tr(Lang, ?T("Logged messages for ~s at ~s")), [Server, Date])))] ++
+               case Res of
+                     ok -> [?CT(<<"Submitted">>), ?P];
+                     error -> [?CT(<<"Bad format">>), ?P];
+@@ -1757,12 +1788,12 @@ user_messages_stats(User, Server, Query, Lang) ->
+    case Value of
+         {'EXIT', CReason} ->
+             ?ERROR_MSG("Failed to get_user_stats: ~p", [CReason]),
+-            [?XC(<<"h1">>, ?T(<<"Error occupied while fetching days">>))];
++            [?XC(<<"h1">>, tr(Lang, ?T("Error occupied while fetching days")))];
+         {error, GReason} ->
+             ?ERROR_MSG("Failed to get_user_stats: ~p", [GReason]),
+-            [?XC(<<"h1">>, ?T(<<"Error occupied while fetching days">>))];
++            [?XC(<<"h1">>, tr(Lang,?T("Error occupied while fetching days")))];
+         {ok, []} ->
+-            [?XC(<<"h1">>, list_to_binary(io_lib:format(?T(<<"No logged messages for ~s">>), [Jid])))];
++            [?XC(<<"h1">>, list_to_binary(io_lib:format(tr(Lang, ?T("No logged messages for ~s")), [Jid])))];
+         {ok, Dates} ->
+             Fun = fun({Date, Count}) ->
+                       DateBin = iolist_to_binary(Date),
+@@ -1814,12 +1845,12 @@ user_messages_stats_at(User, Server, Query, Lang, Date) ->
+    case Value of
+         {'EXIT', CReason} ->
+            ?ERROR_MSG("Failed to get_user_messages_at: ~p", [CReason]),
+-           [?XC(<<"h1">>, ?T(<<"Error occupied while fetching messages">>))];
++           [?XC(<<"h1">>, tr(Lang, ?T("Error occupied while fetching messages")))];
+         {error, GReason} ->
+            ?ERROR_MSG("Failed to get_user_messages_at: ~p", [GReason]),
+-           [?XC(<<"h1">>, ?T(<<"Error occupied while fetching messages">>))];
++           [?XC(<<"h1">>, tr(Lang, ?T("Error occupied while fetching messages")))];
+         {ok, []} ->
+-           [?XC(<<"h1">>, list_to_binary(io_lib:format(?T(<<"No logged messages for ~s at ~s">>), [Jid, Date])))];
++           [?XC(<<"h1">>, list_to_binary(io_lib:format(tr(Lang, ?T("No logged messages for ~s at ~s")), [Jid, Date])))];
+         {ok, User_messages} ->
+            Res =  case catch user_messages_at_parse_query(Server,
+                                                           Date,
+@@ -1888,7 +1919,7 @@ user_messages_stats_at(User, Server, Query, Lang, Date) ->
+                                body=Body}) ->
+                       Text = case Subject of
+                                   "" -> iolist_to_binary(Body);
+-                                  _ -> iolist_to_binary([binary_to_list(?T(<<"Subject">>)) ++ ": " ++ Subject ++ "\n" ++ Body])
++                                  _ -> iolist_to_binary([binary_to_list(tr(Lang, ?T("Subject"))) ++ ": " ++ Subject ++ "\n" ++ Body])
+                              end,
+                       Resource = case PRes of
+                                       [] -> [];
+@@ -1915,7 +1946,7 @@ user_messages_stats_at(User, Server, Query, Lang, Date) ->
+            % Filtered user messages in html
+            Msgs = lists:map(Msgs_Fun, lists:sort(User_messages_filtered)),
  
---- src/mod_roster_odbc-2.0.3.erl      2009-02-03 08:28:26.000000000 +0200
-+++ src/mod_roster_odbc.erl    2009-02-03 08:47:04.000000000 +0200
-@@ -48,7 +48,7 @@
- -include("mod_roster.hrl").
- -include("web/ejabberd_http.hrl").
- -include("web/ejabberd_web_admin.hrl").
--
-+-include("mod_logdb.hrl").
+-           [?XC(<<"h1">>, list_to_binary(io_lib:format(?T(<<"Logged messages for ~s at ~s">>), [Jid, Date])))] ++
++           [?XC(<<"h1">>, list_to_binary(io_lib:format(tr(Lang, ?T("Logged messages for ~s at ~s")), [Jid, Date])))] ++
+             case Res of
+                  ok -> [?CT(<<"Submitted">>), ?P];
+                  error -> [?CT(<<"Bad format">>), ?P];
+diff --git a/src/mod_logdb_mysql.erl b/src/mod_logdb_mysql.erl
+index 21d65e6578..66b50acc86 100644
+--- a/src/mod_logdb_mysql.erl
++++ b/src/mod_logdb_mysql.erl
+@@ -94,11 +94,11 @@ stop(VHost) ->
+ init([VHost, Opts]) ->
+    crypto:start(),
  
- start(Host, Opts) ->
-     IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
-@@ -937,6 +937,14 @@
-     Res = user_roster_parse_query(User, Server, Items1, Query),
-     Items = get_roster(LUser, LServer),
-     SItems = lists:sort(Items),
-+
-+    Settings = case gen_mod:is_loaded(Server, mod_logdb) of
-+         true ->
-+             mod_logdb:get_user_settings(User, Server);
-+         false ->
-+             []
-+    end,
-+
-     FItems =
-       case SItems of
-           [] ->
-@@ -984,7 +992,33 @@
-                                             [?INPUTT("submit",
-                                                      "remove" ++
-                                                      ejabberd_web_admin:term_to_id(R#roster.jid),
--                                                     "Remove")])])
-+                                                      "Remove")]),
-+                                         case gen_mod:is_loaded(Server, mod_logdb) of
-+                                              true ->
-+                                                 Peer = jlib:jid_to_string(R#roster.jid),
-+                                                 A = lists:member(Peer, Settings#user_settings.dolog_list),
-+                                                 B = lists:member(Peer, Settings#user_settings.donotlog_list),
-+                                                 {Name, Value} =
-+                                                   if
-+                                                     A ->
-+                                                       {"donotlog", "Do Not Log Messages"};
-+                                                     B ->
-+                                                       {"dolog", "Log Messages"};
-+                                                     Settings#user_settings.dolog_default == true ->
-+                                                       {"donotlog", "Do Not Log Messages"};
-+                                                     Settings#user_settings.dolog_default == false ->
-+                                                       {"dolog", "Log Messages"}
-+                                                   end,
-+
-+                                                 ?XAE("td", [{"class", "valign"}],
-+                                                      [?INPUTT("submit",
-+                                                               Name ++
-+                                                               ejabberd_web_admin:term_to_id(R#roster.jid),
-+                                                               Value)]);
-+                                              false ->
-+                                                 ?X([])
-+                                         end
-+                                        ])
-                           end, SItems))])]
-       end,
-     [?XC("h1", ?T("Roster of ") ++ us_to_list(US))] ++
-@@ -1066,11 +1100,42 @@
-                                                {"subscription", "remove"}],
-                                               []}]}}),
-                             throw(submitted);
--                        false ->
--                            ok
--                    end
--
--            end
-+                          false ->
-+                            case lists:keysearch(
-+                                   "donotlog" ++ ejabberd_web_admin:term_to_id(JID), 1, Query) of
-+                                {value, _} ->
-+                                     Peer = jlib:jid_to_string(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),
-+                                     Sett = Settings#user_settings{donotlog_list=DNLL, dolog_list=DLL},
-+                                     % TODO: check returned value
-+                                     ok = mod_logdb:set_user_settings(User, Server, Sett),
-+                                     throw(nothing);
-+                                false ->
-+                                   case lists:keysearch(
-+                                          "dolog" ++ ejabberd_web_admin:term_to_id(JID), 1, Query) of
-+                                       {value, _} ->
-+                                          Peer = jlib:jid_to_string(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),
-+                                          Sett = Settings#user_settings{donotlog_list=DNLL, dolog_list=DLL},
-+                                          % TODO: check returned value
-+                                          ok = mod_logdb:set_user_settings(User, Server, Sett),
-+                                          throw(nothing);
-+                                       false ->
-+                                           ok
-+                                   end % dolog
-+                            end % donotlog
-+                      end % remove
-+              end % validate
-       end, Items),
-     nothing.
+-   Server = gen_mod:get_opt(server, Opts, fun(A) -> A end, <<"localhost">>),
+-   Port = gen_mod:get_opt(port, Opts, fun(A) -> A end, 3306),
+-   DB = gen_mod:get_opt(db, Opts, fun(A) -> A end, <<"logdb">>),
+-   User = gen_mod:get_opt(user, Opts, fun(A) -> A end, <<"root">>),
+-   Password = gen_mod:get_opt(password, Opts, fun(A) -> A end, <<"">>),
++   Server = mod_logdb:get_opt(server, Opts, <<"localhost">>),
++   Port = mod_logdb:get_opt(port, Opts, 3306),
++   DB = mod_logdb:get_opt(db, Opts, <<"logdb">>),
++   User = mod_logdb:get_opt(user, Opts, <<"root">>),
++   Password = mod_logdb:get_opt(password, Opts, <<"">>),
+    St = #state{vhost=VHost,
+                server=Server, port=Port, db=DB,
+diff --git a/src/mod_logdb_mysql5.erl b/src/mod_logdb_mysql5.erl
+index c05ab958e2..72fa72e72e 100644
+--- a/src/mod_logdb_mysql5.erl
++++ b/src/mod_logdb_mysql5.erl
+@@ -99,11 +99,11 @@ stop(VHost) ->
+ init([VHost, Opts]) ->
+    crypto:start(),
+-   Server = gen_mod:get_opt(server, Opts, fun(A) -> A end, <<"localhost">>),
+-   Port = gen_mod:get_opt(port, Opts, fun(A) -> A end, 3306),
+-   DB = gen_mod:get_opt(db, Opts, fun(A) -> A end, <<"logdb">>),
+-   User = gen_mod:get_opt(user, Opts, fun(A) -> A end, <<"root">>),
+-   Password = gen_mod:get_opt(password, Opts, fun(A) -> A end, <<"">>),
++   Server = mod_logdb:get_opt(server, Opts, <<"localhost">>),
++   Port = mod_logdb:get_opt(port, Opts, 3306),
++   DB = mod_logdb:get_opt(db, Opts, <<"logdb">>),
++   User = mod_logdb:get_opt(user, Opts, <<"root">>),
++   Password = mod_logdb:get_opt(password, Opts, <<"">>),
+    St = #state{vhost=VHost,
+                server=Server, port=Port, db=DB,
+diff --git a/src/mod_logdb_pgsql.erl b/src/mod_logdb_pgsql.erl
+index 202c6ed4a8..7f74887b9d 100644
+--- a/src/mod_logdb_pgsql.erl
++++ b/src/mod_logdb_pgsql.erl
+@@ -101,12 +101,12 @@ stop(VHost) ->
+ %
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ init([VHost, Opts]) ->
+-   Server = gen_mod:get_opt(server, Opts, fun(A) -> A end, <<"localhost">>),
+-   DB = gen_mod:get_opt(db, Opts, fun(A) -> A end, <<"ejabberd_logdb">>),
+-   User = gen_mod:get_opt(user, Opts, fun(A) -> A end, <<"root">>),
+-   Port = gen_mod:get_opt(port, Opts, fun(A) -> A end, 5432),
+-   Password = gen_mod:get_opt(password, Opts, fun(A) -> A end, <<"">>),
+-   Schema = binary_to_list(gen_mod:get_opt(schema, Opts, fun(A) -> A end, <<"public">>)),
++   Server = mod_logdb:get_opt(server, Opts, <<"localhost">>),
++   Port = mod_logdb:get_opt(port, Opts, 5432),
++   DB = mod_logdb:get_opt(db, Opts, <<"logdb">>),
++   User = mod_logdb:get_opt(user, Opts, <<"root">>),
++   Password = mod_logdb:get_opt(password, Opts, <<"">>),
++   Schema = mod_logdb:get_opt(schema, Opts, <<"public">>),
+    ?MYDEBUG("Starting pgsql backend for ~s", [VHost]),
+
+From 55274ef5a3deb5979e0d97cdb48768eb472c36ec Mon Sep 17 00:00:00 2001
+From: Oleh Palii <o.palij@gmail.com>
+Date: Sat, 31 Aug 2019 22:43:11 +0300
+Subject: [PATCH 3/3] mod_logdb mod_opt_type fixes
+
+---
+ src/mod_logdb.erl | 33 ++++++++++++++++++++++-----------
+ 1 file changed, 22 insertions(+), 11 deletions(-)
+
+diff --git a/src/mod_logdb.erl b/src/mod_logdb.erl
+index 0b5c2ec687..0766241fec 100644
+--- a/src/mod_logdb.erl
++++ b/src/mod_logdb.erl
+@@ -220,24 +220,35 @@ get_commands_spec() ->
+             result = {res, rescode}}].
+ mod_opt_type(dbs) ->
+-    fun (A) when is_list(A) -> A end;
++    econf:map(
++        econf:enum([mnesia, mysql, mysql5, pgsql]),
++        econf:map(
++            econf:enum([user, password, server, port, db, schema]),
++            econf:string()
++        )
++    );
+ mod_opt_type(vhosts) ->
+-    fun (A) when is_list(A) -> A end;
++    econf:map(
++        econf:string(),
++        econf:enum([mnesia, mysql, mysql5, pgsql])
++    );
+ mod_opt_type(poll_users_settings) ->
+-    fun (I) when is_integer(I) -> I end;
++    econf:non_neg_int();
+ mod_opt_type(groupchat) ->
+-    fun (all) -> all;
+-        (send) -> send;
+-        (none) -> none
+-    end;
++    econf:enum([all, send, none]);
+ mod_opt_type(dolog_default) ->
+-    fun (B) when is_boolean(B) -> B end;
++    econf:bool();
++mod_opt_type(drop_messages_on_user_removal) ->
++    econf:bool();
+ mod_opt_type(ignore_jids) ->
+-    fun (A) when is_list(A) -> A end;
++    econf:list(econf:string());
+ mod_opt_type(purge_older_days) ->
+-    fun (I) when is_integer(I) -> I end;
++    econf:either(
++        never,
++        econf:non_neg_int()
++    );
+ mod_opt_type(_) ->
+-    [dbs, vhosts, poll_users_settings, groupchat, dolog_default, ignore_jids, purge_older_days].
++    [dbs, vhosts, poll_users_settings, groupchat, dolog_default, drop_messages_on_user_removal, ignore_jids, purge_older_days].
  
+ handle_call({cleanup}, _From, State) ->
+     cleanup(State),
This page took 0.68326 seconds and 4 git commands to generate.