X-Git-Url: http://git.pld-linux.org/?a=blobdiff_plain;f=ejabberd-mod_logdb.patch;h=449c1c3e1362d0e0866e269517846f7325809fa5;hb=e6d2753694e8bb53d4842cc5e7e17d9d6de8ca6c;hp=b8b0c659ea860139269d0f9debd0e3c9a4fbabef;hpb=046546efc084fcf551dfc1260e464761437b78cc;p=packages%2Fejabberd.git diff --git a/ejabberd-mod_logdb.patch b/ejabberd-mod_logdb.patch index b8b0c65..449c1c3 100644 --- a/ejabberd-mod_logdb.patch +++ b/ejabberd-mod_logdb.patch @@ -1,11 +1,39 @@ +From 9a2ed8d2b20ef052b71b065e686cc049d18999ac Mon Sep 17 00:00:00 2001 +From: Oleh Palii +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 70e739f..76df8cf 100644 +index 8009d529ff..fd3d3b0ebb 100644 --- a/priv/msgs/nl.msg +++ b/priv/msgs/nl.msg -@@ -419,3 +419,17 @@ +@@ -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."}. - {"Your messages to ~s are being blocked. To unblock them, visit ~s","Uw berichten aan ~s worden geblokkeerd. Om ze te deblokkeren, ga naar ~s"}. +% mod_logdb +{"Users Messages", "Gebruikersberichten"}. +{"Date", "Datum"}. @@ -21,13 +49,13 @@ index 70e739f..76df8cf 100644 +{"Body", "Berichtveld"}. +{"Messages", "Berichten"}. diff --git a/priv/msgs/pl.msg b/priv/msgs/pl.msg -index 4bc2063..7540386 100644 +index 2ca75b259c..fffae5742e 100644 --- a/priv/msgs/pl.msg +++ b/priv/msgs/pl.msg -@@ -419,3 +419,29 @@ +@@ -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."}. - {"Your messages to ~s are being blocked. To unblock them, visit ~s","Twoje wiadomości do ~s są blokowane. Aby je odblokować, odwiedź ~s"}. +% mod_logdb +{"Users Messages", "Wiadomości użytkownika"}. +{"Date", "Data"}. @@ -55,13 +83,13 @@ index 4bc2063..7540386 100644 +{"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 ece7348..b159f98 100644 +index f7dff97ea1..42be5d4f15 100644 --- a/priv/msgs/ru.msg +++ b/priv/msgs/ru.msg -@@ -419,3 +419,33 @@ +@@ -507,3 +507,33 @@ {"Your Jabber account was successfully created.","Ваш Jabber-аккаунт был успешно создан."}. {"Your Jabber account was successfully deleted.","Ваш Jabber-аккаунт был успешно удален."}. - {"Your messages to ~s are being blocked. To unblock them, visit ~s","Ваши сообщения к ~s блокируются. Для снятия блокировки перейдите по ссылке ~s"}. + {"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", "Дата"}. @@ -93,13 +121,13 @@ index ece7348..b159f98 100644 +{"Do not drop", "Не удалять"}. +{"Drop messages on user removal", "Удалять сообщения при удалении пользователя"}. diff --git a/priv/msgs/uk.msg b/priv/msgs/uk.msg -index 6e21c90..9838624 100644 +index 0fbc336d51..c0b90047fa 100644 --- a/priv/msgs/uk.msg +++ b/priv/msgs/uk.msg -@@ -407,3 +407,33 @@ +@@ -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-акаунт було успішно видалено."}. - {"Your messages to ~s are being blocked. To unblock them, visit ~s","Ваші повідомлення до ~s блокуються. Для розблокування відвідайте ~s"}. +% mod_logdb +{"Users Messages", "Повідомлення користувачів"}. +{"Date", "Дата"}. @@ -130,35 +158,32 @@ index 6e21c90..9838624 100644 +{"Drop", "Видаляти"}. +{"Do not drop", "Не видаляти"}. +{"Drop messages on user removal", "Видаляти повідомлення під час видалення користувача"}. -diff --git a/rebar.config.script b/rebar.config.script -index f342a6c..2ad3f1f 100644 ---- a/rebar.config.script -+++ b/rebar.config.script -@@ -81,9 +81,9 @@ PostHooks = [ConfigureCmd("p1_tls", ""), - - CfgDeps = lists:flatmap( - fun({mysql, true}) -> -- [{p1_mysql, ".*", {git, "git://github.com/processone/mysql"}}]; -+ [{p1_mysql, ".*", {git, "git://github.com/paleg/mysql", {branch, "multi"}}}]; - ({pgsql, true}) -> -- [{p1_pgsql, ".*", {git, "git://github.com/processone/pgsql"}}]; -+ [{p1_pgsql, ".*", {git, "git://github.com/paleg/pgsql", {branch, "errordescr_fix"}}}]; - ({pam, true}) -> - [{p1_pam, ".*", {git, "git://github.com/processone/epam"}}]; - ({zlib, true}) -> +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 0000000..06a894b +index 0000000000..8bad112969 --- /dev/null +++ b/src/gen_logdb.erl -@@ -0,0 +1,164 @@ +@@ -0,0 +1,162 @@ +%%%---------------------------------------------------------------------- +%%% File : gen_logdb.erl -+%%% Author : Oleg Palij (mailto,xmpp:o.palij@gmail.com) ++%%% Author : Oleg Palij (mailto:o.palij@gmail.com) +%%% Purpose : Describes generic behaviour for mod_logdb backends. -+%%% Version : trunk -+%%% Id : $Id: gen_logdb.erl 1273 2009-02-05 18:12:57Z malik $ -+%%% Url : http://www.dp.uz.gov.ua/o.palij/mod_logdb/ ++%%% Url : https://paleg.github.io/mod_logdb/ +%%%---------------------------------------------------------------------- + +-module(gen_logdb). @@ -318,17 +343,15 @@ index 0000000..06a894b + undefined. diff --git a/src/mod_logdb.erl b/src/mod_logdb.erl new file mode 100644 -index 0000000..516f92b +index 0000000000..bf0240d139 --- /dev/null +++ b/src/mod_logdb.erl -@@ -0,0 +1,2152 @@ +@@ -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: mod_logdb.erl 1360 2009-07-30 06:00:14Z malik $ -+%%% Url : http://www.dp.uz.gov.ua/o.palij/mod_logdb/ ++%%% Url : https://paleg.github.io/mod_logdb/ +%%%---------------------------------------------------------------------- + +-module(mod_logdb). @@ -340,24 +363,23 @@ index 0000000..516f92b +% 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, remove_user/2]). ++-export([send_packet/1, receive_packet/1, offline_message/1, remove_user/2]). +-export([get_local_identity/5, + get_local_features/5, + get_local_items/5, + adhoc_local_items/4, + adhoc_local_commands/4 -+% get_sm_identity/5, -+% get_sm_features/5, -+% get_sm_items/5, -+% adhoc_sm_items/4, -+% adhoc_sm_commands/4]). + ]). +% ejabberdctl -+-export([rebuild_stats/3, ++-export([rebuild_stats/1, + copy_messages/1, copy_messages_ctl/3, copy_messages_int_tc/1]). +% +-export([get_vhost_stats/1, get_vhost_stats_at/2, @@ -384,10 +406,9 @@ index 0000000..516f92b + 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("ejabberd_web_admin.hrl"). +-include("ejabberd_http.hrl"). @@ -416,7 +437,14 @@ index 0000000..516f92b + 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) -> @@ -430,10 +458,10 @@ index 0000000..516f92b + 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,[]}]); -+ DBsResult -> DBsResult ++ {value, _} -> DBsRaw + end, + VHostDB = gen_mod:get_opt(vhosts, Opts, fun(A) -> A end, [{VHost, mnesia}]), -+ % 10 is default becouse of using in clustered environment ++ % 10 is default because of using in clustered environment + PollUsersSettings = gen_mod:get_opt(poll_users_settings, Opts, fun(A) -> A end, 10), + + {DBName, DBOpts} = @@ -451,7 +479,7 @@ index 0000000..516f92b + end + end, + -+ ?MYDEBUG("Starting mod_logdb for ~s with ~s 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)), + @@ -475,13 +503,10 @@ index 0000000..516f92b + ejabberd_hooks:delete(remove_user, VHost, ?MODULE, remove_user, 90), + ejabberd_hooks:delete(user_send_packet, VHost, ?MODULE, send_packet, 90), + ejabberd_hooks:delete(user_receive_packet, VHost, ?MODULE, receive_packet, 90), -+ %ejabberd_hooks:delete(adhoc_sm_commands, VHost, ?MODULE, adhoc_sm_commands, 110), -+ %ejabberd_hooks:delete(adhoc_sm_items, VHost, ?MODULE, adhoc_sm_items, 110), ++ ejabberd_hooks:delete(offline_message_hook, VHost, ?MODULE, offline_message, 40), ++ + ejabberd_hooks:delete(adhoc_local_commands, VHost, ?MODULE, adhoc_local_commands, 50), + ejabberd_hooks:delete(adhoc_local_items, VHost, ?MODULE, adhoc_local_items, 50), -+ %ejabberd_hooks:delete(disco_sm_identity, VHost, ?MODULE, get_sm_identity, 50), -+ %ejabberd_hooks:delete(disco_sm_features, VHost, ?MODULE, get_sm_features, 50), -+ %ejabberd_hooks:delete(disco_sm_items, VHost, ?MODULE, get_sm_items, 50), + ejabberd_hooks:delete(disco_local_identity, VHost, ?MODULE, get_local_identity, 50), + ejabberd_hooks:delete(disco_local_features, VHost, ?MODULE, get_local_features, 50), + ejabberd_hooks:delete(disco_local_items, VHost, ?MODULE, get_local_items, 50), @@ -493,14 +518,7 @@ index 0000000..516f92b + + ?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) -> @@ -508,8 +526,40 @@ index 0000000..516f92b + %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), @@ -648,10 +698,10 @@ index 0000000..516f92b + 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]), @@ -710,17 +760,13 @@ index 0000000..516f92b + ejabberd_hooks:add(remove_user, VHost, ?MODULE, remove_user, 90), + ejabberd_hooks:add(user_send_packet, VHost, ?MODULE, send_packet, 90), + ejabberd_hooks:add(user_receive_packet, VHost, ?MODULE, receive_packet, 90), ++ ejabberd_hooks:add(offline_message_hook, VHost, ?MODULE, offline_message, 40), + ++ ejabberd_hooks:add(adhoc_local_commands, VHost, ?MODULE, adhoc_local_commands, 50), + ejabberd_hooks:add(disco_local_items, VHost, ?MODULE, get_local_items, 50), -+ ejabberd_hooks:add(disco_local_features, VHost, ?MODULE, get_local_features, 50), + ejabberd_hooks:add(disco_local_identity, VHost, ?MODULE, get_local_identity, 50), -+ %ejabberd_hooks:add(disco_sm_items, VHost, ?MODULE, get_sm_items, 50), -+ %ejabberd_hooks:add(disco_sm_features, VHost, ?MODULE, get_sm_features, 50), -+ %ejabberd_hooks:add(disco_sm_identity, VHost, ?MODULE, get_sm_identity, 50), ++ ejabberd_hooks:add(disco_local_features, VHost, ?MODULE, get_local_features, 50), + ejabberd_hooks:add(adhoc_local_items, VHost, ?MODULE, adhoc_local_items, 50), -+ ejabberd_hooks:add(adhoc_local_commands, VHost, ?MODULE, adhoc_local_commands, 50), -+ %ejabberd_hooks:add(adhoc_sm_items, VHost, ?MODULE, adhoc_sm_items, 50), -+ %ejabberd_hooks:add(adhoc_sm_commands, VHost, ?MODULE, adhoc_sm_commands, 50), + + ejabberd_hooks:add(webadmin_menu_host, VHost, ?MODULE, webadmin_menu, 70), + ejabberd_hooks:add(webadmin_user, VHost, ?MODULE, webadmin_user, 50), @@ -729,17 +775,7 @@ index 0000000..516f92b + + ?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}, @@ -801,19 +837,32 @@ index 0000000..516f92b +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% 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}. + -+receive_packet(_JID, 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}. ++ ++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, 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}). + @@ -822,23 +871,20 @@ index 0000000..516f92b +% 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 @@ -847,68 +893,44 @@ index 0000000..516f92b + +% 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 -+ <<"error">> -> throw(ignore); -+ [] -> <<"normal">>; -+ MType -> MType -+ end, -+ -+ case Message_type of -+ <<"groupchat">> when State#state.groupchat == send, Direction == to -> -+ ok; -+ <<"groupchat">> when State#state.groupchat == send, Direction == from -> -+ throw(ignore); -+ <<"groupchat">> when State#state.groupchat == half -> -+ Rooms = ets:match(muc_online_room, '$1'), -+ Ni=lists:foldl(fun([{muc_online_room, {GName, GHost}, Pid}], Names) -> -+ case gen_fsm:sync_send_all_state_event(Pid, {get_jid_nick,Owner}) of -+ [] -> Names; -+ Nick -> -+ lists:append(Names, [jlib:jid_to_string({GName, GHost, Nick})]) -+ end -+ end, [], Rooms), -+ case lists:member(jlib:jid_to_string(Peer), Ni) of -+ true when Direction == from -> -+ throw(ignore); -+ _ -> -+ ok -+ end; -+ <<"groupchat">> when State#state.groupchat == none -> -+ throw(ignore); -+ _ -> -+ ok -+ end, ++packet_parse(_Owner, _Peer, #message{type = error}, _Direction, _State) -> ++ ignore; ++packet_parse(_Owner, _Peer, #message{meta = #{sm_copy := true}}, _Direction, _State) -> ++ ignore; ++packet_parse(_Owner, _Peer, #message{meta = #{from_offline := true}}, _Direction, _State) -> ++ ignore; ++packet_parse(Owner, Peer, #message{body = Body, subject = Subject, type = Type}, Direction, State) -> ++ %?MYDEBUG("Owner=~p, Peer=~p, Direction=~p", [Owner, Peer, Direction]), ++ %?MYDEBUG("Body=~p, Subject=~p, Type=~p", [Body, Subject, Type]), ++ SubjectText = xmpp:get_text(Subject), ++ BodyText = xmpp:get_text(Body), ++ if (SubjectText == <<"">>) and (BodyText == <<"">>) -> ++ throw(ignore); ++ true -> ok ++ end, + -+ Message_body = 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) -> @@ -933,7 +955,7 @@ index 0000000..516f92b + true -> State#state.dolog_default + end; + _ -> State#state.dolog_default -+ end, ++ end, + lists:all(fun(O) -> O end, + [not lists:member(OwnerBin, State#state.ignore_jids), + not lists:member(PeerBin, State#state.ignore_jids), @@ -1059,7 +1081,7 @@ index 0000000..516f92b + {value, _} -> + PMsgs = lists:filter( + fun(Msg) -> -+ ID = jlib:encode_base64(term_to_binary(Msg#msg.timestamp)), ++ ID = misc:encode_base64(term_to_binary(Msg#msg.timestamp)), + lists:member({<<"selected">>, ID}, Query) + end, Msgs), + Proc = gen_mod:get_module_proc(VHost, ?PROCNAME), @@ -1074,7 +1096,7 @@ index 0000000..516f92b + Dates = get_dates(VHost), + PDates = lists:filter( + fun(Date) -> -+ ID = jlib:encode_base64( << User/binary, (iolist_to_binary(Date))/binary >> ), ++ ID = misc:encode_base64( << User/binary, (iolist_to_binary(Date))/binary >> ), + lists:member({<<"selected">>, ID}, Query) + end, Dates), + Proc = gen_mod:get_module_proc(VHost, ?PROCNAME), @@ -1101,7 +1123,7 @@ index 0000000..516f92b + Dates = get_dates(VHost), + PDates = lists:filter( + fun(Date) -> -+ ID = jlib:encode_base64( << VHost/binary, (iolist_to_binary(Date))/binary >> ), ++ ID = misc:encode_base64( << VHost/binary, (iolist_to_binary(Date))/binary >> ), + lists:member({<<"selected">>, ID}, Query) + end, Dates), + Proc = gen_mod:get_module_proc(VHost, ?PROCNAME), @@ -1125,7 +1147,7 @@ index 0000000..516f92b + {value, _} -> + PStats = lists:filter( + fun({User, _Count}) -> -+ ID = jlib:encode_base64( << (iolist_to_binary(User))/binary, VHost/binary >> ), ++ ID = misc:encode_base64( << (iolist_to_binary(User))/binary, VHost/binary >> ), + lists:member({<<"selected">>, ID}, Query) + end, Stats), + Proc = gen_mod:get_module_proc(VHost, ?PROCNAME), @@ -1145,11 +1167,9 @@ index 0000000..516f92b + 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 -> @@ -1161,34 +1181,24 @@ index 0000000..516f92b + + {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]) -> @@ -1202,10 +1212,10 @@ index 0000000..516f92b +copy_messages_int_tc([FromDBMod, ToDBMod, VHost, Date]) -> + ?INFO_MSG("Going to copy messages from ~p for ~p at ~p", [FromDBMod, VHost, Date]), + -+ ok = FromDBMod:rebuild_stats_at(VHost, binary_to_list(Date)), ++ ok = FromDBMod:rebuild_stats_at(VHost, Date), + catch mod_logdb:rebuild_stats_at(VHost, Date), -+ {ok, FromStats} = FromDBMod:get_vhost_stats_at(VHost, binary_to_list(Date)), -+ ToStats = case mod_logdb:get_vhost_stats_at(VHost, Date) of ++ {ok, FromStats} = FromDBMod:get_vhost_stats_at(VHost, Date), ++ ToStats = case mod_logdb:get_vhost_stats_at(VHost, iolist_to_binary(Date)) of + {ok, Stats} -> Stats; + {error, _} -> [] + end, @@ -1216,13 +1226,20 @@ index 0000000..516f92b + StatsLength = length(FromStats), + + CopyFun = if -+ % destination table is empty -+ FromDBMod /= mod_logdb_mnesia_old, ToStats == [] -> ++ % destination table is empty ++ ToStats == [] -> + fun({User, _Count}, Acc) -> -+ {ok, Msgs} = FromDBMod:get_user_messages_at(binary_to_list(User), VHost, binary_to_list(Date)), ++ {ok, Msgs} = FromDBMod:get_user_messages_at(User, VHost, Date), + MAcc = + lists:foldl(fun(Msg, MFAcc) -> -+ ok = ToDBMod:log_message(VHost, Msg), ++ MsgBinary = Msg#msg{owner_name=iolist_to_binary(User), ++ peer_name=iolist_to_binary(Msg#msg.peer_name), ++ peer_server=iolist_to_binary(Msg#msg.peer_server), ++ peer_resource=iolist_to_binary(Msg#msg.peer_resource), ++ type=iolist_to_binary(Msg#msg.type), ++ subject=iolist_to_binary(Msg#msg.subject), ++ body=iolist_to_binary(Msg#msg.body)}, ++ ok = ToDBMod:log_message(VHost, MsgBinary), + MFAcc + 1 + end, 0, Msgs), + NewAcc = Acc + 1, @@ -1230,10 +1247,10 @@ index 0000000..516f92b + %timer:sleep(100), + NewAcc + end; -+ % destination table is not empty -+ FromDBMod /= mod_logdb_mnesia_old, ToStats /= [] -> ++ % destination table is not empty ++ true -> + fun({User, _Count}, Acc) -> -+ {ok, ToMsgs} = ToDBMod:get_user_messages_at(binary_to_list(User), VHost, binary_to_list(Date)), ++ {ok, ToMsgs} = ToDBMod:get_user_messages_at(User, VHost, Date), + lists:foreach(fun(#msg{timestamp=Tst}) when length(Tst) == 16 -> + ets:insert(mod_logdb_temp, {Tst}); + % mysql, pgsql removes final zeros after decimal point @@ -1242,12 +1259,19 @@ index 0000000..516f92b + [T] = io_lib:format("~.5f", [F]), + ets:insert(mod_logdb_temp, {T}) + end, ToMsgs), -+ {ok, Msgs} = FromDBMod:get_user_messages_at(binary_to_list(User), VHost, binary_to_list(Date)), ++ {ok, Msgs} = FromDBMod:get_user_messages_at(User, VHost, Date), + MAcc = + lists:foldl(fun(#msg{timestamp=ToTimestamp} = Msg, MFAcc) -> + case ets:member(mod_logdb_temp, ToTimestamp) of + false -> -+ ok = ToDBMod:log_message(VHost, Msg), ++ MsgBinary = Msg#msg{owner_name=iolist_to_binary(User), ++ peer_name=iolist_to_binary(Msg#msg.peer_name), ++ peer_server=iolist_to_binary(Msg#msg.peer_server), ++ peer_resource=iolist_to_binary(Msg#msg.peer_resource), ++ type=iolist_to_binary(Msg#msg.type), ++ subject=iolist_to_binary(Msg#msg.subject), ++ body=iolist_to_binary(Msg#msg.body)}, ++ ok = ToDBMod:log_message(VHost, MsgBinary), + ets:insert(mod_logdb_temp, {ToTimestamp}), + MFAcc + 1; + true -> @@ -1259,79 +1283,8 @@ index 0000000..516f92b + ?INFO_MSG("Copied ~p messages for ~p (~p/~p) at ~p", [MAcc, User, NewAcc, StatsLength, Date]), + %timer:sleep(100), + NewAcc -+ end; -+ % copying from mod_logmnesia -+ true -> -+ fun({User, _Count}, Acc) -> -+ ToStats = -+ case ToDBMod:get_user_messages_at(binary_to_list(User), VHost, binary_to_list(Date)) of -+ {ok, []} -> -+ ok; -+ {ok, ToMsgs} -> -+ lists:foreach(fun(#msg{timestamp=Tst}) when length(Tst) == 16 -> -+ ets:insert(mod_logdb_temp, {Tst}); -+ % mysql, pgsql removes final zeros after decimal point -+ (#msg{timestamp=Tst}) when length(Tst) < 15 -> -+ {F, _} = string:to_float(Tst++".0"), -+ [T] = io_lib:format("~.5f", [F]), -+ ets:insert(mod_logdb_temp, {T}) -+ end, ToMsgs); -+ {error, _} -> -+ ok -+ end, -+ {ok, Msgs} = FromDBMod:get_user_messages_at(binary_to_list(User), VHost, binary_to_list(Date)), -+ -+ MAcc = -+ lists:foldl( -+ fun({msg, TU, TS, TR, FU, FS, FR, Type, Subj, Body, Timest}, -+ MFAcc) -> -+ [Timestamp] = if is_float(Timest) == true -> -+ io_lib:format("~.5f", [Timest]); -+ % early versions of mod_logmnesia -+ is_integer(Timest) == true -> -+ io_lib:format("~.5f", [Timest-719528*86400.0]); -+ true -> -+ ?ERROR_MSG("Incorrect timestamp ~p", [Timest]), -+ throw(error) -+ end, -+ case ets:member(mod_logdb_temp, Timestamp) of -+ false -> -+ if -+ % from -+ TS == VHost -> -+ TMsg = #msg{timestamp=Timestamp, -+ owner_name=TU, -+ peer_name=FU, peer_server=FS, peer_resource=FR, -+ direction=from, -+ type=Type, -+ subject=Subj, body=Body}, -+ ok = ToDBMod:log_message(VHost, TMsg); -+ true -> ok -+ end, -+ if -+ % to -+ FS == VHost -> -+ FMsg = #msg{timestamp=Timestamp, -+ owner_name=FU, -+ peer_name=TU, peer_server=TS, peer_resource=TR, -+ direction=to, -+ type=Type, -+ subject=Subj, body=Body}, -+ ok = ToDBMod:log_message(VHost, FMsg); -+ true -> ok -+ end, -+ ets:insert(mod_logdb_temp, {Timestamp}), -+ MFAcc + 1; -+ true -> % not ets:member -+ MFAcc -+ end % case -+ end, 0, Msgs), % foldl -+ NewAcc = Acc + 1, -+ ?INFO_MSG("Copied ~p messages for ~p (~p/~p) at ~p", [MAcc, User, NewAcc, StatsLength, Date]), -+ %timer:sleep(100), -+ NewAcc -+ end % fun -+ end, % if FromDBMod /= mod_logdb_mnesia_old ++ end ++ end, + + if + FromStats == [] -> @@ -1340,7 +1293,7 @@ index 0000000..516f92b + ?INFO_MSG("Stats are equal at ~p", [Date]); + FromStatsS /= ToStatsS -> + lists:foldl(CopyFun, 0, FromStats), -+ ok = ToDBMod:rebuild_stats_at(VHost, binary_to_list(Date)) ++ ok = ToDBMod:rebuild_stats_at(VHost, Date) + %timer:sleep(1000) + end, + @@ -1390,22 +1343,19 @@ index 0000000..516f92b +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-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; @@ -1416,7 +1366,7 @@ index 0000000..516f92b + 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} -> @@ -1426,24 +1376,25 @@ index 0000000..516f92b + {result, Items} + end + end; -+get_local_items(Acc, From, #jid{lserver = LServer} = To, Node, Lang) -> ++get_local_items(Acc, From, #jid{lserver = LServer} = To, ++ Node, Lang) -> + case gen_mod:is_loaded(LServer, mod_adhoc) of -+ false -> -+ Acc; ++ false -> Acc; + _ -> -+ LNode = str:tokens(Node, <<"/">>), ++ LNode = tokenize(Node), + AllowAdmin = acl:match_rule(LServer, mod_logdb_admin, From), ++ Err = xmpp:err_forbidden(<<"Denied by ACL">>, Lang), + case LNode of + [<<"mod_logdb">>] -> -+ ?ITEMS_RESULT(AllowAdmin, LNode, {error, ?ERR_FORBIDDEN}); ++ ?ITEMS_RESULT(AllowAdmin, LNode, {error, Err}); + [<<"mod_logdb_users">>] -> -+ ?ITEMS_RESULT(AllowAdmin, LNode, {error, ?ERR_FORBIDDEN}); ++ ?ITEMS_RESULT(AllowAdmin, LNode, {error, Err}); + [<<"mod_logdb_users">>, <<$@, _/binary>>] -> -+ ?ITEMS_RESULT(AllowAdmin, LNode, {error, ?ERR_FORBIDDEN}); ++ ?ITEMS_RESULT(AllowAdmin, LNode, {error, Err}); + [<<"mod_logdb_users">>, _User] -> -+ ?ITEMS_RESULT(AllowAdmin, LNode, {error, ?ERR_FORBIDDEN}); ++ ?ITEMS_RESULT(AllowAdmin, LNode, {error, Err}); + [<<"mod_logdb_settings">>] -> -+ ?ITEMS_RESULT(AllowAdmin, LNode, {error, ?ERR_FORBIDDEN}); ++ ?ITEMS_RESULT(AllowAdmin, LNode, {error, Err}); + _ -> + Acc + end @@ -1452,11 +1403,14 @@ index 0000000..516f92b +-define(T(Lang, Text), translate:translate(Lang, Text)). + +-define(NODE(Name, Node), -+ #xmlel{name = <<"item">>, -+ attrs = -+ [{<<"jid">>, Server}, {<<"name">>, ?T(Lang, Name)}, -+ {<<"node">>, Node}], -+ children = []}). ++ #disco_item{jid = jid:make(Server), ++ node = Node, ++ name = ?T(Lang, Name)}). ++ ++-define(NS_ADMINX(Sub), ++ <<(?NS_ADMIN)/binary, "#", Sub/binary>>). ++ ++tokenize(Node) -> str:tokens(Node, <<"/#">>). + +get_local_items(_Host, [], Server, Lang) -> + {result, @@ -1467,29 +1421,22 @@ index 0000000..516f92b + [?NODE(<<"Messages logging engine users">>, <<"mod_logdb_users">>), + ?NODE(<<"Messages logging engine settings">>, <<"mod_logdb_settings">>)] + }; -+get_local_items(Host, [<<"mod_logdb_users">>], Server, Lang) -> -+ {result, get_all_vh_users(Host, Server, Lang)}; ++get_local_items(Host, [<<"mod_logdb_users">>], Server, _Lang) -> ++ {result, get_all_vh_users(Host, Server)}; +get_local_items(Host, [<<"mod_logdb_users">>, <<$@, Diap/binary>>], Server, Lang) -> -+ case catch ejabberd_auth:get_vh_registered_users(Host) of -+ {'EXIT', _Reason} -> -+ ?ERR_INTERNAL_SERVER_ERROR; -+ Users -> -+ SUsers = lists:sort([{S, U} || {U, S} <- Users]), -+ case catch begin -+ [S1, S2] = ejabberd_regexp:split(Diap, <<"-">>), -+ N1 = jlib:binary_to_integer(S1), -+ N2 = jlib:binary_to_integer(S2), -+ Sub = lists:sublist(SUsers, N1, N2 - N1 + 1), -+ lists:map(fun({S, U}) -> -+ ?NODE(<< U/binary, "@", S/binary >>, -+ << (iolist_to_binary("mod_logdb_users/"))/binary, U/binary, "@", S/binary >>) -+ end, Sub) -+ end of -+ {'EXIT', _Reason} -> -+ ?ERR_NOT_ACCEPTABLE; -+ Res -> -+ {result, Res} -+ end ++ Users = ejabberd_auth:get_vh_registered_users(Host), ++ SUsers = lists:sort([{S, U} || {U, S} <- Users]), ++ try ++ [S1, S2] = ejabberd_regexp:split(Diap, <<"-">>), ++ N1 = binary_to_integer(S1), ++ N2 = binary_to_integer(S2), ++ Sub = lists:sublist(SUsers, N1, N2 - N1 + 1), ++ {result, lists:map(fun({S, U}) -> ++ ?NODE(<< U/binary, "@", S/binary >>, ++ << (iolist_to_binary("mod_logdb_users/"))/binary, U/binary, "@", S/binary >>) ++ end, Sub)} ++ catch _:_ -> ++ xmpp:err_not_acceptable() + end; +get_local_items(_Host, [<<"mod_logdb_users">>, _User], _Server, _Lang) -> + {result, []}; @@ -1497,35 +1444,36 @@ index 0000000..516f92b + {result, []}; +get_local_items(_Host, Item, _Server, _Lang) -> + ?MYDEBUG("asked for items in ~p", [Item]), -+ {error, ?ERR_ITEM_NOT_FOUND}. ++ {error, xmpp:err_item_not_found()}. + -+-define(INFO_RESULT(Allow, Feats), ++-define(INFO_RESULT(Allow, Feats, Lang), + case Allow of -+ deny -> {error, ?ERR_FORBIDDEN}; ++ deny -> {error, xmpp:err_forbidden(<<"Denied by ACL">>, Lang)}; + allow -> {result, Feats} + end). + -+get_local_features(Acc, From, #jid{lserver = LServer} = _To, Node, _Lang) -> ++get_local_features(Acc, From, ++ #jid{lserver = LServer} = _To, Node, Lang) -> + case gen_mod:is_loaded(LServer, mod_adhoc) of + false -> + Acc; + _ -> -+ LNode = str:tokens(Node, <<"/">>), ++ LNode = tokenize(Node), + AllowUser = acl:match_rule(LServer, mod_logdb, From), + AllowAdmin = acl:match_rule(LServer, mod_logdb_admin, From), + case LNode of + [<<"mod_logdb">>] when AllowUser == allow; AllowAdmin == allow -> -+ ?INFO_RESULT(allow, [?NS_COMMANDS]); ++ ?INFO_RESULT(allow, [?NS_COMMANDS], Lang); + [<<"mod_logdb">>] -> -+ ?INFO_RESULT(deny, [?NS_COMMANDS]); ++ ?INFO_RESULT(deny, [?NS_COMMANDS], Lang); + [<<"mod_logdb_users">>] -> -+ ?INFO_RESULT(AllowAdmin, []); ++ ?INFO_RESULT(AllowAdmin, [], Lang); + [<<"mod_logdb_users">>, [$@ | _]] -> -+ ?INFO_RESULT(AllowAdmin, []); ++ ?INFO_RESULT(AllowAdmin, [], Lang); + [<<"mod_logdb_users">>, _User] -> -+ ?INFO_RESULT(AllowAdmin, [?NS_COMMANDS]); ++ ?INFO_RESULT(AllowAdmin, [?NS_COMMANDS], Lang); + [<<"mod_logdb_settings">>] -> -+ ?INFO_RESULT(AllowAdmin, [?NS_COMMANDS]); ++ ?INFO_RESULT(AllowAdmin, [?NS_COMMANDS], Lang); + [] -> + Acc; + _ -> @@ -1534,87 +1482,65 @@ index 0000000..516f92b + end. + +-define(INFO_IDENTITY(Category, Type, Name, Lang), -+ [#xmlel{name = <<"identity">>, -+ attrs = -+ [{<<"category">>, Category}, {<<"type">>, Type}, -+ {<<"name">>, ?T(Lang, Name)}], -+ children = []}]). ++ [#identity{category = Category, type = Type, name = ?T(Lang, Name)}]). + +-define(INFO_COMMAND(Name, Lang), + ?INFO_IDENTITY(<<"automation">>, <<"command-node">>, + Name, Lang)). + +get_local_identity(Acc, _From, _To, Node, Lang) -> -+ LNode = str:tokens(Node, <<"/">>), ++ LNode = tokenize(Node), + case LNode of + [<<"mod_logdb">>] -> + ?INFO_COMMAND(<<"Messages logging engine">>, Lang); + [<<"mod_logdb_users">>] -> + ?INFO_COMMAND(<<"Messages logging engine users">>, Lang); -+ [<<"mod_logdb_users">>, [$@ | _]] -> -+ Acc; + [<<"mod_logdb_users">>, User] -> + ?INFO_COMMAND(User, Lang); + [<<"mod_logdb_settings">>] -> + ?INFO_COMMAND(<<"Messages logging engine settings">>, Lang); -+ [] -> -+ Acc; + _ -> + Acc + end. + -+%get_sm_items(Acc, From, To, Node, Lang) -> -+% ?MYDEBUG("get_sm_items Acc=~p From=~p To=~p Node=~p Lang=~p", [Acc, From, To, Node, Lang]), -+% Acc. -+ -+%get_sm_features(Acc, From, To, Node, Lang) -> -+% ?MYDEBUG("get_sm_features Acc=~p From=~p To=~p Node=~p Lang=~p", [Acc, From, To, Node, Lang]), -+% Acc. -+ -+%get_sm_identity(Acc, From, To, Node, Lang) -> -+% ?MYDEBUG("get_sm_identity Acc=~p From=~p To=~p Node=~p Lang=~p", [Acc, From, To, Node, Lang]), -+% Acc. -+ -+adhoc_local_items(Acc, From, #jid{lserver = LServer, server = Server} = To, -+ Lang) -> ++adhoc_local_items(Acc, From, ++ #jid{lserver = LServer, server = Server} = To, Lang) -> ++ % TODO: case acl:match_rule(LServer, ???, From) of + Items = case Acc of + {result, Its} -> Its; + empty -> [] + end, -+ Nodes = recursively_get_local_items(LServer, "", Server, Lang), ++ Nodes = recursively_get_local_items(LServer, ++ <<"">>, Server, Lang), + Nodes1 = lists:filter( -+ fun(N) -> -+ Nd = 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 = str:tokens(Node, <<"/">>), -+ Items = case get_local_items(LServer, LNode, Server, Lang) of -+ {result, Res} -> -+ Res; -+ {error, _Error} -> -+ [] ++recursively_get_local_items(LServer, ++ Node, Server, Lang) -> ++ LNode = tokenize(Node), ++ Items = case get_local_items(LServer, LNode, ++ Server, Lang) of ++ {result, Res} -> Res; ++ {error, _Error} -> [] + end, + Nodes = lists:flatten( + lists:map( -+ fun(N) -> -+ S = 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. @@ -1622,19 +1548,21 @@ index 0000000..516f92b +-define(COMMANDS_RESULT(Allow, From, To, Request), + case Allow of + deny -> -+ {error, ?ERR_FORBIDDEN}; ++ {error, xmpp:err_forbidden(<<"Denied by ACL">>, Lang)}; + allow -> + adhoc_local_commands(From, To, Request) + end). + +adhoc_local_commands(Acc, From, #jid{lserver = LServer} = To, -+ #adhoc_request{node = Node} = Request) -> -+ LNode = str:tokens(Node, <<"/">>), ++ #adhoc_command{node = Node, lang = Lang} = Request) -> ++ LNode = tokenize(Node), + AllowUser = acl:match_rule(LServer, mod_logdb, From), + AllowAdmin = acl:match_rule(LServer, mod_logdb_admin, From), + case LNode of + [<<"mod_logdb">>] when AllowUser == allow; AllowAdmin == allow -> + ?COMMANDS_RESULT(allow, From, To, Request); ++ [<<"mod_logdb_users">>, <<$@, _/binary>>] when AllowAdmin == allow -> ++ Acc; + [<<"mod_logdb_users">>, _User] when AllowAdmin == allow -> + ?COMMANDS_RESULT(allow, From, To, Request); + [<<"mod_logdb_settings">>] when AllowAdmin == allow -> @@ -1644,110 +1572,90 @@ index 0000000..516f92b + end. + +adhoc_local_commands(From, #jid{lserver = LServer} = _To, -+ #adhoc_request{lang = Lang, ++ #adhoc_command{lang = Lang, + node = Node, -+ sessionid = SessionID, ++ sid = SessionID, + action = Action, + xdata = XData} = Request) -> -+ LNode = str:tokens(Node, <<"/">>), ++ LNode = tokenize(Node), + %% If the "action" attribute is not present, it is + %% understood as "execute". If there was no + %% element in the first response (which there isn't in our + %% case), "execute" and "complete" are equivalent. -+ ActionIsExecute = lists:member(Action, -+ [<<"">>, <<"execute">>, <<"complete">>]), -+ if Action == <<"cancel">> -> ++ ActionIsExecute = Action == execute orelse Action == complete, ++ if Action == cancel -> + %% User cancels request -+ adhoc:produce_response( -+ Request, -+ #adhoc_response{status = canceled}); -+ XData == false, ActionIsExecute -> ++ #adhoc_command{status = canceled, lang = Lang, ++ node = Node, sid = SessionID}; ++ XData == undefined, ActionIsExecute -> + %% User requests form -+ case get_form(LServer, LNode, From, Lang) of ++ case get_form(LServer, LNode, Lang) of + {result, Form} -> -+ adhoc:produce_response( ++ xmpp_util:make_adhoc_response( + Request, -+ #adhoc_response{status = executing, -+ elements = Form}); ++ #adhoc_command{status = executing, ++ xdata = Form}); + {error, Error} -> + {error, Error} + end; -+ XData /= false, ActionIsExecute -> ++ XData /= undefined, ActionIsExecute -> + %% User returns form. -+ case jlib:parse_xdata_submit(XData) of -+ invalid -> -+ {error, ?ERR_BAD_REQUEST}; -+ Fields -> -+ case catch set_form(From, LServer, LNode, Lang, Fields) of -+ {result, _Res} -> -+ adhoc:produce_response( -+ #adhoc_response{lang = Lang, -+ node = Node, -+ sessionid = SessionID, -+ status = completed}); -+ {'EXIT', _} -> {error, ?ERR_BAD_REQUEST}; -+ {error, Error} -> {error, Error} -+ end ++ case catch set_form(From, LServer, LNode, Lang, XData) of ++ {result, Res} -> ++ xmpp_util:make_adhoc_response( ++ Request, ++ #adhoc_command{xdata = Res, status = completed}); ++ {'EXIT', _} -> {error, xmpp:err_bad_request()}; ++ {error, Error} -> {error, Error} + end; + true -> -+ {error, ?ERR_BAD_REQUEST} ++ {error, xmpp:err_bad_request(<<"Unexpected action">>, Lang)} + end. + -+-define(LISTLINE(Label, Value), -+ #xmlel{name = <<"option">>, -+ attrs = [{<<"label">>, ?T(Lang, Label)}], -+ children = [#xmlel{name = <<"value">>, attrs = [], -+ children = [{xmlcdata, Value}] -+ }]}). -+-define(DEFVAL(Value), #xmlel{name = <<"value">>, attrs = [], -+ children = [{xmlcdata, Value}]}). ++-define(TVFIELD(Type, Var, Val), ++ #xdata_field{type = Type, var = Var, values = [Val]}). ++ ++-define(HFIELD(), ++ ?TVFIELD(hidden, <<"FORM_TYPE">>, (?NS_ADMIN))). + +get_user_form(LUser, LServer, Lang) -> -+ %From = jlib:jid_to_string(jlib:jid_remove_resource(Jid)), ++ ?MYDEBUG("get_user_form ~p ~p", [LUser, LServer]), ++ %From = jid:encode(jid:remove_resource(Jid)), + #user_settings{dolog_default=DLD, + dolog_list=DLL, + donotlog_list=DNLL} = get_user_settings(LUser, LServer), -+ {result, -+ [#xmlel{name = <<"x">>, -+ attrs = [{<<"xmlns">>, ?NS_XDATA}], -+ children = [ -+ #xmlel{name = <<"title">>, attrs = [], -+ children = -+ [{xmlcdata, -+ ?T(Lang, <<"Messages logging engine settings">>)}]}, -+ #xmlel{name = <<"instructions">>, attrs = [], -+ children = -+ [{xmlcdata, -+ << (?T(Lang, <<"Set logging preferences">>))/binary, (iolist_to_binary(": "))/binary, -+ LUser/binary, "@", LServer/binary >> }]}, -+ #xmlel{name = <<"field">>, -+ attrs = [{<<"type">>, <<"list-single">>}, -+ {<<"label">>, ?T(Lang, <<"Default">>)}, -+ {<<"var">>, <<"dolog_default">>}], -+ children = -+ [?DEFVAL(jlib:atom_to_binary(DLD)), -+ ?LISTLINE(<<"Log Messages">>, <<"true">>), -+ ?LISTLINE(<<"Do Not Log Messages">>, <<"false">>) -+ ]}, -+ #xmlel{name = <<"field">>, -+ attrs = [{<<"type">>, <<"text-multi">>}, -+ {<<"label">>, ?T(Lang, <<"Log Messages">>)}, -+ {<<"var">>, <<"dolog_list">>}], -+ children = [#xmlel{name = <<"value">>, attrs = [], -+ children = [{xmlcdata, iolist_to_binary(list_to_string(DLL))}]} -+ ] -+ }, -+ #xmlel{name = <<"field">>, -+ attrs = [{<<"type">>, <<"text-multi">>}, -+ {<<"label">>, ?T(Lang, <<"Do Not Log Messages">>)}, -+ {<<"var">>, <<"donotlog_list">>}], -+ children = [#xmlel{name = <<"value">>, attrs = [], -+ children = [{xmlcdata, iolist_to_binary(list_to_string(DNLL))}]} -+ ] -+ } -+ ]}]}. ++ Fs = [ ++ #xdata_field{ ++ type = 'list-single', ++ label = ?T(Lang, <<"Default">>), ++ var = <<"dolog_default">>, ++ values = [misc:atom_to_binary(DLD)], ++ options = [#xdata_option{label = ?T(Lang, <<"Log Messages">>), ++ value = <<"true">>}, ++ #xdata_option{label = ?T(Lang, <<"Do Not Log Messages">>), ++ value = <<"false">>}]}, ++ #xdata_field{ ++ type = 'text-multi', ++ label = ?T(Lang, <<"Log Messages">>), ++ var = <<"dolog_list">>, ++ values = DLL}, ++ #xdata_field{ ++ type = 'text-multi', ++ label = ?T(Lang, <<"Do Not Log Messages">>), ++ var = <<"donotlog_list">>, ++ values = DNLL} ++ ], ++ {result, #xdata{ ++ title = ?T(Lang, <<"Messages logging engine settings">>), ++ type = form, ++ instructions = [<< (?T(Lang, <<"Set logging preferences">>))/binary, ++ (iolist_to_binary(": "))/binary, ++ LUser/binary, "@", LServer/binary >>], ++ fields = [?HFIELD()| ++ Fs]}}. + +get_settings_form(Host, Lang) -> ++ ?MYDEBUG("get_settings_form ~p ~p", [Host, Lang]), + #state{dbmod=_DBMod, + dbs=_DBs, + dolog_default=DLD, @@ -1757,108 +1665,73 @@ index 0000000..516f92b + drop_messages_on_user_removal=MRemoval, + poll_users_settings=PollTime} = mod_logdb:get_module_settings(Host), + -+ %Backends = lists:map(fun({Backend, _Opts}) -> -+ % ?LISTLINE(jlib:atom_to_binary(Backend), jlib:atom_to_binary(Backend)) -+ % end, DBs), -+ %DB = iolist_to_binary(lists:sublist(atom_to_list(DBMod), length(atom_to_list(?MODULE)) + 2, length(atom_to_list(DBMod)))), -+ %DBsL = lists:append([?DEFVAL(DB)], Backends), -+ + PurgeDays = + case PurgeDaysT of + never -> <<"never">>; + Num when is_integer(Num) -> integer_to_binary(Num); + _ -> <<"unknown">> + end, -+ {result, -+ [#xmlel{name = <<"x">>, -+ attrs = [{<<"xmlns">>, ?NS_XDATA}], -+ children = [#xmlel{name = <<"title">>, attrs = [], -+ children = -+ [{xmlcdata, -+ <<(?T(Lang, <<"Messages logging engine settings">>))/binary, -+ (iolist_to_binary(" (run-time)"))/binary >>}]}, -+ #xmlel{name = <<"instructions">>, attrs = [], -+ children = -+ [{xmlcdata, ?T(Lang, <<"Set run-time settings">>)}]}, -+% #xmlel{name = <<"field">>, -+% attrs = [{<<"type">>, <<"list-single">>}, -+% {<<"label">>, ?T(Lang, <<"Backend">>)}, -+% {<<"var">>, <<"backend">>}], -+% children = DBsL}, -+% #xmlel{name = <<"field">>, -+% attrs = [{<<"type">>, <<"text-multi">>}, -+% {<<"label">>, ?T(Lang, <<"dbs">>)}, -+% {<<"var">>, <<"dbs">>}], -+% children = [#xmlel{name = <<"value">>, attrs = [], -+% children = [{xmlcdata, iolist_to_binary(lists:flatten(io_lib:format("~p.",[DBs])))}]} -+% ] -+% }, -+ #xmlel{name = <<"field">>, -+ attrs = [{<<"type">>, <<"list-single">>}, -+ {<<"label">>, ?T(Lang, <<"Default">>)}, -+ {<<"var">>, <<"dolog_default">>}], -+ children = -+ [?DEFVAL(jlib:atom_to_binary(DLD)), -+ ?LISTLINE(<<"Log Messages">>, <<"true">>), -+ ?LISTLINE(<<"Do Not Log Messages">>, <<"false">>) -+ ]}, -+ #xmlel{name = <<"field">>, -+ attrs = [{<<"type">>, <<"list-single">>}, -+ {<<"label">>, ?T(Lang, <<"Drop messages on user removal">>)}, -+ {<<"var">>, <<"drop_messages_on_user_removal">>}], -+ children = -+ [?DEFVAL(jlib:atom_to_binary(MRemoval)), -+ ?LISTLINE(<<"Drop">>, <<"true">>), -+ ?LISTLINE(<<"Do not drop">>, <<"false">>) -+ ]}, -+ #xmlel{name = <<"field">>, -+ attrs = [{<<"type">>, <<"list-single">>}, -+ {<<"label">>, ?T(Lang, <<"Groupchat messages logging">>)}, -+ {<<"var">>, <<"groupchat">>}], -+ children = -+ [?DEFVAL(jlib:atom_to_binary(GroupChat)), -+ ?LISTLINE(<<"all">>, <<"all">>), -+ ?LISTLINE(<<"none">>, <<"none">>), -+ ?LISTLINE(<<"send">>, <<"send">>), -+ ?LISTLINE(<<"half">>, <<"half">>) -+ ]}, -+ #xmlel{name = <<"field">>, -+ attrs = [{<<"type">>, <<"text-multi">>}, -+ {<<"label">>, ?T(Lang, <<"Jids/Domains to ignore">>)}, -+ {<<"var">>, <<"ignore_list">>}], -+ children = [#xmlel{name = <<"value">>, attrs = [], -+ children = [{xmlcdata, iolist_to_binary(list_to_string(IgnoreJids))}]} -+ ] -+ }, -+ #xmlel{name = <<"field">>, -+ attrs = [{<<"type">>, <<"text-single">>}, -+ {<<"label">>, ?T(Lang, <<"Purge messages older than (days)">>)}, -+ {<<"var">>, <<"purge_older_days">>}], -+ children = [#xmlel{name = <<"value">>, attrs = [], -+ children = [{xmlcdata, iolist_to_binary(PurgeDays)}]} -+ ] -+ }, -+ #xmlel{name = <<"field">>, -+ attrs = [{<<"type">>, <<"text-single">>}, -+ {<<"label">>, ?T(Lang, <<"Poll users settings (seconds)">>)}, -+ {<<"var">>, <<"poll_users_settings">>}], -+ children = [#xmlel{name = <<"value">>, attrs = [], -+ children = [{xmlcdata, integer_to_binary(PollTime)}]} -+ ] -+ } -+ ]} -+ ]}. -+ -+%get_form(_Host, [<<"mod_logdb">>], #jid{luser = LUser, lserver = LServer} = _Jid, Lang) -> -+% get_user_form(LUser, LServer, Lang); -+get_form(_Host, [<<"mod_logdb_users">>, User], _JidFrom, Lang) -> -+ #jid{luser=LUser, lserver=LServer} = jlib:string_to_jid(User), ++ Fs = [ ++ #xdata_field{ ++ type = 'list-single', ++ label = ?T(Lang, <<"Default">>), ++ var = <<"dolog_default">>, ++ values = [misc:atom_to_binary(DLD)], ++ options = [#xdata_option{label = ?T(Lang, <<"Log Messages">>), ++ value = <<"true">>}, ++ #xdata_option{label = ?T(Lang, <<"Do Not Log Messages">>), ++ value = <<"false">>}]}, ++ #xdata_field{ ++ type = 'list-single', ++ label = ?T(Lang, <<"Drop messages on user removal">>), ++ var = <<"drop_messages_on_user_removal">>, ++ values = [misc:atom_to_binary(MRemoval)], ++ options = [#xdata_option{label = ?T(Lang, <<"Drop">>), ++ value = <<"true">>}, ++ #xdata_option{label = ?T(Lang, <<"Do not drop">>), ++ value = <<"false">>}]}, ++ #xdata_field{ ++ type = 'list-single', ++ label = ?T(Lang, <<"Groupchat messages logging">>), ++ var = <<"groupchat">>, ++ values = [misc:atom_to_binary(GroupChat)], ++ options = [#xdata_option{label = ?T(Lang, <<"all">>), ++ value = <<"all">>}, ++ #xdata_option{label = ?T(Lang, <<"none">>), ++ value = <<"none">>}, ++ #xdata_option{label = ?T(Lang, <<"send">>), ++ value = <<"send">>}]}, ++ #xdata_field{ ++ type = 'text-multi', ++ label = ?T(Lang, <<"Jids/Domains to ignore">>), ++ var = <<"ignore_list">>, ++ values = IgnoreJids}, ++ #xdata_field{ ++ type = 'text-single', ++ label = ?T(Lang, <<"Purge messages older than (days)">>), ++ var = <<"purge_older_days">>, ++ values = [iolist_to_binary(PurgeDays)]}, ++ #xdata_field{ ++ type = 'text-single', ++ label = ?T(Lang, <<"Poll users settings (seconds)">>), ++ var = <<"poll_users_settings">>, ++ values = [integer_to_binary(PollTime)]} ++ ], ++ {result, #xdata{ ++ title = ?T(Lang, <<"Messages logging engine settings (run-time)">>), ++ instructions = [?T(Lang, <<"Set run-time settings">>)], ++ type = form, ++ fields = [?HFIELD()| ++ Fs]}}. ++ ++get_form(_Host, [<<"mod_logdb_users">>, User], Lang) -> ++ #jid{luser=LUser, lserver=LServer} = jid:decode(User), + get_user_form(LUser, LServer, Lang); -+get_form(Host, [<<"mod_logdb_settings">>], _JidFrom, Lang) -> ++get_form(Host, [<<"mod_logdb_settings">>], Lang) -> + get_settings_form(Host, Lang); -+get_form(_Host, Command, _, _Lang) -> ++get_form(_Host, Command, _Lang) -> + ?MYDEBUG("asked for form ~p", [Command]), -+ {error, ?ERR_SERVICE_UNAVAILABLE}. ++ {error, xmpp:err_service_unavailable()}. + +check_log_list([]) -> + ok; @@ -1870,11 +1743,9 @@ index 0000000..516f92b + {_, _} -> ok + end, + % this check for Head to be valid jid -+ case jlib:string_to_jid(Head) of -+ error -> -+ throw(error); -+ _ -> -+ check_log_list(Tail) ++ case catch jid:decode(Head) of ++ {'EXIT', _Reason} -> throw(error); ++ _ -> check_log_list(Tail) + end. + +check_ignore_list([]) -> @@ -1888,112 +1759,81 @@ index 0000000..516f92b + {_, _} -> ok; + nomatch -> throw(error) + end, ++ Jid2Test = case Head of ++ << $@, _Rest/binary >> -> << "a", Head/binary >>; ++ Jid -> Jid ++ end, + % this check for Head to be valid jid -+ case jlib:string_to_jid(Head) of -+ error -> -+ % this check for Head to be valid domain "@domain.org" -+ case Head of -+ << $@, Rest/binary >> -> -+ % TODO: this allows spaces and special characters in Head. May be change to nodeprep? -+ case jlib:nameprep(Rest) of -+ error -> throw(error); -+ _ -> check_ignore_list(Tail) -+ end; -+ _ -> throw(error) -+ end; -+ _ -> -+ check_ignore_list(Tail) ++ case catch jid:decode(Jid2Test) of ++ {'EXIT', _Reason} -> throw(error); ++ _ -> check_ignore_list(Tail) + end. + ++get_value(Field, XData) -> hd(get_values(Field, XData)). ++ ++get_values(Field, XData) -> ++ xmpp_util:get_xdata_values(Field, XData). ++ +parse_users_settings(XData) -> -+ DLD = case lists:keysearch(<<"dolog_default">>, 1, XData) of -+ {value, {_, [String]}} when String == <<"true">>; String == <<"false">> -> -+ list_to_bool(String); -+ _ -> -+ throw(bad_request) -+ end, -+ DLL = case lists:keysearch(<<"dolog_list">>, 1, XData) of -+ false -> -+ throw(bad_request); -+ {value, {_, [[]]}} -> -+ []; -+ {value, {_, List1}} -> -+ case catch check_log_list(List1) of -+ error -> -+ throw(bad_request); -+ ok -> -+ List1 -+ end -+ end, -+ DNLL = case lists:keysearch(<<"donotlog_list">>, 1, XData) of -+ false -> -+ throw(bad_request); -+ {value, {_, [[]]}} -> -+ []; -+ {value, {_, List2}} -> -+ case catch check_log_list(List2) of -+ error -> -+ throw(bad_request); -+ ok -> -+ List2 -+ end ++ DLD = case get_value(<<"dolog_default">>, XData) of ++ ValueDLD when ValueDLD == <<"true">>; ++ ValueDLD == <<"false">> -> ++ list_to_bool(ValueDLD); ++ _ -> throw(bad_request) + end, ++ ++ ListDLL = get_values(<<"dolog_list">>, XData), ++ DLL = case catch check_log_list(ListDLL) of ++ ok -> ListDLL; ++ error -> throw(bad_request) ++ end, ++ ++ ListDNLL = get_values(<<"donotlog_list">>, XData), ++ DNLL = case catch check_log_list(ListDNLL) of ++ ok -> ListDNLL; ++ error -> throw(bad_request) ++ end, ++ + #user_settings{dolog_default=DLD, + dolog_list=DLL, + donotlog_list=DNLL}. + +parse_module_settings(XData) -> -+ DLD = case lists:keysearch(<<"dolog_default">>, 1, XData) of -+ {value, {_, [Str1]}} when Str1 == <<"true">>; Str1 == <<"false">> -> -+ list_to_bool(Str1); -+ _ -> -+ throw(bad_request) -+ end, -+ MRemoval = case lists:keysearch(<<"drop_messages_on_user_removal">>, 1, XData) of -+ {value, {_, [Str5]}} when Str5 == <<"true">>; Str5 == <<"false">> -> -+ list_to_bool(Str5); -+ _ -> -+ throw(bad_request) ++ DLD = case get_value(<<"dolog_default">>, XData) of ++ ValueDLD when ValueDLD == <<"true">>; ++ ValueDLD == <<"false">> -> ++ list_to_bool(ValueDLD); ++ _ -> throw(bad_request) + end, -+ GroupChat = case lists:keysearch(<<"groupchat">>, 1, XData) of -+ {value, {_, [Str2]}} when Str2 == <<"none">>; -+ Str2 == <<"all">>; -+ Str2 == <<"send">>; -+ Str2 == <<"half">> -> -+ jlib:binary_to_atom(Str2); -+ _ -> -+ throw(bad_request) ++ MRemoval = case get_value(<<"drop_messages_on_user_removal">>, XData) of ++ ValueMRemoval when ValueMRemoval == <<"true">>; ++ ValueMRemoval == <<"false">> -> ++ list_to_bool(ValueMRemoval); ++ _ -> throw(bad_request) ++ end, ++ GroupChat = case get_value(<<"groupchat">>, XData) of ++ ValueGroupChat when ValueGroupChat == <<"none">>; ++ ValueGroupChat == <<"all">>; ++ ValueGroupChat == <<"send">> -> ++ misc:binary_to_atom(ValueGroupChat); ++ _ -> throw(bad_request) + end, -+ Ignore = case lists:keysearch(<<"ignore_list">>, 1, XData) of -+ {value, {_, List}} -> -+ case catch check_ignore_list(List) of -+ ok -> -+ List; -+ error -> -+ throw(bad_request) -+ end; -+ _ -> -+ throw(bad_request) ++ ListIgnore = get_values(<<"ignore_list">>, XData), ++ Ignore = case catch check_ignore_list(ListIgnore) of ++ ok -> ListIgnore; ++ error -> throw(bad_request) + end, -+ Purge = case lists:keysearch(<<"purge_older_days">>, 1, XData) of -+ {value, {_, [<<"never">>]}} -> -+ never; -+ {value, {_, [Str3]}} -> -+ case catch binary_to_integer(Str3) of -+ {'EXIT', {badarg, _}} -> throw(bad_request); -+ Int1 -> Int1 -+ end; -+ _ -> -+ throw(bad_request) ++ Purge = case get_value(<<"purge_older_days">>, XData) of ++ <<"never">> -> never; ++ ValuePurge -> ++ case catch binary_to_integer(ValuePurge) of ++ IntValuePurge when is_integer(IntValuePurge) -> IntValuePurge; ++ _ -> throw(bad_request) ++ end + end, -+ Poll = case lists:keysearch(<<"poll_users_settings">>, 1, XData) of -+ {value, {_, [Str4]}} -> -+ case catch binary_to_integer(Str4) of -+ {'EXIT', {badarg, _}} -> throw(bad_request); -+ Int2 -> Int2 -+ end; -+ _ -> -+ throw(bad_request) ++ Poll = case catch binary_to_integer(get_value(<<"poll_users_settings">>, XData)) of ++ IntValuePoll when is_integer(IntValuePoll) -> IntValuePoll; ++ _ -> throw(bad_request) + end, + #state{dolog_default=DLD, + groupchat=GroupChat, @@ -2002,65 +1842,47 @@ index 0000000..516f92b + 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}; ++ ?ERROR_MSG("Failed to set user form: bad_request", []), ++ {error, xmpp:err_bad_request(Txt, Lang)}; + {'EXIT', Reason} -> -+ ?ERROR_MSG("Failed to set form ~p", [Reason]), -+ {error, ?ERR_BAD_REQUEST}; ++ ?ERROR_MSG("Failed to set user form ~p", [Reason]), ++ {error, xmpp:err_bad_request(Txt, Lang)}; + UserSettings -> + case mod_logdb:set_user_settings(LUser, LServer, UserSettings) of + ok -> -+ {result, []}; ++ {result, undefined}; + error -> -+ {error, ?ERR_INTERNAL_SERVER_ERROR} ++ {error, xmpp:err_internal_server_error()} + end + end; -+set_form(_From, _Host, [<<"mod_logdb_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}; -+ {'EXIT', Reason} -> -+ ?ERROR_MSG("Failed to set form ~p", [Reason]), -+ {error, ?ERR_BAD_REQUEST}; -+ UserSettings -> -+ case mod_logdb:set_user_settings(LUser, LServer, UserSettings) of -+ ok -> -+ {result, []}; -+ error -> -+ {error, ?ERR_INTERNAL_SERVER_ERROR} -+ end -+ end; -+set_form(_From, Host, [<<"mod_logdb_settings">>], _Lang, XData) -> ++set_form(_From, Host, [<<"mod_logdb_settings">>], Lang, XData) -> ++ Txt = "Parse module settings failed", + case catch parse_module_settings(XData) of -+ bad_request -> {error, ?ERR_BAD_REQUEST}; ++ bad_request -> ++ ?ERROR_MSG("Failed to set settings form: bad_request", []), ++ {error, xmpp:err_bad_request(Txt, Lang)}; + {'EXIT', Reason} -> -+ ?ERROR_MSG("Failed to set form ~p", [Reason]), -+ {error, ?ERR_BAD_REQUEST}; ++ ?ERROR_MSG("Failed to set settings form ~p", [Reason]), ++ {error, xmpp:err_bad_request(Txt, Lang)}; + Settings -> + case mod_logdb:set_module_settings(Host, Settings) of + ok -> -+ {result, []}; ++ {result, undefined}; + error -> -+ {error, ?ERR_INTERNAL_SERVER_ERROR} ++ {error, xmpp:err_internal_server_error()} + end + end; +set_form(From, _Host, Node, _Lang, XData) -> -+ User = jlib:jid_to_string(jlib:jid_remove_resource(From)), ++ User = jid:encode(jid:remove_resource(From)), + ?MYDEBUG("set form for ~p at ~p XData=~p", [User, Node, XData]), -+ {error, ?ERR_SERVICE_UNAVAILABLE}. ++ {error, xmpp: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. -+ -+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} -> + []; @@ -2069,19 +1891,19 @@ index 0000000..516f92b + case length(SUsers) of + N when N =< 100 -> + lists:map(fun({S, U}) -> -+ ?NODE(<< U/binary, "@", S/binary >>, -+ << (iolist_to_binary("mod_logdb_users/"))/binary, U/binary, "@", S/binary >>) -+ end, -+ SUsers); ++ #disco_item{jid = jid:make(Server), ++ node = <<"mod_logdb_users/", U/binary, $@, S/binary>>, ++ name = << U/binary, "@", S/binary >>} ++ end, SUsers); + N -> -+ NParts = trunc(math:sqrt(N * 0.618)) + 1, ++ NParts = trunc(math:sqrt(N * 6.17999999999999993783e-1)) + 1, + M = trunc(N / NParts) + 1, + lists:map(fun(K) -> + L = K + M - 1, + Node = <<"@", -+ (iolist_to_binary(integer_to_list(K)))/binary, ++ (integer_to_binary(K))/binary, + "-", -+ (iolist_to_binary(integer_to_list(L)))/binary ++ (integer_to_binary(L))/binary + >>, + {FS, FU} = lists:nth(K, SUsers), + {LS, LU} = @@ -2092,7 +1914,9 @@ index 0000000..516f92b + <>, -+ ?NODE(Name, << (iolist_to_binary("mod_logdb_users/"))/binary, Node/binary >>) ++ #disco_item{jid = jid:make(Host), ++ node = <<"mod_logdb_users/", Node/binary>>, ++ name = Name} + end, lists:seq(1, N, M)) + end + end. @@ -2183,7 +2007,7 @@ index 0000000..516f92b + {ok, Dates} -> + Fun = fun({Date, Count}) -> + DateBin = iolist_to_binary(Date), -+ ID = jlib:encode_base64( << Server/binary, DateBin/binary >> ), ++ ID = misc:encode_base64( << Server/binary, DateBin/binary >> ), + ?XE(<<"tr">>, + [?XAE(<<"td">>, [{<<"class">>, <<"valign">>}], + [?INPUT(<<"checkbox">>, <<"selected">>, ID)]), @@ -2236,9 +2060,9 @@ index 0000000..516f92b + end, + Fun = fun({User, Count}) -> + UserBin = iolist_to_binary(User), -+ ID = jlib:encode_base64( << UserBin/binary, Server/binary >> ), ++ ID = misc:encode_base64( << UserBin/binary, Server/binary >> ), + ?XE(<<"tr">>, -+ [?XAE(<<"td">>, [{<<"class">>, <<"valign">>}], ++ [?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)) @@ -2267,7 +2091,7 @@ index 0000000..516f92b + 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} -> @@ -2291,7 +2115,7 @@ index 0000000..516f92b + {ok, Dates} -> + Fun = fun({Date, Count}) -> + DateBin = iolist_to_binary(Date), -+ ID = jlib:encode_base64( << User/binary, DateBin/binary >> ), ++ ID = misc:encode_base64( << User/binary, DateBin/binary >> ), + ?XE(<<"tr">>, + [?XAE(<<"td">>, [{<<"class">>, <<"valign">>}], + [?INPUT(<<"checkbox">>, <<"selected">>, ID)]), @@ -2332,7 +2156,7 @@ index 0000000..516f92b + 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]), @@ -2359,7 +2183,7 @@ index 0000000..516f92b + UR = ejabberd_hooks:run_fold(roster_get, Server, [], [{User, Server}]), + UserRoster = + lists:map(fun(Item) -> -+ {jlib:jid_to_string(Item#roster.jid), Item#roster.name} ++ {jid:encode(Item#roster.jid), Item#roster.name} + end, UR), + + UniqUsers = lists:foldl(fun(#msg{peer_name=PName, peer_server=PServer}, List) -> @@ -2374,7 +2198,7 @@ index 0000000..516f92b + CheckedUsers = case lists:keysearch(<<"filter">>, 1, Query) of + {value, _} -> + lists:filter(fun(UFUser) -> -+ ID = jlib:encode_base64(term_to_binary(UFUser)), ++ ID = misc:encode_base64(term_to_binary(UFUser)), + lists:member({<<"selected">>, ID}, Query) + end, UniqUsers); + false -> [] @@ -2382,7 +2206,7 @@ index 0000000..516f92b + + % UniqUsers in html (noone selected -> everyone selected) + Users = lists:map(fun(UHUser) -> -+ ID = jlib:encode_base64(term_to_binary(UHUser)), ++ ID = misc:encode_base64(term_to_binary(UHUser)), + Input = case lists:member(UHUser, CheckedUsers) of + true -> [?INPUTC(<<"checkbox">>, <<"selected">>, ID)]; + false when CheckedUsers == [] -> [?INPUTC(<<"checkbox">>, <<"selected">>, ID)]; @@ -2430,7 +2254,7 @@ index 0000000..516f92b + PName++"@"++PServer; + N -> N + end, -+ ID = jlib:encode_base64(term_to_binary(Timestamp)), ++ ID = misc:encode_base64(term_to_binary(Timestamp)), + ?XE(<<"tr">>, + [?XE(<<"td">>, [?INPUT(<<"checkbox">>, <<"selected">>, ID)]), + ?XC(<<"td">>, iolist_to_binary(convert_timestamp(Timestamp))), @@ -2476,17 +2300,15 @@ index 0000000..516f92b + end. diff --git a/src/mod_logdb.hrl b/src/mod_logdb.hrl new file mode 100644 -index 0000000..d44f0df +index 0000000000..49791f4e69 --- /dev/null +++ b/src/mod_logdb.hrl -@@ -0,0 +1,35 @@ +@@ -0,0 +1,33 @@ +%%%---------------------------------------------------------------------- +%%% File : mod_logdb.hrl -+%%% Author : Oleg Palij (mailto,xmpp:o.palij@gmail.com) ++%%% Author : Oleg Palij (mailto:o.palij@gmail.com) +%%% Purpose : -+%%% Version : trunk -+%%% Id : $Id: mod_logdb.hrl 1273 2009-02-05 18:12:57Z malik $ -+%%% Url : http://www.dp.uz.gov.ua/o.palij/mod_logdb/ ++%%% Url : https://paleg.github.io/mod_logdb/ +%%%---------------------------------------------------------------------- + +-define(logdb_debug, true). @@ -2517,25 +2339,21 @@ index 0000000..d44f0df + {<<"checked">>, <<"true">>}])). diff --git a/src/mod_logdb_mnesia.erl b/src/mod_logdb_mnesia.erl new file mode 100644 -index 0000000..a8ae766 +index 0000000000..a08d5262c2 --- /dev/null +++ b/src/mod_logdb_mnesia.erl -@@ -0,0 +1,557 @@ +@@ -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: mod_logdb_mnesia.erl 1273 2009-02-05 18:12:57Z malik $ -+%%% Url : http://www.dp.uz.gov.ua/o.palij/mod_logdb/ ++%%% Url : https://paleg.github.io/mod_logdb/ +%%%---------------------------------------------------------------------- + +-module(mod_logdb_mnesia). +-author('o.palij@gmail.com'). + +-include("mod_logdb.hrl"). -+-include("ejabberd.hrl"). -+-include("jlib.hrl"). +-include("logger.hrl"). + +-behaviour(gen_logdb). @@ -2894,9 +2712,9 @@ index 0000000..a8ae766 + Table = term_to_binary(ATable), + case ejabberd_regexp:run( Table, << VHost/binary, <<"$">>/binary >> ) of + match -> -+ case re:run(Table, "_[0-9]+-[0-9]+-[0-9]+_") of ++ case re:run(Table, "[0-9]+-[0-9]+-[0-9]+") of + {match, [{S, E}]} -> -+ lists:append(Dates, [lists:sublist(binary_to_list(Table), S+2, E-2)]); ++ lists:append(Dates, [lists:sublist(binary_to_list(Table), S+1, E)]); + nomatch -> + Dates + end; @@ -3078,292 +2896,23 @@ index 0000000..a8ae766 + {type, bag}, + {attributes, record_info(fields, msg)}, + {record_name, msg}]). -diff --git a/src/mod_logdb_mnesia_old.erl b/src/mod_logdb_mnesia_old.erl -new file mode 100644 -index 0000000..e962d9a ---- /dev/null -+++ b/src/mod_logdb_mnesia_old.erl -@@ -0,0 +1,259 @@ -+%%%---------------------------------------------------------------------- -+%%% File : mod_logdb_mnesia_old.erl -+%%% Author : Oleg Palij (mailto,xmpp:o.palij@gmail.com) -+%%% Purpose : mod_logmnesia backend for mod_logdb (should be used only for copy_tables functionality) -+%%% Version : trunk -+%%% Id : $Id: mod_logdb_mnesia_old.erl 1273 2009-02-05 18:12:57Z malik $ -+%%% Url : http://www.dp.uz.gov.ua/o.palij/mod_logdb/ -+%%%---------------------------------------------------------------------- -+ -+-module(mod_logdb_mnesia_old). -+-author('o.palij@gmail.com'). -+ -+-include("ejabberd.hrl"). -+-include("jlib.hrl"). -+-include("logger.hrl"). -+ -+-behaviour(gen_logdb). -+ -+-export([start/2, stop/1, -+ log_message/2, -+ rebuild_stats/1, -+ rebuild_stats_at/2, -+ rebuild_stats_at1/2, -+ delete_messages_by_user_at/3, delete_all_messages_by_user_at/3, delete_messages_at/2, -+ get_vhost_stats/1, get_vhost_stats_at/2, get_user_stats/2, get_user_messages_at/3, -+ get_dates/1, -+ get_users_settings/1, get_user_settings/2, set_user_settings/3, -+ drop_user/2]). -+ -+-record(stats, {user, server, table, count}). -+-record(msg, {to_user, to_server, to_resource, from_user, from_server, from_resource, id, type, subject, body, timestamp}). -+ -+tables_prefix() -> "messages_". -+% stats_table should not start with tables_prefix(VHost) ! -+% i.e. lists:prefix(tables_prefix(VHost), atom_to_list(stats_table())) must be /= true -+stats_table() -> list_to_atom("messages-stats"). -+% table name as atom from Date -+-define(ATABLE(Date), list_to_atom(tables_prefix() ++ Date)). -+-define(LTABLE(Date), tables_prefix() ++ Date). -+ -+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -+% -+% gen_logdb callbacks -+% -+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -+start(_Opts, _VHost) -> -+ case mnesia:system_info(is_running) of -+ yes -> -+ ok = create_stats_table(), -+ {ok, ok}; -+ no -> -+ ?ERROR_MSG("Mnesia not running", []), -+ error; -+ Status -> -+ ?ERROR_MSG("Mnesia status: ~p", [Status]), -+ error -+ end. -+ -+stop(_VHost) -> -+ ok. -+ -+log_message(_VHost, _Msg) -> -+ error. -+ -+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -+% -+% gen_logdb callbacks (maintaince) -+% -+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -+rebuild_stats(_VHost) -> -+ ok. -+ -+rebuild_stats_at(VHost, Date) -> -+ Table = ?LTABLE(Date), -+ {Time, Value}=timer:tc(?MODULE, rebuild_stats_at1, [VHost, Table]), -+ ?INFO_MSG("rebuild_stats_at ~p elapsed ~p sec: ~p~n", [Date, Time/1000000, Value]), -+ Value. -+rebuild_stats_at1(VHost, Table) -> -+ CFun = fun(Msg, Stats) -> -+ To = Msg#msg.to_user ++ "@" ++ Msg#msg.to_server, -+ Stats_to = if -+ Msg#msg.to_server == VHost -> -+ case lists:keysearch(To, 1, Stats) of -+ {value, {Who_to, Count_to}} -> -+ lists:keyreplace(To, 1, Stats, {Who_to, Count_to + 1}); -+ false -> -+ lists:append(Stats, [{To, 1}]) -+ end; -+ true -> -+ Stats -+ end, -+ From = Msg#msg.from_user ++ "@" ++ Msg#msg.from_server, -+ Stats_from = if -+ Msg#msg.from_server == VHost -> -+ case lists:keysearch(From, 1, Stats_to) of -+ {value, {Who_from, Count_from}} -> -+ lists:keyreplace(From, 1, Stats_to, {Who_from, Count_from + 1}); -+ false -> -+ lists:append(Stats_to, [{From, 1}]) -+ end; -+ true -> -+ Stats_to -+ end, -+ Stats_from -+ end, -+ DFun = fun(#stats{table=STable, server=Server} = Stat, _Acc) -+ when STable == Table, Server == VHost -> -+ mnesia:delete_object(stats_table(), Stat, write); -+ (_Stat, _Acc) -> ok -+ end, -+ case mnesia:transaction(fun() -> -+ mnesia:write_lock_table(list_to_atom(Table)), -+ mnesia:write_lock_table(stats_table()), -+ % Calc stats for VHost at Date -+ AStats = mnesia:foldl(CFun, [], list_to_atom(Table)), -+ % Delete all stats for VHost at Date -+ mnesia:foldl(DFun, [], stats_table()), -+ % Write new calc'ed stats -+ lists:foreach(fun({Who, Count}) -> -+ Jid = jlib:string_to_jid(Who), -+ JUser = Jid#jid.user, -+ WStat = #stats{user=JUser, server=VHost, table=Table, count=Count}, -+ mnesia:write(stats_table(), WStat, write) -+ end, AStats) -+ end) of -+ {aborted, Reason} -> -+ ?ERROR_MSG("Failed to rebuild_stats_at for ~p at ~p: ~p", [VHost, Table, Reason]), -+ error; -+ {atomic, _} -> -+ ok -+ end. -+ -+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -+% -+% gen_logdb callbacks (delete) -+% -+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -+delete_messages_by_user_at(_VHost, _Msgs, _Date) -> -+ error. -+ -+delete_all_messages_by_user_at(_User, _VHost, _Date) -> -+ error. -+ -+delete_messages_at(VHost, Date) -> -+ Table = list_to_atom(tables_prefix() ++ Date), -+ -+ DFun = fun(#msg{to_server=To_server, from_server=From_server}=Msg, _Acc) -+ when To_server == VHost; From_server == VHost -> -+ mnesia:delete_object(Table, Msg, write); -+ (_Msg, _Acc) -> ok -+ end, -+ -+ case mnesia:transaction(fun() -> -+ mnesia:foldl(DFun, [], Table) -+ end) of -+ {aborted, Reason} -> -+ ?ERROR_MSG("Failed to delete_messages_at for ~p at ~p: ~p", [VHost, Date, Reason]), -+ error; -+ {atomic, _} -> -+ ok -+ end. -+ -+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -+% -+% gen_logdb callbacks (get) -+% -+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -+get_vhost_stats(_VHost) -> -+ {error, "does not emplemented"}. -+ -+get_vhost_stats_at(VHost, Date) -> -+ Fun = fun() -> -+ Pat = #stats{user='$1', server=VHost, table=tables_prefix()++Date, count = '$2'}, -+ mnesia:select(stats_table(), [{Pat, [], [['$1', '$2']]}]) -+ end, -+ case mnesia:transaction(Fun) of -+ {atomic, Result} -> -+ RFun = fun([User, Count]) -> -+ {User, Count} -+ end, -+ {ok, lists:reverse(lists:keysort(2, lists:map(RFun, Result)))}; -+ {aborted, Reason} -> {error, Reason} -+ end. -+ -+get_user_stats(_User, _VHost) -> -+ {error, "does not emplemented"}. -+ -+get_user_messages_at(User, VHost, Date) -> -+ Table_name = tables_prefix() ++ Date, -+ case mnesia:transaction(fun() -> -+ Pat_to = #msg{to_user=User, to_server=VHost, _='_'}, -+ Pat_from = #msg{from_user=User, from_server=VHost, _='_'}, -+ mnesia:select(list_to_atom(Table_name), -+ [{Pat_to, [], ['$_']}, -+ {Pat_from, [], ['$_']}]) -+ end) of -+ {atomic, Result} -> -+ Msgs = lists:map(fun(#msg{to_user=To_user, to_server=To_server, to_resource=To_res, -+ from_user=From_user, from_server=From_server, from_resource=From_res, -+ type=Type, -+ subject=Subj, -+ body=Body, timestamp=Timestamp} = _Msg) -> -+ Subject = case Subj of -+ "None" -> ""; -+ _ -> Subj -+ end, -+ {msg, To_user, To_server, To_res, From_user, From_server, From_res, Type, Subject, Body, Timestamp} -+ end, Result), -+ {ok, Msgs}; -+ {aborted, Reason} -> -+ {error, Reason} -+ end. -+ -+get_dates(_VHost) -> -+ Tables = mnesia:system_info(tables), -+ MessagesTables = -+ lists:filter(fun(Table) -> -+ lists:prefix(tables_prefix(), atom_to_list(Table)) -+ end, -+ Tables), -+ lists:map(fun(Table) -> -+ lists:sublist(atom_to_list(Table), -+ length(tables_prefix())+1, -+ length(atom_to_list(Table))) -+ end, -+ MessagesTables). -+ -+get_users_settings(_VHost) -> -+ {ok, []}. -+get_user_settings(_User, _VHost) -> -+ {ok, []}. -+set_user_settings(_User, _VHost, _Set) -> -+ ok. -+drop_user(_User, _VHost) -> -+ ok. -+ -+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -+% -+% internal -+% -+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -+% called from db_logon/2 -+create_stats_table() -> -+ SName = stats_table(), -+ case mnesia:create_table(SName, -+ [{disc_only_copies, [node()]}, -+ {type, bag}, -+ {attributes, record_info(fields, stats)}, -+ {record_name, stats} -+ ]) of -+ {atomic, ok} -> -+ ?INFO_MSG("Created stats table", []), -+ ok; -+ {aborted, {already_exists, _}} -> -+ ok; -+ {aborted, Reason} -> -+ ?ERROR_MSG("Failed to create stats table: ~p", [Reason]), -+ error -+ end. diff --git a/src/mod_logdb_mysql.erl b/src/mod_logdb_mysql.erl new file mode 100644 -index 0000000..62f437c +index 0000000000..21d65e6578 --- /dev/null +++ b/src/mod_logdb_mysql.erl -@@ -0,0 +1,1055 @@ +@@ -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: mod_logdb_mysql.erl 1360 2009-07-30 06:00:14Z malik $ -+%%% Url : http://www.dp.uz.gov.ua/o.palij/mod_logdb/ ++%%% Url : https://paleg.github.io/mod_logdb/ +%%%---------------------------------------------------------------------- + +-module(mod_logdb_mysql). +-author('o.palij@gmail.com'). + +-include("mod_logdb.hrl"). -+-include("ejabberd.hrl"). -+-include("jlib.hrl"). +-include("logger.hrl"). + +-behaviour(gen_logdb). @@ -3522,8 +3071,8 @@ index 0000000..62f437c + "'", Peer_resource_id, "',", + "'", atom_to_list(Msg#msg.direction), "',", + "'", binary_to_list(Msg#msg.type), "',", -+ "'", binary_to_list( ejabberd_odbc:escape(Msg#msg.subject) ), "',", -+ "'", binary_to_list( ejabberd_odbc:escape(Msg#msg.body) ), "',", ++ "'", binary_to_list( ejabberd_sql:escape(Msg#msg.subject) ), "',", ++ "'", binary_to_list( ejabberd_sql:escape(Msg#msg.body) ), "',", + "'", Msg#msg.timestamp, "');"], + + Reply = @@ -3899,14 +3448,13 @@ index 0000000..62f437c +get_dates_int(DBRef, VHost) -> + case sql_query_internal(DBRef, ["SHOW TABLES"]) of + {data, Tables} -> ++ Reg = "^" ++ lists:sublist(prefix(),2,length(prefix())) ++ ".*" ++ escape_vhost(VHost), + lists:foldl(fun([Table], Dates) -> -+ Reg = lists:sublist(prefix(),2,length(prefix())) ++ ".*" ++ escape_vhost(VHost), + case re:run(Table, Reg) of -+ {match, [{1, _}]} -> -+ ?MYDEBUG("matched ~p against ~p", [Table, Reg]), -+ case re:run(Table,"[0-9]+-[0-9]+-[0-9]+") of ++ {match, _} -> ++ case re:run(Table, "[0-9]+-[0-9]+-[0-9]+") of + {match, [{S, E}]} -> -+ lists:append(Dates, [lists:sublist(Table,S,E)]); ++ lists:append(Dates, [lists:sublist(Table, S+1, E)]); + nomatch -> + Dates + end; @@ -4341,7 +3889,7 @@ index 0000000..62f437c + +get_resource_id_from_db(DBRef, VHost, Resource) -> + SQuery = ["SELECT resource_id FROM ",resources_table(VHost)," ", -+ "WHERE resource=\"",binary_to_list(ejabberd_odbc:escape(iolist_to_binary(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, []} -> @@ -4360,7 +3908,7 @@ index 0000000..62f437c + % no such resource in db + {ok, []} -> + IQuery = ["INSERT INTO ",resources_table(VHost)," ", -+ "SET resource=\"",binary_to_list(ejabberd_odbc:escape(iolist_to_binary(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), @@ -4406,25 +3954,21 @@ index 0000000..62f437c + {error, Reason}. diff --git a/src/mod_logdb_mysql5.erl b/src/mod_logdb_mysql5.erl new file mode 100644 -index 0000000..d1f399f +index 0000000000..c05ab958e2 --- /dev/null +++ b/src/mod_logdb_mysql5.erl -@@ -0,0 +1,983 @@ +@@ -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: mod_logdb_mysql5.erl 1360 2009-07-30 06:00:14Z malik $ -+%%% Url : http://www.dp.uz.gov.ua/o.palij/mod_logdb/ ++%%% Url : https://paleg.github.io/mod_logdb/ +%%%---------------------------------------------------------------------- + +-module(mod_logdb_mysql5). +-author('o.palij@gmail.com'). + +-include("mod_logdb.hrl"). -+-include("ejabberd.hrl"). -+-include("jlib.hrl"). +-include("logger.hrl"). + +-behaviour(gen_logdb). @@ -4778,11 +4322,11 @@ index 0000000..d1f399f + "'", 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_odbc:escape(Msg#msg.peer_resource) ), "',", ++ "'", binary_to_list( ejabberd_sql:escape(Msg#msg.peer_resource) ), "',", + "'", atom_to_list(Msg#msg.direction), "',", + "'", binary_to_list(Msg#msg.type), "',", -+ "'", binary_to_list( ejabberd_odbc:escape(Msg#msg.subject) ), "',", -+ "'", binary_to_list( ejabberd_odbc:escape(Msg#msg.body) ), "',", ++ "'", 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 @@ -4898,13 +4442,13 @@ index 0000000..d1f399f +get_dates_int(DBRef, VHost) -> + case sql_query_internal(DBRef, ["SHOW TABLES"]) of + {data, Tables} -> ++ Reg = "^" ++ lists:sublist(prefix(),2,length(prefix())) ++ ".*" ++ escape_vhost(VHost), + lists:foldl(fun([Table], Dates) -> -+ Reg = lists:sublist(prefix(),2,length(prefix())) ++ ".*" ++ escape_vhost(VHost), + case re:run(Table, Reg) of -+ {match, [{1, _}]} -> -+ case re:run(Table,"[0-9]+-[0-9]+-[0-9]+") of ++ {match, _} -> ++ case re:run(Table, "[0-9]+-[0-9]+-[0-9]+") of + {match, [{S, E}]} -> -+ lists:append(Dates, [lists:sublist(Table,S,E)]); ++ lists:append(Dates, [lists:sublist(Table, S+1, E)]); + nomatch -> + Dates + end; @@ -5395,28 +4939,24 @@ index 0000000..d1f399f +END;", [logmessage_name(VHost),UName,UName,UName,UName,SName,SName,RName,RName,UName,UName,SName,RName,StName,StName]). diff --git a/src/mod_logdb_pgsql.erl b/src/mod_logdb_pgsql.erl new file mode 100644 -index 0000000..3c2ae95 +index 0000000000..202c6ed4a8 --- /dev/null +++ b/src/mod_logdb_pgsql.erl -@@ -0,0 +1,1108 @@ +@@ -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: mod_logdb_pgsql.erl 1360 2009-07-30 06:00:14Z malik $ -+%%% Url : http://www.dp.uz.gov.ua/o.palij/mod_logdb/ ++%%% Url : https://paleg.github.io/mod_logdb/ +%%%---------------------------------------------------------------------- + +-module(mod_logdb_pgsql). +-author('o.palij@gmail.com'). + +-include("mod_logdb.hrl"). -+-include("ejabberd.hrl"). -+-include("jlib.hrl"). +-include("logger.hrl"). + +-behaviour(gen_logdb). @@ -5564,11 +5104,11 @@ index 0000000..3c2ae95 + "'", 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_odbc:escape(Msg#msg.peer_resource) ), "',", ++ "'", binary_to_list( ejabberd_sql:escape(Msg#msg.peer_resource) ), "',", + "'", atom_to_list(Msg#msg.direction), "',", + "'", binary_to_list(Msg#msg.type), "',", -+ "'", binary_to_list( ejabberd_odbc:escape(Msg#msg.subject) ), "',", -+ "'", binary_to_list( ejabberd_odbc:escape(Msg#msg.body) ), "',", ++ "'", 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 @@ -5900,7 +5440,7 @@ index 0000000..3c2ae95 + lists:foldl(fun({_Schema, Table, _Type, _Owner}, Dates) -> + case re:run(Table,"[0-9]+-[0-9]+-[0-9]+") of + {match, [{S, E}]} -> -+ lists:append(Dates, [lists:sublist(Table,S,E)]); ++ lists:append(Dates, [lists:sublist(Table, S+1, E)]); + nomatch -> + Dates + end @@ -6507,37 +6047,20 @@ index 0000000..3c2ae95 +get_result(Rez) -> + {error, undefined, Rez}. + -diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl -index b1c5c92..df99681 100644 ---- a/src/mod_muc_room.erl -+++ b/src/mod_muc_room.erl -@@ -743,6 +743,12 @@ handle_sync_event({change_config, Config}, _From, - handle_sync_event({change_state, NewStateData}, _From, - StateName, _StateData) -> - {reply, {ok, NewStateData}, StateName, NewStateData}; -+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}. diff --git a/src/mod_roster.erl b/src/mod_roster.erl -index 7415aa3..f2a69f9 100644 +index 426589319c..6b51d3c381 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl -@@ -60,6 +60,8 @@ - - -include("ejabberd_web_admin.hrl"). +@@ -65,6 +65,8 @@ + -define(ROSTER_ITEM_CACHE, roster_item_cache). + -define(ROSTER_VERSION_CACHE, roster_version_cache). +-include("mod_logdb.hrl"). + + -type c2s_state() :: ejabberd_c2s:state(). -export_type([subscription/0]). - start(Host, Opts) -> -@@ -1358,6 +1360,14 @@ user_roster(User, Server, Query, Lang) -> +@@ -943,6 +945,14 @@ user_roster(User, Server, Query, Lang) -> Query), Items = get_roster(LUser, LServer), SItems = lists:sort(Items), @@ -6550,17 +6073,17 @@ index 7415aa3..f2a69f9 100644 + end, + FItems = case SItems of - [] -> [?CT(<<"None">>)]; + [] -> [?CT(?T("None"))]; _ -> -@@ -1415,7 +1425,33 @@ user_roster(User, Server, Query, Lang) -> +@@ -1000,7 +1010,33 @@ user_roster(User, Server, Query, Lang) -> [?INPUTT(<<"submit">>, <<"remove", (ejabberd_web_admin:term_to_id(R#roster.jid))/binary>>, -- <<"Remove">>)])]) -+ <<"Remove">>)]), -+ case gen_mod:is_loaded(Server, mod_logdb) of +- ?T("Remove"))])]) ++ ?T("Remove"))]), ++ case gen_mod:is_loaded(Server, mod_logdb) of + true -> -+ Peer = jlib:jid_to_string(R#roster.jid), ++ Peer = jid:encode(R#roster.jid), + A = lists:member(Peer, Settings#user_settings.dolog_list), + B = lists:member(Peer, Settings#user_settings.donotlog_list), + {Name, Value} = @@ -6577,49 +6100,49 @@ index 7415aa3..f2a69f9 100644 + + ?XAE(<<"td">>, [{<<"class">>, <<"valign">>}], + [?INPUTT(<<"submit">>, -+ <>, + Value)]); + false -> + ?X([]) -+ end ++ end + ]) end, SItems)))])] end, -@@ -1540,9 +1576,42 @@ user_roster_item_parse_query(User, Server, Items, - = - []}]}}), +@@ -1107,9 +1143,42 @@ user_roster_item_parse_query(User, Server, Items, + sub_els = [#roster_query{ + items = [RosterItem]}]}), throw(submitted); - false -> ok - end - end + false -> + case lists:keysearch( -+ <<"donotlog">>, (ejabberd_web_admin:term_to_id(JID))/binary, 1, Query) of ++ <<"donotlog", (ejabberd_web_admin:term_to_id(JID))/binary>>, 1, Query) of + {value, _} -> -+ Peer = jlib:jid_to_string(JID), ++ Peer = jid:encode(JID), + Settings = mod_logdb:get_user_settings(User, Server), + DNLL = case lists:member(Peer, Settings#user_settings.donotlog_list) of + false -> lists:append(Settings#user_settings.donotlog_list, [Peer]); + true -> Settings#user_settings.donotlog_list + end, -+ DLL = lists:delete(jlib:jid_to_string(JID), Settings#user_settings.dolog_list), ++ DLL = lists:delete(jid:encode(JID), Settings#user_settings.dolog_list), + Sett = Settings#user_settings{donotlog_list=DNLL, dolog_list=DLL}, + % TODO: check returned value + ok = mod_logdb:set_user_settings(User, Server, Sett), + throw(nothing); + false -> + case lists:keysearch( -+ <<"dolog">>, (ejabberd_web_admin:term_to_id(JID))/binary, 1, Query) of ++ <<"dolog", (ejabberd_web_admin:term_to_id(JID))/binary>>, 1, Query) of + {value, _} -> -+ Peer = jlib:jid_to_string(JID), ++ Peer = jid:encode(JID), + Settings = mod_logdb:get_user_settings(User, Server), + DLL = case lists:member(Peer, Settings#user_settings.dolog_list) of + false -> lists:append(Settings#user_settings.dolog_list, [Peer]); + true -> Settings#user_settings.dolog_list + end, -+ DNLL = lists:delete(jlib:jid_to_string(JID), Settings#user_settings.donotlog_list), ++ DNLL = lists:delete(jid:encode(JID), Settings#user_settings.donotlog_list), + Sett = Settings#user_settings{donotlog_list=DNLL, dolog_list=DLL}, + % TODO: check returned value + ok = mod_logdb:set_user_settings(User, Server, Sett), @@ -6633,3 +6156,584 @@ index 7415aa3..f2a69f9 100644 end, Items), nothing. + +From 5043114bc1a74caa522e8a1569b485ccc1808a79 Mon Sep 17 00:00:00 2001 +From: Oleh Palii +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]). ++ + -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. ++ + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % + % 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. ++ + -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. + +--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)}]). + + -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 = tokenize(Node), +@@ -1198,10 +1231,8 @@ recursively_get_local_items(LServer, + + -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). + + 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]. + + 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, + +- [?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)), + +- [?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(), + +- 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 +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),